« Back to History
loom_patia_machine.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php /* ============================================================================= File: /erp/public/loom_patia_machine.php (also works if kept under /erp/) Purpose: Machine-wise Patia (Date × Karigar columns) — Taka No REMOVED Filters: Khata, Karigar (optional), Machine, Month, Half Scope : Page-local only. No global/base changes. Company-scoped. ============================================================================= */ /* ======================= SECTION A: Auth + ACL (Reusable) ======================= */ header('X-Frame-Options: SAMEORIGIN'); error_reporting(E_ALL); ini_set('display_errors', 1); /* Works from /erp OR /erp/public */ $APP_ROOT = is_dir(__DIR__ . '/modules') ? __DIR__ : dirname(__DIR__); /* Auth */ require $APP_ROOT . '/modules/auth/auth.php'; require_login(); $u = auth_user(); $company_id = (int)($u['company_id'] ?? 0); $user_id = (int)($u['id'] ?? 0); $user_role = (string)($u['role'] ?? 'Employee'); // 'Owner','Admin','Manager','Developer','Employee' $role = strtolower($user_role); /* PDO */ $pdo = $GLOBALS['pdo'] ?? null; if (!$pdo) { require $APP_ROOT . '/core/db.php'; } /* इस पेज का slug (page_permissions.slug से match) */ $PAGE_SLUG = 'loom_patia_machine'; /* Feature flags */ $ALLOW_OWNER_ALWAYS = true; // Owner bypass $DENY_IF_MISSING_ROW = true; // slug row missing => deny (false करने पर allow-by-default) /* DB ACL check using page_permissions */ $allowed = false; if ($ALLOW_OWNER_ALWAYS && $role === 'owner') { $allowed = true; } else { try { $st = $pdo->prepare("SELECT allow_roles, allow_user_ids, is_enabled FROM page_permissions WHERE company_id=? AND slug=? LIMIT 1"); $st->execute([$company_id, $PAGE_SLUG]); $perm = $st->fetch(PDO::FETCH_ASSOC); if ($perm) { if ((int)$perm['is_enabled'] === 1) { $roles = json_decode($perm['allow_roles'] ?? '[]', true) ?: []; $uids = json_decode($perm['allow_user_ids'] ?? '[]', true) ?: []; $allowed = in_array($user_role, $roles, true) || in_array($user_id, array_map('intval', $uids), true); } else { $allowed = false; } } else { $allowed = !$DENY_IF_MISSING_ROW; } } catch (Throwable $e) { $allowed = false; // safer default on DB errors } } if (!$allowed) { http_response_code(403); echo "<h1>403 Forbidden</h1>" . "<p>Access denied for slug: <b>" . htmlspecialchars($PAGE_SLUG, ENT_QUOTES, 'UTF-8') . "</b></p>"; exit; } /* (Optional) Extra strict rule — DEFAULT OFF (पुराने custom rules यहाँ रखें) */ $STRICT_KARIGAR_GUARD = false; // <- OFF रखें ताकि DB-ACL primary रहे if ($STRICT_KARIGAR_GUARD) { if (!in_array($role, ['owner','admin'], true)) { http_response_code(403); echo "403\nAccess denied. Only Owner/Admin or mapped Karigar can view."; exit; } } /* =================== /SECTION A: Auth + ACL (Reusable) =================== */ /* ---------- Helpers ---------- */ function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } function gv($k,$d=null){ return isset($_GET[$k]) ? (is_array($_GET[$k])?$_GET[$k]:trim((string)$_GET[$k])) : $d; } function n($v,$dec=0){ return number_format((float)$v, $dec, '.', ''); } function _table_exists(PDO $pdo, $name){ try { $pdo->query("SELECT 1 FROM `$name` LIMIT 1"); return true; } catch(Exception $e){ return false; } } function half_month_range(int $y,int $m,int $half){ $start = sprintf('%04d-%02d-%02d', $y, $m, $half===1?1:16); $end = (new DateTime("$y-$m-01"))->modify($half===1?'+14 days':'last day of this month')->format('Y-m-d'); return [$start,$end]; } function date_list($start,$end){ $out=[]; $d=new DateTime($start); $e=new DateTime($end); for(; $d <= $e; $d->modify('+1 day')) $out[]=$d->format('Y-m-d'); return $out; } /* ---------- Filters ---------- */ $today = new DateTime('now'); $month_str = gv('month', $today->format('Y-m')); $half = (int)gv('half', 1); [$Y,$M] = array_map('intval', explode('-', $month_str)); [$date_from,$date_to] = half_month_range($Y,$M,$half); $khata_id = (int)gv('khata_id', 0); $karigar_id = (int)gv('karigar_id', 0); // OPTIONAL $machine_id = (int)gv('machine_id', 0); $do_csv = (int)gv('csv',0) === 1; /* ---------- Khatas (with ranges) ---------- */ $KHATAS=[]; $KHATA_MAP=[]; if (_table_exists($pdo,'khatas')){ $st=$pdo->prepare("SELECT id, code, name, machine_from, machine_to FROM khatas WHERE company_id=? AND (is_active=1 OR is_active IS NULL) ORDER BY id"); $st->execute([$company_id]); foreach($st->fetchAll(PDO::FETCH_ASSOC) as $r){ $KHATAS[]=$r; $KHATA_MAP[(int)$r['id']] = [ 'code'=>$r['code'],'name'=>$r['name'], 'from'=>(int)$r['machine_from'],'to'=>(int)$r['machine_to'] ]; } } /* ---------- Karigar dropdown source + name map (SIMPLE: karigar_name only) ---------- */ $KARIGAR_LIST = []; // [{id,name}] $KNAME_BY_ID = []; // id => name if (_table_exists($pdo,'loom_karigar_master')){ $args = [$company_id]; $sql = "SELECT id, karigar_name AS kname, khata_id FROM loom_karigar_master WHERE company_id=?"; if ($khata_id > 0) { $sql .= " AND khata_id=?"; $args[] = $khata_id; } $sql .= " ORDER BY karigar_name"; $q = $pdo->prepare($sql); $q->execute($args); foreach ($q->fetchAll(PDO::FETCH_ASSOC) as $r) { $id = (int)$r['id']; $nm = trim((string)($r['kname'] ?? '')); if ($nm === '') $nm = 'Karigar#'.$id; $KARIGAR_LIST[] = ['id'=>$id,'name'=>$nm]; $KNAME_BY_ID[$id] = $nm; } } /* ---------- Fetch entries (company/date + optional khata + optional machine) ---------- */ $params = [$company_id, $date_from, $date_to]; $where = "company_id=? AND entry_date BETWEEN ? AND ?"; if ($khata_id>0){ $where.=" AND khata_id=?"; $params[]=$khata_id; } if ($machine_id>0){ $where.=" AND machine_id=?"; $params[]=$machine_id; } $sql = "SELECT id, entry_date, machine_id, khata_id, lines_json FROM loom_production_entry WHERE $where ORDER BY entry_date, id"; $stmt = $pdo->prepare($sql); $stmt->execute($params); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); /* ---------- Build aggregates ---------- */ $DATES = date_list($date_from,$date_to); $DAY = []; // per-date, per-karigar sum $MACHINE_SET = []; // unique machines post-filters $KARIGAR_TOTALS = []; // per-karigar total $ALL_KARIGARS_SET = []; // for deciding columns when karigar not selected foreach($rows as $r){ $ej = $r['lines_json']; if(!$ej) continue; $decoded = json_decode($ej,true); if(!is_array($decoded)) continue; if (isset($decoded['karigar_id']) || isset($decoded['meter'])) $decoded = [$decoded]; foreach($decoded as $line){ if(!is_array($line)) continue; $kid = (int)($line['karigar_id'] ?? 0); if ($karigar_id>0 && $kid !== $karigar_id) continue; $mtr = (float)($line['meter'] ?? 0.0); if ($mtr==0) continue; $dt = isset($line['date']) && $line['date'] ? substr($line['date'],0,10) : substr($r['entry_date'],0,10); if($dt<$date_from || $dt>$date_to) continue; $mc = (int)$r['machine_id']; if ($mc>0) $MACHINE_SET[$mc] = true; if(!isset($DAY[$dt])) $DAY[$dt] = []; if(!isset($DAY[$dt][$kid])) $DAY[$dt][$kid] = 0.0; $DAY[$dt][$kid] += $mtr; if(!isset($KARIGAR_TOTALS[$kid])) $KARIGAR_TOTALS[$kid]=0.0; $KARIGAR_TOTALS[$kid] += $mtr; $ALL_KARIGARS_SET[$kid] = true; if (!isset($KNAME_BY_ID[$kid])) $KNAME_BY_ID[$kid] = 'Karigar#'.$kid; // fallback } } /* Ensure every date shows (even if empty) */ foreach($DATES as $d){ if (!isset($DAY[$d])) $DAY[$d]=[]; } /* Machines for small header meta */ $MACHINES = array_keys($MACHINE_SET); sort($MACHINES); /* Decide table columns (karigar list) */ $COL_KARIGARS = []; if ($karigar_id>0) { $COL_KARIGARS = [$karigar_id]; } else { $COL_KARIGARS = array_keys($ALL_KARIGARS_SET); usort($COL_KARIGARS, function($a,$b) use($KNAME_BY_ID){ return strcasecmp($KNAME_BY_ID[$a]??('K#'.$a), $KNAME_BY_ID[$b]??('K#'.$b)); }); } /* Machine dropdown options */ $MACHINE_OPTIONS = []; if ($khata_id>0 && isset($KHATA_MAP[$khata_id]['from'],$KHATA_MAP[$khata_id]['to'])){ $from = (int)$KHATA_MAP[$khata_id]['from']; $to = (int)$KHATA_MAP[$khata_id]['to']; if ($from>0 && $to>=$from){ for($i=$from;$i<=$to;$i++) $MACHINE_OPTIONS[] = $i; } } if (empty($MACHINE_OPTIONS)){ $qq = $pdo->prepare("SELECT DISTINCT machine_id FROM loom_production_entry WHERE company_id=? ORDER BY machine_id ASC"); $qq->execute([$company_id]); foreach($qq->fetchAll(PDO::FETCH_ASSOC) as $rr){ $mc=(int)$rr['machine_id']; if($mc>0) $MACHINE_OPTIONS[]=$mc; } } $MACHINE_OPTIONS = array_values(array_unique($MACHINE_OPTIONS)); sort($MACHINE_OPTIONS); /* ---------- CSV export (Taka column removed) ---------- */ if ($do_csv){ header('Content-Type: text/csv; charset=utf-8'); $kar_lbl = ($karigar_id>0 ? $karigar_id : 'ALL'); $mc_lbl = ($machine_id>0 ? ('_MC'.$machine_id) : ''); header('Content-Disposition: attachment; filename=loom_patia_machine_daily_'.$kar_lbl.$mc_lbl.'_'.$month_str.'_H'.$half.'.csv'); $out = fopen('php://output','w'); // header row: Date, karigar names... $head = ['Date']; foreach($COL_KARIGARS as $kid){ $head[] = $KNAME_BY_ID[$kid] ?? ('Karigar#'.$kid); } fputcsv($out,$head); foreach($DATES as $d){ $row = [date('d-m-Y', strtotime($d))]; foreach($COL_KARIGARS as $kid){ $val = (float)($DAY[$d][$kid] ?? 0); $row[] = n($val,0); } fputcsv($out,$row); } // totals row $tot = ['Total']; foreach($COL_KARIGARS as $kid){ $tot[] = n($KARIGAR_TOTALS[$kid]??0,0); } fputcsv($out,$tot); fclose($out); exit; } /* ---------- UI ---------- */ $MM_GREEN='#34A853'; $MM_BG='#F5FFF7'; $MM_STONE='#5F6368'; $period_lbl = sprintf('%s (Half %d) [%s ➜ %s]', $month_str, $half, $date_from, $date_to); ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Patia — Machine-wise (Daily)</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,'Helvetica Neue',Arial;margin:0;background:<?=h($MM_BG)?>;color:#222;} .wrap{max-width:1280px;margin:20px auto;padding:0 16px;} .card{background:#fff;border-radius:10px;box-shadow:0 4px 14px rgba(0,0,0,.06);padding:14px 16px;margin-bottom:14px;} .row{display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end} .field{display:flex;flex-direction:column;min-width:160px} label{font-size:12px;color:<?=$MM_STONE?>;margin-bottom:4px} select,input[type="month"]{padding:8px 10px;border:1px solid #ddd;border-radius:8px} .btn{background:<?=$MM_GREEN?>;color:#fff;border:none;border-radius:8px;padding:10px 14px;cursor:pointer} .btn-outline{background:#fff;color:<?=$MM_GREEN?>;border:1px solid <?=$MM_GREEN?>} .pill{display:inline-block;padding:6px 10px;background:#eaf7ee;border-radius:999px;color:#0b6e2b;font-size:12px;margin-left:6px} .muted{color:<?=$MM_STONE?>} table{width:100%;border-collapse:separate;border-spacing:0;margin-top:10px} thead th{background:#f7f7f7;position:sticky;top:0;z-index:2} th,td{border-bottom:1px solid #eee;padding:8px 10px;text-align:center;white-space:nowrap} th:first-child,td:first-child{text-align:left} .zero{color:#d00; font-weight:600;} .klist{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px} .kchip{display:inline-block;padding:6px 10px;background:#eef6ff;border:1px solid #dfe9ff;border-radius:999px;font-size:12px} @media print{ .no-print{display:none !important;} body{background:#fff;} .wrap{max-width:100%;margin:0;padding:0;} .card{box-shadow:none;border-radius:0;margin:0;padding:0;} thead{display:table-header-group} tfoot{display:table-footer-group} } </style> </head> <body> <div class="wrap"> <div class="card no-print"> <form class="row" method="get" id="filterForm"> <!-- Khata --> <div class="field"> <label>Khata</label> <select name="khata_id" id="khata"> <option value="0">All Khata</option> <?php foreach($KHATAS as $k): ?> <option value="<?=h($k['id'])?>" <?= $khata_id===(int)$k['id']?'selected':''?>> <?=h($k['code'])?> — <?=h($k['name'])?> </option> <?php endforeach; ?> </select> </div> <!-- Karigar --> <div class="field"> <label>Karigar</label> <select name="karigar_id" id="karigar"> <option value="0" <?= $karigar_id===0?'selected':''?>>All Karigars</option> <?php foreach($KARIGAR_LIST as $k): ?> <option value="<?=h($k['id'])?>" <?= $karigar_id===$k['id']?'selected':''?>><?=h($k['name'])?></option> <?php endforeach; ?> </select> </div> <!-- Machine --> <div class="field"> <label>Machine</label> <select name="machine_id" id="machine"> <option value="0" <?= $machine_id===0?'selected':''; ?>>All Machines</option> <?php foreach($MACHINE_OPTIONS as $mc): ?> <option value="<?=h($mc)?>" <?= $machine_id===$mc?'selected':''?>>MC <?=h($mc)?></option> <?php endforeach; ?> </select> </div> <div class="field"> <label>Month</label> <input type="month" name="month" value="<?=h($month_str)?>"> </div> <div class="field"> <label>Half</label> <select name="half"> <option value="1" <?= $half===1?'selected':''?>>1–15</option> <option value="2" <?= $half===2?'selected':''?>>16–End</option> </select> </div> <div class="field"> <button class="btn" type="submit">Apply</button> </div> <div class="field"> <a class="btn btn-outline" href="?<?=http_build_query([ 'khata_id'=>$khata_id,'karigar_id'=>$karigar_id,'machine_id'=>$machine_id,'month'=>$month_str,'half'=>$half,'csv'=>1 ])?>">Export CSV</a> </div> <div class="field"><button type="button" class="btn" onclick="window.print()">Print</button></div> <div class="field"><button type="button" class="btn btn-outline" onclick="window.print()">PDF</button></div> <div style="margin-left:auto" class="muted"> <?php $period_lbl = sprintf('%s (Half %d) [%s ➜ %s]', $month_str, $half, $date_from, $date_to); ?> Period: <span class="pill"><?=h($period_lbl)?></span> </div> </form> </div> <div class="card"> <div style="text-align:center;margin-bottom:8px"> <div style="font-weight:700">Machine-wise Patia (Daily)</div> <div class="muted"><?=h(date('F-Y', strtotime($month_str.'-01')))?> · <?= $machine_id>0 ? 'Machine MC '.h($machine_id) : 'All Machines' ?></div> </div> <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px"> <div> <?php if($khata_id>0): $kh = $KHATA_MAP[$khata_id] ?? null; $kh_code = $kh['code'] ?? $khata_id; ?> <span class="pill">Khata <?= h($kh_code) ?></span> <?php endif; ?> <?php if($karigar_id>0): ?><span class="pill"><?=h($KNAME_BY_ID[$karigar_id] ?? 'Karigar')?></span><?php endif; ?> <?php if($machine_id>0): ?><span class="pill">MC <?=h($machine_id)?></span><?php endif; ?> </div> <div class="muted no-print">Machines: <?= ($machine_id>0 ? 1 : count($MACHINES)) ?> | Days: <?=count($DATES)?></div> </div> <?php // Chips for selected machine: unique karigars + totals (desc) $KARIGAR_CHIPS_HTML = ''; if ($machine_id>0 && !empty($KARIGAR_TOTALS)){ arsort($KARIGAR_TOTALS); $chips = []; foreach($KARIGAR_TOTALS as $kid=>$mtr){ $nm = $KNAME_BY_ID[$kid] ?? ('Karigar#'.$kid); $chips[] = '<span class="kchip">'.h($nm).' — '.h(n($mtr,0)).' m</span>'; } $KARIGAR_CHIPS_HTML = implode(' ', $chips); } ?> <?php if($machine_id>0): ?> <div class="klist no-print" style="margin-bottom:8px"> <?php if ($KARIGAR_CHIPS_HTML!=='') echo $KARIGAR_CHIPS_HTML; else echo '<span class="muted">No karigar data for this machine in selected period.</span>'; ?> </div> <?php endif; ?> <div style="overflow:auto;max-height:70vh"> <table> <thead> <tr> <th style="min-width:120px">Date</th> <?php foreach($COL_KARIGARS as $kid): ?> <th><?=h($KNAME_BY_ID[$kid] ?? ('Karigar#'.$kid))?></th> <?php endforeach; ?> </tr> </thead> <tbody> <?php foreach($DATES as $d): ?> <tr> <td><?=h(date('d-m-Y', strtotime($d)))?></td> <?php foreach($COL_KARIGARS as $kid): $val = (float)($DAY[$d][$kid] ?? 0); if ($val>0): ?> <td><?= n($val,0) ?></td> <?php else: ?> <td class="zero">0</td> <?php endif; endforeach; ?> </tr> <?php endforeach; ?> </tbody> <tfoot> <tr> <td><strong>Total</strong></td> <?php foreach($COL_KARIGARS as $kid): ?> <td><strong><?= n($KARIGAR_TOTALS[$kid]??0,0) ?></strong></td> <?php endforeach; ?> </tr> </tfoot> </table> </div> </div> </div> </body> </html>