« Back to History
cutting_entry.php
|
20260721_154032.php
Initial Bulk Import
Copy Code
<?php /* ============================================================================= File: /erp/cutting_entry.php Purpose: Cutting Entry Form with Strict Date Validation & Persistence ============================================================================= */ error_reporting(E_ALL); ini_set('display_errors', 1); require_once __DIR__ . '/modules/auth/page_acl.php'; $ctx = page_require_access('cutting_entry'); $pdo = $ctx['pdo']; $company_id = (int)$ctx['company_id']; require_once __DIR__ . '/helpers/activity_helper.php'; $msg = ''; $error = ''; /* ===== 1. FETCH NEXT CUTTING ID FOR DISPLAY ===== */ try { $st_cid = $pdo->prepare("SELECT MAX(cutting_id) FROM cutting_entry WHERE company_id = :cid"); $st_cid->execute([':cid' => $company_id]); $next_cutting_id_display = (int)$st_cid->fetchColumn() + 1; } catch (Exception $e) { $next_cutting_id_display = 1; } /* ===== 2. INSERT / SAVE LOGIC ===== */ if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { $gray_id = (int)$_POST['gray_id']; $raw_date = trim($_POST['cutting_date'] ?? ''); // Strict Regular Expression Validation for DD-MM-YYYY format (4-digit year starting with 20) if (!preg_match('/^\d{2}-\d{2}-20\d{2}$/', $raw_date)) { throw new Exception("Invalid Date format! Please use DD-MM-YYYY format with a valid 4-digit year (e.g., 13-12-2026)."); } $d = DateTime::createFromFormat('d-m-Y', $raw_date); if (!$d || $d->format('d-m-Y') !== $raw_date) { throw new Exception("The provided date is logically invalid."); } $cutting_date = $d->format('Y-m-d'); // Fetch Gray Info using gray_id $g_stmt = $pdo->prepare("SELECT rec_date, mill_id, quality_id, finish_mts FROM gray_receive WHERE gray_id=:gid AND company_id=:cid"); $g_stmt->execute([':gid' => $gray_id, ':cid' => $company_id]); $g = $g_stmt->fetch(PDO::FETCH_ASSOC); if (!$g) throw new Exception("Invalid Gray ID Selected"); $fresh = (int)$_POST['fresh_saree']; $second = (int)$_POST['second_saree']; $cut_type_id = (int)$_POST['cut_type_id']; $cutter_id = (int)$_POST['cutter_id']; $is_complete = (int)$_POST['is_complete']; $design_url = trim($_POST['design_url'] ?? ''); $c_stmt = $pdo->prepare("SELECT cut_name FROM saree_cut_types WHERE id=:id AND company_id=:cid"); $c_stmt->execute([':id' => $cut_type_id, ':cid' => $company_id]); $cut_val = (float)$c_stmt->fetchColumn(); $used_meter = ($fresh + $second) * $cut_val; $pdo->beginTransaction(); $st_cid = $pdo->prepare("SELECT MAX(cutting_id) FROM cutting_entry WHERE company_id = :cid FOR UPDATE"); $st_cid->execute([':cid' => $company_id]); $next_cutting_id = (int)$st_cid->fetchColumn() + 1; // A. INSERT INTO CUTTING ENTRY $ins = $pdo->prepare(" INSERT INTO cutting_entry (cutting_id, company_id, gray_id, rec_date, cutting_date, mill_id, quality_id, total_meter, fresh_saree, second_saree, cutter_id, cut_type_id, entry_remark, design_url) VALUES (:ctid, :cid, :gid, :rd, :cd, :mill, :quality, :tm, :fresh, :second, :cutter, :cuttype, :remark, :design_url) "); $ins->execute([ ':ctid' => $next_cutting_id, ':cid' => $company_id, ':gid' => $gray_id, ':rd' => $g['rec_date'], ':cd' => $cutting_date, ':mill' => $g['mill_id'], ':quality' => $g['quality_id'], ':tm' => $used_meter, ':fresh' => $fresh, ':second' => $second, ':cutter' => $cutter_id, ':cuttype' => $cut_type_id, ':remark' => $_POST['remark'] ?? "Cut ID: $next_cutting_id", ':design_url' => $design_url ]); // B. UPDATE CUTTING STOCK $pdo->prepare(" INSERT INTO cutting_stock (company_id, quality, in_pcs, out_pcs, balance_pcs, updated_at) VALUES (:cid, :quality, :pcs, 0, :pcs, NOW()) ON DUPLICATE KEY UPDATE in_pcs = in_pcs + VALUES(in_pcs), balance_pcs = balance_pcs + VALUES(in_pcs), updated_at = NOW() ")->execute([ ':cid' => $company_id, ':quality' => $g['quality_id'], ':pcs' => $fresh ]); // C. MARK GRAY AS CLOSED if ($is_complete == 1) { $pdo->prepare("UPDATE gray_receive SET status='closed' WHERE gray_id=:gid AND company_id=:cid") ->execute([':gid' => $gray_id, ':cid' => $company_id]); } $pdo->commit(); activity_create('cutting', 'cutting_entry_create', (int)$pdo->lastInsertId(), 'Created cutting entry'); header("Location: cutting_entry.php?success=1&cutting_id=" . urlencode((string)$next_cutting_id) . "&used_meter=" . urlencode(number_format($used_meter, 2, '.', '')) . "&last_date=" . urlencode($raw_date)); exit; } catch (Exception $e) { if ($pdo->inTransaction()) $pdo->rollBack(); $error = $e->getMessage(); } } if (isset($_GET['success']) && $_GET['success'] === '1') { $success_cutting_id = trim((string)($_GET['cutting_id'] ?? '')); $success_used_meter = trim((string)($_GET['used_meter'] ?? '')); if ($success_cutting_id !== '') { $msg = "Cutting Saved Successfully! Cutting ID: " . $success_cutting_id; if ($success_used_meter !== '') { $msg .= " | Used: " . $success_used_meter . " Mtrs."; } } } /* ===== 3. FETCH OPEN GRAY ENTRIES ===== */ $stmt = $pdo->prepare(" SELECT gr.gray_id, gr.rec_date, gr.finish_mts, gr.quality_id, mm.mill_name, fq.quality_name, (gr.finish_mts - IFNULL((SELECT SUM(total_meter) FROM cutting_entry WHERE gray_id = gr.gray_id), 0)) AS pending_mts FROM gray_receive gr LEFT JOIN mill_master mm ON gr.mill_id = mm.id LEFT JOIN fabric_quality_master fq ON gr.quality_id = fq.id WHERE gr.company_id = :cid AND gr.status='open' ORDER BY gr.gray_id DESC "); $stmt->execute([':cid' => $company_id]); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $cutters = $pdo->query("SELECT id, cutter_name FROM saree_cutters WHERE company_id=$company_id AND is_active=1")->fetchAll(PDO::FETCH_ASSOC); $cuts = $pdo->query("SELECT id, cut_name FROM saree_cut_types WHERE company_id=$company_id AND is_active=1")->fetchAll(PDO::FETCH_ASSOC); // Resolve target date for input value rendering $resolved_date_value = trim((string)($_GET['last_date'] ?? '')); if ($resolved_date_value === '' || !preg_match('/^\d{2}-\d{2}-20\d{2}$/', $resolved_date_value)) { $resolved_date_value = date('d-m-Y'); } require_once __DIR__ . '/partials/header.php'; ?> <div class="container mt-4"> <div class="card p-3 shadow-sm border-0"> <div class="d-flex justify-content-between align-items-center mb-4"> <h4 class="text-primary mb-0 fw-bold">Cutting Entry</h4> <div class="fs-5 fw-bold text-dark bg-light border p-2 px-3 rounded shadow-sm"> Current Cutting ID: <span class="badge bg-danger fs-6"><?= $next_cutting_id_display ?></span> </div> </div> <?php if($msg): ?> <div class="alert alert-success fw-bold"><?= $msg ?></div> <?php endif; ?> <?php if($error): ?> <div class="alert alert-danger fw-bold"><?= htmlspecialchars($error) ?></div> <?php endif; ?> <form method="post" id="cuttingForm" autocomplete="off"> <input type="hidden" name="gray_id" id="gray_id"> <div class="row g-3 mb-3"> <div class="col-md-2"> <label class="form-label text-danger fw-bold">Cutting Date</label> <!-- HTML5 Pattern ensures only DD-MM-YYYY format with 20XX year can be typed --> <input type="text" name="cutting_date" id="cutting_date" class="form-control border-danger" value="<?= htmlspecialchars($resolved_date_value, ENT_QUOTES, 'UTF-8') ?>" pattern="\d{2}-\d{2}-20\d{2}" placeholder="DD-MM-YYYY" required> </div> <div class="col-md-2"> <label class="form-label">Quick Gray ID</label> <input type="number" id="quick_id" class="form-control fw-bold border-primary" placeholder="Enter Gray ID"> </div> <div class="col-md-5"> <label class="form-label">Select Gray Entry</label> <select id="gray_select" class="form-select" required> <option value="">-- Choose Entry --</option> <?php foreach($rows as $r): ?> <option value="<?= $r['gray_id'] ?>" data-meter="<?= $r['pending_mts'] ?>"> Gray ID: <?= $r['gray_id'] ?> | <?= htmlspecialchars($r['mill_name']) ?> | <?= htmlspecialchars($r['quality_name']) ?> (<?= $r['pending_mts'] ?> Mtr left) </option> <?php endforeach; ?> </select> </div> <div class="col-md-3"> <label class="form-label fw-bold text-success">Available Meter</label> <input type="text" id="total_meter" class="form-control bg-light fw-bold" readonly> </div> </div> <div class="row g-3 mb-4"> <div class="col-md-6 mb-3"> <label class="form-label">Design Image URL (Google Drive)</label> <div class="input-group"> <input type="text" name="design_url" id="design_url" class="form-control" placeholder="Paste Google Drive file link..."> <button type="button" id="openImageBtn" class="btn btn-outline-secondary" style="min-width:120px;" disabled>Open Image</button> </div> <div id="design_img_preview_wrap" class="mt-2" style="display:none;"> <img id="design_img_preview" src="" alt="Design Preview" class="img-thumbnail" style="max-width:320px;max-height:180px;"> </div> </div> <div class="col-md-2"> <label class="form-label">Fresh Saree</label> <input type="number" name="fresh_saree" id="fresh" class="form-control" required> </div> <div class="col-md-2"> <label class="form-label">Second Saree</label> <input type="number" name="second_saree" id="second" class="form-control" value="0" required> </div> <div class="col-md-3"> <label class="form-label">Cut Type (Mtrs)</label> <select name="cut_type_id" class="form-select" required> <option value="">Select Cut</option> <?php foreach($cuts as $c): ?> <option value="<?= $c['id'] ?>"><?= htmlspecialchars($c['cut_name']) ?></option> <?php endforeach; ?> </select> </div> <div class="col-md-3"> <label class="form-label">Cutter Name</label> <select name="cutter_id" class="form-select" required> <option value="">Select Cutter</option> <?php foreach($cutters as $c): ?> <option value="<?= $c['id'] ?>"><?= htmlspecialchars($c['cutter_name']) ?></option> <?php endforeach; ?> </select> </div> <div class="col-md-2"> <label class="form-label">Mark Close?</label> <select name="is_complete" class="form-select"> <option value="0">No (Keep Open)</option> <option value="1">Yes (Finish Thaan)</option> </select> </div> <div class="col-md-12"> <label class="form-label">Remark</label> <input type="text" name="remark" class="form-control" placeholder="Any notes..."> </div> </div> <button type="submit" id="saveBtn" class="btn btn-primary px-5 py-2 fw-bold">SAVE CUTTING ENTRY</button> </form> </div> </div> <script> const quick = document.getElementById('quick_id'); const select = document.getElementById('gray_select'); const grayIdHidden = document.getElementById('gray_id'); const meterDisp = document.getElementById('total_meter'); quick.addEventListener('input', function(){ let v = this.value; let found = false; for(let i=0; i<select.options.length; i++){ if(select.options[i].value == v){ select.selectedIndex = i; select.dispatchEvent(new Event('change')); found = true; break; } } if(!found) { select.selectedIndex = 0; meterDisp.value = ''; grayIdHidden.value = ''; } }); select.addEventListener('change', function(){ let o = this.options[this.selectedIndex]; grayIdHidden.value = this.value; meterDisp.value = (o && o.dataset.meter) ? o.dataset.meter : '0'; }); document.getElementById('cuttingForm').addEventListener('submit', function(e){ const dateInput = document.getElementById('cutting_date').value.trim(); // Strict pattern check: Must be 2 digits, 2 digits, and exactly '20' followed by 2 digits const dateRegex = /^\d{2}-\d{2}-20\d{2}$/; if(!dateRegex.test(dateInput)) { e.preventDefault(); alert('Invalid Date Format! Please type date in DD-MM-YYYY format where year must be 4 digits (e.g., 13-12-2026).'); document.getElementById('cutting_date').focus(); return false; } const btn = document.getElementById('saveBtn'); btn.disabled = true; btn.innerText = 'Processing...'; }); const designUrlInput = document.getElementById('design_url'); const previewWrap = document.getElementById('design_img_preview_wrap'); const previewImg = document.getElementById('design_img_preview'); const openBtn = document.getElementById('openImageBtn'); function extractDriveFileId(url) { const match = url.match(/\/file\/d\/([\w-]+)/); return match ? match[1] : null; } function updatePreview() { const url = designUrlInput.value.trim(); const fileId = extractDriveFileId(url); if (fileId) { const previewUrl = `https://drive.google.com/thumbnail?id=${fileId}&sz=w1200`; previewImg.src = previewUrl; previewWrap.style.display = ''; openBtn.disabled = false; openBtn.onclick = function() { window.open(`https://drive.google.com/file/d/${fileId}/view`, '_blank'); }; } else { previewImg.src = ''; previewWrap.style.display = 'none'; openBtn.disabled = true; openBtn.onclick = null; } } designUrlInput.addEventListener('input', updatePreview); if (designUrlInput.value) updatePreview(); </script> <?php require_once __DIR__ . '/partials/footer.php'; ?>