« Back to History
efficiency_report.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php /* efficiency_report.php Updated: compact MC column, inline SVG donuts per-row, print opens new window + auto print, keeps server-side PDF (export=pdf) and CSV (export=csv). Added: improved print CSS to hide top nav / control area and add centered company + date header on printed output. Added: printed header now includes the Average donut (same metric shown on page). */ error_reporting(E_ALL); ini_set('display_errors',1); /* bootstrap/auth */ require_once __DIR__ . '/../modules/auth/page_acl.php'; $ctx = page_require_access('efficiency_report'); $pdo = $ctx['pdo'] ?? null; $company_id = (int)($ctx['company_id'] ?? 0); $company_name = $ctx['company_name'] ?? 'Company'; if (!($pdo instanceof PDO)) { http_response_code(500); exit('DB connection missing'); } /* inputs */ $from_date = $_GET['from'] ?? date('Y-m-01'); $to_date = $_GET['to'] ?? date('Y-m-t'); $print_only = (isset($_GET['print']) && (int)$_GET['print'] === 1); $export = trim($_GET['export'] ?? ''); try { $fd = new DateTime($from_date); $td = new DateTime($to_date); if($fd>$td){ $tmp=$fd;$fd=$td;$td=$tmp;$from_date=$fd->format('Y-m-d'); $to_date=$td->format('Y-m-d'); } } catch(Throwable $e){ $from_date=date('Y-m-01'); $to_date=date('Y-m-t'); } /* load qualities */ $qualityMap = []; try { $st = $pdo->prepare("SELECT id, quality_name, COALESCE(avg_per_day_pro,0) AS expected FROM qualities WHERE company_id = :cid"); $st->execute([':cid'=>$company_id]); foreach($st->fetchAll(PDO::FETCH_ASSOC) as $r){ $qualityMap[(int)$r['id']] = ['name'=>$r['quality_name'],'expected'=>(float)$r['expected']]; } } catch(Throwable $e){ $qualityMap = []; } /* fetch production (machine,date,quality) */ $sql = "SELECT machine_id, entry_date, quality_id, SUM(meter_total) AS day_meter FROM production_entry WHERE company_id = :cid AND entry_date BETWEEN :from AND :to GROUP BY machine_id, entry_date, quality_id ORDER BY machine_id, entry_date"; $st = $pdo->prepare($sql); $st->execute([':cid'=>$company_id, ':from'=>$from_date, ':to'=>$to_date]); $rows = $st->fetchAll(PDO::FETCH_ASSOC); /* organize */ $machines=[]; $datesWithData=[]; foreach($rows as $r){ $mid=(int)$r['machine_id']; $d=$r['entry_date']; $qid=(int)$r['quality_id']; $meter=(float)$r['day_meter']; $datesWithData[$d]=true; if(!isset($machines[$mid])) $machines[$mid]=['days'=>[],'qualities_used'=>[]]; if(!isset($machines[$mid]['days'][$d])) $machines[$mid]['days'][$d]=[]; $machines[$mid]['days'][$d][]=['quality_id'=>$qid,'meter'=>$meter]; if($qid) $machines[$mid]['qualities_used'][$qid] = $qualityMap[$qid]['name'] ?? ('Q'.$qid); } $dates = array_keys($datesWithData); sort($dates); /* compute summaries */ $final=[]; $company_total_meter=0; $company_total_expected=0; $company_machine_count=0; foreach($machines as $mid=>$mdata){ $total_meter=0; $expected_total=0; $work_days=0; foreach($dates as $d){ $entries = $mdata['days'][$d] ?? []; $day_sum=0; $day_expected=0; foreach($entries as $e){ $day_sum += (float)$e['meter']; $qid=(int)$e['quality_id']; $day_expected += ($qualityMap[$qid]['expected'] ?? 0.0); } if($day_sum>0){ $work_days++; $total_meter += $day_sum; $expected_total += $day_expected; } } $eff = ($expected_total>0) ? round(($total_meter/$expected_total)*100,1) : 0.0; $final[$mid]=['machine_id'=>$mid,'days'=>$mdata['days'],'qualities'=>$mdata['qualities_used'],'total_meter'=>$total_meter,'expected_total'=>$expected_total,'work_days'=>$work_days,'eff'=>$eff]; $company_total_meter += $total_meter; $company_total_expected += $expected_total; $company_machine_count++; } $company_efficiency = ($company_total_expected>0) ? round(($company_total_meter/$company_total_expected)*100,2) : 0.0; /* CSV export */ if($export==='csv'){ header('Content-Type:text/csv; charset=utf-8'); header('Content-Disposition:attachment; filename=efficiency_'.date('Ymd').'.csv'); $out = fopen('php://output','w'); $hdr=['Machine','Qualities']; foreach($dates as $d) $hdr[]=$d; $hdr = array_merge($hdr,['Total','Work Days','Expected Total','Efficiency %']); fputcsv($out,$hdr); foreach($final as $m){ $row=[]; $row[]=$m['machine_id']; $row[] = !empty($m['qualities'])?implode(', ',array_values($m['qualities'])):''; foreach($dates as $d){ $sum=0; foreach($m['days'][$d] ?? [] as $e) $sum += (float)$e['meter']; $row[] = $sum>0?number_format($sum,0,'','') : ''; } $row[] = number_format($m['total_meter'],0,'',''); $row[] = $m['work_days']; $row[] = $m['expected_total']>0?number_format($m['expected_total'],0,'','') : ''; $row[] = $m['expected_total']>0?($m['eff'].'%') : ''; fputcsv($out,$row); } fclose($out); exit; } /* PDF export (server-side using Dompdf if present) */ if($export==='pdf'){ // Build simple HTML (SVG donuts included inline) and try Dompdf; fallback to HTML view $companyName = $company_name; ob_start(); ?> <!doctype html><html><head><meta charset="utf-8"> <style> body{font-family:DejaVu Sans,Arial; font-size:12px;color:#222;margin:18px} table{width:100%;border-collapse:collapse} th,td{border:1px solid #ddd;padding:6px;vertical-align:top} th{background:#f6f8fa} .mccol{width:120px} .donut-td{text-align:center} .small{font-size:11px;color:#444} </style> </head><body> <h2 style="margin:0 0 6px 0"><?=htmlspecialchars($companyName)?> — Machine Efficiency Report</h2> <div class="small">Range: <?=htmlspecialchars($from_date)?> — <?=htmlspecialchars($to_date)?> | Machines: <?=count($final)?></div> <table> <thead><tr><th class="mccol">Mc. Qua.</th> <?php foreach($dates as $d) echo '<th>'.htmlspecialchars($d).'</th>'; ?> <th>Total</th><th>Work Days</th><th>Expected</th><th>Eff</th></tr></thead> <tbody> <?php foreach($final as $m): // donut params $pct = max(0,min(100, (float)$m['eff'] )); $r = 14; $c = 2 * pi() * $r; $dash = round($pct/100*$c,2); $gap = round($c-$dash,2); if ($pct>=80) $col='#16a34a'; elseif($pct>=60) $col='#b36b00'; elseif($pct>=40) $col='#b35200'; else $col='#b02a2a'; ?> <tr> <td class="mccol"><?=htmlspecialchars($m['machine_id'])?> <?php if(!empty($m['qualities'])): ?><div class="small"><?=htmlspecialchars(implode(', ', array_values($m['qualities'])))?></div><?php endif; ?> </td> <?php foreach($dates as $d): $sum=0; foreach($m['days'][$d] ?? [] as $e) $sum += (float)$e['meter']; ?><td><?= $sum>0?number_format($sum,0):'-' ?></td><?php endforeach; ?> <td><?=number_format($m['total_meter'],0)?></td><td><?= $m['work_days'] ?></td><td><?= $m['expected_total']>0?number_format($m['expected_total'],0):'-' ?></td> <td class="donut-td"> <svg width="36" height="36" viewBox="0 0 36 36"> <circle cx="18" cy="18" r="<?=$r?>" fill="transparent" stroke="#eee" stroke-width="4"></circle> <circle cx="18" cy="18" r="<?=$r?>" fill="transparent" stroke="<?=$col?>" stroke-width="4" stroke-dasharray="<?=$dash?> <?=$c?>" transform="rotate(-90 18 18)" stroke-linecap="round"></circle> <text x="18" y="21" font-size="7" text-anchor="middle" fill="#111"><?= $pct ?>%</text> </svg> </td> </tr> <?php endforeach; ?> </tbody> </table> </body></html> <?php $html = ob_get_clean(); // try dompdf $dompdf_ok=false; foreach(['/vendor/autoload.php','/lib/dompdf/autoload.inc.php'] as $rel){ $p=__DIR__.$rel; if(is_file($p)){ require_once $p; $dompdf_ok = class_exists('\Dompdf\Dompdf'); break; } } if($dompdf_ok){ try { $dompdf = new \Dompdf\Dompdf(['isRemoteEnabled'=>true,'isHtml5ParserEnabled'=>true]); $dompdf->loadHtml($html); $dompdf->setPaper('A4','landscape'); $dompdf->render(); $dompdf->stream('efficiency_report_'.date('Y-m-d').'.pdf', ['Attachment'=>true]); exit; } catch(Throwable $e){ // fallback to HTML output header('Content-Type:text/html; charset=utf-8'); echo $html; exit; } } else { header('Content-Type:text/html; charset=utf-8'); echo $html; exit; } } /* ---------------- Render HTML page -------------------------------------- */ if (!$print_only) require_once __DIR__ . '/../partials/header.php'; ?> <style> /* compact styling */ .erp-wrap{padding:14px;font-family:Arial,Helvetica,sans-serif;color:#111} .header{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px} .avg-canvas{width:120px;height:120px;position:relative} .controls{display:flex;gap:8px;align-items:center;flex-wrap:wrap} .btn{padding:6px 8px;border-radius:6px;border:1px solid #d0d7de;background:#f7fbff;cursor:pointer;font-size:13px} .table-wrap{overflow:auto;border:1px solid #eef2f2;border-radius:6px;background:#fff;padding:6px} .eff-table{border-collapse:collapse;width:100%;table-layout:fixed;font-size:12px} .eff-table th,.eff-table td{border:1px solid #eef5f4;padding:6px 6px;vertical-align:middle;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .eff-table th{background:#f6f9fb;font-weight:700;text-align:center} .col-mc{width:90px;text-align:center;font-weight:700} .col-dates{width:60px;text-align:center} .smallq{display:block;font-size:11px;color:#444;margin-top:4px;text-align:center} .cell-zero{background:#fdecec;color:#b93838;text-align:center} .cell-pos{background:#f0fbf6;color:#116530;text-align:right} .donut-inline{display:inline-block;vertical-align:middle;margin-left:6px} /* PRINT: hide navigation / top controls / avg chart and keep the table visible. scoped to this page — module-specific fix (no global changes). */ @media print{ /* common topbars that many pages use */ header, footer, .topbar, .navbar, .erp-nav, /* this page specific containers */ .header, /* hides top control area (title + controls) */ .controls, /* hides Export/Load/Print buttons */ .avg-canvas, /* hides average donut chart on the page - we will add a separate donut in print window */ /* fallback selectors used in different themes */ .erp-nav-wrap, .nav-wrapper, .main-navbar { display:none !important; } /* Page print layout */ body{padding:6mm} .eff-table th,.eff-table td{font-size:10px;padding:4px} /* ensure the table stretches full width on print */ .table-wrap{border:0;padding:0;box-shadow:none;background:transparent} .erp-wrap{padding:0} } /* small responsive tweak for the on-page view (not print) */ @media (max-width:900px){ .col-dates{width:48px} } </style> <div class="erp-wrap"> <div class="header"> <div> <h3 style="margin:0 0 6px 0;">Machine Efficiency Report</h3> <div style="color:#556;font-size:13px">Range: <?=htmlspecialchars($from_date)?> — <?=htmlspecialchars($to_date)?> | Dates with data: <?=count($dates)?></div> </div> <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap"> <div class="avg-canvas"> <canvas id="avgChart" width="120" height="120"></canvas> <div style="position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;font-weight:800;"> <?= $company_efficiency ?>%<br><span style="font-size:12px;color:#556">Average</span> </div> </div> <div class="controls"> <form method="get" style="display:flex;gap:6px;align-items:center;margin:0"> <label style="color:#444">From</label> <input type="date" name="from" value="<?=htmlspecialchars($from_date)?>"> <label style="color:#444">To</label> <input type="date" name="to" value="<?=htmlspecialchars($to_date)?>"> <button class="btn" type="submit">Load</button> </form> <button class="btn" onclick="location.href='?<?=htmlspecialchars(http_build_query(array_merge($_GET,['export'=>'csv'])))?>'">Export CSV</button> <!-- Server-side PDF --> <button class="btn" onclick="location.href='?<?=htmlspecialchars(http_build_query(array_merge($_GET,['export'=>'pdf'])))?>'" >Export PDF</button> <!-- Print (clean) via JS open window + auto print --> <button class="btn" onclick="openPrintWindow()">Print (clean)</button> </div> </div> </div> <div class="table-wrap" id="printableContent"> <?php if(empty($final)): ?> <div style="padding:12px">No production data for selected date range.</div> <?php else: ?> <table class="eff-table" id="effTable"> <thead> <tr> <th class="col-mc">Mc. Qua.</th> <?php foreach($dates as $d): ?><th class="col-dates"><?=date('d-M',strtotime($d))?></th><?php endforeach; ?> <th style="width:80px">Total</th><th style="width:60px">Work</th><th style="width:80px">Expected</th><th style="width:70px">Eff</th> </tr> </thead> <tbody> <?php foreach($final as $m): $eff = (float)$m['eff']; $pct = max(0,min(100,$eff)); $r=14; $c = 2*pi()*$r; $dash = round($pct/100*$c,2); if ($pct>=80) $col='#16a34a'; elseif($pct>=60) $col='#b36b00'; elseif($pct>=40) $col='#b35200'; else $col='#b02a2a'; ?> <tr> <td class="col-mc"> <?=htmlspecialchars($m['machine_id'])?> <?php if(!empty($m['qualities'])): ?><span class="smallq"><?=htmlspecialchars(implode(', ', array_values($m['qualities'])))?></span><?php endif; ?> </td> <?php foreach($dates as $d): $sum = 0; foreach($m['days'][$d] ?? [] as $e) $sum += (float)$e['meter']; if($sum>0) echo '<td class="cell-pos">'.number_format($sum,0).'</td>'; else echo '<td class="cell-zero">-</td>'; endforeach; ?> <td style="text-align:right;font-weight:700"><?=number_format($m['total_meter'],0)?></td> <td style="text-align:center"><?= $m['work_days'] ?></td> <td style="text-align:center"><?= $m['expected_total']>0 ? number_format($m['expected_total'],0) : '-' ?></td> <td style="text-align:center"> <!-- inline SVG donut --> <svg width="36" height="36" viewBox="0 0 36 36" style="vertical-align:middle"> <circle cx="18" cy="18" r="<?=$r?>" fill="transparent" stroke="#eee" stroke-width="4"></circle> <circle cx="18" cy="18" r="<?=$r?>" fill="transparent" stroke="<?=$col?>" stroke-width="4" stroke-dasharray="<?=$dash?> <?=$c?>" transform="rotate(-90 18 18)" stroke-linecap="round"></circle> <text x="18" y="21" font-size="7" text-anchor="middle" fill="#111"><?= ($pct>0?($pct.'%'):'-') ?></text> </svg> </td> </tr> <?php endforeach; ?> </tbody> <tfoot> <tr> <th style="text-align:left;padding-left:8px">Company Total</th> <?php foreach($dates as $_d): ?><th></th><?php endforeach; ?> <th style="text-align:right;font-weight:800"><?=number_format($company_total_meter,0)?></th> <th style="text-align:center"><?= $company_machine_count ?></th> <th style="text-align:center"><?= $company_total_expected>0 ? number_format($company_total_expected / max(1,$company_machine_count),0) : '-' ?></th> <th style="text-align:center;font-weight:800"><?= $company_total_expected>0 ? ($company_efficiency.'%') : '-' ?></th> </tr> </tfoot> </table> <?php endif; ?> </div> </div> <?php if(!$print_only) require_once __DIR__ . '/../partials/footer.php'; ?> <!-- Chart.js for top average --> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> (function(){ const companyEff = <?= json_encode($company_efficiency) ?>; const ctx = document.getElementById('avgChart'); if(ctx){ new Chart(ctx, { type:'doughnut', data:{ datasets:[{ data:[companyEff, Math.max(0,100-companyEff)], backgroundColor:['#16a34a','#eee'], borderWidth:0 }] }, options:{ cutout:'70%', plugins:{legend:{display:false}, tooltip:{enabled:false}} } }); } })(); /* openPrintWindow: open a NEW window with printable HTML (no header/footer), then auto print The printed header will show: Company Name (centered), Range: from — to, and Average donut to the left. */ function openPrintWindow(){ const content = document.getElementById('printableContent'); if(!content){ alert('Printable content not found'); return; } const w = window.open('', '_blank', 'width=1000,height=800,menubar=no,toolbar=no,location=no'); if(!w){ alert('Popup blocked — allow popups for this site'); return; } const companyName = <?= json_encode($company_name) ?>; const fromDate = <?= json_encode($from_date) ?>; const toDate = <?= json_encode($to_date) ?>; const companyEff = <?= json_encode($company_efficiency) ?>; const title = companyName + ' — Machine Efficiency Report'; const rangeLine = 'Range: ' + fromDate + ' — ' + toDate; // minimal inline CSS for print window (centered title + donut + table styles) const css = `<style> body{font-family:Arial,Helvetica,sans-serif;color:#111;margin:12px} .print-header{display:flex;align-items:center;gap:12px;justify-content:center;margin-bottom:10px} .print-donut{width:86px;height:86px;display:flex;align-items:center;justify-content:center} .print-title{text-align:center} .print-title h2{margin:0;font-size:18px} .print-title .range{font-size:12px;color:#444;margin-top:4px} table{width:100%;border-collapse:collapse} th,td{border:1px solid #ddd;padding:6px;font-size:11px;vertical-align:top} th{background:#f6f9fb} .cell-zero{background:#fdecec} .cell-pos{background:#f0fbf6} @media print{ body{margin:6mm} table{page-break-inside:auto} tr{page-break-inside:avoid;page-break-after:auto} } </style>`; // Build donut SVG using companyEff (calculate stroke-dasharray in JS) const donutSVG = (function(){ const pct = Math.max(0, Math.min(100, Number(companyEff) || 0)); const r = 14; const c = 2 * Math.PI * r; const dash = (pct/100) * c; const gap = c - dash; let col = '#b02a2a'; if (pct >= 80) col = '#16a34a'; else if (pct >= 60) col = '#b36b00'; else if (pct >= 40) col = '#b35200'; const svg = `<svg width="86" height="86" viewBox="0 0 36 36" aria-hidden="true"> <circle cx="18" cy="18" r="${r}" fill="transparent" stroke="#eee" stroke-width="4"></circle> <circle cx="18" cy="18" r="${r}" fill="transparent" stroke="${col}" stroke-width="4" stroke-dasharray="${dash} ${c}" transform="rotate(-90 18 18)" stroke-linecap="round"></circle> <text x="18" y="21" font-size="7" text-anchor="middle" fill="#111">${pct}%</text> </svg>`; return svg; })(); // Build printable HTML: donut + centered title + the table content (only) const html = `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title>${css}</head><body> <div class="print-header"> <div class="print-donut">${donutSVG}</div> <div class="print-title"> <h2>${escapeHtml(companyName)} — Machine Efficiency Report</h2> <div class="range">${escapeHtml(rangeLine)}</div> </div> </div> ${content.innerHTML} <script> setTimeout(function(){ window.focus(); window.print(); /*window.close();*/ }, 400); <\/script> </body></html>`; w.document.open(); w.document.write(html); w.document.close(); } /* client-side escape helper used inside this original window when building strings for print window (safe) */ function escapeHtml (s) { return (''+s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,'''); } </script>