« Back to History
roll_palti_machine_master.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php /* ========================================================= File: /erp/roll_palti_machine_master.php Purpose: Manage "roll_palti_machine_data" (Machine + Khata + Employee) Scope : Page-local only; multi-company aware; header guarded ========================================================= */ error_reporting(E_ALL); ini_set('display_errors', 1); /* ---- Auth + PDO ---- */ require __DIR__ . '/modules/auth/auth.php'; require_login(); $u = auth_user(); $company_id = (int)($u['company_id'] ?? 0); $user_role = strtolower($u['role'] ?? ''); $pdo = $GLOBALS['pdo'] ?? null; if (!$pdo) { require __DIR__ . '/core/db.php'; } /* ---- Header vars (legacy friendly) ---- */ if (!isset($company) || !is_array($company)) { $company = ['id'=>$company_id, 'name'=>($u['company_name'] ?? '')]; } /* ---- Header include decision ---- */ $__header = __DIR__ . '/partials/header.php'; if (!isset($USE_HEADER)) { $IS_AJAX = !empty($_GET['ajax']) || !empty($_GET['act']) || (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])==='xmlhttprequest'); $IS_EXPORT = isset($_GET['fmt']) || isset($_GET['download']); $USE_HEADER = !($IS_AJAX || $IS_EXPORT); } if (!isset($PAGE_ID) || $PAGE_ID==='') { $PAGE_ID = 'roll_palti_machine_master'; } $PAGE = $PAGE_ID; /* ---- Utility: JSON reply ---- */ 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; } /* ========================================================= TABLE ENSURE + MIGRATIONS ========================================================= */ $pdo->exec(" CREATE TABLE IF NOT EXISTS roll_palti_machine_data ( id BIGINT PRIMARY KEY AUTO_INCREMENT, company_id BIGINT NOT NULL, machine_no VARCHAR(50) NOT NULL, khata VARCHAR(100) NULL, employee VARCHAR(120) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uniq_company_machine (company_id, machine_no), INDEX idx_company (company_id), INDEX idx_emp (company_id, employee) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; "); /* -- Migration: if old typo table exists: roll_palty_machine_data -> roll_palti_machine_data -- */ $oldExists = (bool)$pdo->query("SHOW TABLES LIKE 'roll_palty_machine_data'")->fetchColumn(); $newExists = (bool)$pdo->query("SHOW TABLES LIKE 'roll_palti_machine_data'")->fetchColumn(); if ($oldExists && $newExists) { $cntNew = (int)$pdo->query("SELECT COUNT(*) FROM roll_palti_machine_data")->fetchColumn(); $cntOld = (int)$pdo->query("SELECT COUNT(*) FROM roll_palty_machine_data")->fetchColumn(); if ($cntOld > 0 && $cntNew === 0) { // Best-effort copy (matching columns only) $pdo->exec("INSERT IGNORE INTO roll_palti_machine_data (company_id, machine_no, khata, employee, created_at) SELECT company_id, machine_no, khata, employee, NOW() FROM roll_palty_machine_data"); } } elseif ($oldExists && !$newExists) { $pdo->exec("RENAME TABLE roll_palty_machine_data TO roll_palti_machine_data"); } /* -- Ensure missing columns exist (handles older installs) -- */ $hasCreated = (bool)$pdo->query("SHOW COLUMNS FROM roll_palti_machine_data LIKE 'created_at'")->fetchColumn(); if (!$hasCreated) { $pdo->exec("ALTER TABLE roll_palti_machine_data ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER employee"); } $hasUpdated = (bool)$pdo->query("SHOW COLUMNS FROM roll_palti_machine_data LIKE 'updated_at'")->fetchColumn(); if (!$hasUpdated) { $pdo->exec("ALTER TABLE roll_palti_machine_data ADD COLUMN updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP AFTER created_at"); } /* ---- Role check for edit ---- */ $CAN_EDIT = in_array($user_role, ['owner','admin'], true); /* ========================================================= AJAX HANDLERS ========================================================= */ if (isset($_GET['ajax'])) { try { $ajax = $_GET['ajax']; if ($ajax === 'list') { $q = trim($_GET['q'] ?? ''); $page = max(1, (int)($_GET['page'] ?? 1)); $per = min(200, max(10, (int)($_GET['per'] ?? 50))); $off = ($page-1)*$per; $where = ['company_id = :cid']; $param = [':cid'=>$company_id]; if ($q !== '') { $where[] = '(machine_no LIKE :q OR employee LIKE :q OR khata LIKE :q)'; $param[':q'] = "%$q%"; } $hasUpdated = (bool)$pdo->query("SHOW COLUMNS FROM roll_palti_machine_data LIKE 'updated_at'")->fetchColumn(); $cols = "id, machine_no, khata, employee, created_at".($hasUpdated ? ", updated_at" : ""); $sql = "SELECT SQL_CALC_FOUND_ROWS $cols FROM roll_palti_machine_data WHERE ".implode(' AND ', $where)." ORDER BY machine_no ASC LIMIT :off, :per"; $st = $pdo->prepare($sql); foreach ($param as $k=>$v) $st->bindValue($k, $v); $st->bindValue(':off', $off, PDO::PARAM_INT); $st->bindValue(':per', $per, PDO::PARAM_INT); $st->execute(); $rows = $st->fetchAll(PDO::FETCH_ASSOC); $total = (int)$pdo->query("SELECT FOUND_ROWS()")->fetchColumn(); jsend(true, ['rows'=>$rows, 'total'=>$total, 'page'=>$page, 'per'=>$per]); } if ($ajax === 'create') { if (!$CAN_EDIT) jsend(false,'Forbidden',403); $machine_no = trim($_POST['machine_no'] ?? ''); $khata = trim($_POST['khata'] ?? ''); $employee = trim($_POST['employee'] ?? ''); $errs=[]; if ($machine_no==='') $errs[]='Machine No required'; if ($employee==='') $errs[]='Employee required'; if (mb_strlen($machine_no)>50) $errs[]='Machine No too long'; if (mb_strlen($khata)>100) $errs[]='Khata too long'; if (mb_strlen($employee)>120) $errs[]='Employee too long'; if ($errs) jsend(false, implode(', ',$errs), 400); $st = $pdo->prepare("INSERT INTO roll_palti_machine_data (company_id, machine_no, khata, employee) VALUES (?,?,?,?)"); $ok = $st->execute([$company_id, $machine_no, $khata, $employee]); if (!$ok) jsend(false, 'Insert failed (duplicate machine?)', 500); jsend(true, ['id'=>$pdo->lastInsertId()]); } if ($ajax === 'update') { if (!$CAN_EDIT) jsend(false,'Forbidden',403); $id = (int)($_POST['id'] ?? 0); $machine_no = trim($_POST['machine_no'] ?? ''); $khata = trim($_POST['khata'] ?? ''); $employee = trim($_POST['employee'] ?? ''); if ($id<=0) jsend(false,'Bad id',400); $row = $pdo->prepare("SELECT id FROM roll_palti_machine_data WHERE id=? AND company_id=?"); $row->execute([$id,$company_id]); if (!$row->fetch()) jsend(false,'Not found',404); $errs=[]; if ($machine_no==='') $errs[]='Machine No required'; if ($employee==='') $errs[]='Employee required'; if (mb_strlen($machine_no)>50) $errs[]='Machine No too long'; if (mb_strlen($khata)>100) $errs[]='Khata too long'; if (mb_strlen($employee)>120) $errs[]='Employee too long'; if ($errs) jsend(false, implode(', ',$errs), 400); $st = $pdo->prepare("UPDATE roll_palti_machine_data SET machine_no=?, khata=?, employee=? WHERE id=? AND company_id=?"); $ok = $st->execute([$machine_no, $khata, $employee, $id, $company_id]); if (!$ok) jsend(false,'Update failed (duplicate machine?)',500); jsend(true, ['id'=>$id]); } if ($ajax === 'delete') { if (!$CAN_EDIT) jsend(false,'Forbidden',403); $id = (int)($_POST['id'] ?? 0); if ($id<=0) jsend(false,'Bad id',400); $st = $pdo->prepare("DELETE FROM roll_palti_machine_data WHERE id=? AND company_id=?"); $ok = $st->execute([$id,$company_id]); jsend(true, ['deleted'=>$ok?1:0]); } jsend(false,'Unknown ajax',400); } catch (Throwable $e) { jsend(false, 'server: '.$e->getMessage(), 500); } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Roll/Palti Machines • Mister Manager</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style> :root{ --primary:#34A853; --bg:#F5FFF7; --text:#202124; --radius:16px; --shadow:0 8px 20px rgba(0,0,0,.06) } *{box-sizing:border-box} body{margin:0;font-family:Inter,system-ui,Arial,sans-serif;background:var(--bg);color:var(--text)} .wrap{max-width:1100px;margin:24px auto;padding:12px} .card{background:#fff;border-radius:16px;padding:20px;box-shadow:var(--shadow)} .hdr{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px} .row{display:grid;grid-template-columns:1fr 1fr 1fr 120px;gap:10px} input{width:100%;padding:12px;border:1px solid #ddd;border-radius:10px} .btn{border:0;border-radius:10px;padding:10px 12px;font-weight:700;cursor:pointer} .btn.primary{background:var(--primary);color:#fff} .btn.link{background:#fff;border:1px solid #ddd} table{width:100%;border-collapse:collapse} th,td{border-bottom:1px dashed #eee;padding:8px;text-align:left} .actions button{margin-right:8px} .muted{color:#666} .noedit{opacity:.6} </style> </head> <body> <?php if ($USE_HEADER && is_file($__header)) { include_once $__header; } ?> <div class="wrap"> <div class="hdr"> <h2>Roll / Palti — Machine Master</h2> <div> <a class="btn link" href="/erp/roll_palti_entry.php">Entry</a> <a class="btn link" href="/erp/roll_palti_report.php">Report</a> </div> </div> <div class="card" style="margin-bottom:16px"> <div style="font-weight:700;margin-bottom:8px">Add / Update</div> <form id="createForm" class="row" onsubmit="return false;"> <input type="text" id="c_machine" placeholder="Machine No" required> <input type="text" id="c_khata" placeholder="Khata (optional)"> <input type="text" id="c_employee" placeholder="Employee" required> <button class="btn primary" id="btnCreate">Save</button> </form> <div class="muted" style="margin-top:6px">Note: Duplicate machine per company not allowed.</div> </div> <div class="card"> <div style="display:flex; gap:10px; align-items:center; margin-bottom:8px"> <input id="search" placeholder="Search machine/employee/khata…" style="flex:1"> <button class="btn link" onclick="loadList(1)">Search</button> </div> <table id="grid"> <thead> <tr><th>#</th><th>Machine</th><th>Khata</th><th>Employee</th><th>Updated</th><th class="actions">Actions</th></tr> </thead> <tbody></tbody> </table> <div id="pager" class="muted" style="margin-top:8px"></div> </div> </div> <script> const CAN_EDIT = <?=json_encode($CAN_EDIT)?>; function el(tag, attrs={}, ...children){ const e=document.createElement(tag); Object.entries(attrs).forEach(([k,v])=>{ if(k==='class') e.className=v; else if(k==='dataset') Object.assign(e.dataset,v); else if(k in e) e[k]=v; else e.setAttribute(k,v); }); children.forEach(c=>{ if(c!=null) e.append(c); }); return e; } async function loadList(page=1){ const q = document.getElementById('search').value.trim(); const u = new URLSearchParams({ajax:'list', q, page, per:50}); const r = await fetch(location.pathname+'?'+u.toString(), {headers:{'X-Requested-With':'XMLHttpRequest'}}); const j = await r.json(); if(!j.ok){ alert(j.error||'Load failed'); return; } const tb = document.querySelector('#grid tbody'); tb.innerHTML=''; j.data.rows.forEach((row, idx)=>{ const tr = el('tr'); tr.append(el('td',{}, String((j.data.page-1)*50 + idx + 1))); // editable cells const tdM = el('td'); const mIn = el('input',{value:row.machine_no, disabled:!CAN_EDIT}); tdM.append(mIn); const tdK = el('td'); const kIn = el('input',{value:row.khata||'', disabled:!CAN_EDIT}); tdK.append(kIn); const tdE = el('td'); const eIn = el('input',{value:row.employee, disabled:!CAN_EDIT}); tdE.append(eIn); tr.append(tdM, tdK, tdE); tr.append(el('td',{}, row.updated_at || row.created_at || '')); const tdA = el('td', {class:'actions'}); if (CAN_EDIT){ const btnSave = el('button',{class:'btn primary'},'Update'); btnSave.onclick = async ()=>{ const fd = new FormData(); fd.append('id', row.id); fd.append('machine_no', mIn.value.trim()); fd.append('khata', kIn.value.trim()); fd.append('employee', eIn.value.trim()); const rr = await fetch(location.pathname+'?ajax=update', {method:'POST', body:fd}); const jj = await rr.json(); if(!jj.ok){ alert(jj.error||'Update failed'); return; } loadList(j.data.page); }; const btnDel = el('button',{class:'btn link'},'Delete'); btnDel.onclick = async ()=>{ if(!confirm('Delete this machine?')) return; const fd = new FormData(); fd.append('id', row.id); const rr = await fetch(location.pathname+'?ajax=delete', {method:'POST', body:fd}); const jj = await rr.json(); if(!jj.ok){ alert(jj.error||'Delete failed'); return; } loadList(1); }; tdA.append(btnSave, btnDel); } else { tdA.append(el('span',{class:'muted noedit'},'View only')); } tr.append(tdA); tb.append(tr); }); document.getElementById('pager').textContent = `Total: ${j.data.total}`; } document.getElementById('btnCreate').onclick = async ()=>{ const m = document.getElementById('c_machine').value.trim(); const k = document.getElementById('c_khata').value.trim(); const e = document.getElementById('c_employee').value.trim(); if(!m || !e){ alert('Machine & Employee required'); return; } const fd = new FormData(); fd.append('machine_no', m); fd.append('khata', k); fd.append('employee', e); const r = await fetch(location.pathname+'?ajax=create', {method:'POST', body:fd}); const j = await r.json(); if(!j.ok){ alert(j.error||'Create failed'); return; } document.getElementById('c_machine').value=''; document.getElementById('c_khata').value=''; document.getElementById('c_employee').value=''; loadList(1); }; loadList(1); </script> </body> </html>