« Back to History
taka_edit_bulk.php
|
20260721_154033.php
Initial Bulk Import
Copy Code
<?php /* ---------------- BOOTSTRAP / AUTH ---------------- */ require_once __DIR__ . '/modules/auth/page_acl.php'; $ctx = page_require_access('taka_edit_bulk'); $pdo = $ctx['pdo'] ?? null; $COMPANY_ID = (int)($ctx['company_id'] ?? 0); $user_id = (int)(($ctx['user']['id'] ?? 0)); if (!$pdo) require_once __DIR__ . '/core/db.php'; require_once __DIR__ . '/helpers/activity_helper.php'; /* ---------------- JSON HELPERS ---------------- */ function jok($a=[]){ header('Content-Type: application/json'); echo json_encode(['ok'=>true]+$a); exit; } function jerr($m,$c=400){ http_response_code($c); echo json_encode(['ok'=>false,'msg'=>$m]); exit; } function normalize_ymd(string $month, string $period): array { $from = $month . ($period === 'H1' ? '-01' : '-16'); $to = $period === 'H1' ? $month . '-15' : date('Y-m-t', strtotime($month . '-01')); return [$from, $to]; } function expand_production_rows(array $rows, int $karigarFilter = 0): array { $expanded = []; $karigars = []; foreach ($rows as $row) { $lines = json_decode((string)($row['lines_json'] ?? '[]'), true); if (!is_array($lines)) { $lines = []; } if (!$lines) { $entry = [ 'id' => (int)$row['id'], 'entry_date' => (string)$row['entry_date'], 'machine_id' => (int)$row['machine_id'], 'quality_id' => (int)$row['quality_id'], 'taka_no' => (int)$row['taka_no'], 'meter_total' => (float)$row['meter_total'], 'karigar_id' => 0, 'karigar_name' => 'No Karigar', 'karigar_meter' => (float)$row['meter_total'], ]; if ($karigarFilter === 0) { $expanded[] = $entry; } continue; } foreach ($lines as $line) { $karigarId = (int)($line['karigar_id'] ?? 0); $karigarName = trim((string)($line['karigar_name'] ?? '')); if ($karigarName === '') { $karigarName = $karigarId > 0 ? ('Karigar ' . $karigarId) : 'Unknown Karigar'; } $karigars[$karigarId] = $karigarName; if ($karigarFilter > 0 && $karigarId !== $karigarFilter) { continue; } $expanded[] = [ 'id' => (int)$row['id'], 'entry_date' => (string)$row['entry_date'], 'machine_id' => (int)$row['machine_id'], 'quality_id' => (int)$row['quality_id'], 'taka_no' => (int)$row['taka_no'], 'meter_total' => (float)$row['meter_total'], 'karigar_id' => $karigarId, 'karigar_name' => $karigarName, 'karigar_meter' => (float)($line['meter'] ?? 0), ]; } } usort($expanded, static function ($a, $b) { $nameCmp = strnatcasecmp((string)$a['karigar_name'], (string)$b['karigar_name']); if ($nameCmp !== 0) { return $nameCmp; } $dateCmp = strcmp((string)$a['entry_date'], (string)$b['entry_date']); if ($dateCmp !== 0) { return $dateCmp; } return ((int)$a['id']) <=> ((int)$b['id']); }); asort($karigars, SORT_NATURAL | SORT_FLAG_CASE); return [$expanded, $karigars]; } /* ---------------- AJAX ---------------- */ $act = $_GET['act'] ?? ''; if ($act) { switch ($act) { case 'khata_list': $st=$pdo->prepare("SELECT id,code,machine_from,machine_to FROM khatas WHERE company_id=? AND is_active=1 ORDER BY code"); $st->execute([$COMPANY_ID]); jok(['khatas'=>$st->fetchAll(PDO::FETCH_ASSOC)]); case 'quality_list': $st=$pdo->prepare("SELECT quality_id AS id, quality_name AS name FROM quality_rates WHERE company_id=? ORDER BY quality_name"); $st->execute([$COMPANY_ID]); jok(['qualities'=>$st->fetchAll(PDO::FETCH_ASSOC)]); case 'fetch_rows': $machine=(int)($_GET['machine_id']??0); $month=trim((string)($_GET['month']??'')); $period=strtoupper(trim((string)($_GET['period']??''))); $karigarId=(int)($_GET['karigar_id']??0); if(!$machine||!preg_match('/^\d{4}-\d{2}$/',$month)||!in_array($period,['H1','H2'],true)) jerr('Invalid filter'); [$from, $to] = normalize_ymd($month, $period); $st=$pdo->prepare(" SELECT id,entry_date,machine_id,quality_id,taka_no,meter_total , lines_json FROM production_entry WHERE company_id=? AND machine_id=? AND entry_date BETWEEN ? AND ? ORDER BY entry_date,id "); $st->execute([$COMPANY_ID,$machine,$from,$to]); [$expandedRows, $karigars] = expand_production_rows($st->fetchAll(PDO::FETCH_ASSOC), $karigarId); $karigarOptions = []; foreach ($karigars as $id => $name) { if ((int)$id <= 0) { continue; } $karigarOptions[] = ['id' => (int)$id, 'name' => (string)$name]; } jok(['rows'=>$expandedRows,'karigars'=>$karigarOptions]); case 'delete_karigar': $machine=(int)($_POST['machine_id']??0); $month=trim((string)($_POST['month']??'')); $period=strtoupper(trim((string)($_POST['period']??''))); $karigarId=(int)($_POST['karigar_id']??0); if(!$machine||!$karigarId||!preg_match('/^\d{4}-\d{2}$/',$month)||!in_array($period,['H1','H2'],true)) jerr('Invalid filter'); [$from, $to] = normalize_ymd($month, $period); $st = $pdo->prepare(" SELECT id, lines_json FROM production_entry WHERE company_id=? AND machine_id=? AND entry_date BETWEEN ? AND ? ORDER BY id "); $st->execute([$COMPANY_ID, $machine, $from, $to]); $updateStmt = $pdo->prepare("UPDATE production_entry SET lines_json=?, meter_total=?, updated_at=NOW() WHERE id=? AND company_id=?"); $deleteStmt = $pdo->prepare("DELETE FROM production_entry WHERE id=? AND company_id=?"); $affectedRows = 0; $deletedRows = 0; while ($row = $st->fetch(PDO::FETCH_ASSOC)) { $lines = json_decode((string)($row['lines_json'] ?? '[]'), true); if (!is_array($lines) || !$lines) { continue; } $remaining = []; $found = false; $meterTotal = 0.0; foreach ($lines as $line) { if ((int)($line['karigar_id'] ?? 0) === $karigarId) { $found = true; continue; } $remaining[] = $line; $meterTotal += (float)($line['meter'] ?? 0); } if (!$found) { continue; } $affectedRows++; if (!$remaining) { $deleteStmt->execute([(int)$row['id'], $COMPANY_ID]); $deletedRows += $deleteStmt->rowCount(); continue; } $updateStmt->execute([ json_encode(array_values($remaining), JSON_UNESCAPED_UNICODE), $meterTotal, (int)$row['id'], $COMPANY_ID, ]); } log_activity([ 'activity_type' => 'delete', 'module_name' => 'production_entry_bulk', 'action_name' => 'delete_karigar', 'reference_id' => 0, 'activity_note' => 'Deleted karigar ' . $karigarId . ' from production entries', ]); jok(['affected'=>$affectedRows,'deleted'=>$deletedRows]); case 'bulk_update_machine': $ids=$_POST['ids']??[]; $machine_new=(int)($_POST['machine_id']??0); if(!$ids||!$machine_new) jerr('Invalid'); $ids=array_map('intval',$ids); $in=implode(',',array_fill(0,count($ids),'?')); $params=array_merge([$machine_new],$ids,[$COMPANY_ID]); $st=$pdo->prepare("UPDATE production_entry SET machine_id=? WHERE id IN ($in) AND company_id=?"); $st->execute($params); log_activity([ 'activity_type' => 'update', 'module_name' => 'production_entry_bulk', 'action_name' => 'bulk_update_machine', 'reference_id' => 0, 'activity_note' => 'Bulk machine update, ids: ' . implode(',', $ids), ]); jok(['updated'=>$st->rowCount()]); case 'bulk_update_quality': $ids=$_POST['ids']??[]; $quality_new=(int)($_POST['quality_id']??0); if(!$ids||!$quality_new) jerr('Invalid'); $ids=array_map('intval',$ids); $in=implode(',',array_fill(0,count($ids),'?')); $params=array_merge([$quality_new],$ids,[$COMPANY_ID]); $st=$pdo->prepare("UPDATE production_entry SET quality_id=? WHERE id IN ($in) AND company_id=?"); $st->execute($params); log_activity([ 'activity_type' => 'update', 'module_name' => 'production_entry_bulk', 'action_name' => 'bulk_update_quality', 'reference_id' => 0, 'activity_note' => 'Bulk quality update, ids: ' . implode(',', $ids), ]); jok(['updated'=>$st->rowCount()]); case 'bulk_delete': $ids=$_POST['ids']??[]; if(!$ids) jerr('Invalid'); $ids=array_values(array_filter(array_map('intval',$ids))); if(!$ids) jerr('Invalid'); $in=implode(',',array_fill(0,count($ids),'?')); $params=array_merge($ids,[$COMPANY_ID]); $st=$pdo->prepare("DELETE FROM production_entry WHERE id IN ($in) AND company_id=?"); $st->execute($params); log_activity([ 'activity_type' => 'delete', 'module_name' => 'production_entry_bulk', 'action_name' => 'bulk_delete', 'reference_id' => 0, 'activity_note' => 'Bulk delete production entries, ids: ' . implode(',', $ids), ]); jok(['deleted'=>$st->rowCount()]); } exit; } /* ---------------- UI ---------------- */ require_once __DIR__.'/partials/header.php'; ?> <div class="container py-4"> <div class="card shadow-sm"> <div class="card-header bg-primary text-white fw-semibold"> Production Entry – Bulk Edit </div> <div class="card-body"> <!-- FILTER --> <div class="row g-3 mb-4"> <div class="col-md-3"> <label class="form-label">Khata</label> <select id="khata" class="form-select"></select> </div> <div class="col-md-2"> <label class="form-label">Machine</label> <select id="machine" class="form-select"> <option value="">Select Machine</option> </select> </div> <div class="col-md-2"> <label class="form-label">Month</label> <input type="month" id="month" class="form-control" value="<?= date('Y-m') ?>"> </div> <div class="col-md-2"> <label class="form-label">Period</label> <select id="period" class="form-select"> <option value="H1">H1</option> <option value="H2">H2</option> </select> </div> <div class="col-md-3"> <label class="form-label">Karigar</label> <select id="karigar" class="form-select"> <option value="">All Karigar</option> </select> </div> <div class="col-md-2 d-flex align-items-end"> <button class="btn btn-primary w-100" id="apply"> Apply </button> </div> </div> <!-- TABLE --> <div class="table-responsive"> <table class="table table-bordered table-striped align-middle"> <thead class="table-light"> <tr> <th><input type="checkbox" id="chkAll"></th> <th>ID</th> <th>Date</th> <th>Machine</th> <th>Quality</th> <th>Taka</th> <th>Meter</th> </tr> </thead> <tbody id="tbody"></tbody> </table> </div> <!-- BULK ACTIONS --> <div class="row g-4 mt-4"> <div class="col-md-6"> <div class="card border-primary"> <div class="card-body"> <h6 class="fw-bold mb-3">Change Machine</h6> <div class="row g-2"> <div class="col-md-8"> <select id="bulk_machine" class="form-select"></select> </div> <div class="col-md-4"> <button class="btn btn-success w-100" id="btnMove"> Update </button> </div> </div> </div> </div> </div> <div class="col-md-6"> <div class="card border-warning"> <div class="card-body"> <h6 class="fw-bold mb-3">Change Quality</h6> <div class="row g-2"> <div class="col-md-8"> <select id="bulk_quality" class="form-select"></select> </div> <div class="col-md-4"> <button class="btn btn-warning w-100" id="btnQuality"> Update </button> </div> </div> </div> </div> </div> <div class="col-md-6"> <div class="card border-danger"> <div class="card-body"> <h6 class="fw-bold mb-3 text-danger">Delete Selected Rows</h6> <p class="text-muted small mb-3">Selected production entries permanently delete ho jayengi.</p> <button class="btn btn-danger w-100" id="btnDelete"> Delete </button> </div> </div> </div> <div class="col-md-6"> <div class="card border-danger"> <div class="card-body"> <h6 class="fw-bold mb-3 text-danger">Delete Full Karigar</h6> <p class="text-muted small mb-3">Current filter ke andar selected karigar ki sari line entries remove ho jayengi.</p> <button class="btn btn-outline-danger w-100" id="btnDeleteKarigar"> Delete Selected Karigar </button> </div> </div> </div> </div> </div> </div> </div> <script> async function j(u,o){ const r=await fetch(u,o); const d=await r.json(); if(!d.ok) throw d.msg; return d; } j('?act=khata_list').then(r=>{ khata.innerHTML='<option value="">Select Khata</option>'; r.khatas.forEach(k=>{ khata.innerHTML+=`<option data-from="${k.machine_from}" data-to="${k.machine_to}">${k.code}</option>`; }); }); j('?act=quality_list').then(r=>{ bulk_quality.innerHTML='<option value="">Select Quality</option>'; r.qualities.forEach(q=>{ bulk_quality.innerHTML+=`<option value="${q.id}">${q.name}</option>`; }); }); function fillKarigars(items, selectedValue=''){ const previous = selectedValue || karigar.value || ''; karigar.innerHTML='<option value="">All Karigar</option>'; items.forEach(k=>{ karigar.innerHTML += `<option value="${k.id}">${k.name}</option>`; }); if ([...karigar.options].some(o => o.value === String(previous))) { karigar.value = String(previous); } } khata.addEventListener('change',function(){ machine.innerHTML='<option value="">Select Machine</option>'; const opt=this.options[this.selectedIndex]; if(!opt) return; const from=parseInt(opt.getAttribute('data-from')); const to=parseInt(opt.getAttribute('data-to')); if(!from||!to) return; for(let i=from;i<=to;i++){ machine.innerHTML+=`<option value="${i}">Loom ${String(i).padStart(3,'0')}</option>`; } }); machine.addEventListener('change',()=>{ bulk_machine.innerHTML=''; [...machine.options].forEach(o=>{ if(o.value) bulk_machine.appendChild(o.cloneNode(true)); }); }); apply.onclick=async()=>{ const m=machine.value,mon=month.value,p=period.value,k=karigar.value; if(!m||!mon||!p) return alert('Select filters'); const r=await j(`?act=fetch_rows&machine_id=${m}&month=${encodeURIComponent(mon)}&period=${encodeURIComponent(p)}&karigar_id=${encodeURIComponent(k)}`); fillKarigars(r.karigars || [], k); tbody.innerHTML=''; let currentKarigar=''; r.rows.forEach(x=>{ if (x.karigar_name !== currentKarigar) { currentKarigar = x.karigar_name; tbody.innerHTML += ` <tr class="table-secondary"> <td colspan="8" class="fw-bold">${currentKarigar}</td> </tr>`; } tbody.innerHTML+=` <tr> <td><input type="checkbox" data-id="${x.id}" data-karigar-id="${x.karigar_id || 0}"></td> <td>${x.id}</td> <td>${x.entry_date}</td> <td>Loom ${String(x.machine_id).padStart(3,'0')}</td> <td>${x.quality_id}</td> <td>${x.taka_no}</td> <td>${x.karigar_meter ?? x.meter_total}</td> </tr>`; }); chkAll.checked=false; }; chkAll.onchange=()=>document.querySelectorAll('#tbody input[type=checkbox]') .forEach(c=>c.checked=chkAll.checked); btnMove.onclick=async()=>{ const ids=[...new Set([...tbody.querySelectorAll('input:checked')].map(i=>i.dataset.id))]; const m=bulk_machine.value; if(!ids.length||!m) return alert('Select rows & machine'); const fd=new FormData(); ids.forEach(i=>fd.append('ids[]',i)); fd.append('machine_id',m); const r=await j('?act=bulk_update_machine',{method:'POST',body:fd}); alert('Machine updated: '+r.updated); apply.click(); }; btnQuality.onclick=async()=>{ const ids=[...new Set([...tbody.querySelectorAll('input:checked')].map(i=>i.dataset.id))]; const q=bulk_quality.value; if(!ids.length||!q) return alert('Select rows & quality'); const fd=new FormData(); ids.forEach(i=>fd.append('ids[]',i)); fd.append('quality_id',q); const r=await j('?act=bulk_update_quality',{method:'POST',body:fd}); alert('Quality updated: '+r.updated); apply.click(); }; btnDelete.onclick=async()=>{ const ids=[...new Set([...tbody.querySelectorAll('input:checked')].map(i=>i.dataset.id))]; if(!ids.length) return alert('Select rows'); if(!confirm('Selected rows permanently delete karni hain?')) return; const fd=new FormData(); ids.forEach(i=>fd.append('ids[]',i)); const r=await j('?act=bulk_delete',{method:'POST',body:fd}); alert('Rows deleted: '+r.deleted); chkAll.checked=false; apply.click(); }; btnDeleteKarigar.onclick=async()=>{ const m=machine.value,mon=month.value,p=period.value,k=karigar.value; if(!m||!mon||!p||!k) return alert('Machine, month, period aur karigar select karo'); if(!confirm('Selected karigar ki current filter wali sari entries remove karni hain?')) return; const fd=new FormData(); fd.append('machine_id',m); fd.append('month',mon); fd.append('period',p); fd.append('karigar_id',k); const r=await j('?act=delete_karigar',{method:'POST',body:fd}); alert('Karigar entries updated: '+r.affected+' | full rows deleted: '+r.deleted); chkAll.checked=false; apply.click(); }; </script> <?php require_once __DIR__.'/partials/footer.php'; ?>