« Back to History
karigar_wise_production.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php /* ============================================================================= Karigar-wise Loom Production Report (Patia) - Grid: Date (rows) × Machine (columns), values = meters for chosen Karigar - Uses line-level dates in lines_json: [{"karigar_id","meter","date"}] - If loom_karigar_master.user_id == logged user → lock to that karigar (no dropdown) - Mr manager palette; fully mobile-responsive; button animation + spinner - Print & PDF buttons (PDF via browser "Save as PDF") with A4 print CSS - Page-local only. No global/base changes. ============================================================================= */ error_reporting(E_ALL); ini_set('display_errors',1); /* [1] AUTH & PDO */ require __DIR__ . '/../modules/auth/auth.php'; require_login(); $u = auth_user(); $company_id = (int)$u['company_id']; $user_id = (int)$u['id']; $pdo = $GLOBALS['pdo'] ?? null; if(!$pdo){ require __DIR__ . '/../core/db.php'; } /* [2] HELPERS */ function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } function half_month_range(int $y,int $m,int $half){ $start = sprintf('%04d-%02d-%02d',$y,$m,$half===1?1:16); $end = sprintf('%04d-%02d-%02d',$y,$m,$half===1?15:(int)date('t',strtotime("$y-$m-01"))); return [$start,$end]; } function date_list($s,$e){ $o=[]; $t=strtotime($s); $E=strtotime($e); while($t<=$E){ $o[]=date('Y-m-d',$t); $t=strtotime('+1 day',$t);} return $o; } function tbl_cols(PDO $pdo,string $tbl){ try{$rs=$pdo->query("SHOW COLUMNS FROM `$tbl`")->fetchAll(PDO::FETCH_ASSOC); return array_column($rs,'Field');}catch(Throwable $e){return[];} } function norm_line_date($v,int $Y,int $M,$row_entry){ $s=is_string($v)||is_int($v)?trim((string)$v):''; if($s==='') return $row_entry?:''; if(preg_match('/^\d{4}-\d{2}-\d{2}$/',$s)) return $s; if(preg_match('/^\d{1,2}$/',$s)){ $d=max(1,min(31,(int)$s)); $last=(int)date('t',strtotime("$Y-$M-01")); $d=min($d,$last); return sprintf('%04d-%02d-%02d',$Y,$M,$d); } $t=strtotime($s); return $t?date('Y-m-d',$t):($row_entry?:''); } /* Machines map: id => [no, khata_id] */ function load_machine_map(PDO $pdo,int $co): array { foreach([ "SELECT id, machine_no AS no, khata_id FROM machines WHERE company_id=?", "SELECT id, code AS no, khata_id FROM machines WHERE company_id=?" ] as $sql){ try{ $st=$pdo->prepare($sql); $st->execute([$co]); $map=[]; foreach($st as $r){ $map[(int)$r['id']]=['no'=>(string)$r['no'],'khata_id'=>isset($r['khata_id'])?(int)$r['khata_id']:null]; } if($map) return $map; }catch(Throwable $e){} } // Fallback from production try{ $st=$pdo->prepare("SELECT DISTINCT machine_id AS id FROM loom_production_entry WHERE company_id=?"); $st->execute([$co]); $map=[]; foreach($st as $r){ $id=(int)$r['id']; $map[$id]=['no'=>(string)$id,'khata_id'=>null]; } return $map; }catch(Throwable $e){ return []; } } /* Khata list (MST codes from loom_karigar_master if present) */ function load_khatas(PDO $pdo,int $co): array { try{ $st=$pdo->prepare("SELECT DISTINCT khata_id AS id, khata_code FROM loom_karigar_master WHERE company_id=? AND khata_id IS NOT NULL AND khata_code<>'' ORDER BY khata_id"); $st->execute([$co]); $rows=$st->fetchAll(PDO::FETCH_ASSOC); if($rows){ foreach($rows as &$r){ $r['name']=$r['khata_code']; } return $rows; } }catch(Throwable $e){} foreach([ "SELECT id, name FROM khatas WHERE company_id=? ORDER BY id", "SELECT id, name FROM khata WHERE company_id=? ORDER BY id", ] as $sql){ try{$st=$pdo->prepare($sql);$st->execute([$co]);$r=$st->fetchAll(PDO::FETCH_ASSOC); if($r) return $r;}catch(Throwable $e){} } return []; } /* Karigar options + names */ function load_karigars(PDO $pdo,int $co): array { try{ $st=$pdo->prepare("SELECT id, karigar_name FROM loom_karigar_master WHERE company_id=? AND is_active=1 ORDER BY karigar_name"); $st->execute([$co]); $o=[]; foreach($st as $r){ $o[(int)$r['id']] = (string)$r['karigar_name']; } return $o; }catch(Throwable $e){ return []; } } /* Resolve current user's karigar_id if loom_karigar_master.user_id exists */ function current_user_karigar_id(PDO $pdo,int $co,int $user_id): ?int { $cols = tbl_cols($pdo,'loom_karigar_master'); if(in_array('user_id',$cols,true)){ try{ $st=$pdo->prepare("SELECT id FROM loom_karigar_master WHERE company_id=? AND user_id=? AND is_active=1 LIMIT 1"); $st->execute([$co,$user_id]); $id=$st->fetchColumn(); if($id) return (int)$id; }catch(Throwable $e){} } return null; } /* [3] FILTERS/STATE */ $khatas = load_khatas($pdo,$company_id); $machinesMap = load_machine_map($pdo,$company_id); $kar_opts = load_karigars($pdo,$company_id); $locked_karigar_id = current_user_karigar_id($pdo,$company_id,$user_id); $sel_karigar = $locked_karigar_id ?? ( isset($_GET['karigar_id']) ? (int)$_GET['karigar_id'] : ( array_key_first($kar_opts) ?? 0 ) ); $sel_khata = isset($_GET['khata_id']) ? (int)$_GET['khata_id'] : 0; // optional $sel_year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y'); $sel_month = isset($_GET['month'])? (int)$_GET['month']: (int)date('n'); $sel_half = isset($_GET['half']) ? (int)$_GET['half'] : 1; [$from_date,$to_date] = half_month_range($sel_year,$sel_month,$sel_half); $month_start = sprintf('%04d-%02d-01',$sel_year,$sel_month); $month_end = sprintf('%04d-%02d-%02d',$sel_year,$sel_month,(int)date('t',strtotime($month_start))); /* [4] AGGREGATE for selected karigar */ $matrix = []; // [date][machine_id] = meters $mc_ids = []; // machines encountered try{ $sql = "SELECT entry_date, machine_id, khata_id, lines_json FROM loom_production_entry WHERE company_id=? AND entry_date BETWEEN ? AND ?"; $params = [$company_id, $month_start, $month_end]; if ($sel_khata){ $sql .= " AND khata_id=?"; $params[]=$sel_khata; } $sql .= " ORDER BY entry_date"; $st=$pdo->prepare($sql); $st->execute($params); while($r=$st->fetch(PDO::FETCH_ASSOC)){ $row_entry = $r['entry_date'] ?? ''; $mid = (int)($r['machine_id'] ?? 0); if($mid<=0) continue; $arr = json_decode((string)$r['lines_json'], true); if(!is_array($arr)) continue; foreach($arr as $ln){ if( (int)($ln['karigar_id'] ?? 0) !== $sel_karigar ) continue; $mtr = isset($ln['meter']) ? (float)$ln['meter'] : 0.0; if($mtr<=0) continue; $ld = norm_line_date($ln['date'] ?? '', $sel_year,$sel_month,$row_entry); if(!$ld) continue; if($ld < $from_date || $ld > $to_date) continue; $matrix[$ld][$mid] = ($matrix[$ld][$mid] ?? 0) + $mtr; $mc_ids[$mid] = true; } } }catch(Throwable $e){ // page-local silent } /* Build machine columns sorted */ $mc_cols = array_keys($mc_ids); usort($mc_cols, function($a,$b) use($machinesMap){ $na = $machinesMap[$a]['no'] ?? (string)$a; $nb = $machinesMap[$b]['no'] ?? (string)$b; if (is_numeric($na) && is_numeric($nb)) return (int)$na <=> (int)$nb; return strnatcasecmp($na,$nb); }); /* Row dates for the half */ $dates = date_list($from_date,$to_date); /* Labels */ $kar_name = $kar_opts[$sel_karigar] ?? ('Karigar '.$sel_karigar); $khata_lbl = ''; if($sel_khata){ foreach($khatas as $k){ if((int)$k['id']===$sel_khata){ $khata_lbl = $k['khata_code'] ?? ($k['name'] ?? ('Khata '.$sel_khata)); break; } } } /* For filename */ $half_label = $sel_half==1 ? '1-15' : '16-EOM'; $month_names=[1=>"Jan",2=>"Feb",3=>"Mar",4=>"Apr",5=>"May",6=>"Jun",7=>"Jul",8=>"Aug",9=>"Sep",10=>"Oct",11=>"Nov",12=>"Dec"]; $period_label = ($month_names[$sel_month] ?? $sel_month).'_'.$half_label.'_'.$sel_year; $base_filename = "Karigar-Patia-".preg_replace('/[^A-Za-z0-9_-]+/','_', $kar_name)."-".$period_label; ?> <!doctype html> <html> <head> <meta charset="utf-8"> <title>Karigar-wise Loom Production Report (Patia)</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> :root{ --mm-primary:#34A853; --mm-bg:#F5FFF7; --mm-text:#333; --mm-sub:#5F6368; --mm-accent:#A7D8DE; --mm-hi:#FFF9C4; --card:#fff; --bd:#e6e6e6; } *{box-sizing:border-box} body{font-family:system-ui,Segoe UI,Roboto,Arial,sans-serif;background:var(--mm-bg);color:var(--mm-text);margin:0;padding:16px;} .wrap{max-width:1100px;margin:0 auto;} h1{margin:0 0 12px;font-size:22px;color:var(--mm-primary)} .card{background:var(--card);border:1px solid var(--bd);border-radius:14px;box-shadow:0 2px 10px rgba(0,0,0,.04);padding:12px;margin-bottom:14px;} /* Filters */ .filters{display:grid;grid-template-columns:repeat(10,minmax(120px,1fr));gap:10px;align-items:end} label{font-size:12px;color:var(--mm-sub);display:block;margin-bottom:4px} select,button{width:100%;padding:10px 12px;border:1px solid var(--bd);border-radius:12px;font-size:14px;background:#fff} .muted{color:var(--mm-sub);font-size:13px;padding:10px 0} .pill{display:inline-block;padding:2px 8px;border-radius:999px;background:#e8f5ee;color:var(--mm-primary);font-size:12px;margin-left:6px} /* Buttons */ .btn{background:var(--mm-primary);color:#fff;border:none;cursor:pointer;position:relative;overflow:hidden;transition:transform .08s ease, box-shadow .2s} .btn:active{transform:scale(0.98)} .btn.loading{pointer-events:none;opacity:.9} .btn .spinner{display:none;width:16px;height:16px;border:2px solid #fff;border-right-color:transparent;border-radius:50%;position:absolute;right:12px;top:50%;transform:translateY(-50%);animation:spin 0.8s linear infinite} .btn.loading .spinner{display:inline-block} .btn-ghost{background:#fff;color:var(--mm-primary);border:1px solid var(--mm-primary)} .btn-ghost:active{transform:scale(0.98)} @keyframes spin{to{transform:translateY(-50%) rotate(360deg)}} /* Table */ .tbl-wrap{overflow-x:auto;border:1px solid var(--bd);border-radius:12px} table{width:100%;border-collapse:collapse;font-size:14px;min-width:640px} th,td{border-bottom:1px solid var(--bd);padding:8px 10px;text-align:right} thead th{background:var(--mm-accent);font-weight:600} th.date,td.date{text-align:left;white-space:nowrap;position:sticky;left:0;background:#fff;border-right:1px solid var(--bd);z-index:2} tbody tr:nth-child(odd){background:#fafafa} .total{background:var(--mm-hi);font-weight:600} /* Mobile responsive tweaks */ @media (max-width: 980px){ .filters{grid-template-columns:repeat(2,1fr)} table{font-size:13px;min-width:560px} .btn{padding:10px 40px 10px 12px} } @media (max-width: 420px){ table{font-size:12px;min-width:520px} } /* Print styles (A4) */ @media print{ @page{ size: A4 portrait; margin: 12mm; } body{ background:#fff; padding:0; } .card, .filters, .actions-bar{ page-break-inside: avoid; } .filters, .actions-bar{ display:none !important; } .tbl-wrap{ border:none; } th.date, td.date{ position:static; background:#fff; } thead{ display: table-header-group; } tfoot{ display: table-footer-group; } } </style> </head> <body> <div class="wrap"> <h1>Karigar-wise Loom Production Report <small class="pill">Patia</small></h1> <div class="card"> <form method="get" class="filters" id="filtersForm"> <div> <label>Karigar</label> <?php if($locked_karigar_id): ?> <input type="hidden" name="karigar_id" value="<?= (int)$sel_karigar ?>"> <div class="muted"><?= h($kar_name) ?><span class="pill">locked</span></div> <?php else: ?> <select name="karigar_id" required> <?php foreach($kar_opts as $id=>$nm): ?> <option value="<?= (int)$id ?>" <?= $sel_karigar==$id?'selected':'' ?>><?= h($nm) ?></option> <?php endforeach; ?> </select> <?php endif; ?> </div> <div> <label>Khata (optional)</label> <select name="khata_id"> <option value="0" <?= $sel_khata===0?'selected':'' ?>>All</option> <?php foreach($khatas as $k): $lbl=$k['khata_code'] ?? ($k['name'] ?? ('Khata '.$k['id'])); ?> <option value="<?= (int)$k['id'] ?>" <?= $sel_khata==(int)$k['id']?'selected':'' ?>><?= h($lbl) ?></option> <?php endforeach; ?> </select> </div> <div> <label>Month</label> <select name="month"><?php $mn=[1=>"Jan",2=>"Feb",3=>"Mar",4=>"Apr",5=>"May",6=>"Jun",7=>"Jul",8=>"Aug",9=>"Sep",10=>"Oct",11=>"Nov",12=>"Dec"]; foreach($mn as $i=>$nm): ?> <option value="<?= $i ?>" <?= $sel_month==$i?'selected':'' ?>><?= $nm ?></option><?php endforeach; ?> </select> </div> <div> <label>Half</label> <select name="half"> <option value="1" <?= $sel_half==1?'selected':'' ?>>1–15</option> <option value="2" <?= $sel_half==2?'selected':'' ?>>16–EOM</option> </select> </div> <div> <label>Year</label> <select name="year"><?php foreach([date('Y')-1,date('Y'),date('Y')+1] as $yr): ?> <option value="<?= (int)$yr ?>" <?= $sel_year==$yr?'selected':'' ?>><?= (int)$yr ?></option><?php endforeach; ?> </select> </div> <div> <button type="submit" class="btn" id="showBtn" data-fn="<?= h($base_filename) ?>"> Show Report <span class="spinner" aria-hidden="true"></span> </button> </div> <div class="actions-bar" style="display:flex;gap:10px;align-items:end"> <button type="button" class="btn-ghost" id="printBtn">Print</button> <button type="button" class="btn-ghost" id="pdfBtn" data-fn="<?= h($base_filename) ?>">PDF</button> <div class="muted" style="margin-left:auto"> <span class="pill"><?= h($kar_name) ?></span> <?php if($khata_lbl): ?> • <span class="pill"><?= h($khata_lbl) ?></span><?php endif; ?> • Range: <span class="pill"><?= h($from_date) ?> → <?= h($to_date) ?></span> </div> </div> </form> </div> <div class="card tbl-wrap"> <?php $colTotals = array_fill_keys($mc_cols, 0.0); $grand = 0.0; ?> <table> <thead> <tr> <th class="date">Date</th> <?php foreach($mc_cols as $mid): $lbl=$machinesMap[$mid]['no'] ?? $mid; ?> <th><?= h($lbl) ?></th> <?php endforeach; ?> <th>Total</th> </tr> </thead> <tbody> <?php foreach($dates as $d): ?> <?php $rowTotal=0.0; ?> <tr> <td class="date"><?= date('d-M', strtotime($d)) ?></td> <?php foreach($mc_cols as $mid): $val=isset($matrix[$d][$mid])?(float)$matrix[$d][$mid]:0.0; $rowTotal+=$val; $colTotals[$mid]+=$val; $grand+=$val; ?> <td><?= $val>0 ? number_format($val,0) : '' ?></td> <?php endforeach; ?> <td class="total"><?= $rowTotal>0 ? number_format($rowTotal,0) : '' ?></td> </tr> <?php endforeach; ?> <tr class="total"> <td class="date">Total</td> <?php foreach($mc_cols as $mid): ?> <td><?= $colTotals[$mid]>0 ? number_format($colTotals[$mid],0) : '' ?></td> <?php endforeach; ?> <td><?= $grand>0 ? number_format($grand,0) : '' ?></td> </tr> </tbody> </table> </div> </div> <script> /* Show button loading animation */ (function(){ const form = document.getElementById('filtersForm'); const btn = document.getElementById('showBtn'); if(!form || !btn) return; form.addEventListener('submit', function(){ btn.classList.add('loading'); const els = form.querySelectorAll('select,button,input'); els.forEach(el=>{ if(el!==btn) el.disabled=true; }); }); })(); /* Print & PDF (Save-as-PDF) */ (function(){ const printBtn = document.getElementById('printBtn'); const pdfBtn = document.getElementById('pdfBtn'); function doPrint(filename){ const oldTitle = document.title; if(filename){ document.title = filename; } // helps some browsers suggest file name window.print(); setTimeout(()=>{ document.title = oldTitle; }, 750); } if(printBtn){ printBtn.addEventListener('click', ()=> doPrint(null)); } if(pdfBtn){ pdfBtn.addEventListener('click', ()=>{ const fn = pdfBtn.dataset.fn ? (pdfBtn.dataset.fn + '.pdf') : null; doPrint(fn); }); } })(); </script> </body> </html>