« Back to History
loom_patia_machine1.php
|
20260721_154033.php
Initial Bulk Import
Copy Code
<?php /* ============================================================================ File: /erp/loom_salary_report.php Title: Salary Report (Karigar × Quality) — RAW meters, No Rounding + Debug Scope: File-local; Multi-company aware; No global/base changes. Notes: - Uses RAW production meters only (no factors/multipliers). - Robust date normalization for lines_json dates. - No rounding anywhere. ============================================================================ */ header('X-Frame-Options: SAMEORIGIN'); error_reporting(E_ALL); ini_set('display_errors',1); /* [0] Auth + DB */ 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'; } /* [1] Helpers */ function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } function gv($k,$d=null){ return isset($_GET[$k])?trim((string)$_GET[$k]):$d; } function pv($k,$d=null){ return isset($_POST[$k])?trim((string)$_POST[$k]):$d; } function safe_json($s){ $a=json_decode($s,true); return is_array($a)?$a:[]; } function yrmo_half_range(int $y,int $m,string $half): array { $start = sprintf('%04d-%02d-01',$y,$m); $end = date('Y-m-t', strtotime($start)); if($half==='2'){ $start = sprintf('%04d-%02d-16',$y,$m); } if($half==='1'){ $end = sprintf('%04d-%02d-15',$y,$m); } return [$start,$end]; } /* [2] Filters (GET) */ $year = (int)gv('year', date('Y')); $month = (int)gv('month', date('n')); $half = gv('half','1'); // '1' or '2' [$date_from,$date_to] = yrmo_half_range($year,$month,$half); $khata_id = (int)gv('khata_id', 0); $machine_id= (int)gv('machine_id', 0); /* [3] Company name (for header) */ $company_name = "Company #$company_id"; try{ $st = $pdo->prepare("SELECT name FROM companies WHERE id=?"); $st->execute([$company_id]); $nm = $st->fetchColumn(); if($nm){ $company_name = $nm; } }catch(Exception $e){} /* [4] Masters */ // Qualities + rates $QUALS=[]; $QUAL_RATE=[]; $q = $pdo->prepare("SELECT quality_id AS id, quality_name AS name, rate FROM quality_rates WHERE company_id=? ORDER BY quality_name"); $q->execute([$company_id]); foreach($q as $r){ $id=(int)$r['id']; $QUALS[$id]=$r['name']; $QUAL_RATE[$id]=(float)$r['rate']; } // Karigar list from loom_karigar_master (name + entry_name + khata_code) $KAR_NAME=[]; $KAR_ENTRY=[]; $KAR_KHATA=[]; $HAS_BANK=[]; $st=$pdo->prepare("SELECT id, karigar_name, entry_name, khata_code FROM loom_karigar_master WHERE company_id=? ORDER BY karigar_name"); $st->execute([$company_id]); foreach($st as $r){ $id=(int)$r['id']; $KAR_NAME[$id] = $r['karigar_name'] ?: ("Karigar #$id"); $KAR_ENTRY[$id] = $r['entry_name'] ?? ''; $KAR_KHATA[$id] = $r['khata_code'] ?? ''; } // Bank presence map try{ $st=$pdo->prepare("SELECT karigar_id FROM loom_bank_master WHERE company_id=? AND (is_default=1 OR is_default IS NULL) AND (COALESCE(account_no,'')<>'' OR COALESCE(upi_id,'')<>'')"); $st->execute([$company_id]); foreach($st as $r){ $HAS_BANK[(int)$r['karigar_id']] = true; } }catch(Exception $e){} /* [5] Fetch production rows in range */ $sql = "SELECT id, entry_date, machine_id, khata_id, quality_id, lines_json FROM loom_production_entry WHERE company_id=? AND entry_date BETWEEN ? AND ?"; $args = [$company_id,$date_from,$date_to]; if($khata_id>0){ $sql.=" AND khata_id=?"; $args[]=$khata_id; } if($machine_id>0){ $sql.=" AND machine_id=?"; $args[]=$machine_id; } $sql.=" ORDER BY entry_date,id"; $rows = $pdo->prepare($sql); $rows->execute($args); $rows = $rows->fetchAll(PDO::FETCH_ASSOC); /* [6] Aggregate meters per (karigar × quality) — robust date normalization, RAW meters only */ function norm_line_date($val, $row_entry_date, int $year, int $month){ $s = trim((string)$val); if ($s === '' || strtolower($s) === 'null') { return $row_entry_date; } // fallback // "1".."31" if (preg_match('/^\d{1,2}$/', $s)) { $day = (int)$s; if ($day >= 1 && $day <= 31) return sprintf('%04d-%02d-%02d', $year, $month, $day); } // DD-MM-YYYY or DD/MM/YYYY if (preg_match('/^(\d{1,2})[-\/](\d{1,2})[-\/](\d{4})$/', $s, $m)) { $d = (int)$m[1]; $mo = (int)$m[2]; $y = (int)$m[3]; if (checkdate($mo, $d, $y)) return sprintf('%04d-%02d-%02d', $y, $mo, $d); } // YYYY/MM/DD or YYYY-MM-DD if (preg_match('/^(\d{4})[-\/](\d{1,2})[-\/](\d{1,2})$/', $s, $m)) { $y = (int)$m[1]; $mo = (int)$m[2]; $d = (int)$m[3]; if (checkdate($mo, $d, $y)) return sprintf('%04d-%02d-%02d', $y, $mo, $d); } // already YYYY-MM-DD if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $s)) return $s; return $row_entry_date; } $karigar_set=[]; $quality_set=[]; $MTR=[]; $MTR_KAR_TOT=[]; $_skipped_bad_date = 0; $_skipped_out_range = 0; $from_ts = strtotime($date_from.' 00:00:00'); $to_ts = strtotime($date_to.' 23:59:59'); // inclusive end foreach($rows as $row){ $qid=(int)$row['quality_id']; if($qid<=0) continue; $quality_set[$qid]=true; $row_entry = substr((string)$row['entry_date'], 0, 10); if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $row_entry)) { $row_entry = date('Y-m-d', strtotime($row['entry_date'])); } $lines = safe_json($row['lines_json']); foreach($lines as $ln){ $kid = (int)($ln['karigar_id'] ?? 0); $m = (float)($ln['meter'] ?? 0); if($kid<=0 || $m<=0) continue; $dYmd = norm_line_date($ln['date'] ?? '', $row_entry, $year, $month); $ts = strtotime($dYmd); if (!$ts) { $_skipped_bad_date++; $ts = strtotime($row_entry); } if ($ts < $from_ts || $ts > $to_ts) { $_skipped_out_range++; continue; } $karigar_set[$kid] = true; $MTR[$kid][$qid] = ($MTR[$kid][$qid] ?? 0) + $m; $MTR_KAR_TOT[$kid] = ($MTR_KAR_TOT[$kid] ?? 0) + $m; } } $quality_ids = array_keys($quality_set); usort($quality_ids, fn($a,$b)=>strcasecmp($QUALS[$a]??'', $QUALS[$b]??'')); $kar_list = array_keys($karigar_set); usort($kar_list, fn($a,$b)=>strcasecmp($KAR_NAME[$a]??("K$a"), $KAR_NAME[$b]??("K$b"))); /* [7] Money (Extra/Deduction/Advance) — best-effort sum with common column names */ function sum_money_tbl($pdo,$company_id,$tbl,$kid,$from,$to,$date_cols=['date','entry_date','created_at'],$amt_cols=['amount','amt','value']){ foreach($date_cols as $dc){ foreach($amt_cols as $ac){ try{ $st=$pdo->prepare("SELECT SUM($ac) s FROM $tbl WHERE company_id=? AND karigar_id=? AND $dc BETWEEN ? AND ?"); $st->execute([$company_id,$kid,$from,$to]); $v=$st->fetchColumn(); if($v!==false && $v!==null) return (float)$v; }catch(Exception $e){} }} return 0.0; } $EXTRA=$DEDUCT=$ADV=[]; foreach($kar_list as $kid){ $EXTRA[$kid] = sum_money_tbl($pdo,$company_id,'extra_data',$kid,$date_from,$date_to); $DEDUCT[$kid] = sum_money_tbl($pdo,$company_id,'deduction_data',$kid,$date_from,$date_to); $ADV[$kid] = sum_money_tbl($pdo,$company_id,'advance_data',$kid,$date_from,$date_to); } /* [8] Gross/Net per karigar — EXACT (no rounding at all) */ $GROSS=[]; $NET=[]; foreach($kar_list as $kid){ $g=0.0; foreach($quality_ids as $qid){ $g += ($MTR[$kid][$qid] ?? 0) * ($QUAL_RATE[$qid] ?? 0); } $GROSS[$kid]=$g; $NET[$kid] = $g + ($EXTRA[$kid]??0) - ($DEDUCT[$kid]??0) - ($ADV[$kid]??0); } /* Bank/Cash split (exact) + grand */ $bank_total=0.0; $cash_total=0.0; $grand_net=0.0; foreach($kar_list as $kid){ $n = (float)($NET[$kid] ?? 0); $grand_net += $n; if(!empty($HAS_BANK[$kid])) $bank_total += $n; else $cash_total += $n; } /* [9] Ensure report tables exist (for save) */ $pdo->exec("CREATE TABLE IF NOT EXISTS salary_reports ( id BIGINT PRIMARY KEY AUTO_INCREMENT, company_id BIGINT NOT NULL, date_from DATE NOT NULL, date_to DATE NOT NULL, khata_id BIGINT NOT NULL DEFAULT 0, machine_id BIGINT NOT NULL DEFAULT 0, month INT NOT NULL, half CHAR(1) NOT NULL, year INT NOT NULL, created_by BIGINT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, meta TEXT NULL, UNIQUE KEY uniq_period (company_id, date_from, date_to, khata_id, machine_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); $pdo->exec("CREATE TABLE IF NOT EXISTS salary_report_items ( id BIGINT PRIMARY KEY AUTO_INCREMENT, company_id BIGINT NOT NULL, report_id BIGINT NOT NULL, karigar_id BIGINT NOT NULL, karigar_name VARCHAR(120) NULL, entry_name VARCHAR(120) NULL, khata_code VARCHAR(50) NULL, meters_json TEXT NULL, total_mtr DECIMAL(12,2) DEFAULT 0, gross DECIMAL(12,2) DEFAULT 0, extra DECIMAL(12,2) DEFAULT 0, deduction DECIMAL(12,2) DEFAULT 0, advance DECIMAL(12,2) DEFAULT 0, net DECIMAL(12,2) DEFAULT 0, INDEX(report_id), INDEX(company_id, karigar_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); /* [10] SAVE / UPDATE report (unique per period) */ $flash=''; $saved_report_id=null; if ($_SERVER['REQUEST_METHOD']==='POST' && pv('action')==='save_report') { // Find existing $sel=$pdo->prepare("SELECT id FROM salary_reports WHERE company_id=? AND date_from=? AND date_to=? AND khata_id=? AND machine_id=? LIMIT 1"); $sel->execute([$company_id,$date_from,$date_to,$khata_id,$machine_id]); $existing_id = $sel->fetchColumn(); $meta = [ 'company_name'=>$company_name, 'quality_rate'=>$QUAL_RATE, 'quality_names'=>$QUALS, 'filters'=>['khata_id'=>$khata_id,'machine_id'=>$machine_id,'month'=>$month,'half'=>$half,'year'=>$year], 'skipped'=>['bad_date'=>$_skipped_bad_date,'out_of_range'=>$_skipped_out_range], 'rounding'=>'disabled' ]; if($existing_id){ $saved_report_id = (int)$existing_id; $up = $pdo->prepare("UPDATE salary_reports SET month=?, half=?, year=?, created_by=?, meta=? WHERE id=? AND company_id=?"); $up->execute([$month,(string)$half,$year,$user_id,json_encode($meta,JSON_UNESCAPED_UNICODE),$saved_report_id,$company_id]); $pdo->prepare("DELETE FROM salary_report_items WHERE company_id=? AND report_id=?") ->execute([$company_id,$saved_report_id]); $mode='updated'; } else { $ins=$pdo->prepare("INSERT INTO salary_reports (company_id,date_from,date_to,khata_id,machine_id,month,half,year,created_by,meta) VALUES (?,?,?,?,?,?,?,?,?,?)"); $ins->execute([$company_id,$date_from,$date_to,$khata_id,$machine_id,$month,(string)$half,$year,$user_id, json_encode($meta,JSON_UNESCAPED_UNICODE)]); $saved_report_id = (int)$pdo->lastInsertId(); $mode='saved'; } // Insert fresh items (store EXACT values; no rounding) $sti = $pdo->prepare("INSERT INTO salary_report_items (company_id,report_id,karigar_id,karigar_name,entry_name,khata_code,meters_json,total_mtr,gross,extra,deduction,advance,net) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)"); foreach($kar_list as $kid){ $meters = []; foreach($quality_ids as $qid){ if(($MTR[$kid][$qid] ?? 0)>0){ $meters[$qid] = round($MTR[$kid][$qid],2); } } $sti->execute([ $company_id,$saved_report_id,$kid, $KAR_NAME[$kid]??null, $KAR_ENTRY[$kid]??null, $KAR_KHATA[$kid]??null, json_encode($meters,JSON_UNESCAPED_UNICODE), round($MTR_KAR_TOT[$kid]??0,2), round($GROSS[$kid]??0,2), round($EXTRA[$kid]??0,2), round($DEDUCT[$kid]??0,2), round($ADV[$kid]??0,2), round($NET[$kid]??0,2) ]); } $flash = "Report {$mode}. ID: ".$saved_report_id; } /* ============================= HTML ============================== */ ?> <!doctype html> <html> <head> <meta charset="utf-8"> <title>Salary Report (No Rounding)</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> :root{ --green:#34A853; --mint:#F5FFF7; --stone:#5F6368; --sage:#D0E8D0; --sky:#A7D8DE; --yellow:#FFF9C4; } *{box-sizing:border-box} body{margin:0;background:var(--mint);color:#222;font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Arial} .wrap{max-width:1200px;margin:20px auto;padding:0 12px} .h1{color:#2c7d3f;margin:6px 0 12px} .hbar{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;background:#e9f7ee;border:1px solid #d6f0dd;padding:12px;border-radius:12px} .hbar label{font-size:12px;color:#333;display:block;margin-bottom:4px} .hbar select{padding:8px 10px;border:1px solid #cfe8d0;border-radius:10px;background:#fff;min-width:140px} .btn{background:var(--green);color:#fff;border:none;border-radius:12px;padding:9px 14px;font-weight:600;cursor:pointer} .btn.gray{background:#5F6368} .badge{background:#d8f7e1;padding:6px 10px;border-radius:999px;font-size:12px;color:#2b6b3c} .card{background:#fff;border:1px solid #ecf2ec;border-radius:14px;overflow:hidden;margin-top:16px} .table{width:100%;border-collapse:separate;border-spacing:0} .table th,.table td{padding:10px 12px;border-bottom:1px solid #eef5ef} .table thead th{position:sticky;top:0;background:#cfe9d7;color:#1b4d2a;text-align:right} .table thead th:first-child{text-align:left} .table tbody td{text-align:right} .table tbody td:first-child{text-align:left} .table tfoot td{background:#fff9e8;font-weight:700} .head2{background:#b7dcd0;color:#0f4230} .subrate{font-size:11px;opacity:.8} .totalcol{background:#f3faf4;font-weight:700} .small{font-size:12px;color:#555} .right{margin-left:auto} .flash{background:#e7ffe9;border:1px solid #c7f0cb;color:#1e5a2c;padding:8px 12px;border-radius:10px;margin:10px 0;display:inline-block} .kentry{display:block;font-size:11px;color:#666} .summary{background:#f1f9f2;border:1px dashed #cde8cf;padding:8px 12px;border-radius:10px;margin:10px 0;color:#165c30} .debug{background:#fffbe6;color:#8b0000} </style> </head> <body> <div class="wrap"> <h2 class="h1">Salary Report <span class="small">(No Rounding)</span></h2> <div class="small" style="margin-bottom:8px;"><b>Company:</b> <?= h($company_name) ?></div> <?php if($flash): ?><div class="flash"><?= h($flash) ?></div><?php endif; ?> <!-- Filters --> <form class="hbar" method="get" action=""> <div> <label>Khata</label> <select name="khata_id"> <option value="0">All</option> <?php $KHATAS=[]; try{ $kq=$pdo->prepare("SELECT DISTINCT khata_id, khata_code FROM loom_karigar_master WHERE company_id=? AND khata_id IS NOT NULL ORDER BY khata_code"); $kq->execute([$company_id]); foreach($kq as $r){ $KHATAS[(int)$r['khata_id']] = $r['khata_code']; } }catch(Exception $e){} foreach($KHATAS as $id=>$code): ?> <option value="<?= $id ?>" <?= $khata_id===$id?'selected':''?>><?= h($code) ?></option> <?php endforeach; ?> </select> </div> <div> <label>Machine</label> <select name="machine_id"> <option value="0">All Machines</option> <?php $MACH=[]; try{ $st2=$pdo->prepare("SELECT id, machine_code FROM machines WHERE company_id=? ORDER BY id"); $st2->execute([$company_id]); foreach($st2 as $r){ $MACH[(int)$r['id']] = $r['machine_code'] ?: ('MC '.$r['id']); } }catch(Exception $e){} foreach($MACH as $id=>$label): ?> <option value="<?= $id ?>" <?= $machine_id===$id?'selected':''?>><?= h($label) ?></option> <?php endforeach; ?> </select> </div> <div> <label>Month</label> <select name="month"> <?php for($m=1;$m<=12;$m++): ?> <option value="<?= $m ?>" <?= $m===$month?'selected':''?>><?= date('M', mktime(0,0,0,$m,1,$year)) ?></option> <?php endfor; ?> </select> </div> <div> <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> <label>Year</label> <select name="year"> <?php for($y=date('Y')-2;$y<=date('Y')+1;$y++): ?> <option value="<?= $y ?>" <?= $y===$year?'selected':''?>><?= $y ?></option> <?php endfor; ?> </select> </div> <button class="btn right" type="submit">Apply</button> <?php // Hidden export payload (exact net) $export_rows = []; foreach ($kar_list as $kid) { $amt = (float)($NET[$kid] ?? 0); if ($amt != 0.0) $export_rows[] = ['employee_id' => $kid, 'amount' => round($amt,2)]; } $export_payload = json_encode($export_rows, JSON_UNESCAPED_UNICODE); $period_label = strtoupper(date('M', mktime(0,0,0,$month,1,$year))).' '. ($half==='1' ? '1-15' : ('16-'.date('t', strtotime(sprintf('%04d-%02d-01',$year,$month))))); ?> <button class="btn gray" type="submit" form="exportForm">Export Excel</button> </form> <form id="exportForm" method="post" action="/erp/loom_payment_export.php" style="display:none"> <input type="hidden" name="salary_rows" value='<?= h($export_payload) ?>'> <input type="hidden" name="period_label" value="<?= h($period_label) ?>"> <input type="hidden" name="period_start" value="<?= h($date_from) ?>"> <input type="hidden" name="period_end" value="<?= h($date_to) ?>"> </form> <!-- Summary (EXACT — no rounding) --> <div class="summary"> <b>Summary:</b> Bank / <?= number_format($bank_total,2) ?>, Cash / <?= number_format($cash_total,2) ?>, <b>Total</b> <?= number_format($grand_net,2) ?> <span class="small"> (Exact net; no rounding)</span> </div> <!-- Save/Update Report (unique per period) --> <form method="post" action="" style="margin-bottom:10px"> <input type="hidden" name="action" value="save_report"> <button class="btn" type="submit">Save Salary (unique per period)</button> <span class="small" style="margin-left:8px">Same period pe dubara press karoge to existing report <b>update</b> ho jayegi.</span> </form> <div class="card"> <table class="table"> <thead> <tr> <th>Karigar</th> <?php foreach($quality_ids as $qid): ?> <th class="head2"> <?= h($QUALS[$qid] ?? ("Q$qid")) ?><br> <span class="subrate">@ <?= number_format($QUAL_RATE[$qid]??0,2) ?></span> </th> <?php endforeach; ?> <th>Total Mtr</th> <th>Gross</th> <th>Extra</th> <th>Deduction</th> <th>Advance</th> <th>Payable</th> </tr> </thead> <tbody> <?php foreach($kar_list as $kid): ?> <tr> <td> <?= h($KAR_NAME[$kid] ?? ("Karigar #$kid")) ?> <?php if(!empty($KAR_ENTRY[$kid])): ?> <span class="kentry"><?= h($KAR_ENTRY[$kid]) ?></span> <?php endif; ?> </td> <?php foreach($quality_ids as $qid): $m=$MTR[$kid][$qid] ?? 0; ?> <td><?= $m>0? number_format($m,2): '' ?></td> <?php endforeach; ?> <td class="totalcol"><?= number_format($MTR_KAR_TOT[$kid]??0,2) ?></td> <td><?= number_format($GROSS[$kid]??0,2) ?></td> <td><?= number_format($EXTRA[$kid]??0,2) ?></td> <td><?= number_format($DEDUCT[$kid]??0,2) ?></td> <td><?= number_format($ADV[$kid]??0,2) ?></td> <td class="totalcol"><?= number_format($NET[$kid]??0,2) ?></td> </tr> <?php endforeach; ?> </tbody> <tfoot> <tr> <td>TOTAL</td> <?php $TOT_METERS_BY_Q=[]; $GRAND_GROSS=0.0; foreach($quality_ids as $qid){ $s=0.0; foreach($kar_list as $kid){ $s += $MTR[$kid][$qid] ?? 0; } $TOT_METERS_BY_Q[$qid]=$s; echo "<td>".number_format($s,2)."</td>"; $GRAND_GROSS += $s * ($QUAL_RATE[$qid]??0.0); } $grand_mtr = array_sum($TOT_METERS_BY_Q); $TOT_EXTRA = array_sum(array_intersect_key($EXTRA, array_flip($kar_list))); $TOT_DED = array_sum(array_intersect_key($DEDUCT,array_flip($kar_list))); $TOT_ADV = array_sum(array_intersect_key($ADV, array_flip($kar_list))); $GRAND_NET = array_sum(array_map(fn($kid)=> (float)($NET[$kid]??0), $kar_list)); ?> <td class="totalcol"><?= number_format($grand_mtr,2) ?></td> <td><?= number_format($GRAND_GROSS,2) ?></td> <td><?= number_format($TOT_EXTRA,2) ?></td> <td><?= number_format($TOT_DED,2) ?></td> <td><?= number_format($TOT_ADV,2) ?></td> <td class="totalcol"><?= number_format($GRAND_NET,2) ?></td> </tr> <!-- DEBUG FOOTER --> <tr> <td colspan="<?= 6 + count($quality_ids) ?>" class="small debug" style="text-align:left;padding:12px;border-top:1px solid #f3e2a2"> <b>Debug Info:</b><br> • Skipped <b><?= (int)$_skipped_bad_date ?></b> lines (unreadable date).<br> • Skipped <b><?= (int)$_skipped_out_range ?></b> lines (outside <?=h($date_from)?> → <?=h($date_to)?>).<br> <span style="color:#333">👉 Non-zero values yahan dikhte hi meter mismatch ka कारण confirm ho jata hai.</span> </td> </tr> </tfoot> </table> </div> </div> </body> </html>