<?php
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use App\Models as M;
use App\Services\SequenceService as Seq;
use App\Services\StockService;
use App\Services\FinanceService;
use App\Services\GhanaTaxService as Tax;
use App\Services\PayrollService;
use App\Http\Controllers\SalesOrderController;
use App\Http\Controllers\PurchaseOrderController;

class DemoSeeder extends Seeder {
    private function d(int $daysAgo): string { return date('Y-m-d', strtotime("$daysAgo days")); }

    public function run(): void {
        mt_srand(20260907);
        $admin = 1;
        $sh = M\Quarry::where('code', 'SH-01')->firstOrFail();
        $ns = M\Quarry::where('code', 'NS-02')->firstOrFail();
        $prod = fn($c) => M\Product::where('code', $c)->firstOrFail();
        $item = fn($c) => M\StockItem::where('code', $c)->firstOrFail();
        $eq = fn($c) => M\Equipment::where('code', $c)->firstOrFail();
        $cust = fn($n) => M\Customer::where('name', $n)->firstOrFail();
        $sup = fn($n) => M\Supplier::where('name', $n)->firstOrFail();
        $hq = M\Warehouse::where('code', 'WH-HQ')->firstOrFail();
        $shs = M\Warehouse::where('code', 'WH-SH')->firstOrFail();
        $fb = M\Warehouse::where('code', 'WH-FB')->firstOrFail();
        $gcb = M\BankAccount::where('account_no', '1234567890123')->firstOrFail();
        $momo = M\BankAccount::where('account_no', '0244000001')->firstOrFail();
        $unitT = M\Unit::where('symbol', 't')->value('id');
        $emp = fn($s) => M\Employee::where('ssnit_no', $s)->firstOrFail();
        $p1 = M\Project::where('code', 'PRJ-2026-001')->firstOrFail();
        $p2 = M\Project::where('code', 'PRJ-2026-002')->firstOrFail();

        // ── 1. Opening stock ──────────────────────────────────────────────
        foreach (M\StockItem::all() as $it) {
            $qty = max($it->reorder_level * 6, $it->category === 'fuel-lubricant' ? 500 : 20);
            if ($it->code === 'DSL') $qty = 12000;
            if ($it->code === 'ANFO') $qty = 2500;
            $wh = in_array($it->code, ['DSL', 'PTR']) ? $fb->id : $hq->id;
            StockService::postLedger('2026-07-01', $it->id, $wh, 'opening', $qty, 0, $it->last_purchase_rate, 'opening', null, [], 'Opening balance');
        }

        // ── 2. Production (45 days × 2 quarries × 2 products) ─────────────
        $lines = M\CrusherLine::all()->groupBy('quarry_id');
        $ops = ['Yaw Mensah', 'Adjoa Mensimah', 'Kwabena Frimpong'];
        $pcs = ['AGG-20', 'AGG-14', 'AGG-10', 'AGG-05', 'AGG-40'];
        for ($d = 45; $d >= 1; $d--) {
            if (date('w', strtotime($this->d(-$d))) == 0) continue; // no Sunday crushing
            foreach ([$sh, $ns] as $q) {
                $np = $q->id === $sh->id ? 3 : 2;
                for ($k = 0; $k < $np; $k++) {
                    $p = $prod($pcs[($d + $k + $q->id) % 5]);
                    $tonnes = mt_rand(120, 420);
                    $ln = $lines[$q->id]->random();
                    $e = M\ProductionEntry::create(['entry_no' => Seq::next('production_entry'), 'entry_date' => $this->d(-$d),
                        'shift' => 'day', 'quarry_id' => $q->id, 'crusher_line_id' => $ln->id, 'product_id' => $p->id,
                        'produced_tonnes' => $tonnes, 'operating_hours' => mt_rand(70, 100) / 10, 'downtime_hours' => mt_rand(0, 15) / 10,
                        'fuel_used_litres' => mt_rand(200, 600), 'operator_name' => $ops[array_rand($ops)],
                        'status' => 'approved', 'created_by' => $admin, 'approved_by' => $admin]);
                    $pile = StockService::stockpileFor($q->id, $p->id);
                    StockService::moveStockpile($pile->id, $this->d(-$d), 'production-in', $tonnes, 0, 'production_entry', $e->id, 'Production '.$e->entry_no);
                }
            }
        }

        // ── 3. Quarry logs ────────────────────────────────────────────────
        $benches = M\PitBench::all();
        for ($i = 0; $i < 5; $i++) {
            $b = $benches->where('quarry_id', $sh->id)->random();
            M\BlastLog::create(['blast_no' => Seq::next('blast'), 'quarry_id' => $sh->id, 'bench_id' => $b->id,
                'blast_date' => $this->d(-(8 + $i * 7)), 'blast_time' => '13:00', 'holes_drilled' => mt_rand(40, 80),
                'total_depth_m' => mt_rand(300, 640), 'explosive_used_kg' => mt_rand(600, 1400), 'detonators_used' => mt_rand(40, 80),
                'blaster_name' => 'Yaw Darko', 'volume_broken_m3' => mt_rand(2500, 5200), 'tonnage_broken' => mt_rand(6500, 13500),
                'weather' => 'Sunny', 'status' => $i < 4 ? 'approved' : 'fired', 'created_by' => $admin]);
        }
        for ($i = 0; $i < 24; $i++) {
            $q = $i % 3 === 2 ? $ns : $sh;
            $b = $benches->where('quarry_id', $q->id)->random();
            $ex = $q->id === $sh->id ? $eq('EQ-EX-001') : $eq('EQ-EX-002');
            M\ExtractionLog::create(['quarry_id' => $q->id, 'bench_id' => $b->id, 'log_date' => $this->d(-(1 + ($i * 2) % 44)),
                'shift' => $i % 2 ? 'day' : 'night', 'equipment_id' => $ex->id, 'dumper_trips' => mt_rand(15, 40),
                'tonnage_extracted' => mt_rand(300, 900), 'overburden_m3' => mt_rand(20, 150), 'operator_name' => 'Kwabena Frimpong', 'created_by' => $admin]);
        }
        $reasons = ['mechanical', 'electrical', 'no-feedstock', 'power', 'weather', 'other'];
        for ($i = 0; $i < 10; $i++) {
            $ln = M\CrusherLine::all()->random();
            M\DowntimeLog::create(['crusher_line_id' => $ln->id, 'log_date' => $this->d(-(2 + $i * 4)),
                'start_time' => '10:00', 'end_time' => '12:30', 'duration_hours' => 2.5,
                'reason_category' => $reasons[$i % 6], 'description' => 'Unplanned stoppage — see maintenance log', 'resolved_by' => 'Kojo Appiah']);
        }
        for ($i = 0; $i < 12; $i++) {
            $p = $prod($pcs[$i % 5]);
            $pile = M\Stockpile::where('quarry_id', $sh->id)->where('product_id', $p->id)->first();
            M\QcTest::create(['test_no' => Seq::next('qc_test'), 'test_date' => $this->d(-(3 + $i * 3)), 'quarry_id' => $sh->id,
                'product_id' => $p->id, 'stockpile_id' => $pile?->id, 'sample_source' => 'stockpile', 'gradation_pass' => true,
                'flakiness_index' => mt_rand(120, 180) / 10, 'los_angeles_abrasion' => mt_rand(180, 260) / 10,
                'aggregate_crushing_value' => mt_rand(170, 240) / 10, 'water_absorption' => mt_rand(5, 15) / 10,
                'silt_content' => mt_rand(10, 40) / 10, 'tested_by' => 'Esi Koomson',
                'verdict' => $i === 7 ? 'conditional' : 'pass', 'approved_by' => $admin]);
        }
        $bal = 2500;
        for ($i = 0; $i < 8; $i++) {
            $rcv = $i % 3 === 0 ? 800 : 0; $iss = mt_rand(150, 450);
            M\ExplosivesRegister::create(['register_date' => $this->d(-(40 - $i * 5)), 'quarry_id' => $sh->id, 'magazine' => 'Magazine A',
                'explosive_type' => $i % 2 ? 'Emulsion' : 'ANFO', 'unit' => 'kg', 'opening_balance' => $bal,
                'received_qty' => $rcv, 'issued_qty' => $iss, 'closing_balance' => $bal + $rcv - $iss,
                'issued_to' => 'Yaw Darko', 'permit_no' => 'MC/EXP/2026/0'.($i + 1), 'created_by' => $admin]);
            $bal = $bal + $rcv - $iss;
        }

        // ── 4. Fleet ──────────────────────────────────────────────────────
        $trucks = ['EQ-DT-001', 'EQ-DT-002', 'EQ-DT-003', 'EQ-DT-004'];
        $drivers = ['Yaw Darko', 'Yaw Boateng', 'Kofi Asante', 'Ama Serwaa'];
        $dslRate = $item('DSL')->last_purchase_rate;
        for ($i = 0; $i < 30; $i++) {
            $e = $eq($i % 4 === 3 ? 'EQ-EX-001' : $trucks[$i % 4]);
            $litres = mt_rand(50, 400);
            $e->current_hours += $litres / 8; $e->current_km += $e->current_km > 0 ? $litres * 2 : 0; $e->save();
            M\FuelLog::create(['log_no' => Seq::next('fuel_log'), 'log_date' => $this->d(-(1 + $i % 40)), 'equipment_id' => $e->id,
                'warehouse_id' => $fb->id, 'fuel_type' => 'diesel', 'qty_litres' => $litres, 'rate_per_litre' => $dslRate,
                'amount' => round($litres * $dslRate, 2), 'meter_hours' => round($e->current_hours, 1), 'meter_km' => round($e->current_km),
                'issued_by' => 'Yaw Antwi', 'received_by' => $drivers[$i % 4]]);
            StockService::postLedger($this->d(-(1 + $i % 40)), $item('DSL')->id, $fb->id, 'issue', 0, $litres, $dslRate, 'fuel_log', 0, ['equipment_id' => $e->id], 'Fuel issue');
        }
        $custNames = ['Consar Construct Ltd', 'Kofi Estates Ltd', 'Atlantic Civil Works', 'Danso Builders', 'Bright Future Developers'];
        for ($i = 0; $i < 25; $i++) {
            $e = $eq($trucks[$i % 4]);
            $trips = mt_rand(1, 4); $ton = $trips * 30;
            $p = $prod($pcs[$i % 5]);
            M\TripLog::create(['trip_no' => Seq::next('trip'), 'trip_date' => $this->d(-(1 + $i % 40)), 'equipment_id' => $e->id,
                'from_location' => 'Shai Hills Quarry', 'to_location' => $custNames[$i % 5].' site',
                'customer_id' => $cust($custNames[$i % 5])->id, 'product_id' => $p->id, 'trips_count' => $trips,
                'tonnage' => $ton, 'start_km' => $e->current_km, 'end_km' => $e->current_km + $trips * 85,
                'driver_name' => $drivers[$i % 4], 'transport_cost' => $ton * 42]);
            $e->current_km += $trips * 85; $e->save();
        }
        foreach (['EQ-EX-001', 'EQ-WL-001', 'EQ-GN-001'] as $cd) {
            $e = $eq($cd);
            M\MeterReading::create(['equipment_id' => $e->id, 'reading_date' => $this->d(-2), 'meter_type' => 'hours',
                'reading_value' => $e->current_hours, 'previous_value' => $e->current_hours - 120, 'source' => 'manual', 'recorded_by' => 'Kojo Appiah']);
        }
        // work orders
        $wos = [
            ['EQ-DT-002', 6, 'in-progress', 'breakdown', 'high', 'Turbo failure — loss of power on grade', 'Kojo Appiah', [['FLT-KIT', 1], ['OIL-15W', 2]], 0],
            ['EQ-EX-001', 20, 'completed', 'preventive', 'medium', '500-hr service: oils, filters, track tension', 'Kojo Appiah', [['FLT-KIT', 2], ['OIL-15W', 3], ['GRS', 1]], 2500],
            ['EQ-GN-001', 15, 'completed', 'preventive', 'medium', '250-hr service + AVR check', 'Kojo Appiah', [['FLT-KIT', 1], ['OIL-15W', 1]], 1200],
            ['EQ-DT-001', 4, 'open', 'tyre', 'medium', 'Two rear tyres worn beyond limit', null, [], 0],
            ['EQ-DR-001', 9, 'open', 'electrical', 'low', 'Beacon light + reverse alarm faulty', null, [], 0],
            ['EQ-WB-001', 30, 'closed', 'breakdown', 'high', 'Water pump seal leak', 'Kojo Appiah', [['FLT-KIT', 1]], 1800],
        ];
        foreach ($wos as [$cd, $ago, $st, $mt, $pr, $prob, $tech, $parts, $lab]) {
            $w = M\MaintenanceWorkOrder::create(['wo_no' => Seq::next('work_order'), 'equipment_id' => $eq($cd)->id,
                'reported_date' => $this->d(-$ago), 'reported_by' => 'Yaw Mensah', 'maintenance_type' => $mt, 'priority' => $pr,
                'problem_description' => $prob, 'assigned_technician' => $tech, 'labour_cost' => $lab, 'status' => $st,
                'started_at' => $st === 'open' ? null : $this->d(-$ago).' 08:00:00',
                'completed_at' => in_array($st, ['completed', 'closed']) ? $this->d(-$ago + 2).' 16:00:00' : null,
                'work_done' => in_array($st, ['completed', 'closed']) ? 'Repairs completed, tested and released.' : null,
                'approved_by' => $st === 'closed' ? $admin : null]);
            $pc = 0;
            foreach ($parts as [$icd, $q]) {
                $it = $item($icd); $a = round($q * $it->avg_cost_rate, 2); $pc += $a;
                $w->parts()->create(['item_id' => $it->id, 'qty' => $q, 'rate' => $it->avg_cost_rate, 'amount' => $a]);
                if (in_array($st, ['completed', 'closed'])) {
                    StockService::postLedger($this->d(-$ago + 1), $it->id, $hq->id, 'issue', 0, $q, $it->avg_cost_rate, 'work_order', $w->id, ['equipment_id' => $w->equipment_id], 'WO '.$w->wo_no);
                }
            }
            $w->update(['parts_cost' => $pc, 'total_cost' => round($pc + $lab, 2)]);
        }
        M\EquipmentDeployment::create(['equipment_id' => $eq('EQ-DT-004')->id, 'project_id' => $p1->id,
            'deployed_date' => $this->d(-30), 'meter_start' => 195000, 'status' => 'deployed', 'remarks' => 'Hauling hardcore to East Legon Hills']);
        $eq('EQ-DT-004')->update(['status' => 'deployed', 'current_project_id' => $p1->id]);

        // ── 5. Sales cycle ×16 ────────────────────────────────────────────
        $vehicles = ['GR 4521-22', 'GR 7810-23', 'GR 3105-20', 'GT 5100-22', 'AS 2200-21'];
        $trans = M\Transporter::all();
        $payPlan = [1, 1, 1, 0.6, 1, 1, 0, 1, 1, 0.6, 1, 0, 1, 1, 0.6, 0]; // fraction paid
        for ($i = 0; $i < 16; $i++) {
            $ago = 3 + $i * 2 + ($i % 3);
            $dt = $this->d(-$ago);
            $c = $cust($custNames[$i % 5]);
            $p = $prod($pcs[($i + 1) % 5]);
            $qty = mt_rand(100, 600);
            $rate = $p->standard_rate;
            $sub = round($qty * $rate, 2);
            $tax = Tax::salesTax($sub);
            $q = M\Quotation::create(['quotation_no' => Seq::next('quotation'), 'quotation_date' => $this->d(-$ago - 4),
                'valid_until' => $this->d(-$ago + 26), 'customer_id' => $c->id, 'quarry_id' => $sh->id,
                'subject' => 'Supply of '.$p->name, 'subtotal' => $sub, 'discount_amount' => 0,
                'levy_nhil' => $tax['nhil'], 'levy_getfund' => $tax['getfund'], 'levy_covid' => $tax['covid'],
                'vat_amount' => $tax['vat'], 'grand_total' => $tax['grand'], 'status' => 'converted',
                'terms' => 'Ex-quarry, payment in 30 days', 'prepared_by' => $admin, 'approved_by' => $admin]);
            $q->lines()->create(['product_id' => $p->id, 'description' => $p->name, 'qty_tonnes' => $qty, 'rate' => $rate, 'discount' => 0, 'amount' => $sub]);
            $o = M\SalesOrder::create(['order_no' => Seq::next('sales_order'), 'order_date' => $this->d(-$ago - 2),
                'customer_id' => $c->id, 'quotation_id' => $q->id, 'quarry_id' => $sh->id,
                'delivery_address' => $c->address, 'required_date' => $this->d(-$ago + 7),
                'subtotal' => $sub, 'discount_amount' => 0, 'tax_amount' => $tax['total_tax'], 'grand_total' => $tax['grand'],
                'advance_received' => 0, 'status' => 'confirmed', 'created_by' => $admin, 'approved_by' => $admin]);
            $o->lines()->create(['product_id' => $p->id, 'qty_ordered' => $qty, 'qty_dispatched' => 0, 'rate' => $rate, 'amount' => $sub]);
            $tare = mt_rand(12000, 15000); $net = $qty * 1000;
            $t = M\WeighbridgeTicket::create(['ticket_no' => Seq::next('weighbridge'), 'ticket_date' => $dt,
                'vehicle_no' => $vehicles[$i % 5], 'driver_name' => $drivers[$i % 4], 'transporter_id' => $trans[$i % 3]->id,
                'customer_id' => $c->id, 'product_id' => $p->id, 'transaction_type' => 'sales-dispatch',
                'gross_weight' => ($tare + $net) / 1000, 'tare_weight' => $tare / 1000, 'net_weight' => $qty,
                'sales_order_id' => $o->id, 'first_weight_time' => $dt.' 07:4'.$i % 10 .':00', 'second_weight_time' => $dt.' 09:1'.$i % 10 .':00',
                'operator_id' => $admin, 'status' => 'closed']);
            $pile = M\Stockpile::where('quarry_id', $sh->id)->where('product_id', $p->id)->firstOrFail();
            $dn = M\DeliveryNote::create(['dn_no' => Seq::next('delivery_note'), 'dn_date' => $dt, 'customer_id' => $c->id,
                'sales_order_id' => $o->id, 'transporter_id' => $trans[$i % 3]->id, 'vehicle_no' => $vehicles[$i % 5],
                'driver_name' => $drivers[$i % 4], 'quarry_id' => $sh->id, 'weighbridge_ticket_id' => $t->id,
                'gate_pass_no' => Seq::next('gate_pass'), 'subtotal' => $sub, 'tax_amount' => $tax['total_tax'],
                'grand_total' => $tax['grand'], 'status' => 'dispatched', 'dispatched_by' => 'Kofi Boateng']);
            $dn->lines()->create(['product_id' => $p->id, 'stockpile_id' => $pile->id, 'qty_tonnes' => $qty, 'rate' => $rate, 'amount' => $sub]);
     
            StockService::moveStockpile($pile->id, $dt, 'dispatch-out', 0, $qty, 'delivery_note', $dn->id, 'Dispatch '.$dn->dn_no);
            $t->update(['delivery_note_id' => $dn->id]);
            $o->lines()->first()->increment('qty_dispatched', $qty);
            SalesOrderController::refreshDispatchStatus($o->fresh());
            $inv = M\SalesInvoice::create(['invoice_no' => Seq::next('sales_invoice'), 'invoice_date' => $dt,
                'due_date' => date('Y-m-d', strtotime($dt.' +'.$c->payment_terms_days.' days')), 'customer_id' => $c->id,
                'sales_order_id' => $o->id, 'delivery_note_id' => $dn->id, 'subtotal' => $sub, 'discount_amount' => 0,
                'levy_nhil' => $tax['nhil'], 'levy_getfund' => $tax['getfund'], 'levy_covid' => $tax['covid'],
                'vat_amount' => $tax['vat'], 'grand_total' => $tax['grand'], 'amount_paid' => 0, 'balance_due' => $tax['grand'],
                'status' => 'sent', 'terms' => 'Payment due in '.$c->payment_terms_days.' days', 'created_by' => $admin, 'approved_by' => $admin]);
            $inv->lines()->create(['product_id' => $p->id, 'description' => $p->name, 'qty' => $qty, 'unit_id' => $unitT, 'rate' => $rate, 'amount' => $sub]);
            $j = FinanceService::postJournal($dt, 'Sales invoice '.$inv->invoice_no, [
                ['account' => '1100', 'debit' => $inv->grand_total, 'narration' => 'AR — '.$inv->invoice_no],
                ['account' => '4000', 'credit' => $inv->subtotal, 'narration' => 'Aggregate sales'],
                ['account' => '2100', 'credit' => $inv->vat_amount, 'narration' => 'VAT payable'],
                ['account' => '2110', 'credit' => $inv->levy_nhil, 'narration' => 'NHIL payable'],
                ['account' => '2120', 'credit' => $inv->levy_getfund, 'narration' => 'GETFund payable'],
                ['account' => '2130', 'credit' => $inv->levy_covid, 'narration' => 'COVID levy payable'],
            ], 'sales', $inv->id, $inv->invoice_no);
            $inv->update(['journal_id' => $j->id]);
            $c->increment('current_balance', $inv->grand_total);
            $dn->update(['status' => 'invoiced']);
            $frac = $payPlan[$i];
            if ($frac > 0) {
                $amt = round($inv->grand_total * $frac, 2);
                $pdt = $this->d(-max(0, $ago - 5));
                $pay = M\CustomerPayment::create(['receipt_no' => Seq::next('customer_payment'), 'payment_date' => $pdt,
                    'customer_id' => $c->id, 'invoice_id' => $inv->id, 'amount' => $amt,
                    'payment_mode' => $i % 3 === 0 ? 'momo' : 'bank-transfer', 'bank_account_id' => $i % 3 === 0 ? $momo->id : $gcb->id,
                    'received_by' => 'Kofi Boateng']);
                $inv->increment('amount_paid', $amt);
                $inv->decrement('balance_due', $amt);
                $inv->update(['status' => $inv->fresh()->balance_due <= 0.009 ? 'paid' : 'partially-paid']);
                $c->decrement('current_balance', $amt);
                FinanceService::bankTxn($pay->bank_account_id, $pdt, 'deposit', $amt, 'Receipt '.$pay->receipt_no, 'sales_receipt', $pay->id, $pay->receipt_no);
                $bk = M\BankAccount::find($pay->bank_account_id);
                FinanceService::postJournal($pdt, 'Customer receipt '.$pay->receipt_no, [
                    ['account' => $bk->gl_account_id, 'debit' => $amt, 'narration' => 'Receipt '.$pay->receipt_no],
                    ['account' => '1100', 'credit' => $amt, 'narration' => 'AR settlement'],
                ], 'sales_receipt', $pay->id, $pay->receipt_no);
            } elseif ($inv->due_date < date('Y-m-d')) {
                $inv->update(['status' => 'overdue']);
            }
        }
        // credit note on first invoice
        $firstInv = M\SalesInvoice::where('balance_due', '>', 1000)->orderBy('id')->first();
        if ($firstInv && $firstInv->balance_due > 1000) {
            $cn = M\SalesCreditNote::create(['cn_no' => Seq::next('credit_note'), 'cn_date' => $this->d(-6),
                'customer_id' => $firstInv->customer_id, 'invoice_id' => $firstInv->id, 'reason' => 'Short load — 5t',
                'status' => 'applied', 'created_by' => $admin, 'approved_by' => $admin, 'amount' => 500]);
            $cn->lines()->create(['description' => 'Short load adjustment', 'qty' => 5, 'rate' => 100, 'amount' => 500]);
            $firstInv->increment('amount_paid', 500);
            $firstInv->decrement('balance_due', 500);
            $firstInv->update(['status' => $firstInv->fresh()->balance_due <= 0.009 ? 'paid' : 'partially-paid']);
            $firstInv->customer()->decrement('current_balance', 500);
            FinanceService::postJournal($this->d(-6), 'Credit note '.$cn->cn_no, [
                ['account' => '4000', 'debit' => 500, 'narration' => 'Sales returns'],
                ['account' => '1100', 'credit' => 500, 'narration' => 'AR — '.$cn->cn_no],
            ], 'credit_note', $cn->id, $cn->cn_no);
        }

        // ── 6. Purchase cycle ×8 ──────────────────────────────────────────
        $poPlan = [
            ['TotalEnergies Ghana', [['DSL', 6000]], 21, 1],
            ['Mantrac Ghana Ltd', [['FLT-KIT', 10], ['OIL-15W', 8]], 18, 1],
            ['Maxam Ghana Ltd', [['ANFO', 1500], ['DET-E', 400]], 25, 0.5],
            ['Ghacem Ltd', [['CEM-50', 800]], 30, 0],
            ['CFAO Ghana', [['TYRE-235', 2], ['BELT-800', 60]], 15, 1],
            ['GOIL Company Ltd', [['DSL', 4000]], 12, 1],
            ['Melcom Ltd', [['BOOT-S', 24], ['HELM', 30], ['VEST', 40]], 35, 0],
            ['Mantrac Ghana Ltd', [['CONE-LN', 1]], 9, 1],
        ];
        foreach ($poPlan as [$snm, $rows, $ago, $frac]) {
            $s = $sup($snm);
            $pr = M\PurchaseRequisition::create(['pr_no' => Seq::next('purchase_requisition'), 'pr_date' => $this->d(-$ago - 6),
                'requested_by' => 2, 'department' => 'Stores', 'quarry_id' => $sh->id,
                'required_date' => $this->d(-$ago + 7), 'purpose' => 'Restock — '.$snm, 'status' => 'approved', 'approved_by' => $admin]);
            $sub = 0; $lines = [];
            foreach ($rows as [$icd, $q]) {
                $it = $item($icd);
                $pr->lines()->create(['item_id' => $it->id, 'description' => $it->name, 'qty' => $q, 'unit_id' => $it->unit_id, 'estimated_rate' => $it->last_purchase_rate]);
                $lines[] = ['item_id' => $it->id, 'description' => $it->name, 'qty_ordered' => $q, 'unit_id' => $it->unit_id, 'rate' => $it->last_purchase_rate];
                $sub += $q * $it->last_purchase_rate;
            }
            $sub = round($sub, 2);
            M\Approval::create(['document_type' => 'purchase_requisition', 'document_id' => $pr->id, 'document_no' => $pr->pr_no,
                'requested_by' => 2, 'decided_by' => $admin, 'status' => 'approved', 'decided_at' => $this->d(-$ago - 5).' 10:00:00']);
            $wht = Tax::withholding($sub, 'goods');
            $ptax = Tax::salesTax($sub);
            $po = M\PurchaseOrder::create(['po_no' => Seq::next('purchase_order'), 'po_date' => $this->d(-$ago - 4),
                'supplier_id' => $s->id, 'requisition_id' => $pr->id, 'quarry_id' => $sh->id,
                'delivery_address' => 'Shai Hills Quarry', 'expected_date' => $this->d(-$ago + 5),
                'subtotal' => $sub, 'tax_amount' => $ptax['total_tax'], 'withholding_tax' => $wht,
                'grand_total' => round($ptax['grand'] - $wht, 2), 'status' => 'sent', 'prepared_by' => $admin, 'approved_by' => $admin,
                'terms' => 'Quote LPO on delivery notes and invoices']);
            foreach ($lines as $l) $po->lines()->create($l + ['qty_received' => 0, 'amount' => round($l['qty_ordered'] * $l['rate'], 2)]);
            $pr->update(['status' => 'converted']);
            M\Approval::create(['document_type' => 'purchase_order', 'document_id' => $po->id, 'document_no' => $po->po_no,
                'requested_by' => $admin, 'decided_by' => $admin, 'status' => 'approved', 'decided_at' => $this->d(-$ago - 3).' 14:00:00']);
            $g = M\GoodsReceipt::create(['grn_no' => Seq::next('goods_receipt'), 'grn_date' => $this->d(-$ago),
                'supplier_id' => $s->id, 'purchase_order_id' => $po->id, 'warehouse_id' => $hq->id,
                'received_by' => 'Yaw Antwi', 'status' => 'posted']);
            foreach ($po->lines as $ol) {
                $g->lines()->create(['item_id' => $ol->item_id, 'qty_ordered' => $ol->qty_ordered, 'qty_received' => $ol->qty_ordered,
                    'qty_accepted' => $ol->qty_ordered, 'qty_rejected' => 0, 'rate' => $ol->rate, 'amount' => $ol->amount]);
                StockService::postLedger($this->d(-$ago), $ol->item_id, $hq->id, 'purchase', $ol->qty_ordered, 0, $ol->rate, 'goods_receipt', $g->id, [], 'GRN '.$g->grn_no);
                $ol->increment('qty_received', $ol->qty_ordered);
            }
            PurchaseOrderController::refreshReceiveStatus($po->fresh());
            $bill = M\PurchaseInvoice::create(['pin_no' => Seq::next('purchase_invoice'), 'invoice_date' => $this->d(-$ago + 1),
                'supplier_id' => $s->id, 'purchase_order_id' => $po->id, 'goods_receipt_id' => $g->id,
                'supplier_invoice_no' => 'SUP-'.mt_rand(10000, 99999), 'subtotal' => $sub, 'tax_amount' => $ptax['total_tax'],
                'withholding_tax' => $wht, 'grand_total' => round($ptax['grand'] - $wht, 2), 'amount_paid' => 0,
                'balance_due' => round($ptax['grand'] - $wht, 2),
                'due_date' => date('Y-m-d', strtotime($this->d(-$ago + 1).' +'.$s->payment_terms_days.' days')),
                'status' => 'draft', 'created_by' => $admin]);
            foreach ($po->lines as $ol) $bill->lines()->create(['description' => $ol->description, 'qty' => $ol->qty_ordered, 'rate' => $ol->rate, 'amount' => $ol->amount]);
            $bl = [
                ['account' => '5000', 'debit' => $sub, 'narration' => 'Purchases — '.$bill->pin_no],
                ['account' => '2140', 'debit' => $ptax['total_tax'], 'narration' => 'Input VAT/levies'],
                ['account' => '2000', 'credit' => $bill->grand_total, 'narration' => 'AP — '.$bill->pin_no],
            ];
            if ($wht > 0) $bl[] = ['account' => '2150', 'credit' => $wht, 'narration' => 'WHT payable'];
            $bj = FinanceService::postJournal($this->d(-$ago + 1), 'Supplier bill '.$bill->pin_no, $bl, 'purchase', $bill->id, $bill->pin_no);
            $bill->update(['status' => 'approved', 'journal_id' => $bj->id, 'approved_by' => $admin]);
            $s->increment('current_balance', $bill->grand_total);
            if ($frac > 0) {
                $amt = round($bill->grand_total * $frac, 2);
                $pay = M\SupplierPayment::create(['payment_no' => Seq::next('supplier_payment'), 'payment_date' => $this->d(-max(0, $ago - 6)),
                    'supplier_id' => $s->id, 'invoice_id' => $bill->id, 'amount' => $amt, 'payment_mode' => 'bank-transfer',
                    'bank_account_id' => $gcb->id, 'paid_by' => 'Ama Serwaa']);
                $bill->increment('amount_paid', $amt);
                $bill->decrement('balance_due', $amt);
                $bill->update(['status' => $bill->fresh()->balance_due <= 0.009 ? 'paid' : 'partially-paid']);
                $s->decrement('current_balance', $amt);
                FinanceService::bankTxn($gcb->id, $pay->payment_date->format('Y-m-d'), 'withdrawal', $amt, 'Payment '.$pay->payment_no, 'supplier_payment', $pay->id, $pay->payment_no);
                FinanceService::postJournal($pay->payment_date->format('Y-m-d'), 'Supplier payment '.$pay->payment_no, [
                    ['account' => '2000', 'debit' => $amt, 'narration' => 'AP settlement'],
                    ['account' => $gcb->gl_account_id, 'credit' => $amt, 'narration' => 'Payment '.$pay->payment_no],
                ], 'supplier_payment', $pay->id, $pay->payment_no);
            }
        }

        // ── 7. HR: attendance, leave, advances ────────────────────────────
        $emps = M\Employee::where('status', 'active')->get();
        $wdays = [];
        for ($d = 1; count($wdays) < 5; $d++) {
            $dt = $this->d(-$d);
            if (date('w', strtotime($dt)) == 0) continue;
            $wdays[] = $dt;
        }
        foreach ($wdays as $dt) {
            foreach ($emps as $e) {
                $r = mt_rand(1, 100);
                $st = $r <= 90 ? 'present' : ($r <= 94 ? 'late' : ($r <= 97 ? 'on-leave' : 'absent'));
                M\Attendance::create(['employee_id' => $e->id, 'attendance_date' => $dt, 'shift' => 'day',
                    'check_in' => $st === 'absent' || $st === 'on-leave' ? null : ($st === 'late' ? '08:45' : '07:55'),
                    'check_out' => $st === 'absent' || $st === 'on-leave' ? null : '17:05',
                    'hours_worked' => $st === 'absent' || $st === 'on-leave' ? 0 : 9,
                    'overtime_hours' => 0, 'status' => $st]);
            }
        }
        $lvT = M\LeaveType::where('name', 'Annual Leave')->value('id');
        $lv1 = M\LeaveApplication::create(['leave_no' => Seq::next('leave'), 'employee_id' => $emp('SSNIT-005')->id,
            'leave_type_id' => $lvT, 'from_date' => '2026-08-10', 'to_date' => '2026-08-14', 'days_requested' => 5,
            'reason' => 'Family engagements', 'covering_officer' => 'Abena Osei', 'status' => 'approved', 'approved_by' => $admin]);
        M\Approval::create(['document_type' => 'leave_application', 'document_id' => $lv1->id, 'document_no' => $lv1->leave_no,
            'requested_by' => 5, 'decided_by' => $admin, 'status' => 'approved', 'decided_at' => '2026-08-05 09:00:00']);
        $lv2 = M\LeaveApplication::create(['leave_no' => Seq::next('leave'), 'employee_id' => $emp('SSNIT-011')->id,
            'leave_type_id' => $lvT, 'from_date' => '2026-09-21', 'to_date' => '2026-09-25', 'days_requested' => 5,
            'reason' => 'Personal', 'covering_officer' => 'Kwabena Frimpong', 'status' => 'pending']);
        M\Approval::create(['document_type' => 'leave_application', 'document_id' => $lv2->id, 'document_no' => $lv2->leave_no,
            'requested_by' => 1, 'status' => 'pending']);
        $ad1 = M\StaffAdvance::create(['advance_no' => Seq::next('advance'), 'employee_id' => $emp('SSNIT-011')->id,
            'advance_date' => '2026-07-20', 'amount' => 3000, 'purpose' => 'School fees', 'repayment_months' => 3,
            'monthly_deduction' => 1000, 'amount_repaid' => 1000, 'balance' => 2000, 'status' => 'approved', 'approved_by' => $admin]);
        M\Approval::create(['document_type' => 'staff_advance', 'document_id' => $ad1->id, 'document_no' => $ad1->advance_no,
            'requested_by' => 1, 'decided_by' => $admin, 'status' => 'approved', 'decided_at' => '2026-07-21 11:00:00']);
        $ad2 = M\StaffAdvance::create(['advance_no' => Seq::next('advance'), 'employee_id' => $emp('SSNIT-009')->id,
            'advance_date' => $this->d(-3), 'amount' => 2500, 'purpose' => 'Rent', 'repayment_months' => 5,
            'monthly_deduction' => 500, 'amount_repaid' => 0, 'balance' => 2500, 'status' => 'pending']);
        M\Approval::create(['document_type' => 'staff_advance', 'document_id' => $ad2->id, 'document_no' => $ad2->advance_no,
            'requested_by' => 1, 'status' => 'pending']);

        // ── 8. Payroll — August 2026 (computed, approved, paid) ───────────
        $run = M\PayrollRun::create(['payroll_no' => Seq::next('payroll_run'), 'payroll_month' => '2026-08-01',
            'status' => 'draft', 'prepared_by' => $admin]);
        $t = ['g' => 0, 'p' => 0, 'se' => 0, 'sr' => 0, 'dd' => 0, 'n' => 0];
        foreach ($emps as $e) {
            $adv = (float) M\StaffAdvance::where('employee_id', $e->id)->where('status', 'approved')->where('balance', '>', 0)->sum('monthly_deduction');
            $adv = min($adv, (float) M\StaffAdvance::where('employee_id', $e->id)->where('status', 'approved')->where('balance', '>', 0)->sum('balance'));
            $c = PayrollService::compute($e, 0, $adv, 0);
            $run->lines()->create(['employee_id' => $e->id] + $c);
            $t['g'] += $c['gross_pay']; $t['p'] += $c['paye']; $t['se'] += $c['ssnit_employee'];
            $t['sr'] += $c['ssnit_employer']; $t['dd'] += $c['total_deductions']; $t['n'] += $c['net_pay'];
        }
        $run->update(['total_gross' => round($t['g'], 2), 'total_payee' => round($t['p'], 2),
            'total_ssnit_employee' => round($t['se'], 2), 'total_ssnit_employer' => round($t['sr'], 2),
            'total_deductions' => round($t['dd'], 2), 'total_net' => round($t['n'], 2),
            'status' => 'approved', 'approved_by' => $admin]);
        $rec = round($run->total_deductions - $run->total_payee - $run->total_ssnit_employee, 2);
        $pl = [
            ['account' => '5200', 'debit' => $run->total_gross, 'narration' => 'Gross salaries'],
            ['account' => '5210', 'debit' => $run->total_ssnit_employer, 'narration' => 'Employer SSNIT'],
            ['account' => '2160', 'credit' => $run->total_payee, 'narration' => 'PAYE payable'],
            ['account' => '2170', 'credit' => round($run->total_ssnit_employee + $run->total_ssnit_employer, 2), 'narration' => 'SSNIT payable'],
            ['account' => '2180', 'credit' => $run->total_net, 'narration' => 'Staff salaries payable'],
        ];
        if ($rec > 0) $pl[] = ['account' => '1300', 'credit' => $rec, 'narration' => 'Advance/other recoveries'];
        $pl[] = ['account' => '2180', 'debit' => $run->total_net, 'narration' => 'Salary settlement '.$run->payroll_no];
        $pl[] = ['account' => $gcb->gl_account_id, 'credit' => $run->total_net, 'narration' => 'Salary payment '.$run->payroll_no];
        FinanceService::postJournal('2026-08-28', 'Payroll '.$run->payroll_no, $pl, 'payroll', $run->id, $run->payroll_no);
        FinanceService::bankTxn($gcb->id, '2026-08-28', 'withdrawal', $run->total_net, 'Salary payment '.$run->payroll_no, 'payroll', $run->id, $run->payroll_no);
        foreach ($run->lines as $l) {
            $left = (float) $l->advances_deduction;
            foreach (M\StaffAdvance::where('employee_id', $l->employee_id)->where('status', 'approved')->where('balance', '>', 0)->orderBy('advance_date')->get() as $a) {
                if ($left <= 0) break;
                $take = min($left, $a->balance);
                $a->increment('amount_repaid', $take);
                $a->decrement('balance', $take);
                if ($a->fresh()->balance <= 0.009) $a->update(['status' => 'settled']);
                $left -= $take;
            }
        }
        $run->update(['status' => 'paid', 'paid_date' => '2026-08-28']);

        // ── 9. Expense claims, journals, bank ─────────────────────────────
        $ex1 = M\ExpenseClaim::create(['claim_no' => Seq::next('expense_claim'), 'claim_date' => $this->d(-12),
            'employee_id' => $emp('SSNIT-005')->id, 'quarry_id' => $sh->id, 'purpose' => 'Customer visits — Tema/Takoradi',
            'total_amount' => 1850, 'status' => 'paid', 'approved_by' => $admin, 'paid_via_bank_id' => $momo->id]);
        $ex1->lines()->create(['expense_date' => $this->d(-13), 'category' => 'fuel', 'description' => 'Fuel — Hilux', 'amount' => 1200, 'receipt_attached' => true]);
        $ex1->lines()->create(['expense_date' => $this->d(-12), 'category' => 'feeding', 'description' => 'Lunch with client', 'amount' => 650, 'receipt_attached' => true]);
        FinanceService::postJournal($this->d(-10), 'Expense claim '.$ex1->claim_no, [
            ['account' => '5300', 'debit' => 1850, 'narration' => $ex1->purpose, 'quarry_id' => $sh->id],
            ['account' => '2180', 'credit' => 1850, 'narration' => 'Staff payable'],
            ['account' => '2180', 'debit' => 1850, 'narration' => 'Settlement '.$ex1->claim_no],
            ['account' => $momo->gl_account_id, 'credit' => 1850, 'narration' => 'Payment '.$ex1->claim_no],
        ], 'expense', $ex1->id, $ex1->claim_no);
        FinanceService::bankTxn($momo->id, $this->d(-10), 'withdrawal', 1850, 'Expense claim '.$ex1->claim_no, 'expense', $ex1->id, $ex1->claim_no);
        M\Approval::create(['document_type' => 'expense_claim', 'document_id' => $ex1->id, 'document_no' => $ex1->claim_no,
            'requested_by' => 5, 'decided_by' => $admin, 'status' => 'approved', 'decided_at' => $this->d(-11).' 15:00:00']);
        $ex2 = M\ExpenseClaim::create(['claim_no' => Seq::next('expense_claim'), 'claim_date' => $this->d(-4),
            'employee_id' => $emp('SSNIT-013')->id, 'project_id' => $p1->id, 'purpose' => 'Site supervision travel',
            'total_amount' => 2400, 'status' => 'approved', 'approved_by' => $admin]);
        $ex2->lines()->create(['expense_date' => $this->d(-5), 'category' => 'fuel', 'description' => 'Fuel — site trips', 'amount' => 1600, 'receipt_attached' => true]);
        $ex2->lines()->create(['expense_date' => $this->d(-4), 'category' => 'accommodation', 'description' => 'Overnight — Dawhenya', 'amount' => 800, 'receipt_attached' => false]);
        M\Approval::create(['document_type' => 'expense_claim', 'document_id' => $ex2->id, 'document_no' => $ex2->claim_no,
            'requested_by' => 1, 'decided_by' => $admin, 'status' => 'approved', 'decided_at' => $this->d(-2).' 10:00:00']);
        $ex3 = M\ExpenseClaim::create(['claim_no' => Seq::next('expense_claim'), 'claim_date' => $this->d(-1),
            'employee_id' => $emp('SSNIT-008')->id, 'quarry_id' => $sh->id, 'purpose' => 'Emergency spares run — Tema',
            'total_amount' => 950, 'status' => 'submitted']);
        $ex3->lines()->create(['expense_date' => $this->d(-1), 'category' => 'transport', 'description' => 'Trotro + taxi fares', 'amount' => 950, 'receipt_attached' => false]);
        M\Approval::create(['document_type' => 'expense_claim', 'document_id' => $ex3->id, 'document_no' => $ex3->claim_no,
            'requested_by' => 1, 'status' => 'pending']);
        FinanceService::postJournal($this->d(-20), 'Accrue audit fees — FY2026 interim', [
            ['account' => '5450', 'debit' => 15000, 'narration' => 'Audit fees accrued'],
            ['account' => '2200', 'credit' => 15000, 'narration' => 'Audit fees accrued'],
        ], 'manual', null, 'ACCR-01');
        FinanceService::bankTxn($gcb->id, $this->d(-7), 'bank-charges', 250, 'Monthly account maintenance', 'manual', null, 'CHG-AUG');
        FinanceService::postJournal($this->d(-7), 'Bank charges — August', [
            ['account' => '5600', 'debit' => 250, 'narration' => 'Account maintenance'],
            ['account' => $gcb->gl_account_id, 'credit' => 250, 'narration' => 'Account maintenance'],
        ], 'bank', null, 'CHG-AUG');

        // ── 10. Projects: diaries, BOQ progress, claim, IPC ───────────────
        for ($i = 0; $i < 10; $i++) {
            M\SiteDiary::create(['project_id' => $i % 2 ? $p2->id : $p1->id, 'diary_date' => $this->d(-(2 + $i * 3)),
                'weather' => $i % 4 === 3 ? 'Rainy' : 'Sunny', 'labour_count' => mt_rand(18, 42),
                'staff_on_site' => 'Site engineer, foreman, 2 masons gang', 'equipment_on_site' => 'Excavator, tipper, roller',
                'work_progress' => 'Earthworks ongoing; sub-base laid 240m', 'materials_received' => '40mm hardcore 6 loads',
                'issues_delays' => $i % 4 === 3 ? 'Rain stopped work 2hrs' : null, 'prepared_by' => 'Kwaku Manu']);
        }
        foreach (M\BoqItem::where('project_id', $p1->id)->get() as $ix => $b) {
            $f = [0.8, 0.55, 0.1, 0.3, 0][$ix] ?? 0;
            $b->update(['qty_executed' => round($b->qty * $f, 3), 'amount_executed' => round($b->amount * $f, 2)]);
        }
        foreach ([['material', 45000, 'Hardcore to site — August'], ['labour', 28000, 'Casual labour — August'], ['fuel', 12500, 'Diesel — site equipment']] as [$cat, $amt, $ds]) {
            M\ProjectExpense::create(['project_id' => $p1->id, 'expense_date' => $this->d(-15), 'category' => $cat,
                'description' => $ds, 'amount' => $amt, 'ref_type' => 'manual', 'incurred_by' => 'Kwaku Manu', 'approved_by' => $admin]);
        }
        $sub1 = M\Subcontract::where('subcontract_no', 'SUB-26-00001')->first();
        $cert = 380000;
        $clm = M\SubcontractorClaim::create(['project_id' => $p1->id, 'subcontract_id' => $sub1->id,
            'claim_no' => Seq::next('subcontractor_claim'), 'claim_date' => $this->d(-14),
            'period_from' => '2026-07-01', 'period_to' => '2026-07-31', 'amount_claimed' => $cert, 'amount_certified' => $cert,
            'retention_deducted' => round($cert * 5 / 100, 2), 'tax_withheld' => Tax::withholding($cert, 'services'),
            'net_payable' => round($cert - $cert * 5 / 100 - Tax::withholding($cert, 'services'), 2),
            'status' => 'certified', 'remarks' => 'Earthworks section A — July']);
        M\ProjectExpense::create(['project_id' => $p1->id, 'expense_date' => $this->d(-14), 'category' => 'subcontract',
            'description' => 'Subcontract claim '.$clm->claim_no.' — '.$sub1->subcontractor_name, 'amount' => $clm->net_payable,
            'ref_type' => 'subcontractor_claim', 'ref_id' => $clm->id]);
        M\Approval::create(['document_type' => 'subcontractor_claim', 'document_id' => $clm->id, 'document_no' => $clm->claim_no,
            'requested_by' => $admin, 'decided_by' => $admin, 'status' => 'approved', 'decided_at' => $this->d(-12).' 12:00:00']);
        $gross = 900000;
        $ipc = M\ClientIpc::create(['project_id' => $p1->id, 'ipc_no' => Seq::next('client_ipc'), 'ipc_date' => $this->d(-11),
            'period_from' => '2026-07-01', 'period_to' => '2026-07-31', 'gross_valuation' => $gross,
            'retention_deducted' => round($gross * 10 / 100, 2), 'advance_repayment' => 0,
            'tax_withheld' => Tax::withholding($gross, 'services'),
            'net_certified' => round($gross - $gross * 10 / 100 - Tax::withholding($gross, 'services'), 2),
            'status' => 'certified', 'remarks' => 'IPC 03 — July valuation']);
        $ipcInv = M\SalesInvoice::create(['invoice_no' => Seq::next('sales_invoice'), 'invoice_date' => $this->d(-11),
            'due_date' => $this->d(19), 'customer_id' => $p1->client_id, 'reference_ipc' => $ipc->id,
            'subtotal' => $ipc->net_certified, 'discount_amount' => 0, 'levy_nhil' => 0, 'levy_getfund' => 0,
            'levy_covid' => 0, 'vat_amount' => 0, 'grand_total' => $ipc->net_certified, 'amount_paid' => 0,
            'balance_due' => $ipc->net_certified, 'status' => 'draft', 'terms' => 'IPC '.$ipc->ipc_no.' — '.$p1->name, 'created_by' => $admin]);
        $ipcInv->lines()->create(['description' => 'IPC '.$ipc->ipc_no.' — '.$p1->name.' (certified works)',
            'qty' => 1, 'rate' => $ipc->net_certified, 'amount' => $ipc->net_certified]);
        M\Approval::create(['document_type' => 'client_ipc', 'document_id' => $ipc->id, 'document_no' => $ipc->ipc_no,
            'requested_by' => $admin, 'decided_by' => $admin, 'status' => 'approved', 'decided_at' => $this->d(-10).' 16:00:00']);

        // ── 11. HSE ─────────────────────────────────────────────────────
        $inc1 = M\HseIncident::create(['incident_no' => Seq::next('hse_incident'), 'incident_date' => $this->d(-22),
            'quarry_id' => $sh->id, 'incident_type' => 'near-miss', 'severity' => 'medium', 'location' => 'Crusher platform',
            'persons_involved' => 'Adjoa Mensimah', 'description' => 'Loose guard rail noticed during rounds',
            'immediate_action' => 'Area barricaded; guard re-fixed same day', 'root_cause' => 'Bolt fatigue',
            'corrective_action' => 'Weekly guard inspection added to checklist', 'lost_time_days' => 0,
            'reported_by' => 'Ama Konadu', 'status' => 'closed']);
        M\HseIncident::create(['incident_no' => Seq::next('hse_incident'), 'incident_date' => $this->d(-5),
            'quarry_id' => $sh->id, 'incident_type' => 'injury', 'severity' => 'low', 'location' => 'Workshop',
            'persons_involved' => 'Kojo Appiah', 'description' => 'Minor cut on hand while handling belt splice',
            'immediate_action' => 'First aid administered', 'lost_time_days' => 0, 'reported_by' => 'Ama Konadu', 'status' => 'open']);
        for ($i = 0; $i < 4; $i++) {
            M\HseInspection::create(['inspection_no' => Seq::next('hse_inspection'), 'inspection_date' => $this->d(-(6 + $i * 9)),
                'quarry_id' => $sh->id, 'inspection_type' => 'weekly', 'inspector' => 'Ama Konadu',
                'findings' => 'Housekeeping good; 2 extinguishers due service', 'corrective_actions' => 'Extinguisher service booked',
                'status' => $i < 3 ? 'closed' : 'open']);
        }
        M\WorkPermit::create(['permit_no' => Seq::next('work_permit'), 'quarry_id' => $sh->id, 'work_type' => 'hot-work',
            'description' => 'Welding crusher chute liner', 'requested_by' => 'Kojo Appiah', 'permit_date' => $this->d(-3),
            'valid_from' => $this->d(-3), 'valid_to' => $this->d(-2), 'precautions' => 'Fire watch + extinguisher at point',
            'approved_by' => 'Yaw Mensah', 'status' => 'closed']);
        M\WorkPermit::create(['permit_no' => Seq::next('work_permit'), 'quarry_id' => $sh->id, 'work_type' => 'blasting',
            'description' => 'Bench B2 production blast', 'requested_by' => 'Yaw Darko', 'permit_date' => $this->d(-1),
            'valid_from' => $this->d(1), 'valid_to' => $this->d(1), 'precautions' => '500m exclusion; siren + guards',
            'status' => 'pending']);
        M\WorkPermit::create(['permit_no' => Seq::next('work_permit'), 'project_id' => $p1->id, 'work_type' => 'excavation',
            'description' => 'Culvert trench — chainage 2+400', 'requested_by' => 'Kwaku Manu', 'permit_date' => $this->d(-8),
            'valid_from' => $this->d(-8), 'valid_to' => $this->d(-5), 'precautions' => 'Shoring + dewatering pump',
            'approved_by' => 'Ama Konadu', 'status' => 'approved']);
        M\HseTraining::create(['training_date' => $this->d(-18), 'topic' => 'Blast safety & exclusion drills',
            'trainer' => 'Ama Konadu', 'venue' => 'Shai Hills mess hall', 'attendees_count' => 32,
            'attendee_list' => 'All pit + crusher crew', 'duration_hours' => 4, 'expiry_date' => '2027-08-20']);
        M\HseTraining::create(['training_date' => $this->d(-40), 'topic' => 'Defensive driving — dumper operators',
            'trainer' => 'DVLA instructor (external)', 'venue' => 'Shai Hills yard', 'attendees_count' => 9,
            'attendee_list' => 'All dumper drivers', 'duration_hours' => 6, 'expiry_date' => '2027-07-28']);
        foreach ([['SSNIT-010', 'Safety Boots (Pair)', 1], ['SSNIT-011', 'Safety Boots (Pair)', 1], ['SSNIT-014', 'Safety Helmet', 1],
                  ['SSNIT-008', 'Reflective Vest', 2], ['SSNIT-010', 'Reflective Vest', 2], ['SSNIT-014', 'Reflective Vest', 2]] as [$ss, $nm, $q]) {
            M\PpeIssue::create(['issue_date' => $this->d(-25), 'employee_id' => $emp($ss)->id, 'item_name' => $nm,
                'qty' => $q, 'warehouse_id' => $shs->id, 'issued_by' => 'Yaw Antwi', 'returnable' => false]);
        }
        $params = [['dust-pm10', 68, 'µg/m³', 150, 'Pit rim north'], ['dust-pm10', 74, 'µg/m³', 150, 'Crusher area'],
            ['noise', 78, 'dB', 85, 'Pit floor'], ['noise', 71, 'dB', 85, 'Gate'], ['vibration', 3.2, 'mm/s', 5, 'Nearest house'],
            ['water-ph', 7.1, 'pH', 9, 'Settling pond'], ['water-turbidity', 22, 'NTU', 50, 'Settling pond'], ['dust-pm10', 162, 'µg/m³', 150, 'Haul road']];
        foreach ($params as $ix => [$pm, $rd, $un, $lm, $loc]) {
            M\EnvironmentalMonitoring::create(['monitoring_date' => $this->d(-(4 + ($ix % 4) * 7)), 'quarry_id' => $sh->id,
                'parameter' => $pm, 'reading_value' => $rd, 'unit' => $un, 'permissible_limit' => $lm,
                'location_point' => $loc, 'compliant' => $rd <= $lm, 'monitored_by' => 'Ama Konadu',
                'remarks' => $rd > $lm ? 'Haul road watering frequency increased' : null]);
        }

        // ── 12. Fixed assets, budget, stock adjustment ────────────────────
        foreach ([['Pit-bottom weighbridge 80t', 'plant-machinery', '2024-06-10', 485000, 15, 'Shai Hills weighbridge', $sh->id],
                  ['Office block — Tema HQ', 'building', '2022-03-01', 1200000, 25, 'Tema HQ', null],
                  ['Borehole + treatment — Shai Hills', 'plant-machinery', '2023-09-15', 145000, 10, 'Shai Hills', $sh->id],
                  ['Workshop tools & compressor', 'plant-machinery', '2025-01-20', 98000, 8, 'Shai Hills workshop', $sh->id]] as [$nm, $ct, $pd, $cost, $life, $loc, $qd]) {
            M\FixedAsset::create(['asset_no' => Seq::next('fixed_asset'), 'name' => $nm, 'category' => $ct,
                'purchase_date' => $pd, 'purchase_cost' => $cost, 'useful_life_years' => $life, 'salvage_value' => 0,
                'accumulated_depreciation' => 0, 'net_book_value' => $cost, 'location' => $loc, 'quarry_id' => $qd, 'status' => 'active']);
        }
        $fy = M\FiscalYear::where('is_current', true)->first();
        $bud = M\Budget::create(['fiscal_year_id' => $fy->id, 'quarry_id' => $sh->id, 'department' => 'Quarry Operations',
            'name' => 'Shai Hills Operating Budget 2026', 'total_amount' => 0, 'status' => 'approved', 'approved_by' => $admin]);
        $btot = 0;
        foreach ([['5150', 95000], ['5250', 60000], ['5200', 210000], ['5300', 45000]] as [$acd, $pm]) {
            $acc = M\Account::where('code', $acd)->first();
            for ($mth = 1; $mth <= 12; $mth++) {
                $bud->lines()->create(['account_id' => $acc->id, 'month' => $mth, 'budgeted_amount' => $pm, 'actual_amount' => 0]);
                $btot += $pm;
            }
        }
        $bud->update(['total_amount' => $btot]);
        $adj = M\StockAdjustment::create(['adjustment_no' => Seq::next('stock_adjustment'), 'adjustment_date' => $this->d(-9),
            'warehouse_id' => $hq->id, 'reason' => 'Cycle count variance — August', 'status' => 'posted',
            'created_by' => $admin, 'approved_by' => $admin]);
        foreach ([['GRS', -2], ['WELD-RD', 3]] as [$icd, $diff]) {
            $it = $item($icd)->fresh();
            $counted = max(0, $it->current_qty + $diff);
            $adj->lines()->create(['item_id' => $it->id, 'counted_qty' => $counted, 'system_qty' => $it->current_qty,
                'diff_qty' => $counted - $it->current_qty, 'rate' => $it->avg_cost_rate]);
            if ($counted > $it->current_qty) StockService::postLedger($this->d(-9), $it->id, $hq->id, 'adjust-in', $counted - $it->current_qty, 0, $it->avg_cost_rate, 'stock_adjustment', $adj->id, [], 'ADJ '.$adj->adjustment_no);
            else StockService::postLedger($this->d(-9), $it->id, $hq->id, 'adjust-out', 0, $it->current_qty - $counted, $it->avg_cost_rate, 'stock_adjustment', $adj->id, [], 'ADJ '.$adj->adjustment_no);
        }
        // diesel top-up transfer HQ→fuel bay + cement issue to project
        $trf = Seq::next('stock_transfer');
        StockService::postLedger($this->d(-16), $item('DSL')->id, $hq->id, 'transfer-out', 0, 500, $dslRate, 'stock_transfer', 0, [], $trf);
        StockService::postLedger($this->d(-16), $item('DSL')->id, $fb->id, 'transfer-in', 500, 0, $dslRate, 'stock_transfer', 0, [], $trf);
        $iss = Seq::next('stock_issue');
        $cem = $item('CEM-50');
        StockService::postLedger($this->d(-13), $cem->id, $hq->id, 'issue', 0, 120, $cem->avg_cost_rate, 'stock_issue', 0, ['project_id' => $p1->id], $iss.' — Culvert works');
        M\ProjectExpense::create(['project_id' => $p1->id, 'expense_date' => $this->d(-13), 'category' => 'material',
            'description' => $iss.': '.$cem->display_name.' × 120', 'amount' => round(120 * $cem->avg_cost_rate, 2), 'ref_type' => 'stock_issue', 'ref_id' => 0]);
    }
}
