« Back to History
beam_to_yarn.php
|
20260920_164915.php
Initial Domain Snapshot
Copy Code
<?php /* beam_to_yarn.php Final production-ready file - JSON-safe AJAX responses (send_response clears buffer) - No browser popups on this page (page-level override for alert/confirm) - ERP header/footer included; uses $ctx['pdo'] - Only two stock types */ ob_start(); // capture any stray output require_once __DIR__ . '/modules/auth/page_acl.php'; $ctx = page_require_access('beam_to_yarn'); $u = $ctx['user'] ?? null; $company_id = (int)($ctx['company_id'] ?? 0); $pdo = $ctx['pdo'] ?? null; if (!$pdo) { require_once __DIR__ . '/core/db.php'; } require_once __DIR__ . '/helpers/activity_helper.php'; $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); /* CSRF */ $csrf = $_SESSION['csrf_token'] ?? bin2hex(random_bytes(16)); $_SESSION['csrf_token'] = $csrf; /* Helper: force JSON responses and clear buffer */ function send_response($success, $html) { if (ob_get_length() !== false) { @ob_clean(); } header('Content-Type: application/json; charset=utf-8'); echo json_encode(['success' => (bool)$success, 'html' => (string)$html], JSON_UNESCAPED_UNICODE); exit; } /* Preload lookups (safe) */ $deniers = []; $colors = []; try { $q = $pdo->prepare("SELECT value FROM denier_lookup WHERE company_id = :cid ORDER BY value"); $q->execute([':cid'=>$company_id]); $deniers = array_column($q->fetchAll(PDO::FETCH_ASSOC), 'value'); $q2 = $pdo->prepare("SELECT value FROM color_lookup WHERE company_id = :cid ORDER BY value"); $q2->execute([':cid'=>$company_id]); $colors = array_column($q2->fetchAll(PDO::FETCH_ASSOC), 'value'); } catch (Exception $e) {} /* -------------------- AJAX: fetch beam composition -------------------- */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'fetch') { header('Content-Type: application/json; charset=utf-8'); $bid = (int)($_POST['beam_quality_id'] ?? 0); $total_meter = (float)($_POST['total_meter'] ?? 0); if (!$bid || !($total_meter > 0)) { echo json_encode(['success'=>false,'msg'=>'Missing beam_quality_id or total_meter']); exit; } $sql = "SELECT y.id, y.seq_no, y.stock_type, y.yarn_type, y.denier, y.color, y.total_tar, y.tpm, y.wastage FROM beam_quality_yarns y JOIN beam_qualities q ON q.id = y.beam_quality_id WHERE y.beam_quality_id = :bid AND q.company_id = :cid ORDER BY y.seq_no ASC, y.id ASC"; $st = $pdo->prepare($sql); $st->execute([':bid'=>$bid, ':cid'=>$company_id]); $rows = $st->fetchAll(PDO::FETCH_ASSOC); if (!$rows) { echo json_encode(['success'=>false,'msg'=>'No yarn rows found for selected quality.']); exit; } $map = []; $map_short = []; try { $st2 = $pdo->prepare("SELECT stock_type, yarn_type, denier, new_denier FROM yarn_weight_data"); $st2->execute(); foreach ($st2->fetchAll(PDO::FETCH_ASSOC) as $r) { $key = strtolower(trim($r['stock_type'])) . '||' . strtolower(trim($r['yarn_type'])) . '||' . trim($r['denier']); $map[$key] = $r['new_denier']; $k2 = strtolower(trim($r['stock_type'])) . '||' . strtolower(trim($r['yarn_type'])); if (!isset($map_short[$k2])) $map_short[$k2] = $r['new_denier']; } } catch (Exception $e) {} $out = []; foreach ($rows as $r) { $k = strtolower(trim($r['stock_type'])) . '||' . strtolower(trim($r['yarn_type'])) . '||' . trim($r['denier']); $k2 = strtolower(trim($r['stock_type'])) . '||' . strtolower(trim($r['yarn_type'])); $new_denier = $map[$k] ?? $map_short[$k2] ?? (float)$r['denier']; $meters_for_yarn = $total_meter * (float)$r['total_tar']; $weight_kg = ($meters_for_yarn * (float)$new_denier) / 9000000.0; $out[] = [ 'seq_no' => (int)$r['seq_no'], 'stock_type'=> $r['stock_type'], 'yarn_type' => $r['yarn_type'], 'color' => $r['color'], 'denier' => $r['denier'], 'new_denier'=> (float)$new_denier, 'total_tar' => (string)$r['total_tar'], 'meters' => round($meters_for_yarn, 3), 'weight_kg' => round($weight_kg, 6), 'tpm' => (string)$r['tpm'], 'wastage' => (string)$r['wastage'], ]; } echo json_encode(['success'=>true,'data'=>$out], JSON_UNESCAPED_UNICODE); exit; } /* -------------------- Cone lookup / add lookup (AJAX) -------------------- */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'cone_lookup') { header('Content-Type: application/json; charset=utf-8'); $color = trim($_POST['color'] ?? ''); $denier = trim($_POST['denier'] ?? ''); if (!$color || !$denier) { echo json_encode(['success'=>false,'msg'=>'Missing color/denier']); exit; } try { $q = $pdo->prepare("SELECT cone_weight FROM cone_weight_matrix WHERE company_id = :cid AND LOWER(color)=LOWER(:color) AND LOWER(denier)=LOWER(:denier) LIMIT 1"); $q->execute([':cid'=>$company_id, ':color'=>$color, ':denier'=>$denier]); $r = $q->fetch(PDO::FETCH_ASSOC); if ($r && $r['cone_weight']) { echo json_encode(['success'=>true,'cone_weight'=> (float)$r['cone_weight'] ]); exit; } $q2 = $pdo->prepare("SELECT cone_weight FROM cone_weight_matrix WHERE company_id = :cid AND LOWER(color)=LOWER(:color) LIMIT 1"); $q2->execute([':cid'=>$company_id, ':color'=>$color]); $r2 = $q2->fetch(PDO::FETCH_ASSOC); if ($r2 && $r2['cone_weight']) { echo json_encode(['success'=>true,'cone_weight'=> (float)$r2['cone_weight'] ]); exit; } } catch (Exception $e) {} echo json_encode(['success'=>false,'msg'=>'No cone weight found']); exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'add_lookup') { header('Content-Type: application/json; charset=utf-8'); $type = trim($_POST['type'] ?? ''); $value = trim($_POST['value'] ?? ''); if (!$type || !$value) { echo json_encode(['success'=>false,'msg'=>'Missing type/value']); exit; } try { if ($type === 'denier') { $ins = $pdo->prepare("INSERT INTO denier_lookup (company_id, value) VALUES (:cid, :val)"); $ins->execute([':cid'=>$company_id, ':val'=>$value]); activity_create('beam', 'beam_to_yarn_lookup_create', (int)$pdo->lastInsertId(), 'Added denier lookup'); } elseif ($type === 'color') { $ins = $pdo->prepare("INSERT INTO color_lookup (company_id, value) VALUES (:cid, :val)"); $ins->execute([':cid'=>$company_id, ':val'=>$value]); activity_create('beam', 'beam_to_yarn_lookup_create', (int)$pdo->lastInsertId(), 'Added color lookup'); } else { echo json_encode(['success'=>false,'msg'=>'Unknown lookup type']); exit; } echo json_encode(['success'=>true,'msg'=>'Saved']); exit; } catch (Exception $e) { echo json_encode(['success'=>false,'msg'=>'DB insert failed']); exit; } } /* -------------------- Save (force JSON responses) -------------------- */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save') { $token = $_POST['csrf_token'] ?? ''; if (!$token || !hash_equals($_SESSION['csrf_token'] ?? '', $token)) { send_response(false, "<div style='color:red;padding:10px;'>CSRF token mismatch.</div>"); } $month = trim($_POST['month'] ?? ''); $stock_type = trim($_POST['stock_type_h'] ?? $_POST['stock_type'] ?? ''); $remark = trim($_POST['remark_h'] ?? $_POST['remark'] ?? ''); $raw_yarns_json = $_POST['yarns_json'] ?? ''; if (!$month || !$stock_type) { send_response(false, "<div style='color:orange;padding:10px;'>Please fill month and stock type.</div>"); } if ($raw_yarns_json === '') { $final_json_string = json_encode([], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); } else { $dec = json_decode($raw_yarns_json, true); if ($dec === null && json_last_error() !== JSON_ERROR_NONE) { send_response(false, "<div style='color:orange;padding:10px;'>Invalid JSON in yarns_json: " . htmlspecialchars(json_last_error_msg()) . "</div>"); } $final_json_string = json_encode($dec, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); } $payload = json_decode($final_json_string, true); $beam_quality_id = $payload['beam_quality_id'] ?? null; $total_meter = $payload['total_meter'] ?? null; try { $ins = $pdo->prepare(" INSERT INTO month_end_stock_report (company_id, month, stock_type, beam_quality_id, total_meter, yarns_json, remark, created_by) VALUES (:cid, :month, :stock_type, :bqid, :tm, :yj, :remark, :created_by) "); $ins->bindValue(':cid', $company_id, PDO::PARAM_INT); $ins->bindValue(':month', $month, PDO::PARAM_STR); $ins->bindValue(':stock_type', $stock_type, PDO::PARAM_STR); if ($beam_quality_id !== null && $beam_quality_id !== '') $ins->bindValue(':bqid', $beam_quality_id, PDO::PARAM_INT); else $ins->bindValue(':bqid', null, PDO::PARAM_NULL); $ins->bindValue(':tm', $total_meter !== null && $total_meter !== '' ? $total_meter : null); $ins->bindValue(':yj', $final_json_string, PDO::PARAM_STR); $ins->bindValue(':remark', $remark, PDO::PARAM_STR); $ins->bindValue(':created_by', $u['id'] ?? null, PDO::PARAM_INT); $ins->execute(); activity_create('beam', 'beam_to_yarn_report_create', (int)$pdo->lastInsertId(), 'Saved beam to yarn month-end entry'); send_response(true, "<div style='color:green;padding:10px;'>Entry saved successfully (new row inserted).</div>"); } catch (PDOException $ex) { $msg = $ex->getMessage(); $sqlState = $ex->getCode(); if (strpos($msg, 'Duplicate') !== false || $sqlState === '23000') { try { if ($beam_quality_id) { $find = $pdo->prepare("SELECT id, yarns_json FROM month_end_stock_report WHERE company_id = :cid AND month = :month AND stock_type = :st AND (beam_quality_id = :bq OR (beam_quality_id IS NULL AND :bq IS NULL)) LIMIT 1"); $find->execute([':cid'=>$company_id, ':month'=>$month, ':st'=>$stock_type, ':bq'=>$beam_quality_id]); } else { $find = $pdo->prepare("SELECT id, yarns_json FROM month_end_stock_report WHERE company_id = :cid AND month = :month AND stock_type = :st LIMIT 1"); $find->execute([':cid'=>$company_id, ':month'=>$month, ':st'=>$stock_type]); } $existing = $find->fetch(PDO::FETCH_ASSOC); if ($existing) { $ex_id = (int)$existing['id']; $existing_json = $existing['yarns_json'] ?: '[]'; $dec_existing = json_decode($existing_json, true); if ($dec_existing === null) $dec_existing = []; if (isset($dec_existing['entries']) && is_array($dec_existing['entries'])) { $merged = $dec_existing; } else { $merged = ['entries' => [$dec_existing]]; } $newEntry = json_decode($final_json_string, true); if (!is_array($newEntry)) $newEntry = ['data'=>$newEntry]; $newEntry['__merged_at'] = date('c'); $newEntry['__merged_by'] = $u['id'] ?? null; $merged['entries'][] = $newEntry; $upd = $pdo->prepare("UPDATE month_end_stock_report SET yarns_json = :yj, remark = :remark, created_by = :created_by WHERE id = :id"); $upd->execute([ ':yj' => json_encode($merged, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES), ':remark' => $remark, ':created_by' => $u['id'] ?? null, ':id' => $ex_id ]); activity_update('beam', 'beam_to_yarn_report_update', $ex_id, 'Merged beam to yarn month-end entry'); send_response(true, "<div style='color:green;padding:10px;'>Duplicate detected: merged new entry into existing row (id={$ex_id}).</div>"); } else { send_response(false, "<div style='color:red;padding:10px;'>Duplicate error but existing row not found. DB message: " . htmlspecialchars($msg) . "</div>"); } } catch (Exception $e2) { send_response(false, "<div style='color:red;padding:10px;'>Merge failed: " . htmlspecialchars($e2->getMessage()) . "</div>"); } } else { send_response(false, "<div style='color:red;padding:10px;'>DB error: " . htmlspecialchars($msg) . "</div>"); } } } /* End server logic - render page */ ob_end_flush(); ?> <?php require_once __DIR__ . '/partials/header.php'; ?> <!-- Page-level override to prevent any alert/confirm popups on this page --> <script> // disable native alert/confirm on this page only (keeps UX clean) window.alert = function(){ /* suppressed on this page */ }; window.confirm = function(){ return true; }; </script> <div class="card"> <h3>Beam to Yarn — Month-end Entry</h3> <form id="mainForm" method="post" action=""> <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf) ?>"> <input type="hidden" name="action" value="save" id="save_action"> <input type="hidden" name="yarns_json" id="save_yarns_json"> <div class="form-grid"> <div class="form-group"> <label for="month">Month</label> <input id="month" name="month" type="month" class="form-control" required> </div> <div class="form-group"> <label for="beam_quality_id">Beam Quality</label> <select id="beam_quality_id" name="beam_quality_id" class="form-control"> <option value="">-- Select Quality --</option> <?php $stmt = $pdo->prepare("SELECT id,name FROM beam_qualities WHERE company_id = :cid ORDER BY name"); $stmt->execute([':cid'=>$company_id]); foreach($stmt->fetchAll(PDO::FETCH_ASSOC) as $q): ?> <option value="<?= (int)$q['id'] ?>"><?= htmlspecialchars($q['name']) ?></option> <?php endforeach; ?> </select> </div> <div class="form-group"> <label for="total_meter">Total Meter</label> <input id="total_meter" name="total_meter" type="number" step="0.001" class="form-control"> </div> <div class="form-group"> <label for="stock_type">Stock Type</label> <select id="stock_type" name="stock_type" class="form-control" required> <option value="">-- Select --</option> <option>Yarn in installed beam</option> <option>Yarn in running beam</option> </select> </div> <div class="form-group"> <label for="remark">Remark</label> <input id="remark" name="remark" class="form-control"> </div> </div> <div class="mt-2"> <button id="btnPrepare" type="button" class="btn btn-primary">Prepare</button> <button id="btnSave" type="button" class="btn btn-success">Save Entry</button> </div> </form> <div id="detailArea" style="display:none; margin-top:18px;"> <h4>Beam Yarn Composition</h4> <div style="margin-bottom:8px;"> <button id="btnFetchBeam" type="button" class="btn btn-primary small-btn">Fetch Beam Rows</button> <span id="beamFetchMsg" style="margin-left:8px;color:#666;font-size:13px;"></span> </div> <table class="table" id="beamTable"> <thead><tr> <th>#</th><th>Stock Type</th><th>Yarn Type</th><th>Color</th><th>Orig Denier</th> <th>New Denier</th><th>Total Tar</th><th>Meters</th><th>Weight (kg)</th><th>TPM</th><th>Wastage</th> </tr></thead> <tbody></tbody> </table> </div> </div> <script> (function(){ function $(id){ return document.getElementById(id); } function escapeHtml(s){ return s==null?'':String(s).replaceAll('&','&').replaceAll('<','<').replaceAll('>','>'); } const btnPrepare = $('btnPrepare'), btnSave = $('btnSave'), btnFetchBeam = $('btnFetchBeam'); const detailArea = $('detailArea'), beamTableBody = document.querySelector('#beamTable tbody'); const beamFetchMsg = $('beamFetchMsg'); function renderBeamRows(rows){ beamTableBody.innerHTML = ''; rows.forEach((r,i)=>{ const tr = document.createElement('tr'); tr.innerHTML = '<td>'+ (i+1) +'</td>'+ '<td>'+ escapeHtml(r.stock_type) +'</td>'+ '<td>'+ escapeHtml(r.yarn_type) +'</td>'+ '<td>'+ escapeHtml(r.color) +'</td>'+ '<td>'+ escapeHtml(String(r.denier)) +'</td>'+ '<td>'+ escapeHtml(String(r.new_denier)) +'</td>'+ '<td>'+ escapeHtml(String(r.total_tar)) +'</td>'+ '<td>'+ escapeHtml(String(r.meters)) +'</td>'+ '<td>'+ escapeHtml(String(r.weight_kg)) +'</td>'+ '<td>'+ escapeHtml(String(r.tpm ?? '')) +'</td>'+ '<td>'+ escapeHtml(String(r.wastage ?? '')) +'</td>'; beamTableBody.appendChild(tr); }); } function fetchBeamData(){ const bq = $('beam_quality_id').value; const tm = parseFloat($('total_meter').value || 0); if (!bq) { return; } if (!(tm > 0)) { return; } beamFetchMsg.textContent = 'Fetching...'; const fd = new FormData(); fd.append('action','fetch'); fd.append('beam_quality_id', bq); fd.append('total_meter', tm); fetch(window.location.href, { method: 'POST', body: fd }) .then(r => r.json()) .then(j => { if (!j.success) { beamFetchMsg.textContent = 'No data: ' + (j.msg || ''); renderBeamRows([]); return; } renderBeamRows(j.data || []); beamFetchMsg.textContent = 'Rows loaded: ' + (j.data ? j.data.length : 0); }) .catch(err => { console.error('fetchBeamData error', err); beamFetchMsg.textContent = 'Fetch error (see console).'; }); } if (btnFetchBeam) btnFetchBeam.addEventListener('click', function(e){ e.preventDefault(); fetchBeamData(); }); btnPrepare.addEventListener('click', function(e){ e.preventDefault(); if (!$('month').value || !$('stock_type').value) return; detailArea.style.display = 'block'; fetchBeamData(); }); btnSave.addEventListener('click', function(e){ e.preventDefault(); if (!$('month').value) return; if (!$('stock_type').value) return; let payload = {}; payload.month = $('month').value; payload.stock_type = $('stock_type').value; payload.beam_quality_id = $('beam_quality_id').value || null; payload.total_meter = $('total_meter').value || null; payload.remark = $('remark').value || ''; payload.created_by = <?= (int)($u['id'] ?? 0) ?>; payload.meta = { prepared_at: new Date().toISOString(), prepared_by: <?= (int)($u['id'] ?? 0) ?> }; payload.sections = { beam: [] }; document.querySelectorAll('#beamTable tbody tr').forEach(tr=>{ const td = tr.querySelectorAll('td'); payload.sections.beam.push({ stock_type: td[1].textContent, yarn_type: td[2].textContent, color: td[3].textContent, denier: td[4].textContent, new_denier: td[5].textContent, total_tar: td[6].textContent, meters: td[7].textContent, weight_kg: td[8].textContent }); }); $('save_yarns_json').value = JSON.stringify(payload); const fd = new FormData($('mainForm')); fd.set('action','save'); const headers = { 'X-Requested-With': 'XMLHttpRequest' }; btnSave.disabled = true; btnSave.textContent = 'Saving...'; fetch(window.location.href, { method: 'POST', body: fd, headers: headers }) .then(r => r.json()) .then(j => { let notice = document.querySelector('.notice'); if (!notice) { notice = document.createElement('div'); notice.className = 'notice'; document.querySelector('.card').insertBefore(notice, document.querySelector('.card').firstChild.nextSibling); } notice.innerHTML = j.html || (j.success ? '<div style="color:green;padding:10px;">Saved</div>' : '<div style="color:red;padding:10px;">Error</div>'); }) .catch(err=>{ console.error('save error', err); let notice = document.querySelector('.notice'); if (!notice) { notice = document.createElement('div'); notice.className = 'notice'; document.querySelector('.card').insertBefore(notice, document.querySelector('.card').firstChild.nextSibling); } notice.innerHTML = '<div style="color:red">Save failed — see console for details.</div>'; }) .finally(()=>{ btnSave.disabled = false; btnSave.textContent = 'Save Entry'; }); }); })(); </script> <?php require_once __DIR__ . '/partials/footer.php'; ?>