<?php
/* RECIPE FIX V2: legacy recipe_item_id fallback + qty_required support */
require_once __DIR__ . '/../../includes/bootstrap.php';
require_login();
date_default_timezone_set('Africa/Harare');
try { $pdo->exec("SET time_zone = '+02:00'"); } catch (Throwable $e) {}
$pageTitle = 'Restaurant Stock Recovery';

if (!function_exists('h')) {
    function h($v): string { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); }
}
function rsr_table_exists(PDO $pdo, string $t): bool {
    static $c=[]; $k=strtolower($t); if(isset($c[$k])) return $c[$k];
    $s=$pdo->prepare("SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=:t");
    $s->execute([':t'=>$t]); return $c[$k]=((int)$s->fetchColumn()>0);
}
function rsr_column_exists(PDO $pdo, string $t, string $cname): bool {
    static $c=[]; $k=strtolower($t.'.'.$cname); if(isset($c[$k])) return $c[$k];
    $s=$pdo->prepare("SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=:t AND COLUMN_NAME=:c");
    $s->execute([':t'=>$t,':c'=>$cname]); return $c[$k]=((int)$s->fetchColumn()>0);
}
function rsr_first_col(PDO $pdo,string $t,array $cols): ?string { foreach($cols as $c){ if(rsr_column_exists($pdo,$t,$c)) return $c; } return null; }
function rsr_find_recipe(PDO $pdo,int $menuItemId): ?array {
    if($menuItemId<=0 || !rsr_table_exists($pdo,'inventory_recipes')) return null;

    // Preferred modern link: recipe points directly to the restaurant menu item.
    $link=rsr_first_col($pdo,'inventory_recipes',['restaurant_menu_item_id','menu_item_id','restaurant_item_id']);
    if($link){
        $where="`$link`=:mid".(rsr_column_exists($pdo,'inventory_recipes','is_active')?" AND COALESCE(is_active,1)=1":"");
        $st=$pdo->prepare("SELECT * FROM inventory_recipes WHERE $where ORDER BY id DESC LIMIT 1");
        $st->execute([':mid'=>$menuItemId]);
        $row=$st->fetch(PDO::FETCH_ASSOC);
        if($row) return $row;
    }

    // Legacy link used by the live POS posting code:
    // restaurant_menu_items.inventory_item_id -> inventory_recipes.recipe_item_id.
    if(rsr_table_exists($pdo,'restaurant_menu_items')
       && rsr_column_exists($pdo,'restaurant_menu_items','inventory_item_id')
       && rsr_column_exists($pdo,'inventory_recipes','recipe_item_id')){
        $activeSql=rsr_column_exists($pdo,'inventory_recipes','is_active')
            ? "AND COALESCE(ir.is_active,1)=1" : "";
        $st=$pdo->prepare("
            SELECT ir.*
            FROM restaurant_menu_items mi
            INNER JOIN inventory_recipes ir
              ON ir.recipe_item_id=mi.inventory_item_id
            WHERE mi.id=:mid
              $activeSql
            ORDER BY ir.id DESC
            LIMIT 1
        ");
        $st->execute([':mid'=>$menuItemId]);
        $row=$st->fetch(PDO::FETCH_ASSOC);
        if($row) return $row;
    }

    return null;
}
function rsr_resolve_store(PDO $pdo,array $recipe,array $menu,int $directItemId=0): int {
    foreach(['default_store_id','store_id'] as $c){ if(isset($recipe[$c]) && (int)$recipe[$c]>0) return (int)$recipe[$c]; }
    if($directItemId>0){
        foreach(['default_store_id','store_id'] as $c){
            if(rsr_column_exists($pdo,'inventory_items',$c)){
                $s=$pdo->prepare("SELECT `$c` FROM inventory_items WHERE id=:id LIMIT 1");$s->execute([':id'=>$directItemId]);$id=(int)$s->fetchColumn();if($id>0)return $id;
            }
        }
    }
    $isBar=((int)($menu['is_bar']??0)===1)||in_array(strtolower((string)($recipe['recipe_type']??'')),['cocktail','mocktail','bar'],true);
    if(rsr_column_exists($pdo,'store_locations','location_type')){
        $s=$pdo->prepare("SELECT id FROM store_locations WHERE COALESCE(is_active,1)=1 AND LOWER(location_type)=:t ORDER BY id LIMIT 1");
        $s->execute([':t'=>$isBar?'bar':'kitchen']);$id=(int)$s->fetchColumn();if($id>0)return $id;
    }
    foreach($isBar?['bar store','bar bulk','bar']:['kitchen store','kitchen','food store'] as $term){
        $s=$pdo->prepare("SELECT id FROM store_locations WHERE COALESCE(is_active,1)=1 AND LOWER(location_name) LIKE :n ORDER BY id LIMIT 1");
        $s->execute([':n'=>'%'.$term.'%']);$id=(int)$s->fetchColumn();if($id>0)return $id;
    }
    return 0;
}
function rsr_existing(PDO $pdo,string $type,int $ref,int $item): bool {
    $s=$pdo->prepare("SELECT COUNT(*) FROM stock_movements WHERE reference_type=:t AND reference_id=:r AND item_id=:i");
    $s->execute([':t'=>$type,':r'=>$ref,':i'=>$item]); return (int)$s->fetchColumn()>0;
}
function rsr_cost(PDO $pdo,int $itemId): float {
    $s=$pdo->prepare("SELECT COALESCE(NULLIF(avg_cost,0),cost_price,0) FROM inventory_items WHERE id=:id LIMIT 1");
    $s->execute([':id'=>$itemId]); return round((float)$s->fetchColumn(),4);
}

function rsr_item_meta(PDO $pdo,int $itemId): array {
    static $cache=[];
    if($itemId<=0) return ['item_name'=>'','item_description'=>'','unit_code'=>'','avg_cost'=>0.0];
    if(isset($cache[$itemId])) return $cache[$itemId];
    $descCol=rsr_column_exists($pdo,'inventory_items','description')?'ii.description':'NULL';
    $unitJoin=rsr_table_exists($pdo,'inventory_units') && rsr_column_exists($pdo,'inventory_items','unit_id')
        ? 'LEFT JOIN inventory_units u ON u.id=ii.unit_id' : '';
    $unitExpr=$unitJoin!=='' ? "COALESCE(u.unit_code,u.unit_name,'')" : "''";
    $st=$pdo->prepare("SELECT ii.item_name,$descCol AS item_description,$unitExpr AS unit_code,COALESCE(NULLIF(ii.avg_cost,0),ii.cost_price,0) AS avg_cost FROM inventory_items ii $unitJoin WHERE ii.id=:id LIMIT 1");
    $st->execute([':id'=>$itemId]);
    $row=$st->fetch(PDO::FETCH_ASSOC)?:[];
    return $cache[$itemId]=[
        'item_name'=>(string)($row['item_name']??''),
        'item_description'=>(string)($row['item_description']??''),
        'unit_code'=>(string)($row['unit_code']??''),
        'avg_cost'=>(float)($row['avg_cost']??0),
    ];
}
function rsr_store_meta(PDO $pdo,int $storeId): array {
    static $cache=[];
    if($storeId<=0) return ['store_name'=>'','store_code'=>''];
    if(isset($cache[$storeId])) return $cache[$storeId];
    $codeExpr=rsr_column_exists($pdo,'store_locations','location_code')?'location_code':'NULL';
    $st=$pdo->prepare("SELECT location_name,$codeExpr AS location_code FROM store_locations WHERE id=:id LIMIT 1");
    $st->execute([':id'=>$storeId]);
    $row=$st->fetch(PDO::FETCH_ASSOC)?:[];
    return $cache[$storeId]=[
        'store_name'=>(string)($row['location_name']??''),
        'store_code'=>(string)($row['location_code']??''),
    ];
}
function rsr_enrich_movement(PDO $pdo,array $m): array {
    $item=rsr_item_meta($pdo,(int)$m['item_id']);
    $store=rsr_store_meta($pdo,(int)$m['store_id']);
    return array_merge($m,$item,$store);
}

function rsr_recipe_line_qty(array $r): float { foreach(['qty_required','quantity','qty','ingredient_qty','quantity_required','usage_qty','amount'] as $c){ if(array_key_exists($c,$r)&&$r[$c]!==''&&$r[$c]!==null)return (float)$r[$c]; } return 0.0; }
function rsr_insert(PDO $pdo,array $m): int {
    $s=$pdo->prepare("INSERT INTO stock_movements(item_id,store_id,movement_type,qty_in,qty_out,unit_cost,total_cost,reference_type,reference_id,notes,movement_date) VALUES(:item,:store,'SALE',0,:qty,:cost,:total,:type,:ref,:notes,:dt)");
    $s->execute([':item'=>$m['item_id'],':store'=>$m['store_id'],':qty'=>$m['qty_out'],':cost'=>$m['unit_cost'],':total'=>$m['total_cost'],':type'=>$m['reference_type'],':ref'=>$m['reference_id'],':notes'=>$m['notes'],':dt'=>$m['movement_date']]);
    return (int)$pdo->lastInsertId();
}
function rsr_order_items(PDO $pdo,int $orderId): array {
    $qtyExpr=rsr_column_exists($pdo,'restaurant_order_items','qty')?"COALESCE(NULLIF(roi.qty,0),".(rsr_column_exists($pdo,'restaurant_order_items','quantity')?'roi.quantity':'0').",0)":(rsr_column_exists($pdo,'restaurant_order_items','quantity')?'COALESCE(roi.quantity,0)':'0');
    $miInv=rsr_column_exists($pdo,'restaurant_menu_items','inventory_item_id')?'mi.inventory_item_id':'NULL';
    $isBar=rsr_column_exists($pdo,'restaurant_menu_items','is_bar')?'COALESCE(mi.is_bar,0)':'0';
    $isFood=rsr_column_exists($pdo,'restaurant_menu_items','is_food')?'COALESCE(mi.is_food,0)':'0';
    $status=rsr_column_exists($pdo,'restaurant_order_items','status')?"AND LOWER(COALESCE(roi.status,'active')) NOT IN ('void','voided','cancelled','canceled')":"";
    $cond=rsr_column_exists($pdo,'restaurant_order_items','is_condiment')?"AND COALESCE(roi.is_condiment,0)=0":"";
    /* Recovery must use the CURRENT menu configuration, not the historical inventory_item_id copied onto the order line when it was sold. */
    $s=$pdo->prepare("SELECT roi.id order_item_id,roi.menu_item_id,$miInv menu_inventory_item_id,roi.item_name,$qtyExpr sold_qty,$isBar is_bar,$isFood is_food FROM restaurant_order_items roi LEFT JOIN restaurant_menu_items mi ON mi.id=roi.menu_item_id WHERE roi.restaurant_order_id=:id $status $cond ORDER BY roi.id");
    $s->execute([':id'=>$orderId]); return $s->fetchAll(PDO::FETCH_ASSOC)?:[];
}
function rsr_process_order(PDO $pdo,array $order,string $mode,bool $dry): array {
    $out=['order_id'=>(int)$order['id'],'order_no'=>(string)($order['order_no']??''),'closed_at'=>(string)($order['closed_at']??''),'items'=>0,'direct_added'=>0,'recipe_added'=>0,'existing'=>0,'not_linked'=>0,'errors'=>[],'movements'=>[],'completed'=>false];
    $dt=trim((string)($order['closed_at']??'')); if($dt==='')$dt=date('Y-m-d H:i:s');
    $items=rsr_order_items($pdo,(int)$order['id']);$out['items']=count($items);
    foreach($items as $oi){
        $oiId=(int)$oi['order_item_id'];$menuId=(int)($oi['menu_item_id']??0);$sold=round((float)($oi['sold_qty']??0),4);$name=trim((string)($oi['item_name']??('Item '.$oiId)));
        if($sold<=0){$out['not_linked']++;continue;}
        $meta=['is_bar'=>(int)($oi['is_bar']??0),'is_food'=>(int)($oi['is_food']??0)];
        $recipe=$menuId>0?rsr_find_recipe($pdo,$menuId):null;
        if($recipe && in_array($mode,['both','recipe'],true)){
            $store=rsr_resolve_store($pdo,$recipe,$meta,0);if($store<=0){$out['errors'][]="$name: no recipe store resolved.";continue;}
            $yield=max(0.0001,(float)($recipe['yield_qty']??1));
            $s=$pdo->prepare("SELECT rl.*,ii.item_name ingredient_name,COALESCE(NULLIF(ii.avg_cost,0),ii.cost_price,0) ingredient_cost FROM inventory_recipe_lines rl INNER JOIN inventory_items ii ON ii.id=rl.ingredient_item_id WHERE rl.recipe_id=:id ORDER BY rl.id");
            $s->execute([':id'=>(int)$recipe['id']]);$rls=$s->fetchAll(PDO::FETCH_ASSOC)?:[];
            if(!$rls){$out['errors'][]="$name: recipe #".(int)$recipe['id']." has no ingredient lines.";continue;}
            foreach($rls as $rl){
                $ingredient=(int)$rl['ingredient_item_id'];$qty=round((rsr_recipe_line_qty($rl)/$yield)*$sold,4);if($ingredient<=0||$qty<=0)continue;
                if(rsr_existing($pdo,'RESTAURANT_RECIPE',$oiId,$ingredient)){$out['existing']++;continue;}
                $cost=round((float)($rl['ingredient_cost']??0),4);$m=rsr_enrich_movement($pdo,['item_id'=>$ingredient,'store_id'=>$store,'qty_out'=>$qty,'unit_cost'=>$cost,'total_cost'=>round($qty*$cost,4),'reference_type'=>'RESTAURANT_RECIPE','reference_id'=>$oiId,'menu_item_name'=>$name,'notes'=>'Restaurant recipe recovery | '.($order['order_no']??('#'.$order['id'])).' | '.$name.' | Ingredient: '.($rl['ingredient_name']??('#'.$ingredient)),'movement_date'=>$dt]);
                if(!$dry)$m['movement_id']=rsr_insert($pdo,$m);$out['recipe_added']++;$out['movements'][]=$m;
            }
            continue;
        }
        if($recipe && $mode==='direct'){$out['not_linked']++;continue;}
        /* Use only the CURRENT direct inventory link on restaurant_menu_items. Historical order-line links are deliberately ignored. */
        $direct=(int)($oi['menu_inventory_item_id']??0);
        if($direct>0 && in_array($mode,['both','direct'],true)){
            $store=rsr_resolve_store($pdo,[],$meta,$direct);if($store<=0){$out['errors'][]="$name: no direct inventory store resolved.";continue;}
            if(rsr_existing($pdo,'RESTAURANT_DIRECT',$oiId,$direct)){$out['existing']++;continue;}
            $cost=rsr_cost($pdo,$direct);$m=rsr_enrich_movement($pdo,['item_id'=>$direct,'store_id'=>$store,'qty_out'=>$sold,'unit_cost'=>$cost,'total_cost'=>round($sold*$cost,4),'reference_type'=>'RESTAURANT_DIRECT','reference_id'=>$oiId,'menu_item_name'=>$name,'notes'=>'Restaurant direct stock recovery | '.($order['order_no']??('#'.$order['id'])).' | '.$name,'movement_date'=>$dt]);
            if(!$dry)$m['movement_id']=rsr_insert($pdo,$m);$out['direct_added']++;$out['movements'][]=$m;
        }else{$out['not_linked']++;}
    }
    $out['completed']=count($out['errors'])===0;
    if(!$dry){
        $err=$out['errors']?implode("\n",array_slice($out['errors'],0,20)):null;
        $s=$pdo->prepare("UPDATE restaurant_orders SET recipe_stock_posted=:p,recipe_stock_posted_at=:pa,recipe_stock_error=:e WHERE id=:id");
        $s->execute([':p'=>$out['completed']?1:0,':pa'=>$out['completed']?$dt:null,':e'=>$err,':id'=>(int)$order['id']]);
    }
    return $out;
}

$dateFrom=trim((string)($_POST['date_from']??'2026-07-01'));$dateTo=trim((string)($_POST['date_to']??'2026-07-31'));$propertyId=(int)($_POST['property_id']??0);$mode=strtolower(trim((string)($_POST['mode']??'both')));$limit=max(1,min(1000,(int)($_POST['batch_limit']??500)));$dry=!isset($_POST['execute_live']);
if(!in_array($mode,['both','direct','recipe'],true))$mode='both';
$properties=[];try{$properties=$pdo->query("SELECT id,name FROM properties ORDER BY name")->fetchAll(PDO::FETCH_ASSOC)?:[];}catch(Throwable $e){}
$errors=[];$results=[];$totals=['orders'=>0,'direct_added'=>0,'recipe_added'=>0,'existing'=>0,'not_linked'=>0,'failed_orders'=>0];
if(($_SERVER['REQUEST_METHOD']??'GET')==='POST'){
    try{
        if(function_exists('verify_csrf'))verify_csrf();
        foreach(['restaurant_orders','restaurant_order_items','restaurant_menu_items','inventory_items','inventory_recipes','inventory_recipe_lines','stock_movements','store_locations'] as $t){if(!rsr_table_exists($pdo,$t))throw new RuntimeException("Required table $t not found.");}
        $where=["ro.closed_at>=:df","ro.closed_at<DATE_ADD(:dt,INTERVAL 1 DAY)","LOWER(COALESCE(ro.status,''))='closed'"];$params=[':df'=>date('Y-m-d 00:00:00',strtotime($dateFrom)),':dt'=>date('Y-m-d',strtotime($dateTo))];
        if($propertyId>0 && rsr_column_exists($pdo,'restaurant_orders','property_id')){$where[]='ro.property_id=:pid';$params[':pid']=$propertyId;}
        $s=$pdo->prepare("SELECT ro.* FROM restaurant_orders ro WHERE ".implode(' AND ',$where)." AND EXISTS(SELECT 1 FROM restaurant_order_items roi WHERE roi.restaurant_order_id=ro.id AND LOWER(COALESCE(roi.status,'active')) NOT IN ('void','voided','cancelled','canceled')) ORDER BY ro.closed_at,ro.id LIMIT $limit");
        $s->execute($params);$orders=$s->fetchAll(PDO::FETCH_ASSOC)?:[];
        foreach($orders as $order){
            try{if(!$dry)$pdo->beginTransaction();$r=rsr_process_order($pdo,$order,$mode,$dry);if(!$dry)$pdo->commit();$results[]=$r;$totals['orders']++;foreach(['direct_added','recipe_added','existing','not_linked'] as $k)$totals[$k]+=$r[$k];if(!$r['completed'])$totals['failed_orders']++;}
            catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();$results[]=['order_id'=>(int)$order['id'],'order_no'=>(string)($order['order_no']??''),'closed_at'=>(string)($order['closed_at']??''),'items'=>0,'direct_added'=>0,'recipe_added'=>0,'existing'=>0,'not_linked'=>0,'errors'=>[$e->getMessage()],'movements'=>[],'completed'=>false];$totals['orders']++;$totals['failed_orders']++;}
        }
    }catch(Throwable $e){$errors[]=$e->getMessage();}
}
require_once __DIR__ . '/../../includes/header.php';
?>
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4"><div><div class="text-uppercase small text-muted fw-bold">Inventory Recovery</div><h2 class="mb-1">Restaurant Stock Recovery</h2><p class="text-muted mb-0">Recreate missing recipe and direct inventory movements without duplicating existing movements.</p></div><a href="planner.php" class="btn btn-outline-secondary">Restaurant Planner</a></div>
<?php if($errors):?><div class="alert alert-danger"><ul class="mb-0"><?php foreach($errors as $e):?><li><?=h($e)?></li><?php endforeach;?></ul></div><?php endif;?>
<div class="card border-0 shadow-sm mb-4"><div class="card-body"><form method="post" id="recoveryForm"><?php if(function_exists('csrf_field'))echo csrf_field();?><div class="row g-3 align-items-end">
<div class="col-md-2"><label class="form-label">From Date</label><input type="date" name="date_from" class="form-control" value="<?=h($dateFrom)?>" required></div>
<div class="col-md-2"><label class="form-label">To Date</label><input type="date" name="date_to" class="form-control" value="<?=h($dateTo)?>" required></div>
<div class="col-md-3"><label class="form-label">Property</label><select name="property_id" class="form-select"><option value="0">All / include legacy NULL property</option><?php foreach($properties as $p):?><option value="<?=(int)$p['id']?>" <?=$propertyId===(int)$p['id']?'selected':''?>><?=h($p['name'])?></option><?php endforeach;?></select></div>
<div class="col-md-2"><label class="form-label">Mode</label><select name="mode" class="form-select"><option value="both" <?=$mode==='both'?'selected':''?>>Direct + Recipes</option><option value="direct" <?=$mode==='direct'?'selected':''?>>Direct only</option><option value="recipe" <?=$mode==='recipe'?'selected':''?>>Recipes only</option></select></div>
<div class="col-md-2"><label class="form-label">Max Orders</label><input type="number" name="batch_limit" class="form-control" min="1" max="1000" value="<?=$limit?>"></div>
<div class="col-md-5"><div class="form-check form-switch pt-2"><input class="form-check-input" type="checkbox" name="execute_live" value="1" id="executeLive"><label class="form-check-label fw-semibold" for="executeLive">Execute live deductions</label><div class="form-text">Leave off first for Dry Run.</div></div></div>
<div class="col-md-4 d-grid"><button class="btn btn-primary btn-lg" id="runButton">Run Recovery</button></div>
</div><div class="alert alert-warning mt-3 mb-0"><strong>Safe sequence:</strong> Dry Run first, inspect results, back up the database, then enable live deductions.</div></form></div></div>
<?php if(($_SERVER['REQUEST_METHOD']??'GET')==='POST'&&!$errors):?><div class="alert <?=$dry?'alert-info':'alert-success'?>"><strong><?=$dry?'Dry Run completed.':'Live recovery completed.'?></strong> <?=$dry?'No movements were inserted.':'Missing movements were inserted using original closed_at timestamps.'?></div>
<div class="row g-3 mb-4"><?php foreach([['Orders',$totals['orders']],['Direct',$totals['direct_added']],['Recipe',$totals['recipe_added']],['Existing',$totals['existing']],['Not linked',$totals['not_linked']],['Errors',$totals['failed_orders']]] as $c):?><div class="col-6 col-lg-2"><div class="card border-0 shadow-sm h-100"><div class="card-body"><div class="small text-muted"><?=h($c[0])?></div><div class="fs-3 fw-bold"><?=(int)$c[1]?></div></div></div></div><?php endforeach;?></div>
<div class="card border-0 shadow-sm"><div class="table-responsive"><table class="table table-hover align-middle mb-0"><thead><tr><th>Order</th><th>Closed</th><th class="text-end">Items</th><th class="text-end">Direct</th><th class="text-end">Recipe</th><th class="text-end">Existing</th><th>Status</th></tr></thead><tbody><?php foreach($results as $r):?><tr><td><strong><?=h($r['order_no']?:('#'.$r['order_id']))?></strong><div class="small text-muted">ID <?=(int)$r['order_id']?></div></td><td><?=h($r['closed_at'])?></td><td class="text-end"><?=(int)$r['items']?></td><td class="text-end"><?=(int)$r['direct_added']?></td><td class="text-end"><?=(int)$r['recipe_added']?></td><td class="text-end"><?=(int)$r['existing']?></td><td><?php if($r['completed']):?><span class="badge text-bg-success">Ready / completed</span><?php else:?><span class="badge text-bg-danger">Needs attention</span><ul class="small text-danger mt-2 mb-0"><?php foreach(array_slice($r['errors'],0,5) as $e):?><li><?=h($e)?></li><?php endforeach;?></ul><?php endif;?><?php if($r['movements']):?><details class="mt-2"><summary class="small">Show movements</summary><div class="table-responsive mt-2"><table class="table table-sm table-bordered align-middle mb-0"><thead><tr><th>Menu item</th><th>Movement</th><th>Inventory item</th><th>Store</th><th>Unit</th><th class="text-end">Qty</th><th class="text-end">Avg cost</th><th class="text-end">Value</th></tr></thead><tbody><?php foreach(array_slice($r['movements'],0,100) as $m):?><tr><td><?=h($m['menu_item_name']??'')?></td><td><span class="badge text-bg-light"><?=h($m['reference_type'])?></span></td><td><strong>#<?=(int)$m['item_id']?> - <?=h($m['item_name']??'')?></strong><?php if(!empty($m['item_description'])):?><div class="text-muted"><?=h($m['item_description'])?></div><?php endif;?></td><td><strong><?=h($m['store_name']??('Store #'.(int)$m['store_id']))?></strong><?php if(!empty($m['store_code'])):?><div class="text-muted"><?=h($m['store_code'])?></div><?php endif;?></td><td><?=h($m['unit_code']??'')?></td><td class="text-end"><?=h(number_format((float)$m['qty_out'],4))?></td><td class="text-end"><?=h(number_format((float)($m['avg_cost']??$m['unit_cost']??0),4))?></td><td class="text-end"><?=h(number_format((float)$m['total_cost'],4))?></td></tr><?php endforeach;?></tbody></table></div></details><?php endif;?></td></tr><?php endforeach;?><?php if(!$results):?><tr><td colspan="7" class="text-center text-muted py-5">No qualifying closed orders found.</td></tr><?php endif;?></tbody></table></div></div><?php endif;?>
</div>
<script>document.addEventListener('DOMContentLoaded',function(){var f=document.getElementById('recoveryForm'),l=document.getElementById('executeLive'),b=document.getElementById('runButton');if(!f||!l||!b)return;l.addEventListener('change',function(){b.classList.toggle('btn-danger',l.checked);b.classList.toggle('btn-primary',!l.checked);b.textContent=l.checked?'Execute Live Recovery':'Run Dry Check';});f.addEventListener('submit',function(e){if(l.checked&&!confirm('This will create missing stock SALE movements. Existing matching movements will be skipped. Have you backed up the database?'))e.preventDefault();});});</script>
<?php require_once __DIR__ . '/../../includes/footer.php'; ?>
