« Back to History
yarn_return_entry.php
|
20260723_000646.php
Initial Domain Snapshot
Copy Code
<?php /* ========================================================= File: /erp/yarn_return_entry.php Purpose: Yarn/TFO/Zari Return Entry Form (Dual Log Architecture) ========================================================= */ error_reporting(E_ALL); ini_set('display_errors', 1); /* ---- Auth + scope ---- */ require_once __DIR__ . '/modules/auth/page_acl.php'; $ctx = page_require_access('yarn_return_entry'); $company_id = (int)($ctx['company_id'] ?? 3); $pdo = $ctx['pdo'] ?? null; $u = $ctx['user'] ?? null; /* ---- JSON helper ---- */ function jsend($ok, $data = [], $code = 200) { while (ob_get_level() > 0) { @ob_end_clean(); } if (!headers_sent()) { header_remove(); header('Content-Type: application/json; charset=utf-8'); http_response_code($code); } echo json_encode( $ok ? ['ok' => true, 'data' => $data] : ['ok' => false, 'error' => $data], JSON_UNESCAPED_UNICODE ); exit; } function normalize_box_qty($value) { $value = (float)$value; $rounded = round($value); return (abs($value - $rounded) <= 0.01) ? (float)$rounded : round($value, 3); } /* ---- AJAX guard ---- */ if (isset($_GET['ajax'])) { if (!is_array($ctx) || empty($ctx['user'] ?? null)) { jsend(false, 'unauthenticated', 401); } set_error_handler(function($severity, $message, $file, $line) { if (!(error_reporting() & $severity)) return false; throw new ErrorException($message, 0, $severity, $file, $line); }); @ini_set('display_errors', '0'); } /* ---- PDO bootstrap ---- */ if (!($pdo instanceof PDO)) { $tryInclude = function(string $path) use (&$pdo) { @include_once $path; if ($pdo instanceof PDO) return true; if (isset($db) && $db instanceof PDO) { $pdo = $db; return true; } if (isset($dbh) && $dbh instanceof PDO) { $pdo = $dbh; return true; } if (isset($conn) && $conn instanceof PDO) { $pdo = $conn; return true; } return false; }; foreach ([__DIR__.'/core/db.php', __DIR__.'/db.php', __DIR__.'/config/db.php', __DIR__.'/config.php'] as $p) { if (is_file($p) && $tryInclude($p)) break; } if (!($pdo instanceof PDO)) { http_response_code(500); exit('DB bootstrap failed.'); } } /* Ensure schema matching image */ $pdo->exec("CREATE TABLE IF NOT EXISTS `yarn_return_entry` ( id BIGINT PRIMARY KEY AUTO_INCREMENT, company_id BIGINT NOT NULL, yarn_out_id BIGINT NOT NULL, return_date DATE NOT NULL, yarn_type VARCHAR(100) NOT NULL, company VARCHAR(150) NOT NULL, denier VARCHAR(50) NOT NULL, color VARCHAR(80) NOT NULL, issue_boxes DECIMAL(12,3) NOT NULL DEFAULT 0, issue_weight DECIMAL(14,3) NOT NULL DEFAULT 0, return_boxes DECIMAL(12,3) NOT NULL DEFAULT 0, return_weight DECIMAL(14,3) NOT NULL DEFAULT 0, assign_to VARCHAR(150) NULL, return_reason VARCHAR(255) NULL, remarks TEXT NULL, created_by INT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_company (company_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); if (!isset($company) || !is_array($company)) { $company = ['id' => $company_id, 'name' => ($u['company_name'] ?? '')]; } $ALLOWED_MATERIALS = ['yarn', 'tfo', 'zari']; $init_material = isset($_POST['material']) && in_array(strtolower($_POST['material']), $ALLOWED_MATERIALS, true) ? strtolower($_POST['material']) : 'yarn'; $init_ytype = trim($_POST['yarn_type'] ?? ''); $init_company = trim($_POST['company'] ?? ''); $init_denier = trim($_POST['denier'] ?? ''); $init_color = trim($_POST['color'] ?? ''); $init_assign = trim($_POST['assign_to'] ?? ''); /* ========================================================= AJAX HANDLER: options ========================================================= */ if (isset($_GET['ajax']) && $_GET['ajax'] === 'options') { try { $level = strtolower($_GET['level'] ?? 'type'); $material = strtolower($_GET['material'] ?? 'yarn'); if (!in_array($material, $ALLOWED_MATERIALS, true)) jsend(false, 'Forbidden material', 400); $mapMaterialToStockType = ['yarn' => 'Yarn', 'tfo' => 'TFO', 'zari' => 'Zari']; $stockType = $mapMaterialToStockType[$material] ?? 'Yarn'; $yt = trim($_GET['yarn_type'] ?? ''); $co = trim($_GET['company'] ?? ''); $de = trim($_GET['denier'] ?? ''); $filters = ['company_id = :cid', 'TRIM(stock_type) COLLATE utf8mb4_unicode_ci = TRIM(:st) COLLATE utf8mb4_unicode_ci']; $params = [':cid' => $company_id, ':st' => $stockType]; if ($level !== 'type' && $yt !== '') { $filters[] = 'TRIM(yarn_types) COLLATE utf8mb4_unicode_ci = TRIM(:yt) COLLATE utf8mb4_unicode_ci'; $params[':yt'] = $yt; } if (in_array($level, ['denier', 'color', 'company'], true) && $co !== '') { $filters[] = 'TRIM(company_name) COLLATE utf8mb4_unicode_ci = TRIM(:co) COLLATE utf8mb4_unicode_ci'; $params[':co'] = $co; } if ($level === 'color' && $de !== '') { $filters[] = 'TRIM(denier) COLLATE utf8mb4_unicode_ci = TRIM(:de) COLLATE utf8mb4_unicode_ci'; $params[':de'] = $de; } $colMap = ['type' => 'yarn_types', 'company' => 'company_name', 'denier' => 'denier', 'color' => 'color']; $col = $colMap[$level] ?? null; $sql = "SELECT DISTINCT TRIM($col) COLLATE utf8mb4_unicode_ci AS v FROM yarn_company_data WHERE " . implode(' AND ', $filters) . " AND $col IS NOT NULL AND TRIM($col) <> '' ORDER BY v"; $st = $pdo->prepare($sql); $st->execute($params); $out = []; while ($r = $st->fetch(PDO::FETCH_ASSOC)) { $out[] = trim((string)$r['v']); } jsend(true, ['options' => array_filter($out)]); } catch (Throwable $e) { jsend(false, $e->getMessage(), 500); } } /* ---- AJAX: assign options ---- */ if (isset($_GET['ajax']) && $_GET['ajax'] === 'assign_options') { try { $st = $pdo->prepare("SELECT name FROM yarn_assigne WHERE company_id = ? ORDER BY name"); $st->execute([$company_id]); jsend(true, ['options' => array_map(fn($r) => $r['name'], $st->fetchAll(PDO::FETCH_ASSOC))]); } catch (Throwable $e) { jsend(false, $e->getMessage(), 500); } } /* ---- AJAX: assign add ---- */ if (isset($_GET['ajax']) && $_GET['ajax'] === 'assign_add') { try { $name = trim($_POST['name'] ?? ''); if ($name === '') jsend(false, 'Name required', 400); $ins = $pdo->prepare("INSERT IGNORE INTO yarn_assigne (company_id, name) VALUES (?, ?)"); $ins->execute([$company_id, $name]); jsend(true, ['created' => ($ins->rowCount() > 0), 'name' => $name]); } catch (Throwable $e) { jsend(false, $e->getMessage(), 500); } } /* ---- AJAX: stock validation panel ---- */ if (isset($_GET['ajax']) && $_GET['ajax'] === 'stock') { try { $material = strtolower($_GET['material'] ?? 'yarn'); if (!in_array($material, $ALLOWED_MATERIALS, true)) jsend(false, 'bad material', 400); $yt = trim($_GET['yarn_type'] ?? ''); $co = trim($_GET['company'] ?? ''); $de = trim($_GET['denier'] ?? ''); $cl = trim($_GET['color'] ?? ''); $sqlL = "SELECT id, return_date, yarn_type, company, denier, color, return_boxes, return_weight, assign_to FROM `yarn_return_entry` WHERE company_id = :cid ORDER BY id DESC LIMIT 1"; $st = $pdo->prepare($sqlL); $st->execute([':cid' => $company_id]); $last = $st->fetch(PDO::FETCH_ASSOC); $current = null; $future = null; $form = ['boxes' => 0, 'weight' => 0, 'avg' => 0]; $diff_pct = null; $status = 'NA'; if ($yt !== '' && $co !== '' && $de !== '' && $cl !== '') { $filters = ['company_id=:cid', 'yarn_type=:yt', 'company=:co', 'denier=:de', 'color=:cl']; $params = [':cid' => $company_id, ':yt' => $yt, ':co' => $co, ':de' => $de, ':cl' => $cl]; $sumSQL = fn($tbl) => "SELECT COALESCE(SUM(boxes),0) boxes, COALESCE(SUM(weight),0) weight FROM `$tbl` WHERE " . implode(' AND ', $filters); $st = $pdo->prepare($sumSQL("{$material}_in")); $st->execute($params); $in = $st->fetch(PDO::FETCH_ASSOC); $st = $pdo->prepare($sumSQL("{$material}_out")); $st->execute($params); $out = $st->fetch(PDO::FETCH_ASSOC); $cur_boxes = normalize_box_qty(max(0.0, (float)$in['boxes'] - (float)$out['boxes'])); $cur_weight = max(0.0, (float)$in['weight'] - (float)$out['weight']); $cur_avg = ($cur_boxes > 0) ? ($cur_weight / $cur_boxes) : 0.0; $f_boxes = normalize_box_qty(max(0.0, (float)($_GET['form_boxes'] ?? 0))); $f_weight = max(0.0, (float)($_GET['form_weight'] ?? 0)); $fut_boxes = normalize_box_qty(max(0, $cur_boxes - $f_boxes)); $fut_weight = max(0, $cur_weight - $f_weight); $fut_avg = ($fut_boxes > 0) ? ($fut_weight / $fut_boxes) : 0.0; $form_avg = ($f_boxes > 0) ? ($f_weight / $f_boxes) : 0.0; $base = ($cur_avg > 0) ? $cur_avg : ($form_avg > 0 ? $form_avg : 1); $diff_pct = (($form_avg - $cur_avg) / $base * 100.0); $abs = abs($diff_pct); $status = ($abs <= 20) ? 'OK' : (($abs <= 30) ? 'NOT_OK' : 'PROBLEM'); $current = ['boxes' => round($cur_boxes, 3), 'weight' => round($cur_weight, 3), 'avg_box_weight' => round($cur_avg, 3)]; $future = ['boxes' => round($fut_boxes, 3), 'weight' => round($fut_weight, 3), 'avg_box_weight' => round($fut_avg, 3)]; $form = ['boxes' => $f_boxes, 'weight' => $f_weight, 'avg' => $form_avg]; } jsend(true, [ 'current' => $current, 'future' => $future, 'form' => $form, 'diff_pct' => is_null($diff_pct) ? null : round($diff_pct, 2), 'status' => $status, 'target_table' => "{$material}_out", 'last_entry' => $last ]); } catch (Throwable $e) { jsend(false, $e->getMessage(), 500); } } /* ---- AJAX: logs view ---- */ if (isset($_GET['ajax']) && $_GET['ajax'] === 'records') { try { $yt = trim($_GET['yarn_type'] ?? ''); $co = trim($_GET['company'] ?? ''); $de = trim($_GET['denier'] ?? ''); $cl = trim($_GET['color'] ?? ''); $filters = ['company_id=:cid']; $params = [':cid' => $company_id]; if ($yt !== '') { $filters[] = 'yarn_type=:yt'; $params[':yt'] = $yt; } if ($co !== '') { $filters[] = 'company=:co'; $params[':co'] = $co; } if ($de !== '') { $filters[] = 'denier=:de'; $params[':de'] = $de; } if ($cl !== '') { $filters[] = 'color=:cl'; $params[':cl'] = $cl; } $sql = "SELECT * FROM `yarn_return_entry` WHERE " . implode(' AND ', $filters) . " ORDER BY id DESC LIMIT 50"; $st = $pdo->prepare($sql); $st->execute($params); jsend(true, ['rows' => $st->fetchAll(PDO::FETCH_ASSOC)]); } catch (Throwable $e) { jsend(false, $e->getMessage(), 500); } } /* ---------------- POST: Dual Insert Engine ---------------- */ $flash = null; require_once __DIR__ . '/helpers/activity_helper.php'; if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['do'] ?? '') === 'save') { $material = $init_material; $out_table = "{$material}_out"; $txn_date = trim($_POST['return_date'] ?? ''); $ytype = trim($_POST['yarn_type'] ?? ''); $company_name = trim($_POST['company'] ?? ''); $denier = trim($_POST['denier'] ?? ''); $color = trim($_POST['color'] ?? ''); $boxes_input = (float)($_POST['return_boxes'] ?? 0); $boxes = normalize_box_qty($boxes_input); $weight = (float)($_POST['return_weight'] ?? 0); $assign_to = trim($_POST['assign_to'] ?? '') ?: null; $reason = trim($_POST['return_reason'] ?? '') ?: null; $remarks = trim($_POST['remarks'] ?? '') ?: null; $errs = []; if (!in_array($material, $ALLOWED_MATERIALS, true)) $errs[] = 'Invalid material scope'; if ($txn_date === '') $errs[] = 'Return Date required'; if ($ytype === '') $errs[] = 'Yarn Type required'; if ($company_name === '') $errs[] = 'Company name required'; if ($denier === '') $errs[] = 'Denier required'; if ($color === '') $errs[] = 'Color required'; if ($boxes <= 0) $errs[] = 'Boxes count must be > 0'; if ($weight <= 0) $errs[] = 'Total Weight must be > 0'; if (!$assign_to) $errs[] = 'Assign To field is mandatory'; if (!$errs) { /* Inventory validation check */ $filters = ['company_id=:cid', 'yarn_type=:yt', 'company=:co', 'denier=:de', 'color=:cl']; $params = [':cid' => $company_id, ':yt' => $ytype, ':co' => $company_name, ':de' => $denier, ':cl' => $color]; $sumSQL = fn($t) => "SELECT COALESCE(SUM(boxes),0) boxes, COALESCE(SUM(weight),0) weight FROM `$t` WHERE " . implode(' AND ', $filters); $st = $pdo->prepare($sumSQL("{$material}_in")); $st->execute($params); $in = $st->fetch(PDO::FETCH_ASSOC); $st = $pdo->prepare($sumSQL("{$material}_out")); $st->execute($params); $out = $st->fetch(PDO::FETCH_ASSOC); $cur_boxes = max(0.0, (float)$in['boxes'] - (float)$out['boxes']); $cur_weight = max(0.0, (float)$in['weight'] - (float)$out['weight']); if ($boxes > $cur_boxes || $weight > $cur_weight) { $errs[] = "Stock deficit detected! Out balance cannot exceed available physical stock."; } } if ($errs) { $flash = ['type' => 'error', 'msg' => implode(', ', $errs)]; } else { try { $pdo->beginTransaction(); /* Entry 1: Core Material OUT Table */ $insOut = $pdo->prepare("INSERT INTO `$out_table` (company_id, txn_date, yarn_type, company, denier, color, boxes, weight, assign_to) VALUES (:cid, :dt, :yt, :co, :de, :cl, :bx, :wt, :asgn)"); $insOut->execute([ ':cid' => $company_id, ':dt' => $txn_date, ':yt' => $ytype, ':co' => $company_name, ':de' => $denier, ':cl' => $color, ':bx' => $boxes, ':wt' => $weight, ':asgn' => $assign_to ]); $parent_out_id = $pdo->lastInsertId(); /* Entry 2: Dual Log to yarn_return_entry Table matching exact structural metrics */ $insRet = $pdo->prepare("INSERT INTO `yarn_return_entry` (company_id, yarn_out_id, return_date, yarn_type, company, denier, color, issue_boxes, issue_weight, return_boxes, return_weight, assign_to, return_reason, remarks, created_by) VALUES (:cid, :out_id, :rdate, :ytype, :comp, :den, :col, :ibx, :iwt, :rbx, :rwt, :asgn, :reas, :rem, :cby)"); $insRet->execute([ ':cid' => $company_id, ':out_id' => $parent_out_id, ':rdate' => $txn_date, ':ytype' => $ytype, ':comp' => $company_name, ':den' => $denier, ':col' => $color, ':ibx' => 0.000, // Explicit tracking fallback metrics ':iwt' => 0.000, ':rbx' => $boxes, ':rwt' => $weight, ':asgn' => $assign_to, ':reas' => $reason, ':rem' => $remarks, ':cby' => (int)($u['id'] ?? 0) ]); $pdo->commit(); $flash = ['type' => 'success', 'msg' => "Dual log records successfully recorded to {$out_table} and yarn_return_entry tables!"]; // Audit Trail creation $logMsg = sprintf('Dual Log Return [%s]: Type:%s Co:%s Den:%s Col:%s Box:%s Wt:%s', strtoupper($material), $ytype, $company_name, $denier, $color, $boxes, $weight); if (function_exists('activity_create')) { activity_create('yarn_return_entry', 'create', (int)$parent_out_id, $logMsg); } } catch (Throwable $ex) { if ($pdo->inTransaction()) { $pdo->rollBack(); } $flash = ['type' => 'error', 'msg' => 'Transaction aborted: ' . $ex->getMessage()]; } } } /* Header config definitions */ $__header = __DIR__ . '/partials/header.php'; $__footer = __DIR__ . '/partials/footer.php'; if (!isset($USE_HEADER)) { $USE_HEADER = !(isset($_GET['ajax'])); } $PAGE = 'yarn_return_entry'; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Yarn Return Entry Management</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> </head> <body class="bg-light"> <?php if ($USE_HEADER && is_file($__header)) { include_once $__header; } ?> <div class="container-fluid py-4 px-4"> <!-- Segment Switches --> <div class="card border-0 shadow-sm mb-4"> <div class="card-body d-flex flex-wrap justify-content-between align-items-center gap-3"> <div class="btn-group shadow-sm" id="segMaterial"> <button class="btn btn-outline-primary fw-bold" data-v="yarn">Yarn</button> <button class="btn btn-outline-primary fw-bold" data-v="tfo">TFO</button> <button class="btn btn-outline-primary fw-bold" data-v="zari">Zari</button> </div> <div class="alert alert-danger py-1 px-3 mb-0 fw-bold border-0 shadow-sm small text-uppercase"> <i class="bi bi-exclamation-triangle-fill me-1"></i> ONLY OUT RETURNS TRANSACTION SCOPE </div> </div> </div> <?php if($flash): ?> <div class="alert alert-<?= ($flash['type'] === 'success') ? 'success' : 'danger' ?> alert-dismissible fade show border-0 shadow-sm mb-4"> <?= htmlspecialchars($flash['msg']) ?> <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> <?php endif; ?> <div class="row g-4"> <!-- Stock panel displays --> <div class="col-md-3"> <div class="card border-0 shadow-sm h-100"> <div class="card-body text-center p-4"> <h6 class="text-uppercase fw-bold text-muted small mb-3">AVG Box Weight Deviation</h6> <h4 class="fw-bold mb-2" id="diffLine">—</h4> <div id="diffPill" class="d-inline-block"></div> <hr class="my-4 text-secondary opacity-25"> <h6 class="text-uppercase fw-bold text-muted small mb-2">Last General Return</h6> <div class="fw-semibold text-dark p-2 bg-light rounded shadow-sm small" id="lastEntry">—</div> </div> </div> </div> <!-- Form Section --> <div class="col-md-6"> <div class="card border-0 shadow-sm"> <div class="card-header bg-white py-3 border-0"> <h5 class="fw-bold mb-0 text-primary">Yarn Return Data Entry Form</h5> </div> <div class="card-body p-4 bg-light"> <form method="post" id="entryForm" autocomplete="off"> <input type="hidden" name="do" value="save"> <input type="hidden" name="material" id="f_material" value="<?= htmlspecialchars($init_material) ?>"> <div class="row mb-3"> <div class="col-md-6"> <label class="form-label small fw-bold">Return Date</label> <input type="date" name="return_date" id="f_date" class="form-control border-2 shadow-sm" required value="<?= htmlspecialchars($_POST['return_date'] ?? date('Y-m-d')) ?>"> </div> <div class="col-md-6"> <label class="form-label small fw-bold">Yarn Type</label> <select name="yarn_type" id="f_yarn_type" class="form-select border-2 shadow-sm" required></select> </div> </div> <div class="row mb-3"> <div class="col-md-6"> <label class="form-label small fw-bold">Company</label> <div class="input-group shadow-sm"> <select name="company" id="f_company" class="form-select border-2" required></select> </div> </div> <div class="col-md-6"> <label class="form-label small fw-bold">Denier</label> <select name="denier" id="f_denier" class="form-select border-2 shadow-sm" required></select> </div> </div> <div class="row mb-3"> <div class="col-md-6"> <label class="form-label small fw-bold">Color</label> <select name="color" id="f_color" class="form-select border-2 shadow-sm" required></select> </div> <div class="col-md-6"> <label class="form-label small fw-bold">Return Boxes</label> <input type="number" step="1" min="0" name="return_boxes" id="f_boxes" class="form-control border-2 shadow-sm" required placeholder="e.g. 15" value="<?= (isset($flash['type']) && $flash['type']==='success') ? '' : htmlspecialchars($_POST['return_boxes'] ?? '') ?>"> </div> </div> <div class="row mb-3"> <div class="col-md-6"> <label class="form-label small fw-bold">Return Weight (kg)</label> <input type="number" step="0.001" min="0" name="return_weight" id="f_weight" class="form-control border-2 shadow-sm" required placeholder="e.g. 345.500" value="<?= (isset($flash['type']) && $flash['type']==='success') ? '' : htmlspecialchars($_POST['return_weight'] ?? '') ?>"> </div> <div class="col-md-6"> <label class="form-label small fw-bold text-danger">Assign To (Returned From)</label> <div class="input-group shadow-sm"> <select name="assign_to" id="f_assign" class="form-select border-2" required></select> </div> </div> </div> <div class="row mb-4"> <div class="col-md-6"> <label class="form-label small fw-bold">Return Reason</label> <input type="text" name="return_reason" class="form-control border-2 shadow-sm" placeholder="Reason for return"> </div> <div class="col-md-6"> <label class="form-label small fw-bold">Remarks</label> <textarea name="remarks" class="form-control border-2 shadow-sm" rows="1" placeholder="Optional notes"></textarea> </div> </div> <div class="d-flex flex-wrap gap-2 pt-2"> <button class="btn btn-primary px-4 fw-bold shadow-sm" type="submit">Submit Return Out Entry</button> <button class="btn btn-outline-secondary px-3" type="button" id="btnRecords">View Logs</button> <button class="btn btn-link text-decoration-none text-muted" type="button" id="btnReset">Reset Fields</button> </div> </form> </div> </div> </div> <!-- Sidebar Inventory Monitor View --> <div class="col-md-3"> <div class="card border-0 shadow-sm mb-4"> <div class="card-body p-4 text-center"> <h6 class="text-uppercase fw-bold text-muted small mb-2">Available Stock</h6> <div class="h3 fw-bold text-primary mb-1" id="curStock">—</div> <p class="small text-muted mb-0">Active Physical Batch Stock</p> </div> </div> <div class="card border-0 shadow-sm"> <div class="card-body p-4 text-center"> <h6 class="text-uppercase fw-bold text-muted small mb-2">Post Deducted Stock</h6> <div class="h3 fw-bold text-success mb-0" id="futStock">—</div> </div> </div> </div> </div> <!-- Data logs display layout grid --> <div class="card border-0 shadow-sm mt-4 overflow-hidden"> <div class="card-header bg-white py-3"> <h6 class="fw-bold mb-0" id="dynTitle">Live Logs Stream</h6> </div> <div class="card-body p-0" id="dynArea"> <div class="p-4 text-center text-muted">Click View Logs to load historical transactional table logs data stream.</div> </div> </div> </div> <script> const $ = s => document.querySelector(s); const $$= s => Array.from(document.querySelectorAll(s)); function formatBoxQty(val){ const n = Number(val || 0); if (!Number.isFinite(n)) return '0'; return Math.abs(n - Math.round(n)) <= 0.01 ? String(Math.round(n)) : n.toFixed(3); } let state = { material: <?=json_encode($init_material)?>, yarn_type: <?=json_encode($init_ytype)?>, company: <?=json_encode($init_company)?>, denier: <?=json_encode($init_denier)?>, color: <?=json_encode($init_color)?>, assign_to: <?=json_encode($init_assign)?>, form_boxes: parseInt($('#f_boxes').value||'0', 10)||0, form_weight: parseFloat($('#f_weight').value||'0')||0 }; function setSegActive(containerSel, val){ $$(containerSel+' button').forEach(b=>b.classList.toggle('active', b.dataset.v===val)); } function loadOptions(level){ const p=new URLSearchParams({ajax:'options', level, material:state.material}); if(state.yarn_type) p.append('yarn_type', state.yarn_type); if(state.company) p.append('company', state.company); if(state.denier) p.append('denier', state.denier); return fetch(location.pathname+'?'+p.toString()) .then(r=>r.json()) .then(j=>{ if(!j || !j.ok) return; const opts=j.data.options||[]; const fill=(id, val)=>{ const el=$(id); if(!el) return; el.innerHTML='<option value="">— Select —</option>'; opts.forEach(v=>{ const o=document.createElement('option'); o.value=v; o.textContent=v; el.appendChild(o); }); if(opts.length===1){ el.value = opts[0]; el.dispatchEvent(new Event('change', { bubbles:true })); } else if(val) { el.value = val; } }; if(level==='type') fill('#f_yarn_type', state.yarn_type); if(level==='company') fill('#f_company', state.company); if(level==='denier') fill('#f_denier', state.denier); if(level==='color') fill('#f_color', state.color); }); } function resetBelow(level){ if(level==='type'){ state.company=''; $('#f_company').innerHTML=''; } if(level==='type'||level==='company'){ state.denier=''; $('#f_denier').innerHTML=''; } if(level!=='color'){ state.color=''; $('#f_color').innerHTML=''; } } function loadAssignOptions(selectVal){ const sel = $('#f_assign'); sel.innerHTML = '<option value="">— Select —</option>'; return fetch(location.pathname+'?ajax=assign_options') .then(r=>r.json()) .then(j=>{ if(!j.ok) return; (j.data.options||[]).forEach(v=>{ const o=document.createElement('option'); o.value=v; o.textContent=v; sel.appendChild(o); }); const add=document.createElement('option'); add.value='__addnew__'; add.textContent='+ Add New…'; sel.appendChild(add); if(selectVal) sel.value = selectVal; else if(state.assign_to) sel.value = state.assign_to; }); } function hookAssignAdd(){ const sel = $('#f_assign'); if(!sel || sel.dataset.bound === '1') return; sel.dataset.bound = '1'; sel.addEventListener('change', ()=>{ if(sel.value === '__addnew__'){ const nameRaw = prompt('Enter new Assign name:'); if(!nameRaw){ sel.value=''; return; } const name = nameRaw.trim(); if(!name){ sel.value=''; return; } const form = new FormData(); form.append('name', name); fetch(location.pathname+'?ajax=assign_add', {method:'POST', body:form}) .then(r=>r.json()) .then(j=>{ if(!j.ok){ alert('Error adding assignee'); sel.value=''; return; } state.assign_to = j.data.name; loadAssignOptions(j.data.name); }); } else { state.assign_to = sel.value; } }); } let lastStockResp = null; function loadStockPanel(){ const p=new URLSearchParams({ ajax:'stock', material:state.material, yarn_type:state.yarn_type||'', company:state.company||'', denier:state.denier||'', color:state.color||'', form_boxes:state.form_boxes||0, form_weight:state.form_weight||0 }); return fetch(location.pathname+'?'+p.toString()) .then(r=>r.json()) .then(j=>{ if(!j.ok) return; lastStockResp = j.data; const c=j.data.current, f=j.data.future; $('#curStock').innerHTML = c ? `<div>Boxes: <b>${formatBoxQty(c.boxes)}</b></div><div>Weight: <b>${c.weight}</b> kg</div><div>AVG: <b>${c.avg_box_weight}</b></div>` : '—'; $('#futStock').innerHTML = f ? `<div>Boxes: <b>${formatBoxQty(f.boxes)}</b></div><div>Weight: <b>${f.weight}</b> kg</div><div>AVG: <b>${f.avg_box_weight}</b></div>` : '—'; if(c && typeof j.data.diff_pct === 'number'){ $('#diffLine').textContent=`Form AVG: ${Number(j.data.form.avg||0).toFixed(3)} • Current: ${c.avg_box_weight} • Diff: ${j.data.diff_pct}%`; let pill=`<span class="badge bg-success">OK (≤20%)</span>`; if(j.data.status==='NOT_OK') pill=`<span class="badge bg-warning text-dark">Not OK (21–30%)</span>`; if(j.data.status==='PROBLEM') pill=`<span class="badge bg-danger">Problem (>30%)</span>`; $('#diffPill').innerHTML=pill; } else { $('#diffLine').textContent='—'; $('#diffPill').innerHTML=`<span class="badge bg-secondary">N/A</span>`; } const last=j.data.last_entry; $('#lastEntry').innerHTML = last ? `<div><b>#${last.id}</b> • ${last.return_date}</div><div>Boxes: <b>${formatBoxQty(last.return_boxes)}</b>, Wt: <b>${Number(last.return_weight).toFixed(3)} kg</b></div><div>To: ${last.assign_to||'-'}</div>` : 'No metrics logged yet.'; }); } function loadRecordsView(){ $('#dynTitle').textContent = `${state.material.toUpperCase()} RETURN HISTORY LOG — Shared Entry Records Array`; const p=new URLSearchParams({ ajax:'records', yarn_type:state.yarn_type||'', company:state.company||'', denier:state.denier||'', color:state.color||'' }); fetch(location.pathname+'?'+p.toString()) .then(r=>r.json()) .then(j=>{ if(!j.ok) return; const rows=j.data.rows||[]; if(rows.length===0){ $('#dynArea').innerHTML='<div class="p-4 text-center text-muted">No historical return logs encountered.</div>'; return; } $('#dynArea').innerHTML = ` <div class="table-responsive"> <table class="table table-sm table-bordered table-striped table-hover align-middle mb-0"> <thead class="table-light text-uppercase small text-center"> <tr> <th>ID</th><th>Out Parent ID</th><th>Return Date</th><th>Type</th><th>Company</th><th>Denier</th><th>Color</th><th class="text-end">Ret Boxes</th><th class="text-end">Ret Weight</th><th>Assigned From</th><th>Reason</th> </tr> </thead> <tbody> ${rows.map(r=>` <tr> <td class="text-center">${r.id}</td><td class="text-center">${r.yarn_out_id}</td><td>${r.return_date}</td><td>${r.yarn_type}</td><td>${r.company}</td><td>${r.denier}</td><td>${r.color}</td> <td class="text-end fw-semibold">${formatBoxQty(r.return_boxes)}</td><td class="text-end fw-semibold">${Number(r.return_weight).toFixed(3)}</td><td>${r.assign_to||''}</td><td>${r.return_reason||''}</td> </tr> `).join('')} </tbody> </table> </div>`; }); } function applySegStates(){ setSegActive('#segMaterial', state.material); initDropdowns(); } $$('#segMaterial button').forEach(b=>b.addEventListener('click', ()=>{ state.material=b.dataset.v; $('#f_material').value=state.material; state.yarn_type=state.company=state.denier=state.color=''; applySegStates(); })); function initDropdowns(){ loadOptions('type').then(()=>{ if(state.yarn_type) $('#f_yarn_type').value=state.yarn_type; return loadOptions('company'); }).then(()=>{ if(state.company) $('#f_company').value=state.company; return loadOptions('denier'); }).then(()=>{ if(state.denier) $('#f_denier').value=state.denier; return loadOptions('color'); }).then(()=>{ if(state.color) $('#f_color').value=state.color; loadStockPanel(); }); } $('#f_yarn_type').addEventListener('change', e=>{ state.yarn_type=e.target.value; resetBelow('type'); loadOptions('company').then(loadStockPanel); }); $('#f_company').addEventListener('change', e => { state.company = e.target.value; resetBelow('company'); loadOptions('denier').then(loadStockPanel); }); $('#f_denier').addEventListener('change', e=>{ state.denier=e.target.value; resetBelow('denier'); loadOptions('color').then(loadStockPanel); }); $('#f_color').addEventListener('change', e=>{ state.color=e.target.value; loadStockPanel(); }); function syncFormNumbers(){ state.form_boxes=parseInt($('#f_boxes').value||'0', 10)||0; state.form_weight=parseFloat($('#f_weight').value||'0')||0; loadStockPanel(); } $('#f_boxes').addEventListener('input', syncFormNumbers); $('#f_weight').addEventListener('input', syncFormNumbers); $('#btnRecords').addEventListener('click', ()=>loadRecordsView()); $('#btnReset').addEventListener('click', ()=>{ state.yarn_type=state.company=state.denier=state.color=''; $('#f_yarn_type').innerHTML=''; $('#f_company').innerHTML=''; $('#f_denier').innerHTML=''; $('#f_color').innerHTML=''; $('#f_boxes').value=''; $('#f_weight').value=''; state.form_boxes=0; state.form_weight=0; initDropdowns(); $('#f_yarn_type').focus(); }); $('#entryForm').addEventListener('submit', async (e)=>{ e.preventDefault(); await loadStockPanel(); const j = lastStockResp; if(!$('#f_yarn_type').value || !$('#f_company').value || !$('#f_denier').value || !$('#f_color').value){ alert('Please verify dropdown attributes values selection.'); return; } const cur = (j && j.current) ? j.current : {boxes:0, weight:0}; const formBoxes = parseFloat($('#f_boxes').value||'0')||0; const formWeight = parseFloat($('#f_weight').value||'0')||0; if(formBoxes > cur.boxes || formWeight > cur.weight){ alert('Stock minus ho jaayega. Inventory limit threshold check failed.'); return; } const diff = (j && typeof j.diff_pct==='number') ? Math.abs(Number(j.diff_pct||0)) : 0; if (diff > 30) { if (!confirm(`AVG Box Deviation reads ${diff}%. Proceed insertion log execution?`)) return; } e.target.submit(); }); /* Init executions sequence mapping */ applySegStates(); loadAssignOptions(state.assign_to || '').then(()=>hookAssignAdd()); <?php if(isset($flash['type']) && $flash['type']==='success'): ?> $('#f_boxes').value=''; $('#f_weight').value=''; state.form_boxes=0; state.form_weight=0; loadStockPanel(); $('#f_boxes').focus(); <?php endif; ?> </script> <?php if ($USE_HEADER && is_file($__footer)) { include_once $__footer; } ?> </body> </html>