« Back to History
running_beam_manage.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php /* running_beam_manage.php - Includes slot-aware automatic finish on page load. - Moves old slot duplicates into finish_beam with production summary (if found). - Uses page_require_access / $ctx['pdo'] per project rules. */ require_once __DIR__ . '/modules/auth/page_acl.php'; $ctx = page_require_access('running_beam_manage'); require_once __DIR__ . '/modules/activity/activity_logger.php'; $u = $ctx['user'] ?? null; $company_id = (int)($ctx['company_id'] ?? 0); $pdo = $ctx['pdo'] ?? null; $user_id = (int)($u['id'] ?? 0); // minimal helper if (!function_exists('h')) { function h($str) { return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8'); } } // DB fallback if (!$pdo) { require_once __DIR__ . '/core/db.php'; if (!$pdo) die("DB connection failed."); } // CSRF if (empty($_SESSION['csrf_token'])) $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); $csrf = $_SESSION['csrf_token']; /* ---------------- Helper: normalize_slot ------------------ */ function normalize_slot($beam_type) { $s = strtolower(trim((string)$beam_type)); if ($s === '') return ''; if (in_array($s, ['primary','p','pri','pr'])) return 'primary'; if (in_array($s, ['secondary','sec','s','second','secnd'])) return 'secondary'; return $s; } /* ---------------- Helper: calculate_production_for_beam ------------------ Attempts to aggregate production_entry for given machine between mounted_at and finished_at. Returns array: [total, from_date, to_date, count, warping_date, quality_id, warper_id, taka_no, extra_mtr_sum, threads_count] */ function calculate_production_for_beam($machine_no, $mounted_at, $finished_at, $pdo, $company_id = null) { $from = null; if (!empty($mounted_at)) $from = date('Y-m-d', strtotime($mounted_at)); $to = date('Y-m-d', strtotime($finished_at)); $params = []; $sql = "SELECT entry_date, meter_total, taka_no, quality_id, created_by, extra_mtr FROM production_entry WHERE 1=1 "; $machine_is_numeric = is_numeric($machine_no); if ($machine_is_numeric) { $sql .= " AND (machine_id = :m OR machine_no = :m_str)"; $params[':m'] = (int)$machine_no; $params[':m_str'] = (string)$machine_no; } else { $sql .= " AND (machine_no = :m_str OR machine_no LIKE :m_like)"; $params[':m_str'] = (string)$machine_no; $params[':m_like'] = "%".$machine_no."%"; } if ($from !== null) { $sql .= " AND DATE(entry_date) >= :from_date"; $params[':from_date'] = $from; } if ($to !== null) { $sql .= " AND DATE(entry_date) <= :to_date"; $params[':to_date'] = $to; } // Optional: filter by company_id column if present in production_entry (uncomment if applicable) // $sql .= " AND company_id = :cid"; $params[':cid'] = $company_id; $sql .= " ORDER BY entry_date ASC, id ASC"; try { $stmt = $pdo->prepare($sql); $stmt->execute($params); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (Exception $e) { error_log("calculate_production_for_beam query failed: ".$e->getMessage()); return [0.0, $from, $to, 0, null, null, null, null, 0.0, null]; } $total = 0.0; $count = 0; $first_date = null; $last_date = null; $warping_date = null; $quality_id = null; $warper_id = null; $taka_no = null; $extra_mtr_sum = 0.0; $threads_count = null; foreach ($rows as $r) { $m = (float)($r['meter_total'] ?? 0); $total += $m; $count++; $d = isset($r['entry_date']) ? date('Y-m-d', strtotime($r['entry_date'])) : null; if (!$first_date && $d) $first_date = $d; if ($d) $last_date = $d; if (!$quality_id && isset($r['quality_id'])) $quality_id = $r['quality_id']; if (!$warper_id && isset($r['created_by'])) $warper_id = $r['created_by']; if (!$taka_no && isset($r['taka_no'])) $taka_no = $r['taka_no']; if (isset($r['extra_mtr'])) $extra_mtr_sum += (float)$r['extra_mtr']; } return [$total, $first_date, $last_date, $count, $warping_date, $quality_id, $warper_id, $taka_no, $extra_mtr_sum, $threads_count]; } /* ---------------- Helper: move_beam_to_finish ------------------ Moves a single running_beam row into finish_beam with production summary. Uses transaction outside (caller may wrap multiple moves). */ function move_beam_to_finish($pdo, $company_id, $r, $user_id = 0) { try { // compute production summary between source_entry_date and now $now = date('Y-m-d H:i:s'); list($prod_total, $from_date, $to_date, $countRows, $warping_date, $quality_id, $warper_id, $taka_no, $extra_mtr_sum, $threads_count) = calculate_production_for_beam($r['machine_no'] ?? '', $r['source_entry_date'] ?? null, $now, $pdo, $company_id); $insSql = "INSERT INTO finish_beam (source_running_id, company_id, beam_no, beam_type, pasaria_id, pissing_entry_id, machine_no, source_entry_date, created_at, finished_at, meter_from_date, meter_to_date, production_meter_total, production_entries_count, warping_date, quality_id, warper_id, taka_no, extra_mtr, threads_count, remark) VALUES (:source_running_id, :company_id, :beam_no, :beam_type, :pasaria_id, :pissing_entry_id, :machine_no, :source_entry_date, :created_at, :finished_at, :meter_from_date, :meter_to_date, :production_meter_total, :production_entries_count, :warping_date, :quality_id, :warper_id, :taka_no, :extra_mtr, :threads_count, :remark) "; $ins = $pdo->prepare($insSql); $ins->execute([ ':source_running_id' => $r['id'], ':company_id' => $company_id, ':beam_no' => $r['beam_no'] ?? null, ':beam_type' => $r['beam_type'] ?? null, ':pasaria_id' => $r['pasaria_id'] ?? null, ':pissing_entry_id' => $r['pissing_entry_id'] ?? null, ':machine_no' => $r['machine_no'] ?? null, ':source_entry_date' => $r['source_entry_date'] ?? null, ':created_at' => $r['created_at'] ?? null, ':finished_at' => $now, ':meter_from_date' => $from_date, ':meter_to_date' => $to_date, ':production_meter_total' => $prod_total, ':production_entries_count' => $countRows, ':warping_date' => $warping_date, ':quality_id' => $quality_id, ':warper_id' => $warper_id, ':taka_no' => $taka_no, ':extra_mtr' => $extra_mtr_sum, ':threads_count' => $threads_count, ':remark' => $r['remark'] ?? null ]); activity_log([ 'company_id' => $company_id, 'user_id' => (int)$user_id, 'module' => 'beam', 'action_name' => 'finish', 'entity_type' => 'running_beam', 'entity_id' => (int)$r['id'], 'remarks' => 'Moved running beam to finish' ]); } catch (Exception $e) { // log insert error and continue with delete fallback (we try to delete source anyway to avoid duplicate stuck rows) error_log("move_beam_to_finish: INSERT error for running_beam id {$r['id']}: " . $e->getMessage()); } // delete source running_beam row try { $del = $pdo->prepare("DELETE FROM running_beam WHERE id = :id AND company_id = :cid LIMIT 1"); $del->execute([':id' => $r['id'], ':cid' => $company_id]); } catch (Exception $e) { error_log("move_beam_to_finish: DELETE error for running_beam id {$r['id']}: " . $e->getMessage()); } } /* ---------------- AUTOMATIC CLEANUP (slot-aware) ------------------ Runs on page load. Keeps newest per slot (primary/secondary) and ensures <=2 per machine. */ try { $pdo->beginTransaction(); $stmt = $pdo->prepare(" SELECT id, beam_no, beam_type, pasaria_id, pissing_entry_id, machine_no, source_entry_date, beam_status, remark, created_at FROM running_beam WHERE company_id = :cid ORDER BY machine_no ASC, created_at DESC, id DESC "); $stmt->execute([':cid' => $company_id]); $all = $stmt->fetchAll(PDO::FETCH_ASSOC); // group by machine_no $byMachine = []; foreach ($all as $r) { $m = (string)($r['machine_no'] ?? ''); if (!isset($byMachine[$m])) $byMachine[$m] = []; $byMachine[$m][] = $r; } foreach ($byMachine as $machineNo => $rows) { // slot-wise: keep newest per normalized slot, older of same slot -> toFinish $slotNewest = []; $toFinish = []; foreach ($rows as $r) { $slot = normalize_slot($r['beam_type']); if ($slot === '') $slot = 'slot_empty_'.$r['id']; if (!isset($slotNewest[$slot])) { $slotNewest[$slot] = $r; } else { $toFinish[] = $r; } } // remaining newest-per-slot $remaining = array_values($slotNewest); usort($remaining, function($a,$b){ $ta = strtotime($a['created_at'] ?? '1970-01-01 00:00:00'); $tb = strtotime($b['created_at'] ?? '1970-01-01 00:00:00'); return $tb <=> $ta; // newest first }); // enforce max 2 if (count($remaining) > 2) { $excess = count($remaining) - 2; for ($i = 0; $i < $excess; $i++) { $idx = count($remaining) - 1 - $i; if (isset($remaining[$idx])) $toFinish[] = $remaining[$idx]; } } // perform move for all toFinish rows foreach ($toFinish as $r) { move_beam_to_finish($pdo, $company_id, $r, $user_id); } } $pdo->commit(); } catch (Exception $e) { try { $pdo->rollBack(); } catch (Exception $_) {} error_log("running_beam_manage cleanup failed: ".$e->getMessage()); // continue rendering page } /* ---------------- DELETE ------------------ */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delete') { if (!hash_equals($csrf, $_POST['csrf_token'] ?? '')) { die("Invalid CSRF"); } $id = (int)($_POST['id'] ?? 0); if ($id > 0) { $stmt = $pdo->prepare("DELETE FROM running_beam WHERE id = :id AND company_id = :cid LIMIT 1"); $stmt->execute([':id' => $id, ':cid' => $company_id]); activity_log([ 'company_id' => $company_id, 'user_id' => $user_id, 'module' => 'beam', 'action_name' => 'delete', 'entity_type' => 'running_beam', 'entity_id' => $id, 'remarks' => 'Deleted running beam' ]); } header("Location: running_beam_manage.php?deleted=1"); exit; } /* ---------------- UPDATE ------------------ */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'update') { if (!hash_equals($csrf, $_POST['csrf_token'] ?? '')) { die("Invalid CSRF"); } $id = (int)($_POST['id'] ?? 0); if ($id > 0) { $beam_no = trim($_POST['beam_no'] ?? ''); $beam_type = trim($_POST['beam_type'] ?? ''); $pasaria_id = ($_POST['pasaria_id'] ?? '') === '' ? null : (int)$_POST['pasaria_id']; $pissing_entry_id = ($_POST['pissing_entry_id'] ?? '') === '' ? null : (int)$_POST['pissing_entry_id']; $machine_no = trim($_POST['machine_no'] ?? ''); $remark = trim($_POST['remark'] ?? ''); $source_entry_date = $_POST['source_entry_date'] ?? null; if ($source_entry_date) { $source_entry_date = str_replace('T', ' ', $source_entry_date); } else { $source_entry_date = null; } $stmt = $pdo->prepare(" UPDATE running_beam SET beam_no = :beam_no, beam_type = :beam_type, pasaria_id = :pasaria_id, pissing_entry_id = :pissing_entry_id, machine_no = :machine_no, source_entry_date = :source_entry_date, remark = :remark WHERE id = :id AND company_id = :cid LIMIT 1 "); $stmt->execute([ ':beam_no' => $beam_no, ':beam_type' => $beam_type, ':pasaria_id' => $pasaria_id, ':pissing_entry_id' => $pissing_entry_id, ':machine_no' => $machine_no, ':source_entry_date' => $source_entry_date, ':remark' => $remark, ':id' => $id, ':cid' => $company_id ]); activity_log([ 'company_id' => $company_id, 'user_id' => $user_id, 'module' => 'beam', 'action_name' => 'edit', 'entity_type' => 'running_beam', 'entity_id' => $id, 'remarks' => 'Updated running beam' ]); } header("Location: running_beam_manage.php?updated=1"); exit; } /* ---------------- FETCH LIST ------------------ */ $sql = " SELECT id, beam_no, beam_type, pasaria_id, pissing_entry_id, machine_no, source_entry_date, beam_status, remark, created_at FROM running_beam WHERE company_id = :cid ORDER BY machine_no ASC, id ASC "; $stmt = $pdo->prepare($sql); $stmt->execute([':cid' => $company_id]); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); ?> <!DOCTYPE html> <html> <head> <title>Running Beam Manage</title> <meta charset="utf-8"> <style> table { border-collapse: collapse; width: 100%; } th, td { padding: 6px 8px; border: 1px solid #ddd; font-family: Arial, sans-serif; font-size: 13px; } th { background:#f5f5f5; text-align:left; } .msg-success { color: green; margin-bottom:8px; } .msg-error { color: red; margin-bottom:8px; } button { padding:6px 10px; cursor:pointer; } .modal-backdrop { display:none; position:fixed; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,0.6); padding-top:60px; z-index:9999; } .modal { background:#fff; width:420px; margin:auto; padding:18px; border-radius:6px; } label { display:block; margin-top:8px; font-weight:600; } input[type="text"], input[type="number"], input[type="datetime-local"], select { width:100%; padding:6px; box-sizing:border-box; margin-top:4px; } </style> </head> <body> <?php // include header as per project template // header.php is responsible for auth checks and includes main CSS/JS $headerPath = __DIR__ . '/erp/partials/header.php'; if (file_exists($headerPath)) require_once $headerPath; ?> <div style="padding:20px;"> <h2>Running Beam — Manage</h2> <?php if (!empty($_GET['updated'])): ?> <div class="msg-success">Record updated successfully.</div> <?php endif; ?> <?php if (!empty($_GET['deleted'])): ?> <div class="msg-error">Record deleted.</div> <?php endif; ?> <table> <thead> <tr> <th>ID</th> <th>Beam No</th> <th>Type</th> <th>Pasaria</th> <th>Pissing Entry</th> <th>Machine No</th> <th>Source Date</th> <th>Status</th> <th>Remark</th> <th>Created</th> <th>Actions</th> </tr> </thead> <tbody> <?php if (!$rows): ?> <tr><td colspan="11">No records found.</td></tr> <?php else: ?> <?php foreach ($rows as $r): ?> <tr> <td><?= h($r['id']) ?></td> <td><?= h($r['beam_no']) ?></td> <td><?= h($r['beam_type']) ?></td> <td><?= h($r['pasaria_id']) ?></td> <td><?= h($r['pissing_entry_id']) ?></td> <td><?= h($r['machine_no']) ?></td> <td><?= h($r['source_entry_date']) ?></td> <td><?= h($r['beam_status']) ?></td> <td><?= h($r['remark']) ?></td> <td><?= h($r['created_at']) ?></td> <td> <button onclick='editRow(<?= json_encode($r, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP) ?>)'>Edit</button> <form method="post" style="display:inline" onsubmit="return confirm('Delete this record?')"> <input type="hidden" name="csrf_token" value="<?= $csrf ?>"> <input type="hidden" name="action" value="delete"> <input type="hidden" name="id" value="<?= h($r['id']) ?>"> <button type="submit">Delete</button> </form> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> </table> </div> <!-- ================= EDIT MODAL ================= --> <div id="editModal" class="modal-backdrop"> <div class="modal"> <h3>Edit Running Beam</h3> <form method="post"> <input type="hidden" name="csrf_token" value="<?= $csrf ?>"> <input type="hidden" name="action" value="update"> <input type="hidden" name="id" id="id_field"> <label>Beam No</label> <input type="text" id="beam_no" name="beam_no"> <label>Beam Type</label> <select id="beam_type" name="beam_type"> <option value="Primary">Primary</option> <option value="Secondary">Secondary</option> <option value="cut">cut</option> <option value="other">other</option> </select> <label>Pasaria ID</label> <input type="number" id="pasaria_id" name="pasaria_id"> <label>Pissing Entry ID</label> <input type="number" id="pissing_entry_id" name="pissing_entry_id"> <label>Machine No</label> <input type="text" id="machine_no" name="machine_no"> <label>Source Date</label> <input type="datetime-local" id="source_entry_date" name="source_entry_date"> <label>Remark</label> <input type="text" id="remark" name="remark"> <div style="margin-top:12px; text-align:right;"> <button type="submit">Save</button> <button type="button" onclick="closeModal()">Cancel</button> </div> </form> </div> </div> <?php // include footer if available $footerPath = __DIR__ . '/erp/partials/footer.php'; if (file_exists($footerPath)) require_once $footerPath; ?> <script> function editRow(r) { document.getElementById('id_field').value = r.id ?? ""; document.getElementById('beam_no').value = r.beam_no ?? ""; document.getElementById('beam_type').value = r.beam_type ?? ""; document.getElementById('pasaria_id').value = r.pasaria_id ?? ""; document.getElementById('pissing_entry_id').value = r.pissing_entry_id ?? ""; document.getElementById('machine_no').value = r.machine_no ?? ""; document.getElementById('remark').value = r.remark ?? ""; if (r.source_entry_date) { var v = r.source_entry_date.replace(' ', 'T'); if (v.length > 16) v = v.substring(0,16); document.getElementById('source_entry_date').value = v; } else { document.getElementById('source_entry_date').value = ""; } document.getElementById('editModal').style.display = "block"; } function closeModal() { document.getElementById('editModal').style.display = "none"; } // close modal when clicking outside document.getElementById('editModal').addEventListener('click', function(e){ if (e.target === this) closeModal(); }); </script> </body> </html>