« Back to History
zari_machine_stock_entry.php
|
20260721_154034.php
Initial Bulk Import
Copy Code
<?php /* ============================================================================ File: /erp/zari_machine_stock_entry.php Purpose: Zari Machine Batch Entry — final with: - top Material overwrites ALL row material selects immediately - DB weight 0 shown as blank in inputs - Enter moves to next weight input - POST->Redirect->GET so saved values are read back from DB - Prefill/load works per machine + month ============================================================================ */ require __DIR__ . '/modules/auth/auth.php'; require_login(); $u = auth_user(); $COMPANY_ID = (int)($u['company_id'] ?? 0); $USER_ID = (int)($u['id'] ?? 0); $pdo = $GLOBALS['pdo'] ?? null; if (!$pdo) require __DIR__ . '/core/db.php'; if (session_status() !== PHP_SESSION_ACTIVE) session_start(); if (empty($_SESSION['csrf'])) $_SESSION['csrf'] = bin2hex(random_bytes(16)); function csrf_field(){ echo '<input type="hidden" name="csrf" value="'.htmlspecialchars($_SESSION['csrf']).'">'; } function check_csrf(){ if (($_POST['csrf'] ?? '') !== ($_SESSION['csrf'] ?? '')) { http_response_code(400); exit('Bad CSRF'); } } function j($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } function redirect_to($qs = []) { $url = strtok($_SERVER['REQUEST_URI'],'?'); if (!empty($qs)) $url .= '?' . http_build_query($qs); header("Location: $url"); exit; } define('FIXED_PADDLES', 10); /* 8 fixed rows: A/B × S1/S2 × Z/S */ $ROW_TEMPLATES = [ ['side'=>1,'section'=>1,'group'=>7,'pos'=>'Z','label'=>'A - S1 (70) Z'], ['side'=>1,'section'=>1,'group'=>7,'pos'=>'S','label'=>'A - S1 (70) S'], ['side'=>1,'section'=>2,'group'=>6,'pos'=>'Z','label'=>'A - S2 (60) Z'], ['side'=>1,'section'=>2,'group'=>6,'pos'=>'S','label'=>'A - S2 (60) S'], ['side'=>2,'section'=>1,'group'=>7,'pos'=>'Z','label'=>'B - S1 (70) Z'], ['side'=>2,'section'=>1,'group'=>7,'pos'=>'S','label'=>'B - S1 (70) S'], ['side'=>2,'section'=>2,'group'=>6,'pos'=>'Z','label'=>'B - S2 (60) Z'], ['side'=>2,'section'=>2,'group'=>6,'pos'=>'S','label'=>'B - S2 (60) S'], ]; $MATERIAL_NAMES = ['Anmol','Gold','Silver','Copper']; function parse_month_input($v){ $v = trim((string)$v); if ($v === '') return ''; $ts = strtotime($v . '-01'); if ($ts === false) return ''; return date('Y-m-01', $ts); } /* Read GET/POST context */ $selected_machine = isset($_REQUEST['machine_no']) ? (int)$_REQUEST['machine_no'] : 1; if ($selected_machine < 1 || $selected_machine > 7) $selected_machine = 1; $selected_category = $_REQUEST['material_category'] ?? 'yarn'; $selected_entry_month_raw = $_REQUEST['entry_month'] ?? ''; $selected_entry_month = parse_month_input($selected_entry_month_raw); /* Prefill structure (blank defaults) */ $prefill = []; foreach ($ROW_TEMPLATES as $i => $tpl) { $prefill[$i] = ['material_name'=>'','weight_each'=>'','notes'=>'']; } /* Load existing rows for company+machine+month if month provided */ if ($selected_entry_month) { try { $st = $pdo->prepare("SELECT * FROM zari_machine_stock WHERE company_id = :cid AND machine_no = :mno AND entry_month = :em"); $st->execute([':cid'=>$COMPANY_ID, ':mno'=>$selected_machine, ':em'=>$selected_entry_month]); $rows = $st->fetchAll(PDO::FETCH_ASSOC); $map = []; foreach ($rows as $r) { $key = ((int)$r['side']) . '|' . ((int)$r['section_no']) . '|' . strtoupper($r['position']); $map[$key] = $r; } foreach ($ROW_TEMPLATES as $i => $tpl) { $key = $tpl['side'].'|'.$tpl['section'].'|'.$tpl['pos']; if (isset($map[$key])) { $r = $map[$key]; $prefill[$i] = [ 'material_name' => $r['material_name'], 'weight_each' => (string)$r['weight_each'], // keep as string for input value; we'll blank zeros below 'notes' => $r['notes'] ]; } } } catch (Exception $ex) { // ignore DB prefill errors — show blank form } } /* POST handling: save then redirect (PRG pattern) */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_batch'])) { check_csrf(); $machine_no = (int)($_POST['machine_no'] ?? 0); $category = in_array($_POST['material_category'] ?? '', ['yarn','zari','metalic']) ? $_POST['material_category'] : 'yarn'; $entry_month = parse_month_input($_POST['entry_month'] ?? ''); $errors = []; if ($machine_no < 1 || $machine_no > 7) $errors[] = "Invalid machine."; if ($entry_month === '') $errors[] = "Select month."; if (empty($errors)) { $top_material = trim($_POST['material_name_top'] ?? ''); $top_notes = trim($_POST['notes_top'] ?? ''); $saved = 0; foreach ($ROW_TEMPLATES as $i => $tpl) { $mn_key = 'material_name_'.$i; $we_key = 'weight_each_'.$i; // server-side material: prefer POST value if set (even empty), else top material, else existing prefill (if any) // Note: client-side now overwrites all selects live when top material changed, but we still fallback safely. if (isset($_POST[$mn_key])) { $material_name = trim($_POST[$mn_key]); } elseif ($top_material !== '') { $material_name = $top_material; } elseif (!empty($prefill[$i]['material_name'])) { $material_name = $prefill[$i]['material_name']; } else { $material_name = ''; } // weight: prefer POST non-empty, else DB prefill non-zero, else 0 if (isset($_POST[$we_key]) && $_POST[$we_key] !== '') { $weight_each = (float)$_POST[$we_key]; } elseif (isset($prefill[$i]['weight_each']) && $prefill[$i]['weight_each'] !== '' && (float)$prefill[$i]['weight_each'] != 0.0) { $weight_each = (float)$prefill[$i]['weight_each']; } else { $weight_each = 0.0; } // skip totally empty rows if ($material_name === '' && $weight_each <= 0) continue; if ($material_name !== '' && !in_array($material_name, $MATERIAL_NAMES)) { $errors[] = "Invalid material in row ".($i+1); continue; } $quantity = FIXED_PADDLES; $total_weight = round($weight_each * $quantity, 3); $side = (int)$tpl['side']; $section_no = (int)$tpl['section']; $section_group = (int)$tpl['group']; $position = strtoupper($tpl['pos']); try { $sql = "INSERT INTO zari_machine_stock (company_id, machine_no, side, section_no, section_group, position, material_category, material_name, weight_each, quantity, total_weight, recorded_by, notes, entry_month, recorded_at) VALUES (:cid,:mno,:side,:sec,:sg,:pos,:cat,:mname,:we,:qty,:tw,:rb,:notes,:em,NOW()) ON DUPLICATE KEY UPDATE material_name=VALUES(material_name), weight_each=VALUES(weight_each), quantity=VALUES(quantity), total_weight=VALUES(total_weight), recorded_by=VALUES(recorded_by), notes=VALUES(notes), entry_month=VALUES(entry_month), recorded_at=NOW()"; $st = $pdo->prepare($sql); $st->execute([ ':cid'=>$COMPANY_ID, ':mno'=>$machine_no, ':side'=>$side, ':sec'=>$section_no, ':sg'=>$section_group, ':pos'=>$position, ':cat'=>$category, ':mname'=>$material_name, ':we'=>$weight_each, ':qty'=>$quantity, ':tw'=>$total_weight, ':rb'=>$USER_ID, ':notes'=>$top_notes, ':em'=>$entry_month ]); $saved++; } catch (Exception $ex) { $errors[] = "DB error row ".($i+1).": ".$ex->getMessage(); } } // foreach $qs = ['machine_no'=>$machine_no, 'entry_month'=>($entry_month?date('Y-m', strtotime($entry_month)):''), 'material_category'=>$category]; if (!empty($errors)) $qs['err'] = implode(' | ', $errors); if ($saved > 0) $qs['flash'] = "Saved $saved positions for MC $machine_no."; redirect_to($qs); } else { // validation errors -> redirect to GET to show messages redirect_to(['machine_no'=>$machine_no, 'entry_month'=>($entry_month?date('Y-m',strtotime($entry_month)):''), 'err'=>implode(' | ',$errors)]); } } /* include header */ require_once __DIR__ . '/partials/header.php'; ?> <style> .batch-grid{display:grid;gap:12px} .row{display:grid;grid-template-columns:220px 1fr 160px;gap:10px;align-items:center;padding:8px;border:1px solid #eee;border-radius:6px;margin-bottom:6px} .label{font-weight:700} </style> <div class="page-wrap"> <section class="page-card"> <header class="page-card__header"><h2>Zari Machine Batch Entry — 8 positions</h2></header> <div class="page-card__body"> <?php if (!empty($_GET['flash'])): ?><div class="notice notice--success"><?= j($_GET['flash']) ?></div><?php endif; ?> <?php if (!empty($_GET['err'])): ?><div class="notice notice--error"><?= j($_GET['err']) ?></div><?php endif; ?> <form method="post" id="batchForm" class="batch-grid" autocomplete="off"> <?php csrf_field(); ?> <input type="hidden" name="save_batch" value="1"> <div style="display:flex;gap:12px;align-items:center"> <div> <label>Machine</label> <select name="machine_no" id="machine_no"> <?php for($m=1;$m<=7;$m++): $sel = ($selected_machine === $m) ? 'selected' : ''; ?> <option value="<?= $m ?>" <?= $sel ?>>MC <?= $m ?></option> <?php endfor; ?> </select> </div> <div> <label>Category</label> <select name="material_category" id="material_category"> <option value="yarn" <?= $selected_category==='yarn' ? 'selected' : '' ?>>Yarn</option> <option value="zari" <?= $selected_category==='zari' ? 'selected' : '' ?>>Zari</option> <option value="metalic" <?= $selected_category==='metalic' ? 'selected' : '' ?>>Metalic</option> </select> </div> <div> <label>Month</label> <input type="month" name="entry_month" id="entry_month" value="<?= $selected_entry_month ? date('Y-m', strtotime($selected_entry_month)) : '' ?>"> </div> </div> <div style="display:flex;gap:12px;align-items:flex-end;margin-top:8px;"> <div style="min-width:220px;"> <label>Material</label> <select name="material_name_top" id="material_name_top"> <option value="">—</option> <?php foreach($MATERIAL_NAMES as $mn): $sel = (isset($_POST['material_name_top']) && $_POST['material_name_top'] === $mn) ? 'selected' : ''; ?> <option value="<?= j($mn) ?>" <?= $sel ?>><?= j($mn) ?></option> <?php endforeach; ?> </select> </div> <div style="flex:1"> <label>Note</label> <input type="text" name="notes_top" id="notes_top" placeholder="Note (applies to all saved rows)" value="<?= j($_POST['notes_top'] ?? '') ?>"> </div> </div> <div style="margin-top:12px;"> <div class="row" style="background:#f7f7f7"><div class="label">Position</div><div class="label">Material Name</div><div class="label">Weight (per paddle)</div></div> <?php foreach ($ROW_TEMPLATES as $i => $tpl): // Material prefill: prefer POST non-empty (user typed), else DB prefill, else empty $mat_val = ''; if (isset($_POST['material_name_'.$i]) && $_POST['material_name_'.$i] !== '') $mat_val = $_POST['material_name_'.$i]; elseif (!empty($prefill[$i]['material_name'])) $mat_val = $prefill[$i]['material_name']; // Weight prefill: prefer POST non-empty else DB prefill if non-zero else blank if (isset($_POST['weight_each_'.$i]) && $_POST['weight_each_'.$i] !== '') { $weight_val = $_POST['weight_each_'.$i]; } elseif (isset($prefill[$i]['weight_each']) && $prefill[$i]['weight_each'] !== '' && (float)$prefill[$i]['weight_each'] != 0.0) { $weight_val = $prefill[$i]['weight_each']; } else { $weight_val = ''; } ?> <div class="row"> <div class="label"><?= j($tpl['label']) ?></div> <div> <select name="material_name_<?= $i ?>" class="row-material"> <option value="">—</option> <?php foreach($MATERIAL_NAMES as $mn): $sel = ($mat_val === $mn) ? 'selected' : ''; ?> <option value="<?= j($mn) ?>" <?= $sel ?>><?= j($mn) ?></option> <?php endforeach; ?> </select> </div> <div> <input type="number" step="0.001" min="0" name="weight_each_<?= $i ?>" class="weight-input" data-index="<?= $i ?>" placeholder="e.g. 170" value="<?= j($weight_val) ?>"> </div> </div> <?php endforeach; ?> </div> <div style="margin-top:12px;display:flex;gap:8px"> <button type="submit" class="btn btn--primary">Save All Positions</button> <a class="btn btn--muted" href="/erp/zari_machine_stock_report.php">View Report</a> </div> </form> </div> </section> </div> <?php require_once __DIR__ . '/partials/footer.php'; ?> <script> (function(){ // Overwrite ALL row material selects when top material changes (immediate) var topMat = document.getElementById('material_name_top'); var rowMats = document.querySelectorAll('.row-material'); if (topMat) { topMat.addEventListener('change', function(){ var v = this.value; rowMats.forEach(function(s){ s.value = v; }); var firstW = document.querySelector('.weight-input'); if (firstW) firstW.focus(); }); } // Enter moves to next weight input var weightInputs = Array.prototype.slice.call(document.querySelectorAll('.weight-input')); weightInputs.forEach(function(inp, idx){ inp.addEventListener('keydown', function(e){ if (e.key === 'Enter') { e.preventDefault(); var next = weightInputs[idx+1]; if (next) next.focus(); else { var saveBtn = document.querySelector('button[type="submit"]'); if (saveBtn) saveBtn.focus(); } } }); }); // Before submit: ensure if top material is set and some selects are blank, they get top material too. // (client already overwrites all on change, this is extra safety) var form = document.getElementById('batchForm'); if (form) { form.addEventListener('submit', function(){ var top = topMat ? topMat.value : ''; if (top) { rowMats.forEach(function(s){ if (!s.value) s.value = top; }); } }); } })(); </script>