<?php
declare(strict_types=1);
require __DIR__ . '/app/bootstrap.php';
require_auth();
$page=(string)($_GET['page']??'dashboard');

// Finance Centre owns its schema preflight and recovery redirect. Route it
// before the general dashboard stack so a partial database upgrade cannot be
// turned into an unrelated HTTP 500 while layout/actions are being loaded.
if ($page === 'finance') {
    require __DIR__ . '/finance-control.php';
    exit;
}

require_current_schema();
require __DIR__ . '/app/layout.php';
require __DIR__ . '/app/actions.php';
handle_actions();
$pagePermissions=['imports'=>'imports.manage','team'=>'users.manage','settings'=>'settings.manage','finance'=>'reports.view','reports'=>'reports.view'];
if(isset($pagePermissions[$page]))require_permission($pagePermissions[$page]);

if($page==='document-edit'){
    $quickType=(($_GET['type']??'invoice')==='quotation')?'quotation':'invoice';
    $documentId=(int)($_GET['id']??0);$presetCustomerId=(int)($_GET['customer_id']??0);
    if($documentId>0){$documentContext=db()->prepare('SELECT type,customer_id FROM documents WHERE id=?');$documentContext->execute([$documentId]);if($context=$documentContext->fetch()){$quickType=$context['type']==='quotation'?'quotation':'invoice';if(!$presetCustomerId)$presetCustomerId=(int)$context['customer_id'];}}
    $quickParams=['type'=>$quickType];
    if($documentId>0)$quickParams['id']=$documentId;
    if($presetCustomerId>0)$quickParams['customer_id']=$presetCustomerId;
    redirect('quick-invoice.php?'.http_build_query($quickParams));
}

if ($page === 'dashboard') {
    require __DIR__ . '/accounting-dashboard.php';
    exit;
}

// Use one audited calculation path for reports, credits and reversals.
if ($page === 'reports') {
    redirect('finance-control.php?view=reports');
}

if ($page==='customers' && isset($_GET['template'])) {
    header('Content-Type: text/csv; charset=utf-8');header('Content-Disposition: attachment; filename="sodazw-customer-import-template.csv"');
    $out=fopen('php://output','w');fputcsv($out,['name','company_name','phone','whatsapp','email','tax_number','address','city','customer_type','area','preferred_payment','marketing_consent','next_refill_due','notes']);fputcsv($out,['Example Customer','Example Company','+263771234567','+263771234567','customer@example.com','','Harare address','Harare','Household','Avondale','EcoCash','yes',date('Y-m-d',strtotime('+60 days')),'Delete this example row before importing']);fclose($out);exit;
}
if ($page==='customers' && isset($_GET['export'])) {
    header('Content-Type: text/csv; charset=utf-8');header('Content-Disposition: attachment; filename="sodazw-customers-'.date('Y-m-d').'.csv"');
    $out=fopen('php://output','w');fputcsv($out,['name','company_name','phone','whatsapp','email','tax_number','address','city','customer_type','area','preferred_payment','marketing_consent','next_refill_due','notes']);foreach(db()->query('SELECT name,company_name,phone,whatsapp,email,tax_number,address,city,customer_type,area,preferred_payment,marketing_consent,next_refill_due,notes FROM customers WHERE active=1 ORDER BY name') as $row)fputcsv($out,$row);fclose($out);exit;
}
if ($page==='invoices' && isset($_GET['template'])) {
    header('Content-Type: text/csv; charset=utf-8');header('Content-Disposition: attachment; filename="sodazw-invoice-import-template.csv"');
    $out=fopen('php://output','w');$columns=['invoice_number','issue_date','due_date','status','customer_name','company_name','phone','whatsapp','email','address','city','item_description','quantity','unit_price','tax_rate','discount_type','discount_value','delivery_fee','notes','amount_paid','payment_date','payment_method','payment_reference'];fputcsv($out,$columns);fputcsv($out,['OLD-INV-001',date('Y-m-d'),date('Y-m-d',strtotime('+7 days')),'sent','Example Customer','Example Company','+263771234567','+263771234567','customer@example.com','Customer address','Harare','Black Quick Connect Starter Kit','1','150.00','0','fixed','0','0','Delete this example before importing','0','','','']);fclose($out);exit;
}
if ($page==='invoices' && isset($_GET['export'])) {
    header('Content-Type: text/csv; charset=utf-8');header('Content-Disposition: attachment; filename="sodazw-invoices-'.date('Y-m-d').'.csv"');
    $out=fopen('php://output','w');fputcsv($out,['invoice_number','issue_date','due_date','status','customer_name','company_name','phone','whatsapp','email','address','city','item_description','quantity','unit_price','tax_rate','discount_type','discount_value','delivery_fee','notes','amount_paid','payment_date','payment_method','payment_reference']);
    $sql='SELECT d.number,d.issue_date,d.due_date,d.status,c.name,c.company_name,c.phone,c.whatsapp,c.email,c.address,c.city,i.description,i.quantity,i.unit_price,i.tax_rate,d.discount_type,d.discount_value,d.delivery_fee,d.notes,GREATEST(COALESCE((SELECT SUM(p.amount) FROM payments p WHERE p.invoice_id=d.id),0)-COALESCE((SELECT SUM(r.amount) FROM payment_reversals r JOIN payments rp ON rp.id=r.payment_id WHERE rp.invoice_id=d.id),0),0) amount_paid,(SELECT MAX(p.payment_date) FROM payments p WHERE p.invoice_id=d.id) payment_date,(SELECT p.method FROM payments p WHERE p.invoice_id=d.id ORDER BY p.id DESC LIMIT 1) payment_method,(SELECT p.reference FROM payments p WHERE p.invoice_id=d.id ORDER BY p.id DESC LIMIT 1) payment_reference FROM documents d JOIN customers c ON c.id=d.customer_id JOIN document_items i ON i.document_id=d.id WHERE d.type="invoice" ORDER BY d.issue_date,d.id,i.sort_order';foreach(db()->query($sql) as $row)fputcsv($out,$row);fclose($out);exit;
}

if ($page==='imports' && isset($_GET['template'])) {
    $type=(string)$_GET['template'];$templates=[
        'customers'=>[['name','company_name','phone','whatsapp','email','tax_number','address','city','customer_type','area','preferred_payment','marketing_consent','next_refill_due','notes'],['Example Customer','Example Company','+263771234567','+263771234567','customer@example.com','','100 Livingstone Avenue','Harare','Household','Avondale','EcoCash','yes',date('Y-m-d',strtotime('+60 days')),'Delete this row']],
        'invoices'=>[['invoice_number','issue_date','due_date','status','customer_name','company_name','phone','whatsapp','email','address','city','item_description','quantity','unit_price','tax_rate','discount_type','discount_value','delivery_fee','notes','amount_paid','payment_date','payment_method','payment_reference'],['OLD-INV-001',date('Y-m-d'),date('Y-m-d',strtotime('+7 days')),'sent','Example Customer','','+263771234567','','','','Harare','Black Quick Connect Starter Kit','1','150','0','fixed','0','0','Delete this row','0','','','']],
        'products'=>[['sku','name','description','category','unit_price','cost_price','tax_rate','stock_quantity','track_stock','reorder_level'],['SZW-EXAMPLE','Example Product','Delete this row','Other','10.00','6.00','0','5','1','2']],
        'suppliers'=>[['name','contact_name','email','phone','tax_number','address','notes'],['Example Supplier','Contact Name','supplier@example.com','+263771234567','','Harare','Delete this row']],
        'bills'=>[['supplier_name','bill_number','bill_date','due_date','description','currency_code','total','reference','notes','amount_paid','payment_date','payment_account'],['Example Supplier','BILL-001',date('Y-m-d'),date('Y-m-d',strtotime('+14 days')),'Example stock purchase','USD','100.00','','Delete this row','0','','Cash USD']],
        'expenses'=>[['expense_date','category','supplier','description','amount','reference'],[date('Y-m-d'),'Delivery','Example Supplier','Delete this row','10.00','']],
    ];if(!isset($templates[$type])){http_response_code(404);exit('Unknown template.');}header('Content-Type: text/csv; charset=utf-8');header('Content-Disposition: attachment; filename="sodazw-'.$type.'-import-template.csv"');$out=fopen('php://output','w');fputcsv($out,$templates[$type][0]);fputcsv($out,$templates[$type][1]);fclose($out);exit;
}

if ($page==='reports' && ($_GET['export']??'')==='sales') {
    $from=$_GET['from']??date('Y-01-01');$to=$_GET['to']??date('Y-m-d');$stmt=db()->prepare('SELECT d.number,d.issue_date,c.name customer,d.status,d.subtotal,d.tax_total,d.delivery_fee,d.total,COALESCE(SUM(p.amount),0) paid FROM documents d JOIN customers c ON c.id=d.customer_id LEFT JOIN payments p ON p.invoice_id=d.id WHERE d.type="invoice" AND d.issue_date BETWEEN ? AND ? GROUP BY d.id ORDER BY d.issue_date');$stmt->execute([$from,$to]);
    header('Content-Type: text/csv');header('Content-Disposition: attachment; filename="sodazw-sales-'.$from.'-'.$to.'.csv"');$out=fopen('php://output','w');fputcsv($out,['Invoice','Date','Customer','Status','Subtotal','Tax','Delivery','Total','Paid']);foreach($stmt as $row)fputcsv($out,$row);fclose($out);exit;
}

switch($page){
case 'imports':
    render_header('CSV imports','imports');?>
    <div class="page-actions"><div><h2 class="register-title">Import business records</h2><p class="muted">Move existing Soda ZW spreadsheet information into the system using controlled templates.</p></div></div><div class="alert info">Download the correct template first. Delete its example row, keep the headings unchanged, and save the file as CSV before importing.</div>
    <?php if(user()['role']==='owner'):?><section class="panel import-standard-card"><div><span class="eyebrow">INVOICE QUALITY CONTROL</span><h2>Make imported invoices match Soda ZW</h2><p class="muted">Applies standard Soda ZW invoice numbers, terms, payment status and secure sharing while retaining every original invoice number as a source reference. Existing totals, dates, customers and payments are not changed.</p></div><form method="post" onsubmit="return confirm('Standardise all historical imported invoices now? Original invoice numbers will remain stored as source references.')"><?=csrf_field()?><input type="hidden" name="action" value="normalise_imported_invoices"><button class="button primary" type="submit">Standardise imported invoices</button></form></section><?php endif;?>
    <section class="import-grid">
        <?php $importCards=[
            ['customers','Customers','Names, contacts, addresses and tax details.','import_customers','customer_csv'],
            ['invoices','Historical invoices','Creates standard Soda ZW invoices and retains original invoice numbers as source references.','import_invoices','invoice_csv'],
            ['products','Products & opening stock','Prices, costs, categories, opening stock and reorder levels.','import_products','products_csv'],
            ['suppliers','Suppliers','Supplier contacts, tax numbers and addresses.','import_suppliers','suppliers_csv'],
            ['bills','Supplier bills','Existing bills, balances and optional opening payments.','import_bills','bills_csv'],
            ['expenses','Expenses','Historical operating expenses and references.','import_expenses','expenses_csv'],
        ];foreach($importCards as [$key,$title,$description,$action,$field]):?>
        <article class="import-card"><div><span class="import-icon"><?=e(strtoupper(substr($title,0,1)))?></span><h2><?=e($title)?></h2><p><?=e($description)?></p></div><a class="button secondary small" href="index.php?page=imports&template=<?=e($key)?>">Download template</a><form method="post" enctype="multipart/form-data"><?=csrf_field()?><input type="hidden" name="action" value="<?=e($action)?>"><label>Choose completed CSV<input type="file" name="<?=e($field)?>" accept=".csv,text/csv" required></label><?php if(in_array($key,['customers','products','suppliers'],true)):?><label class="check"><input type="checkbox" name="update_existing" value="1"> Update matching records</label><?php endif;?><button class="button primary">Import <?=e(strtolower($title))?></button></form></article>
        <?php endforeach;?></section><?php render_footer();break;

case 'web-orders':
    $allowedOrderStatuses=['new','contacted','confirmed','invoiced','fulfilled','cancelled'];$orderStatus=(string)($_GET['status']??'');
    $where=$orderStatus&&in_array($orderStatus,$allowedOrderStatuses,true)?'WHERE w.status=?':'';$ordersSql='SELECT w.*,d.number invoice_number,(SELECT SUM(quantity) FROM web_order_items WHERE web_order_id=w.id) item_count FROM web_orders w LEFT JOIN documents d ON d.id=w.invoice_id '.$where.' ORDER BY FIELD(w.status,"new","contacted","confirmed","invoiced","fulfilled","cancelled"),w.created_at DESC LIMIT 250';$ordersStmt=db()->prepare($ordersSql);$ordersStmt->execute($where?[$orderStatus]:[]);$websiteOrders=$ordersStmt->fetchAll();
    $orderCounts=db()->query('SELECT status,COUNT(*) total FROM web_orders GROUP BY status')->fetchAll(PDO::FETCH_KEY_PAIR);$newOrderCount=(int)($orderCounts['new']??0);$openOrderCount=$newOrderCount+(int)($orderCounts['contacted']??0)+(int)($orderCounts['confirmed']??0);$invoicedOrderCount=(int)($orderCounts['invoiced']??0)+(int)($orderCounts['fulfilled']??0);$websiteOrderValue=(float)db()->query('SELECT COALESCE(SUM(total),0) FROM web_orders WHERE status<>"cancelled"')->fetchColumn();
    $selectedOrder=null;$selectedItems=[];if($selectedId=(int)($_GET['id']??0)){$detail=db()->prepare('SELECT w.*,d.number invoice_number FROM web_orders w LEFT JOIN documents d ON d.id=w.invoice_id WHERE w.id=?');$detail->execute([$selectedId]);$selectedOrder=$detail->fetch();if($selectedOrder){$itemDetail=db()->prepare('SELECT * FROM web_order_items WHERE web_order_id=? ORDER BY id');$itemDetail->execute([$selectedId]);$selectedItems=$itemDetail->fetchAll();}}
    render_header('Website orders','web-orders');?>
    <div class="page-actions"><div><h2 class="register-title">Website order inbox</h2><p class="muted">Review online orders, contact the customer and create a controlled draft invoice.</p></div><a class="button secondary" href="https://sodazw.co.zw" target="_blank" rel="noopener">Open shop</a></div>
    <section class="stats accounting-stats"><article class="stat"><span>New orders</span><strong><?=$newOrderCount?></strong><small>Waiting for review</small></article><article class="stat"><span>Open orders</span><strong><?=$openOrderCount?></strong><small>New, contacted or confirmed</small></article><article class="stat"><span>Converted</span><strong><?=$invoicedOrderCount?></strong><small>Invoices created</small></article><article class="stat"><span>Order value</span><strong><?=money($websiteOrderValue)?></strong><small>Excluding cancellations</small></article></section>
    <div class="filter-pills"><a class="<?=!$orderStatus?'active':''?>" href="index.php?page=web-orders">All</a><?php foreach($allowedOrderStatuses as $filter):?><a class="<?=$orderStatus===$filter?'active':''?>" href="index.php?page=web-orders&status=<?=e($filter)?>"><?=e(ucfirst($filter))?><?php if(isset($orderCounts[$filter])):?> <b><?=e((string)$orderCounts[$filter])?></b><?php endif;?></a><?php endforeach;?></div>
    <?php if($selectedOrder):?>
    <section class="panel website-order-detail"><div class="panel-head"><div><h2><?=e($selectedOrder['order_number'])?></h2><p>Received <?=e(date('j M Y, H:i',strtotime($selectedOrder['created_at'])))?> · <?=status_badge($selectedOrder['status'])?></p></div><a class="button secondary small" href="index.php?page=web-orders">Close details</a></div>
      <div class="order-review-grid"><div><h3>Customer</h3><p><strong><?=e($selectedOrder['customer_name'])?></strong><br><a href="tel:<?=e($selectedOrder['phone'])?>"><?=e($selectedOrder['phone'])?></a><?php if($selectedOrder['email']):?><br><a href="mailto:<?=e($selectedOrder['email'])?>"><?=e($selectedOrder['email'])?></a><?php endif;?></p></div><div><h3>Fulfilment</h3><p><strong><?=e($selectedOrder['fulfilment_method'])?></strong><br><?=e($selectedOrder['delivery_address']?:'100 Livingstone Avenue pickup')?></p></div><div><h3>Payment preference</h3><p><?=e($selectedOrder['payment_preference']?:'Confirm with customer')?></p></div><div><h3>Customer notes</h3><p><?=nl2br(e($selectedOrder['notes']?:'No notes supplied.'))?></p></div></div>
      <div class="table-wrap"><table><thead><tr><th>Product</th><th>SKU</th><th>Qty</th><th>Price</th><th>Total</th></tr></thead><tbody><?php foreach($selectedItems as $item):?><tr><td><strong><?=e($item['product_name'])?></strong></td><td><?=e($item['sku'])?></td><td><?=e((string)(float)$item['quantity'])?></td><td><?=money($item['unit_price'])?></td><td><?=money($item['line_total'])?></td></tr><?php endforeach;?></tbody><tfoot><tr><th colspan="4">Order total</th><th><?=money($selectedOrder['total'],$selectedOrder['currency_code'])?></th></tr></tfoot></table></div>
      <div class="website-order-actions"><?php $waDigits=preg_replace('/\D+/','',(string)$selectedOrder['phone']);if(str_starts_with($waDigits,'0'))$waDigits='263'.substr($waDigits,1);?><a class="button green" target="_blank" rel="noopener" href="https://wa.me/<?=e($waDigits)?>?text=<?=rawurlencode('Hello '.$selectedOrder['customer_name'].', Soda ZW has received your website order '.$selectedOrder['order_number'].' for '.money($selectedOrder['total'],$selectedOrder['currency_code']).'. We are checking availability and will confirm shortly.')?>">WhatsApp customer</a><?php if($selectedOrder['invoice_id']):?><a class="button primary" href="index.php?page=document-view&id=<?=$selectedOrder['invoice_id']?>">Open invoice <?=e($selectedOrder['invoice_number'])?></a><?php else:?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="convert_web_order"><input type="hidden" name="id" value="<?=$selectedOrder['id']?>"><button class="button primary" type="submit">Confirm &amp; create draft invoice</button></form><?php endif;?><?php foreach(['contacted'=>'Mark contacted','confirmed'=>'Mark confirmed','fulfilled'=>'Mark fulfilled','cancelled'=>'Cancel order'] as $value=>$label):?><?php if((!$selectedOrder['invoice_id']&&in_array($value,['contacted','confirmed','cancelled'],true))||($selectedOrder['invoice_id']&&$value==='fulfilled')):?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="update_web_order"><input type="hidden" name="id" value="<?=$selectedOrder['id']?>"><input type="hidden" name="status" value="<?=e($value)?>"><button class="button <?=$value==='cancelled'?'danger':'secondary'?>" type="submit"><?=e($label)?></button></form><?php endif;?><?php endforeach;?></div>
    </section><?php endif;?>
    <section class="panel"><div class="panel-head"><div><h2>Orders</h2><p>Server-verified product prices from the Soda ZW catalogue.</p></div></div><div class="table-wrap"><table><thead><tr><th>Reference</th><th>Received</th><th>Customer</th><th>Fulfilment</th><th>Status</th><th>Items</th><th>Total</th><th></th></tr></thead><tbody><?php foreach($websiteOrders as $order):?><tr><td><strong><?=e($order['order_number'])?></strong><?php if($order['invoice_number']):?><br><small class="muted"><?=e($order['invoice_number'])?></small><?php endif;?></td><td><?=e(date('j M Y',strtotime($order['created_at'])))?><br><small class="muted"><?=e(date('H:i',strtotime($order['created_at'])))?></small></td><td><strong><?=e($order['customer_name'])?></strong><br><small class="muted"><?=e($order['phone'])?></small></td><td><?=e($order['fulfilment_method'])?></td><td><?=status_badge($order['status'])?></td><td><?=e((string)(float)$order['item_count'])?></td><td><strong><?=money($order['total'],$order['currency_code'])?></strong></td><td><a class="button secondary small" href="index.php?page=web-orders&id=<?=$order['id']?>">Review</a></td></tr><?php endforeach;?><?php if(!$websiteOrders):?><tr><td colspan="8" class="empty"><strong>No website orders in this view</strong>New shop orders will appear here automatically.</td></tr><?php endif;?></tbody></table></div></section>
    <?php render_footer();break;?>

case 'crm':
    $crmStages=['new_enquiry'=>'New enquiry','contacted'=>'Contacted','quotation_sent'=>'Quotation sent','order_confirmed'=>'Order confirmed','awaiting_payment'=>'Awaiting payment','invoiced'=>'Invoiced','preparing_order'=>'Preparing order','out_for_delivery'=>'Out for delivery','completed'=>'Completed','lost'=>'Lost'];
    $openStages=array_keys(array_diff_key($crmStages,['completed'=>1,'lost'=>1]));$stageFilter=(string)($_GET['stage']??'');$where=in_array($stageFilter,array_keys($crmStages),true)?'WHERE o.stage=?':'';$stmt=db()->prepare('SELECT o.*,c.name customer,c.company_name,c.phone,c.whatsapp,c.customer_type,c.area,u.name assignee,d.number document_number FROM crm_opportunities o JOIN customers c ON c.id=o.customer_id LEFT JOIN users u ON u.id=o.assigned_to LEFT JOIN documents d ON d.id=o.document_id '.$where.' ORDER BY FIELD(o.stage,"new_enquiry","contacted","quotation_sent","order_confirmed","awaiting_payment","invoiced","preparing_order","out_for_delivery","completed","lost"),COALESCE(o.next_follow_up_at,"9999-12-31"),o.updated_at DESC');$stmt->execute($where?[$stageFilter]:[]);$opportunities=$stmt->fetchAll();
    $pipelineRows=db()->query('SELECT stage,COUNT(*) total,COALESCE(SUM(value),0) value FROM crm_opportunities GROUP BY stage')->fetchAll(PDO::FETCH_UNIQUE);$openCount=(int)db()->query('SELECT COUNT(*) FROM crm_opportunities WHERE stage NOT IN ("completed","lost")')->fetchColumn();$openValue=(float)db()->query('SELECT COALESCE(SUM(value),0) FROM crm_opportunities WHERE stage NOT IN ("completed","lost")')->fetchColumn();$dueFollowups=(int)db()->query('SELECT COUNT(*) FROM crm_activities WHERE completed_at IS NULL AND due_at IS NOT NULL AND due_at<=NOW()')->fetchColumn();$activeRefills=(int)db()->query('SELECT COUNT(*) FROM refill_jobs WHERE status NOT IN ("completed","cancelled")')->fetchColumn();$activeDeliveries=(int)db()->query('SELECT COUNT(*) FROM deliveries WHERE status NOT IN ("delivered","cancelled")')->fetchColumn();
    $customersList=db()->query('SELECT id,name,company_name,phone,whatsapp,address,area FROM customers WHERE active=1 ORDER BY COALESCE(NULLIF(company_name,""),name)')->fetchAll();$usersList=db()->query('SELECT id,name FROM users WHERE active=1 ORDER BY name')->fetchAll();$selectedOpportunity=null;$activities=[];if($opportunityId=(int)($_GET['opportunity']??0)){$detail=db()->prepare('SELECT o.*,c.name customer,c.company_name,c.phone,c.whatsapp,c.email,c.address,c.area,c.customer_type,c.preferred_payment,u.name assignee,d.number document_number FROM crm_opportunities o JOIN customers c ON c.id=o.customer_id LEFT JOIN users u ON u.id=o.assigned_to LEFT JOIN documents d ON d.id=o.document_id WHERE o.id=?');$detail->execute([$opportunityId]);$selectedOpportunity=$detail->fetch();if($selectedOpportunity){$a=db()->prepare('SELECT a.*,u.name assignee FROM crm_activities a LEFT JOIN users u ON u.id=a.assigned_to WHERE a.opportunity_id=? ORDER BY a.completed_at IS NULL DESC,COALESCE(a.due_at,a.created_at),a.id DESC');$a->execute([$opportunityId]);$activities=$a->fetchAll();}}
    $upcoming=db()->query('SELECT a.*,c.name customer,o.title opportunity FROM crm_activities a JOIN customers c ON c.id=a.customer_id LEFT JOIN crm_opportunities o ON o.id=a.opportunity_id WHERE a.completed_at IS NULL ORDER BY a.due_at IS NULL,a.due_at LIMIT 12')->fetchAll();render_header('Customer CRM','crm');?>
    <div class="page-actions"><div><h2 class="register-title">Soda ZW customer CRM</h2><p class="muted">Turn enquiries into paid orders, refills and completed deliveries.</p></div><div class="button-row"><a class="button secondary" href="index.php?page=refills&new=1">+ Refill job</a><a class="button secondary" href="index.php?page=deliveries&new=1">+ Delivery</a><a class="button primary" href="index.php?page=crm&new=1">+ Opportunity</a></div></div>
    <section class="stats crm-stats"><article class="stat"><span>Open opportunities</span><strong><?=$openCount?></strong><small>Active sales conversations</small></article><article class="stat"><span>Pipeline value</span><strong><?=money($openValue)?></strong><small>Open expected revenue</small></article><article class="stat <?=$dueFollowups?'crm-warning':''?>"><span>Follow-ups due</span><strong><?=$dueFollowups?></strong><small>Calls, WhatsApp and tasks</small></article><article class="stat"><span>Active refills</span><strong><?=$activeRefills?></strong><small>Overnight service queue</small></article><article class="stat"><span>Open deliveries</span><strong><?=$activeDeliveries?></strong><small>Delivery fee required</small></article></section>
    <section class="crm-pipeline" aria-label="Sales pipeline"><?php foreach($crmStages as $key=>$label):if(in_array($key,['completed','lost'],true))continue;$summary=$pipelineRows[$key]??['total'=>0,'value'=>0];?><a class="crm-stage-card <?=$stageFilter===$key?'active':''?>" href="index.php?page=crm&stage=<?=e($key)?>"><span><?=e($label)?></span><strong><?=e((string)$summary['total'])?></strong><small><?=money((float)$summary['value'])?></small></a><?php endforeach;?></section>
    <?php if(isset($_GET['new'])):?><section class="panel"><div class="panel-head"><div><h2>New sales opportunity</h2><p>Create a trackable enquiry before sending a quotation or invoice.</p></div><a class="button secondary small" href="index.php?page=crm">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_crm_opportunity"><label>Customer<select name="customer_id" required><option value="">Select customer</option><?php foreach($customersList as $c):?><option value="<?=$c['id']?>"><?=e($c['company_name']?:$c['name'])?></option><?php endforeach;?></select></label><label>Opportunity title<input name="title" placeholder="e.g. Black starter kit order" required></label><label>Stage<select name="stage"><?php foreach($crmStages as $key=>$label):?><option value="<?=e($key)?>"><?=e($label)?></option><?php endforeach;?></select></label><label>Source<select name="source"><?php foreach(['WhatsApp','Website','Walk-in','Phone','Facebook','Instagram','Referral','Other'] as $source):?><option><?=e($source)?></option><?php endforeach;?></select></label><label>Expected value<input type="number" min="0" step="0.01" name="value" value="0"></label><label>Currency<input name="currency_code" value="USD" maxlength="3"></label><label>Expected close date<input type="date" name="expected_close_date" value="<?=date('Y-m-d')?>"></label><label>Next follow-up<input type="datetime-local" name="next_follow_up_at" value="<?=date('Y-m-d\TH:i',strtotime('+1 day'))?>"></label><label>Assigned to<select name="assigned_to"><option value="">Unassigned</option><?php foreach($usersList as $u):?><option value="<?=$u['id']?>" <?=$u['id']==user()['id']?'selected':''?>><?=e($u['name'])?></option><?php endforeach;?></select></label><label class="full">Notes<textarea name="notes" rows="3" placeholder="Customer requirements, cylinder compatibility and delivery location"></textarea></label><button class="button primary">Save opportunity</button></form></section><?php endif;?>
    <?php if($selectedOpportunity):$wa=preg_replace('/\D+/','',(string)($selectedOpportunity['whatsapp']?:$selectedOpportunity['phone']));if(str_starts_with($wa,'0'))$wa='263'.substr($wa,1);?>
    <section class="panel crm-detail"><div class="panel-head"><div><h2><?=e($selectedOpportunity['title'])?></h2><p><?=e($selectedOpportunity['company_name']?:$selectedOpportunity['customer'])?> · <?=status_badge($selectedOpportunity['stage'])?></p></div><a class="button secondary small" href="index.php?page=crm">Close</a></div><div class="crm-detail-grid"><article><span>Customer</span><strong><?=e($selectedOpportunity['company_name']?:$selectedOpportunity['customer'])?></strong><small><?=e($selectedOpportunity['customer_type'])?> · <?=e($selectedOpportunity['area']?:'Area not set')?></small></article><article><span>Expected value</span><strong><?=money($selectedOpportunity['value'],$selectedOpportunity['currency_code'])?></strong><small><?=e($selectedOpportunity['source'])?> enquiry</small></article><article><span>Next follow-up</span><strong><?=e($selectedOpportunity['next_follow_up_at']?date('j M, H:i',strtotime($selectedOpportunity['next_follow_up_at'])):'Not scheduled')?></strong><small><?=e($selectedOpportunity['assignee']?:'Unassigned')?></small></article><article><span>Preferred payment</span><strong><?=e($selectedOpportunity['preferred_payment']?:'Confirm with customer')?></strong><small><?=e($selectedOpportunity['document_number']?:'No invoice linked')?></small></article></div><div class="crm-action-row"><?php if($wa):?><a class="button green" target="_blank" rel="noopener" href="https://wa.me/<?=e($wa)?>?text=<?=rawurlencode('Hello '.$selectedOpportunity['customer'].', we are following up on your Soda ZW enquiry: '.$selectedOpportunity['title'].'. How may we assist you?')?>">WhatsApp customer</a><?php endif;?><a class="button secondary" href="index.php?page=document-edit&type=quotation&customer_id=<?=$selectedOpportunity['customer_id']?>">Create quotation</a><a class="button secondary" href="index.php?page=document-edit&type=invoice&customer_id=<?=$selectedOpportunity['customer_id']?>">Create invoice</a><a class="button secondary" href="index.php?page=refills&new=1&customer_id=<?=$selectedOpportunity['customer_id']?>&opportunity_id=<?=$selectedOpportunity['id']?>">Create refill</a><a class="button secondary" href="index.php?page=deliveries&new=1&customer_id=<?=$selectedOpportunity['customer_id']?>&opportunity_id=<?=$selectedOpportunity['id']?>">Schedule delivery</a></div><form method="post" class="crm-stage-form"><?=csrf_field()?><input type="hidden" name="action" value="update_crm_stage"><input type="hidden" name="id" value="<?=$selectedOpportunity['id']?>"><label>Move pipeline stage<select name="stage"><?php foreach($crmStages as $key=>$label):?><option value="<?=e($key)?>" <?=$selectedOpportunity['stage']===$key?'selected':''?>><?=e($label)?></option><?php endforeach;?></select></label><label>Lost reason, if applicable<input name="lost_reason" value="<?=e($selectedOpportunity['lost_reason'])?>"></label><button class="button primary">Update stage</button></form>
    <div class="crm-activity-layout"><div><h3>Activity history</h3><?php foreach($activities as $activity):?><article class="crm-activity <?=$activity['completed_at']?'done':''?>"><div><b><?=e(strtoupper($activity['activity_type']))?></b><strong><?=e($activity['subject'])?></strong><small><?=e($activity['due_at']?'Due '.date('j M Y, H:i',strtotime($activity['due_at'])):'Added '.date('j M Y',strtotime($activity['created_at'])))?><?=!empty($activity['assignee'])?' · '.e($activity['assignee']):''?></small><?php if($activity['notes']):?><p><?=nl2br(e($activity['notes']))?></p><?php endif;?></div><?php if(!$activity['completed_at']):?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="complete_crm_activity"><input type="hidden" name="id" value="<?=$activity['id']?>"><button class="button secondary small">Complete</button></form><?php else:?><span class="paid-clear">Done</span><?php endif;?></article><?php endforeach;?><?php if(!$activities):?><div class="empty">No activities recorded.</div><?php endif;?></div><form method="post" class="crm-activity-form"><?=csrf_field()?><input type="hidden" name="action" value="save_crm_activity"><input type="hidden" name="opportunity_id" value="<?=$selectedOpportunity['id']?>"><input type="hidden" name="customer_id" value="<?=$selectedOpportunity['customer_id']?>"><h3>Add follow-up</h3><label>Type<select name="activity_type"><?php foreach(['whatsapp'=>'WhatsApp','call'=>'Phone call','email'=>'Email','meeting'=>'Meeting','task'=>'Task','note'=>'Note'] as $key=>$label):?><option value="<?=e($key)?>"><?=e($label)?></option><?php endforeach;?></select></label><label>Subject<input name="subject" required placeholder="What needs to happen?"></label><label>Due date and time<input type="datetime-local" name="due_at" value="<?=date('Y-m-d\TH:i',strtotime('+1 day'))?>"></label><label>Assigned to<select name="assigned_to"><option value="">Unassigned</option><?php foreach($usersList as $u):?><option value="<?=$u['id']?>" <?=$u['id']==user()['id']?'selected':''?>><?=e($u['name'])?></option><?php endforeach;?></select></label><label>Notes<textarea name="notes" rows="3"></textarea></label><button class="button primary">Add activity</button></form></div></section><?php endif;?>
    <section class="crm-main-grid"><div class="panel"><div class="panel-head"><div><h2>Opportunities</h2><p><?=e($stageFilter?$crmStages[$stageFilter]:'All active and historical opportunities')?></p></div><?php if($stageFilter):?><a class="button secondary small" href="index.php?page=crm">Show all</a><?php endif;?></div><div class="table-wrap"><table><thead><tr><th>Opportunity</th><th>Customer</th><th>Stage</th><th>Follow-up</th><th>Value</th><th></th></tr></thead><tbody><?php foreach($opportunities as $o):?><tr><td><strong><?=e($o['title'])?></strong><br><small class="muted"><?=e($o['source'])?><?=!empty($o['assignee'])?' · '.e($o['assignee']):''?></small></td><td><?=e($o['company_name']?:$o['customer'])?><br><small class="muted"><?=e($o['area']?:$o['customer_type'])?></small></td><td><?=status_badge($o['stage'])?></td><td class="<?=$o['next_follow_up_at']&&$o['next_follow_up_at']<date('Y-m-d H:i:s')?'due-late':''?>"><?=e($o['next_follow_up_at']?date('j M, H:i',strtotime($o['next_follow_up_at'])):'—')?></td><td><strong><?=money($o['value'],$o['currency_code'])?></strong></td><td><a class="button secondary small" href="index.php?page=crm&opportunity=<?=$o['id']?>">Open</a></td></tr><?php endforeach;?><?php if(!$opportunities):?><tr><td colspan="6" class="empty"><strong>No opportunities yet</strong>Create the first enquiry or convert a website order.</td></tr><?php endif;?></tbody></table></div></div><aside class="panel"><div class="panel-head"><div><h2>Next follow-ups</h2><p>Incomplete customer activities</p></div></div><?php foreach($upcoming as $activity):?><article class="crm-upcoming <?=$activity['due_at']&&$activity['due_at']<date('Y-m-d H:i:s')?'overdue':''?>"><span><?=e(strtoupper($activity['activity_type']))?></span><strong><?=e($activity['subject'])?></strong><small><?=e($activity['customer'])?> · <?=e($activity['due_at']?date('j M, H:i',strtotime($activity['due_at'])):'No due date')?></small></article><?php endforeach;?><?php if(!$upcoming):?><div class="empty">No open follow-ups.</div><?php endif;?></aside></section>
    <?php render_footer();break;

case 'whatsapp':
    $waConfig=whatsapp_config();$waConnected=whatsapp_is_configured();$waStatus=in_array($_GET['status']??'', ['open','pending','closed'],true)?$_GET['status']:'open';
    $waStmt=db()->prepare('SELECT wc.*,c.name customer_name,c.company_name,u.name assigned_name,(SELECT body FROM whatsapp_messages wm WHERE wm.conversation_id=wc.id ORDER BY wm.message_at DESC,wm.id DESC LIMIT 1) last_body,(SELECT direction FROM whatsapp_messages wm WHERE wm.conversation_id=wc.id ORDER BY wm.message_at DESC,wm.id DESC LIMIT 1) last_direction FROM whatsapp_conversations wc LEFT JOIN customers c ON c.id=wc.customer_id LEFT JOIN users u ON u.id=wc.assigned_to WHERE wc.status=? ORDER BY wc.last_message_at DESC,wc.id DESC');$waStmt->execute([$waStatus]);$waConversations=$waStmt->fetchAll();
    $waConversationId=(int)($_GET['conversation']??($waConversations[0]['id']??0));$waSelected=null;$waMessages=[];if($waConversationId){$s=db()->prepare('SELECT wc.*,c.name customer_name,c.company_name,c.email,c.phone customer_phone,c.area,u.name assigned_name FROM whatsapp_conversations wc LEFT JOIN customers c ON c.id=wc.customer_id LEFT JOIN users u ON u.id=wc.assigned_to WHERE wc.id=?');$s->execute([$waConversationId]);$waSelected=$s->fetch();if($waSelected){db()->prepare('UPDATE whatsapp_conversations SET unread_count=0 WHERE id=?')->execute([$waConversationId]);$m=db()->prepare('SELECT wm.*,u.name sender_name FROM whatsapp_messages wm LEFT JOIN users u ON u.id=wm.sent_by WHERE wm.conversation_id=? ORDER BY wm.message_at,wm.id');$m->execute([$waConversationId]);$waMessages=$m->fetchAll();}}
    $waUsers=db()->query('SELECT id,name FROM users WHERE active=1 ORDER BY name')->fetchAll();$waCustomers=db()->query('SELECT id,name,company_name,phone,whatsapp FROM customers WHERE active=1 ORDER BY COALESCE(NULLIF(company_name,""),name)')->fetchAll();
    $waUnread=(int)db()->query('SELECT COALESCE(SUM(unread_count),0) FROM whatsapp_conversations')->fetchColumn();$waOpen=(int)db()->query('SELECT COUNT(*) FROM whatsapp_conversations WHERE status="open"')->fetchColumn();$waToday=(int)db()->query('SELECT COUNT(*) FROM whatsapp_messages WHERE direction="inbound" AND message_at>=CURDATE()')->fetchColumn();render_header('WhatsApp inbox','whatsapp');?>
    <div class="page-actions wa2-heading"><div><span class="wa-level-pill <?=$waConnected?'connected':'setup'?>"><i></i>LEVEL 2 - <?=$waConnected?'META CONNECTED':'SETUP REQUIRED'?></span><h2 class="register-title">Shared WhatsApp inbox</h2><p class="muted">Two-way customer conversations for +263 77 173 9696, connected to CRM, invoices, refills and deliveries.</p></div><a class="button secondary" href="index.php?page=whatsapp-level1">Use Level 1 fallback</a></div>
    <?php if(!$waConnected):?><section class="panel wa2-setup"><div><span class="section-kicker">OWNER SETUP</span><h2>Connect the Soda ZW number to Meta</h2><p>The inbox is installed and waiting for the official Cloud API credentials. Level 1 remains available until Meta approves and registers the number.</p></div><ol><li>Create or select the Soda ZW Meta Business Portfolio and WhatsApp Business Account.</li><li>Register <strong>+263 77 173 9696</strong> and copy its Phone Number ID.</li><li>Add the permanent access token, app secret and a private verify token to <code>config/config.php</code>.</li><li>Set Meta's callback URL to <code>https://billing.sodazw.co.zw/api/whatsapp-webhook.php</code> and subscribe to <code>messages</code>.</li><li>Change <code>'enabled' =&gt; false</code> to <code>true</code> after verification succeeds.</li></ol><p class="wa2-security-note">Access tokens are deliberately never entered or displayed inside the dashboard.</p></section><?php endif;?>
    <section class="stats wa2-stats"><article class="stat"><span>Open conversations</span><strong><?=$waOpen?></strong><small>Awaiting or receiving attention</small></article><article class="stat <?=$waUnread?'crm-warning':''?>"><span>Unread messages</span><strong><?=$waUnread?></strong><small>Across the shared inbox</small></article><article class="stat"><span>Messages today</span><strong><?=$waToday?></strong><small>Incoming customer messages</small></article><article class="stat"><span>Connection</span><strong><?=$waConnected?'Live':'Pending'?></strong><small><?=e((string)($waConfig['business_phone']??'263771739696'))?></small></article></section>
    <section class="panel wa2-start"><form method="post" class="toolbar"><?=csrf_field()?><input type="hidden" name="action" value="start_whatsapp_conversation"><label>Open a customer conversation<select name="customer_id" required><option value="">Select customer</option><?php foreach($waCustomers as $customer):if(!($customer['whatsapp']||$customer['phone']))continue;?><option value="<?=$customer['id']?>"><?=e($customer['company_name']?:$customer['name'])?> - <?=e($customer['whatsapp']?:$customer['phone'])?></option><?php endforeach;?></select></label><button class="button secondary">Open conversation</button></form></section>
    <div class="wa2-shell"><aside class="panel wa2-list"><div class="wa2-tabs"><?php foreach(['open'=>'Open','pending'=>'Pending','closed'=>'Closed'] as $key=>$label):?><a class="<?=$waStatus===$key?'active':''?>" href="index.php?page=whatsapp&status=<?=$key?>"><?=$label?></a><?php endforeach;?></div><div class="wa2-conversations"><?php foreach($waConversations as $conversation):?><a class="<?=$waConversationId===$conversation['id']?'active':''?>" href="index.php?page=whatsapp&status=<?=e($waStatus)?>&conversation=<?=$conversation['id']?>"><span class="wa2-avatar"><?=e(strtoupper(substr((string)($conversation['customer_name']?:$conversation['display_name']?:$conversation['phone']),0,1)))?></span><span class="wa2-list-copy"><strong><?=e($conversation['company_name']?:$conversation['customer_name']?:$conversation['display_name']?:('+'.$conversation['phone']))?></strong><small><?=e($conversation['last_body']?:'Conversation opened')?></small><em><?=e($conversation['assigned_name']?:'Unassigned')?><?php if($conversation['last_message_at']):?> &middot; <?=e(date('j M, H:i',strtotime($conversation['last_message_at'])))?><?php endif;?></em></span><?php if($conversation['unread_count']):?><b class="wa2-unread"><?=$conversation['unread_count']?></b><?php endif;?></a><?php endforeach;?><?php if(!$waConversations):?><div class="empty"><strong>No <?=$waStatus?> conversations</strong>Incoming Meta messages will appear here automatically.</div><?php endif;?></div></aside>
    <section class="panel wa2-chat"><?php if($waSelected):?><header class="wa2-chat-head"><div><span class="wa2-avatar"><?=e(strtoupper(substr((string)($waSelected['customer_name']?:$waSelected['display_name']?:$waSelected['phone']),0,1)))?></span><span><h2><?=e($waSelected['company_name']?:$waSelected['customer_name']?:$waSelected['display_name']?:('+'.$waSelected['phone']))?></h2><p>+<?=e($waSelected['phone'])?><?=$waSelected['customer_id']?' &middot; CRM customer':' &middot; Unmatched number'?></p></span></div><form method="post" class="wa2-assign"><?=csrf_field()?><input type="hidden" name="action" value="update_whatsapp_conversation"><input type="hidden" name="conversation_id" value="<?=$waSelected['id']?>"><select name="assigned_to"><option value="">Unassigned</option><?php foreach($waUsers as $team):?><option value="<?=$team['id']?>" <?=$waSelected['assigned_to']==$team['id']?'selected':''?>><?=e($team['name'])?></option><?php endforeach;?></select><select name="status"><?php foreach(['open'=>'Open','pending'=>'Pending','closed'=>'Closed'] as $key=>$label):?><option value="<?=$key?>" <?=$waSelected['status']===$key?'selected':''?>><?=$label?></option><?php endforeach;?></select><button class="button secondary small">Save</button></form></header>
    <?php if(!$waSelected['customer_id']):?><div class="wa2-match"><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="link_whatsapp_customer"><input type="hidden" name="conversation_id" value="<?=$waSelected['id']?>"><select name="customer_id" required><option value="">Link an existing customer</option><?php foreach($waCustomers as $customer):?><option value="<?=$customer['id']?>"><?=e($customer['company_name']?:$customer['name'])?></option><?php endforeach;?></select><button class="button secondary small">Link</button></form><span>or</span><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="create_customer_from_whatsapp"><input type="hidden" name="conversation_id" value="<?=$waSelected['id']?>"><input name="name" value="<?=e($waSelected['display_name']?:'')?>" placeholder="New customer name" required><button class="button primary small">Create customer</button></form></div><?php endif;?>
    <div class="wa2-messages"><?php foreach($waMessages as $message):?><article class="wa2-message <?=$message['direction']?>"><div><p><?=nl2br(e($message['body']?:('Template: '.$message['template_name'])))?></p><small><?=e(date('j M Y, H:i',strtotime($message['message_at'])))?><?php if($message['direction']==='outbound'):?> &middot; <?=e($message['status'])?><?=$message['sender_name']?' &middot; '.e($message['sender_name']):''?><?php endif;?></small><?php if($message['error_message']):?><em><?=e($message['error_message'])?></em><?php endif;?></div></article><?php endforeach;?><?php if(!$waMessages):?><div class="empty">No messages in this conversation yet.</div><?php endif;?></div>
    <?php $waWindowOpen=!empty($waSelected['last_customer_message_at'])&&strtotime($waSelected['last_customer_message_at'])>=time()-86400;?><footer class="wa2-composer"><div class="wa2-window <?=$waWindowOpen?'open':'closed'?>"><b><?=$waWindowOpen?'Service window open':'Service window closed'?></b><span><?=$waWindowOpen?'Free-form replies are available until '.date('j M, H:i',strtotime($waSelected['last_customer_message_at'].' +24 hours')):'Use an approved Meta template to contact this customer.'?></span></div><?php if($waWindowOpen):?><form method="post" class="wa2-reply"><?=csrf_field()?><input type="hidden" name="action" value="send_whatsapp_api"><input type="hidden" name="send_mode" value="text"><input type="hidden" name="conversation_id" value="<?=$waSelected['id']?>"><textarea name="message" rows="3" maxlength="4096" placeholder="Write a Soda ZW reply..." required></textarea><button class="button green" <?=$waConnected?'':'disabled'?>>Send with WhatsApp</button></form><?php endif;?><details class="wa2-template" <?=$waWindowOpen?'':'open'?><summary>Send an approved message template</summary><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="send_whatsapp_api"><input type="hidden" name="send_mode" value="template"><input type="hidden" name="conversation_id" value="<?=$waSelected['id']?>"><label>Approved template name<input name="template_name" placeholder="sodazw_refill_update" pattern="[a-z0-9_]+" required></label><label>Language code<input name="template_language" value="en_US" required></label><label class="full">Body values, one per line<textarea name="template_parameters" rows="3" placeholder="Customer first name&#10;Refill reference&#10;Ready for collection"></textarea><small>Use the exact number and order of variables approved by Meta.</small></label><button class="button green" <?=$waConnected?'':'disabled'?>>Send template</button></form></details></footer>
    <?php else:?><div class="wa2-empty"><b><?=app_nav_icon('whatsapp')?></b><h2>Select a conversation</h2><p>Incoming messages and delivery status will appear here after Meta is connected.</p></div><?php endif;?></section></div>
    <?php render_footer();break;

case 'whatsapp-level1':
    $waCustomers=db()->query('SELECT id,name,company_name,phone,whatsapp,next_refill_due,last_contact_at FROM customers WHERE active=1 AND COALESCE(NULLIF(whatsapp,""),NULLIF(phone,"")) IS NOT NULL ORDER BY COALESCE(NULLIF(company_name,""),name)')->fetchAll();
    $waOverdue=db()->query('SELECT d.id,d.number,d.customer_id,d.due_date,d.currency_code,d.total,c.name,c.company_name,c.phone,c.whatsapp,GREATEST(d.total-COALESCE(SUM(p.amount),0),0) balance FROM documents d JOIN customers c ON c.id=d.customer_id LEFT JOIN payments p ON p.invoice_id=d.id WHERE d.type="invoice" AND d.status="overdue" AND COALESCE(NULLIF(c.whatsapp,""),NULLIF(c.phone,"")) IS NOT NULL GROUP BY d.id HAVING balance>0 ORDER BY d.due_date LIMIT 12')->fetchAll();
    $waRefills=db()->query('SELECT r.*,c.name,c.company_name,c.phone,c.whatsapp FROM refill_jobs r JOIN customers c ON c.id=r.customer_id WHERE r.status NOT IN ("completed","cancelled") AND COALESCE(NULLIF(c.whatsapp,""),NULLIF(c.phone,"")) IS NOT NULL ORDER BY r.due_at IS NULL,r.due_at LIMIT 12')->fetchAll();
    $waDeliveries=db()->query('SELECT d.*,c.name,c.company_name,c.phone,c.whatsapp FROM deliveries d JOIN customers c ON c.id=d.customer_id WHERE d.status NOT IN ("delivered","cancelled") AND COALESCE(NULLIF(c.whatsapp,""),NULLIF(c.phone,"")) IS NOT NULL ORDER BY d.scheduled_for IS NULL,d.scheduled_for LIMIT 12')->fetchAll();
    $waRecent=db()->query('SELECT a.*,c.name,c.company_name,u.name user_name FROM crm_activities a JOIN customers c ON c.id=a.customer_id JOIN users u ON u.id=a.created_by WHERE a.activity_type="whatsapp" AND a.completed_at IS NOT NULL ORDER BY a.completed_at DESC,a.id DESC LIMIT 15')->fetchAll();
    $waReminderCustomers=db()->query('SELECT id,name,company_name,phone,whatsapp,next_refill_due FROM customers WHERE active=1 AND next_refill_due IS NOT NULL AND next_refill_due<=CURDATE() AND COALESCE(NULLIF(whatsapp,""),NULLIF(phone,"")) IS NOT NULL ORDER BY next_refill_due LIMIT 12')->fetchAll();$waReminders=count($waReminderCustomers);render_header('WhatsApp centre','whatsapp');?>
    <div class="page-actions wa-page-heading"><div><span class="wa-level-pill"><i></i>LEVEL 1 - CLICK TO CHAT</span><h2 class="register-title">WhatsApp operations centre</h2><p class="muted">Open prepared customer messages from one place while keeping every initiated contact in the Soda ZW CRM history.</p></div><a class="button secondary" href="https://web.whatsapp.com" target="_blank" rel="noopener noreferrer">Open WhatsApp Web</a></div>
    <section class="wa-summary"><article><span>WhatsApp customers</span><strong><?=count($waCustomers)?></strong><small>Customers with a saved number</small></article><article class="<?=$waOverdue?'warning':''?>"><span>Payment reminders</span><strong><?=count($waOverdue)?></strong><small>Overdue invoices ready to send</small></article><article><span>Active refill updates</span><strong><?=count($waRefills)?></strong><small>Customers awaiting progress</small></article><article><span>Refill reminders</span><strong><?=$waReminders?></strong><small>Expected refill date reached</small></article></section>
    <section class="wa-compose-grid"><article class="panel wa-compose"><div class="panel-head"><div><span class="section-kicker">NEW MESSAGE</span><h2>Start a customer conversation</h2><p>Write a message and continue the conversation in WhatsApp.</p></div><b><?=app_nav_icon('whatsapp')?></b></div><form method="post" target="_blank" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="open_whatsapp"><input type="hidden" name="message_type" value="custom"><label class="full">Customer<select name="customer_id" required><option value="">Select customer</option><?php foreach($waCustomers as $c):?><option value="<?=$c['id']?>"><?=e($c['company_name']?:$c['name'])?> - <?=e($c['whatsapp']?:$c['phone'])?></option><?php endforeach;?></select></label><label class="full">Message<textarea name="message" rows="5" maxlength="1800" required>Hello, this is Soda ZW. How may we assist you with your machine, cylinder refill or delivery today?</textarea></label><button class="button green" type="submit">Open message in WhatsApp</button></form></article><aside class="panel wa-templates"><div class="panel-head"><div><span class="section-kicker">SMART TEMPLATES</span><h2>Messages prepared for you</h2><p>Operational records automatically supply the correct reference, amount and status.</p></div></div><div class="wa-template-list"><div><b>Invoice and balance</b><span>Secure invoice link, total and outstanding balance</span></div><div><b>Payment reminder</b><span>Friendly overdue follow-up with the exact amount</span></div><div><b>Refill progress</b><span>Job reference, overnight due time and total</span></div><div><b>Delivery update</b><span>Schedule, address, delivery fee and current status</span></div></div></aside></section>
    <section class="wa-queue-grid"><article class="panel"><div class="panel-head"><div><h2>Payment reminders</h2><p>Oldest overdue invoices first</p></div><span class="wa-count"><?=count($waOverdue)?></span></div><div class="wa-action-list"><?php foreach($waOverdue as $row):?><div><span><strong><?=e($row['company_name']?:$row['name'])?></strong><small><?=e($row['number'])?> - due <?=e(date('j M Y',strtotime($row['due_date'])))?></small></span><b><?=money($row['balance'],$row['currency_code'])?></b><form method="post" target="_blank"><?=csrf_field()?><input type="hidden" name="action" value="open_whatsapp"><input type="hidden" name="message_type" value="overdue"><input type="hidden" name="customer_id" value="<?=$row['customer_id']?>"><input type="hidden" name="document_id" value="<?=$row['id']?>"><button class="button green small">WhatsApp</button></form></div><?php endforeach;?><?php if(!$waOverdue):?><div class="empty"><strong>All clear</strong>No overdue balances require a WhatsApp reminder.</div><?php endif;?></div></article>
    <article class="panel"><div class="panel-head"><div><h2>Refill updates</h2><p>Active overnight refill jobs</p></div><span class="wa-count"><?=count($waRefills)?></span></div><div class="wa-action-list"><?php foreach($waRefills as $row):?><div><span><strong><?=e($row['company_name']?:$row['name'])?></strong><small><?=e($row['reference'])?> - <?=e(str_replace('_',' ',$row['status']))?></small></span><b><?=e($row['due_at']?date('j M, H:i',strtotime($row['due_at'])):'No due time')?></b><form method="post" target="_blank"><?=csrf_field()?><input type="hidden" name="action" value="open_whatsapp"><input type="hidden" name="message_type" value="refill"><input type="hidden" name="customer_id" value="<?=$row['customer_id']?>"><input type="hidden" name="refill_id" value="<?=$row['id']?>"><button class="button green small">Update</button></form></div><?php endforeach;?><?php if(!$waRefills):?><div class="empty"><strong>No active refills</strong>New refill jobs will appear here automatically.</div><?php endif;?></div></article>
    <article class="panel"><div class="panel-head"><div><h2>Delivery updates</h2><p>Open deliveries awaiting completion</p></div><span class="wa-count"><?=count($waDeliveries)?></span></div><div class="wa-action-list"><?php foreach($waDeliveries as $row):?><div><span><strong><?=e($row['company_name']?:$row['name'])?></strong><small><?=e($row['reference'])?> - <?=e(str_replace('_',' ',$row['status']))?></small></span><b><?=e($row['scheduled_for']?date('j M, H:i',strtotime($row['scheduled_for'])):'Not scheduled')?></b><form method="post" target="_blank"><?=csrf_field()?><input type="hidden" name="action" value="open_whatsapp"><input type="hidden" name="message_type" value="delivery"><input type="hidden" name="customer_id" value="<?=$row['customer_id']?>"><input type="hidden" name="delivery_id" value="<?=$row['id']?>"><button class="button green small">Update</button></form></div><?php endforeach;?><?php if(!$waDeliveries):?><div class="empty"><strong>No open deliveries</strong>Scheduled deliveries will appear here automatically.</div><?php endif;?></div></article>
    <article class="panel"><div class="panel-head"><div><h2>Refill reminders</h2><p>Customers whose expected refill date has arrived</p></div><span class="wa-count"><?=$waReminders?></span></div><div class="wa-action-list"><?php foreach($waReminderCustomers as $row):?><div><span><strong><?=e($row['company_name']?:$row['name'])?></strong><small>Expected <?=e(date('j M Y',strtotime($row['next_refill_due'])))?></small></span><b><?=e($row['whatsapp']?:$row['phone'])?></b><form method="post" target="_blank"><?=csrf_field()?><input type="hidden" name="action" value="open_whatsapp"><input type="hidden" name="message_type" value="refill_reminder"><input type="hidden" name="customer_id" value="<?=$row['id']?>"><button class="button green small">Remind</button></form></div><?php endforeach;?><?php if(!$waReminderCustomers):?><div class="empty"><strong>No reminders due</strong>Expected refill dates are currently clear.</div><?php endif;?></div></article></section>
    <section class="panel wa-history"><div class="panel-head"><div><h2>Recent WhatsApp activity</h2><p>Messages initiated through this dashboard</p></div><a class="button secondary small" href="index.php?page=crm">Open CRM history</a></div><div class="table-wrap"><table><thead><tr><th>Time</th><th>Customer</th><th>Message type</th><th>Initiated by</th><th>Preview</th></tr></thead><tbody><?php foreach($waRecent as $row):?><tr><td><?=e(date('j M Y, H:i',strtotime($row['completed_at'])))?></td><td><strong><?=e($row['company_name']?:$row['name'])?></strong></td><td><?=e($row['subject'])?></td><td><?=e($row['user_name'])?></td><td><span class="wa-preview"><?=e($row['notes'])?></span></td></tr><?php endforeach;?><?php if(!$waRecent):?><tr><td colspan="5" class="empty">Messages opened from the WhatsApp centre will be recorded here.</td></tr><?php endif;?></tbody></table></div></section>
    <?php render_footer();break;

case 'operations':
    $customersList=db()->query('SELECT id,name,company_name FROM customers WHERE active=1 ORDER BY COALESCE(NULLIF(company_name,""),name)')->fetchAll();
    $cylinderSummary=db()->query('SELECT status,COUNT(*) total FROM cylinder_assets GROUP BY status')->fetchAll(PDO::FETCH_KEY_PAIR);$activeRefills=(int)db()->query('SELECT COUNT(*) FROM refill_jobs WHERE status NOT IN ("completed","cancelled")')->fetchColumn();$overdueRefills=(int)db()->query('SELECT COUNT(*) FROM refill_jobs WHERE status NOT IN ("completed","cancelled") AND due_at<NOW()')->fetchColumn();$readyRefills=(int)db()->query('SELECT COUNT(*) FROM refill_jobs WHERE status="ready"')->fetchColumn();$openDeliveries=(int)db()->query('SELECT COUNT(*) FROM deliveries WHERE status NOT IN ("delivered","cancelled")')->fetchColumn();$todayDeliveries=(int)db()->query('SELECT COUNT(*) FROM deliveries WHERE DATE(scheduled_for)=CURDATE() AND status NOT IN ("delivered","cancelled")')->fetchColumn();$unpaidOperations=(float)db()->query('SELECT COALESCE((SELECT SUM(quantity*unit_price) FROM refill_jobs WHERE payment_status<>"paid" AND status<>"cancelled"),0)+COALESCE((SELECT SUM(GREATEST(delivery_fee-amount_collected,0)) FROM deliveries WHERE payment_status<>"paid" AND status<>"cancelled"),0)')->fetchColumn();
    $lowStock=db()->query('SELECT id,sku,name,stock_quantity,reorder_level FROM products WHERE active=1 AND track_stock=1 AND stock_quantity<=reorder_level ORDER BY stock_quantity,reorder_level,name LIMIT 8')->fetchAll();
    $refillQueue=db()->query('SELECT r.*,c.name customer,c.company_name,ca.cylinder_code FROM refill_jobs r JOIN customers c ON c.id=r.customer_id LEFT JOIN cylinder_assets ca ON ca.id=r.cylinder_id WHERE r.status NOT IN ("completed","cancelled") ORDER BY r.due_at IS NULL,r.due_at,r.created_at LIMIT 10')->fetchAll();
    $deliveryQueue=db()->query('SELECT d.*,c.name customer,c.company_name FROM deliveries d JOIN customers c ON c.id=d.customer_id WHERE d.status NOT IN ("delivered","cancelled") ORDER BY d.scheduled_for IS NULL,d.scheduled_for,d.created_at LIMIT 10')->fetchAll();
    $cylinderFilter=in_array($_GET['cylinder_status']??'', ['in_stock_full','in_stock_empty','received','refilling','ready','with_customer','damaged','retired'],true)?$_GET['cylinder_status']:'';$cylinderQuery=trim($_GET['cylinder_q']??'');$where=[];$params=[];if($cylinderFilter){$where[]='ca.status=?';$params[]=$cylinderFilter;}if($cylinderQuery!==''){$where[]='(ca.cylinder_code LIKE ? OR c.name LIKE ? OR c.company_name LIKE ?)';$like='%'.$cylinderQuery.'%';array_push($params,$like,$like,$like);}$stmt=db()->prepare('SELECT ca.*,c.name customer,c.company_name FROM cylinder_assets ca LEFT JOIN customers c ON c.id=ca.customer_id '.($where?'WHERE '.implode(' AND ',$where):'').' ORDER BY FIELD(ca.status,"received","refilling","ready","in_stock_empty","in_stock_full","with_customer","damaged","retired"),ca.updated_at DESC LIMIT 150');$stmt->execute($params);$cylinders=$stmt->fetchAll();
    $selectedCylinder=null;$cylinderEvents=[];if($cylinderId=(int)($_GET['cylinder']??0)){$s=db()->prepare('SELECT ca.*,c.name customer,c.company_name FROM cylinder_assets ca LEFT JOIN customers c ON c.id=ca.customer_id WHERE ca.id=?');$s->execute([$cylinderId]);$selectedCylinder=$s->fetch();if($selectedCylinder){$e=db()->prepare('SELECT ce.*,u.name user_name,r.reference refill_reference FROM cylinder_events ce LEFT JOIN users u ON u.id=ce.created_by LEFT JOIN refill_jobs r ON r.id=ce.refill_job_id WHERE ce.cylinder_id=? ORDER BY ce.created_at DESC,ce.id DESC LIMIT 50');$e->execute([$cylinderId]);$cylinderEvents=$e->fetchAll();}}
    render_header('Operations command centre','operations');?>
    <div class="operations-hero"><div><span>SODA ZW OPERATIONS</span><h2>Refills, cylinders and deliveries in one view</h2><p>Control the overnight queue, know where every registered cylinder is and close delivery exceptions before they affect customers.</p></div><div class="operations-actions"><a class="button secondary" href="index.php?page=deliveries&new=1">+ Delivery</a><a class="button secondary" href="index.php?page=operations&new-cylinder=1">+ Cylinder</a><a class="button primary" href="index.php?page=refills&new=1">+ Refill job</a></div></div>
    <section class="operations-kpis"><article class="<?=$overdueRefills?'needs-attention':''?>"><span>Active refills</span><strong><?=$activeRefills?></strong><small><?=$overdueRefills?> overdue overnight</small></article><article><span>Ready for customer</span><strong><?=$readyRefills?></strong><small>Awaiting pickup or dispatch</small></article><article><span>Open deliveries</span><strong><?=$openDeliveries?></strong><small><?=$todayDeliveries?> scheduled today</small></article><article><span>Operational balance</span><strong><?=money($unpaidOperations)?></strong><small>Unpaid refill and delivery charges</small></article><article class="<?=count($lowStock)?'needs-attention':''?>"><span>Low-stock products</span><strong><?=count($lowStock)?></strong><small>At or below reorder level</small></article></section>
    <?php if(isset($_GET['new-cylinder'])):?><section class="panel operations-form"><div class="panel-head"><div><h2>Register a cylinder</h2><p>Use a durable code written on the cylinder, such as SZW-CYL-001.</p></div><a class="button secondary small" href="index.php?page=operations">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_cylinder_asset"><label>Cylinder code<input name="cylinder_code" placeholder="SZW-CYL-001" maxlength="60" required></label><label>System<select name="cylinder_type"><option value="pink_quick_connect">Pink Quick Connect</option><option value="blue_screw">Blue screw-in</option><option value="compatible_other">Other compatible cylinder</option></select></label><label>Ownership<select name="ownership"><option value="soda_zw">Soda ZW owned</option><option value="customer">Customer owned</option></select></label><label>Customer, if customer-owned<select name="customer_id"><option value="">Not assigned</option><?php foreach($customersList as $c):?><option value="<?=$c['id']?>"><?=e($c['company_name']?:$c['name'])?></option><?php endforeach;?></select></label><label>Opening status<select name="status"><option value="in_stock_full">In stock — full</option><option value="in_stock_empty">In stock — empty</option><option value="with_customer">With customer</option><option value="received">Received for service</option><option value="damaged">Damaged / inspection</option></select></label><label>Location<input name="location" value="100 Livingstone Avenue"></label><label>Next inspection due<input type="date" name="next_inspection_due"></label><label class="full">Notes<textarea name="notes" rows="3"></textarea></label><button class="button primary">Register cylinder</button></form></section><?php endif;?>
    <?php if($selectedCylinder):?><section class="panel cylinder-detail"><div class="panel-head"><div><h2><?=e($selectedCylinder['cylinder_code'])?></h2><p><?=e(ucwords(str_replace('_',' ',$selectedCylinder['cylinder_type'])))?> · <?=status_badge($selectedCylinder['status'])?></p></div><a class="button secondary small" href="index.php?page=operations">Close</a></div><div class="crm-detail-grid"><article><span>Ownership</span><strong><?=e($selectedCylinder['ownership']==='soda_zw'?'Soda ZW':'Customer')?></strong><small><?=e($selectedCylinder['company_name']?:$selectedCylinder['customer']?:'Not assigned')?></small></article><article><span>Location</span><strong><?=e($selectedCylinder['location']?:'Not recorded')?></strong><small>Last movement <?=e(date('j M Y',strtotime($selectedCylinder['updated_at'])))?></small></article><article><span>Last refill</span><strong><?=e($selectedCylinder['last_refill_at']?date('j M Y',strtotime($selectedCylinder['last_refill_at'])):'No refill recorded')?></strong><small>Inspection <?=e($selectedCylinder['next_inspection_due']?:'not scheduled')?></small></article></div><div class="cylinder-detail-layout"><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="update_cylinder_asset"><input type="hidden" name="id" value="<?=$selectedCylinder['id']?>"><label>Status<select name="status"><?php foreach(['in_stock_full'=>'In stock — full','in_stock_empty'=>'In stock — empty','received'=>'Received','refilling'=>'Refilling','ready'=>'Ready','with_customer'=>'With customer','damaged'=>'Damaged / inspection','retired'=>'Retired'] as $key=>$label):?><option value="<?=e($key)?>" <?=$selectedCylinder['status']===$key?'selected':''?>><?=e($label)?></option><?php endforeach;?></select></label><label>Assigned customer<select name="customer_id"><option value="">Not assigned</option><?php foreach($customersList as $c):?><option value="<?=$c['id']?>" <?=$selectedCylinder['customer_id']==$c['id']?'selected':''?>><?=e($c['company_name']?:$c['name'])?></option><?php endforeach;?></select></label><label>Location<input name="location" value="<?=e($selectedCylinder['location'])?>"></label><label>Next inspection<input type="date" name="next_inspection_due" value="<?=e($selectedCylinder['next_inspection_due'])?>"></label><label class="full">Record note<input name="event_notes" placeholder="Reason for this movement or status change"></label><label class="full">Cylinder notes<textarea name="notes" rows="3"><?=e($selectedCylinder['notes'])?></textarea></label><button class="button primary">Save movement</button></form><div class="operations-timeline"><h3>Movement history</h3><?php foreach($cylinderEvents as $event):?><article><i></i><div><strong><?=e(ucwords(str_replace('_',' ',$event['to_status']?:$event['event_type'])))?></strong><small><?=e(date('j M Y, H:i',strtotime($event['created_at'])))?><?=!empty($event['user_name'])?' · '.e($event['user_name']):''?></small><?php if($event['notes']):?><p><?=e($event['notes'])?></p><?php endif;?></div></article><?php endforeach;?><?php if(!$cylinderEvents):?><div class="empty">No movements recorded.</div><?php endif;?></div></div></section><?php endif;?>
    <?php if($selectedCylinder&&!in_array($selectedCylinder['status'],['retired','damaged'],true)):?><section class="panel start-refill-panel"><div class="panel-head"><div><h2>Start an overnight refill</h2><p>Creates the $35 refill job directly from <?=e($selectedCylinder['cylinder_code'])?> and schedules delivery when requested.</p></div></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="start_cylinder_refill"><input type="hidden" name="cylinder_id" value="<?=$selectedCylinder['id']?>"><label>Customer<select name="customer_id" required><option value="">Select customer</option><?php foreach($customersList as $c):?><option value="<?=$c['id']?>" <?=$selectedCylinder['customer_id']===$c['id']?'selected':''?>><?=e($c['company_name']?:$c['name'])?></option><?php endforeach;?></select></label><label>Fulfilment<select name="fulfilment_method"><option>Pickup</option><option>Delivery</option></select></label><label class="full">Delivery address<input name="delivery_address" placeholder="Required for delivery"></label><label>Delivery fee<input type="number" min="0" step="0.01" name="delivery_fee" value="0"></label><label class="full">Service note<input name="notes" placeholder="Condition received or customer instruction"></label><button class="button primary">Start $35 overnight refill</button></form></section><?php endif;?>
    <section class="operations-grid"><div class="panel"><div class="panel-head"><div><h2>Overnight refill queue</h2><p>Oldest due work appears first.</p></div><a class="button secondary small" href="index.php?page=refills">All refills</a></div><div class="operations-list"><?php foreach($refillQueue as $r):?><a href="index.php?page=refills&id=<?=$r['id']?>"><span class="operations-list-icon"><?=e(strtoupper(substr($r['cylinder_type'],0,1)))?></span><span><strong><?=e($r['reference'])?> · <?=e($r['company_name']?:$r['customer'])?></strong><small><?=e($r['cylinder_code']?:ucwords(str_replace('_',' ',$r['cylinder_type'])))?> · due <?=e($r['due_at']?date('j M, H:i',strtotime($r['due_at'])):'not set')?></small></span><?=status_badge($r['status'])?></a><?php endforeach;?><?php if(!$refillQueue):?><div class="empty">The refill queue is clear.</div><?php endif;?></div></div><div class="panel"><div class="panel-head"><div><h2>Dispatch board</h2><p>Scheduled and open deliveries.</p></div><a class="button secondary small" href="index.php?page=deliveries">All deliveries</a></div><div class="operations-list"><?php foreach($deliveryQueue as $d):?><a href="index.php?page=deliveries&id=<?=$d['id']?>"><span class="operations-list-icon delivery">D</span><span><strong><?=e($d['reference'])?> · <?=e($d['company_name']?:$d['customer'])?></strong><small><?=e($d['area']?:$d['delivery_address'])?> · <?=e($d['scheduled_for']?date('j M, H:i',strtotime($d['scheduled_for'])):'unscheduled')?></small></span><?=status_badge($d['status'])?></a><?php endforeach;?><?php if(!$deliveryQueue):?><div class="empty">No open deliveries.</div><?php endif;?></div></div><aside class="panel stock-exceptions"><div class="panel-head"><div><h2>Stock exceptions</h2><p>Reorder before sales are lost.</p></div></div><?php foreach($lowStock as $p):?><a href="index.php?page=products&edit=<?=$p['id']?>"><span><strong><?=e($p['name'])?></strong><small><?=e($p['sku'])?></small></span><b><?=e($p['stock_quantity'])?> / <?=e($p['reorder_level'])?></b></a><?php endforeach;?><?php if(!$lowStock):?><div class="empty">All tracked products are above reorder level.</div><?php endif;?></aside></section>
    <section class="panel cylinder-register"><div class="panel-head"><div><h2>Cylinder register</h2><p>Current ownership, position and service condition.</p></div><span><b><?=array_sum(array_map('intval',$cylinderSummary))?></b> registered</span></div><form class="operations-filter"><input type="hidden" name="page" value="operations"><input name="cylinder_q" value="<?=e($cylinderQuery)?>" placeholder="Search cylinder or customer"><select name="cylinder_status"><option value="">All statuses</option><?php foreach(['in_stock_full'=>'In stock — full','in_stock_empty'=>'In stock — empty','received'=>'Received','refilling'=>'Refilling','ready'=>'Ready','with_customer'=>'With customer','damaged'=>'Damaged','retired'=>'Retired'] as $key=>$label):?><option value="<?=e($key)?>" <?=$cylinderFilter===$key?'selected':''?>><?=e($label)?></option><?php endforeach;?></select><button class="button secondary">Filter</button></form><div class="table-wrap"><table><thead><tr><th>Code</th><th>System</th><th>Ownership/customer</th><th>Location</th><th>Status</th><th>Inspection</th><th></th></tr></thead><tbody><?php foreach($cylinders as $ca):?><tr><td><strong><?=e($ca['cylinder_code'])?></strong></td><td><?=e(ucwords(str_replace('_',' ',$ca['cylinder_type'])))?></td><td><?=e($ca['ownership']==='soda_zw'?'Soda ZW':($ca['company_name']?:$ca['customer']?:'Customer'))?></td><td><?=e($ca['location']?:'—')?></td><td><?=status_badge($ca['status'])?></td><td><?=e($ca['next_inspection_due']?:'—')?></td><td><a class="button secondary small" href="index.php?page=operations&cylinder=<?=$ca['id']?>">Open</a></td></tr><?php endforeach;?><?php if(!$cylinders):?><tr><td colspan="7" class="empty"><strong>No cylinders registered</strong>Add the first Soda ZW or customer-owned cylinder.</td></tr><?php endif;?></tbody></table></div></section>
    <?php render_footer();break;

case 'refills':
    $statuses=['received'=>'Received','awaiting_refill'=>'Awaiting refill','ready'=>'Ready','out_for_delivery'=>'Out for delivery','completed'=>'Completed','cancelled'=>'Cancelled'];$statusFilter=(string)($_GET['status']??'');$where=in_array($statusFilter,array_keys($statuses),true)?'WHERE r.status=?':'';$stmt=db()->prepare('SELECT r.*,c.name customer,c.company_name,c.phone,c.whatsapp,c.area,ca.cylinder_code FROM refill_jobs r JOIN customers c ON c.id=r.customer_id LEFT JOIN cylinder_assets ca ON ca.id=r.cylinder_id '.$where.' ORDER BY FIELD(r.status,"received","awaiting_refill","ready","out_for_delivery","completed","cancelled"),r.due_at,r.id DESC');$stmt->execute($where?[$statusFilter]:[]);$refills=$stmt->fetchAll();$customersList=db()->query('SELECT id,name,company_name,address,area FROM customers WHERE active=1 ORDER BY COALESCE(NULLIF(company_name,""),name)')->fetchAll();$opportunitiesList=db()->query('SELECT o.id,o.title,o.customer_id,c.name customer FROM crm_opportunities o JOIN customers c ON c.id=o.customer_id WHERE o.stage NOT IN ("completed","lost") ORDER BY o.updated_at DESC')->fetchAll();$cylinderAssets=db()->query('SELECT ca.id,ca.cylinder_code,ca.cylinder_type,ca.status,c.name customer,c.company_name FROM cylinder_assets ca LEFT JOIN customers c ON c.id=ca.customer_id WHERE ca.status<>"retired" ORDER BY ca.cylinder_code')->fetchAll();$refillCounts=db()->query('SELECT status,COUNT(*) total FROM refill_jobs GROUP BY status')->fetchAll(PDO::FETCH_KEY_PAIR);$overnightDue=(int)db()->query('SELECT COUNT(*) FROM refill_jobs WHERE status NOT IN ("completed","cancelled") AND due_at<=DATE_ADD(NOW(),INTERVAL 1 DAY)')->fetchColumn();$readyCount=(int)($refillCounts['ready']??0);$unpaidValue=(float)db()->query('SELECT COALESCE(SUM(quantity*unit_price+delivery_fee),0) FROM refill_jobs WHERE payment_status<>"paid" AND status<>"cancelled"')->fetchColumn();$selectedRefill=null;$refillEvents=[];if($id=(int)($_GET['id']??0)){$s=db()->prepare('SELECT r.*,c.name customer,c.company_name,c.phone,c.whatsapp,c.area,ca.cylinder_code FROM refill_jobs r JOIN customers c ON c.id=r.customer_id LEFT JOIN cylinder_assets ca ON ca.id=r.cylinder_id WHERE r.id=?');$s->execute([$id]);$selectedRefill=$s->fetch();if($selectedRefill){$ev=db()->prepare('SELECT re.*,u.name user_name FROM refill_job_events re LEFT JOIN users u ON u.id=re.created_by WHERE re.refill_job_id=? ORDER BY re.created_at DESC,re.id DESC');$ev->execute([$id]);$refillEvents=$ev->fetchAll();}}render_header('Refill jobs','refills');?>
    <div class="page-actions"><div><h2 class="register-title">CO₂ refill operations</h2><p class="muted">Overnight refills are $35 per compatible cylinder. Delivery is charged by location.</p></div><a class="button primary" href="index.php?page=refills&new=1">+ New refill job</a></div><section class="stats"><article class="stat"><span>Active jobs</span><strong><?=array_sum(array_map(fn($k)=>(int)($refillCounts[$k]??0),['received','awaiting_refill','ready','out_for_delivery']))?></strong><small>Not completed</small></article><article class="stat <?=$overnightDue?'crm-warning':''?>"><span>Due within 24 hours</span><strong><?=$overnightDue?></strong><small>Protect overnight promise</small></article><article class="stat"><span>Ready</span><strong><?=$readyCount?></strong><small>Notify customers</small></article><article class="stat"><span>Unpaid job value</span><strong><?=money($unpaidValue)?></strong><small>Includes delivery fees</small></article></section>
    <?php if(isset($_GET['new'])):$prefCustomer=(int)($_GET['customer_id']??0);$prefOpportunity=(int)($_GET['opportunity_id']??0);?><section class="panel"><div class="panel-head"><div><h2>Create refill or cylinder job</h2><p>The due time defaults to overnight. Confirm compatibility before accepting other cylinders.</p></div><a class="button secondary small" href="index.php?page=refills">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_refill_job"><label>Customer<select name="customer_id" required><option value="">Select customer</option><?php foreach($customersList as $c):?><option value="<?=$c['id']?>" <?=$prefCustomer===$c['id']?'selected':''?>><?=e($c['company_name']?:$c['name'])?></option><?php endforeach;?></select></label><label>Linked opportunity<select name="opportunity_id"><option value="">None</option><?php foreach($opportunitiesList as $o):?><option value="<?=$o['id']?>" <?=$prefOpportunity===$o['id']?'selected':''?>><?=e($o['customer'].' — '.$o['title'])?></option><?php endforeach;?></select></label><label>Cylinder type<select name="cylinder_type"><option value="pink_quick_connect">Pink Quick Connect</option><option value="blue_screw">Blue screw-in</option><option value="compatible_other">Other compatible cylinder</option></select></label><label>Service<select name="service_type"><option value="refill">Refill — $35</option><option value="new_cylinder">New cylinder</option><option value="exchange">Exchange</option></select></label><label>Quantity<input type="number" min="1" name="quantity" value="1"></label><label>Unit price<input type="number" min="0" step="0.01" name="unit_price" value="35.00"></label><label>Received date and time<input type="datetime-local" name="received_at" value="<?=date('Y-m-d\TH:i')?>"></label><label>Overnight due time<input type="datetime-local" name="due_at" value="<?=date('Y-m-d\TH:i',strtotime('+1 day'))?>"></label><label>Fulfilment<select name="fulfilment_method"><option>Pickup</option><option>Delivery</option></select></label><label>Delivery fee<input type="number" min="0" step="0.01" name="delivery_fee" value="0"></label><label class="full">Delivery address<input name="delivery_address" placeholder="Required when delivery is selected"></label><label>Payment status<select name="payment_status"><option value="unpaid">Unpaid</option><option value="paid">Paid</option></select></label><label class="full">Notes<textarea name="notes" rows="3" placeholder="Compatibility check, condition and customer instructions"></textarea></label><button class="button primary">Create refill job</button></form></section><?php endif;?>
    <?php if($selectedRefill):$wa=preg_replace('/\D+/','',(string)($selectedRefill['whatsapp']?:$selectedRefill['phone']));if(str_starts_with($wa,'0'))$wa='263'.substr($wa,1);?><section class="panel"><div class="panel-head"><div><h2><?=e($selectedRefill['reference'])?></h2><p><?=e($selectedRefill['company_name']?:$selectedRefill['customer'])?> · <?=status_badge($selectedRefill['status'])?></p></div><a class="button secondary small" href="index.php?page=refills">Close</a></div><div class="crm-detail-grid"><article><span>Cylinder</span><strong><?=e(ucwords(str_replace('_',' ',$selectedRefill['cylinder_type'])))?></strong><small><?=e((string)$selectedRefill['quantity'])?> × <?=money($selectedRefill['unit_price'])?></small></article><article><span>Overnight due</span><strong><?=e($selectedRefill['due_at']?date('j M Y, H:i',strtotime($selectedRefill['due_at'])):'Not set')?></strong><small><?=e($selectedRefill['fulfilment_method'])?></small></article><article><span>Total</span><strong><?=money((float)$selectedRefill['quantity']*(float)$selectedRefill['unit_price']+(float)$selectedRefill['delivery_fee'])?></strong><small>Delivery <?=money($selectedRefill['delivery_fee'])?></small></article><article><span>Payment</span><strong><?=e(ucfirst($selectedRefill['payment_status']))?></strong><small><?=e($selectedRefill['area']?:'Location not set')?></small></article></div><div class="crm-action-row"><?php if($wa):?><a class="button green" target="_blank" rel="noopener" href="https://wa.me/<?=e($wa)?>?text=<?=rawurlencode('Hello '.$selectedRefill['customer'].', your Soda ZW refill '.$selectedRefill['reference'].' is currently '.str_replace('_',' ',$selectedRefill['status']).'. We will update you when it is ready.')?>">WhatsApp update</a><?php endif;?><a class="button secondary" href="index.php?page=document-edit&type=invoice&customer_id=<?=$selectedRefill['customer_id']?>">Create invoice</a></div><form method="post" class="crm-stage-form"><?=csrf_field()?><input type="hidden" name="action" value="update_refill_job"><input type="hidden" name="id" value="<?=$selectedRefill['id']?>"><label>Status<select name="status"><?php foreach($statuses as $key=>$label):?><option value="<?=e($key)?>" <?=$selectedRefill['status']===$key?'selected':''?>><?=e($label)?></option><?php endforeach;?></select></label><label>Payment<select name="payment_status"><?php foreach(['unpaid'=>'Unpaid','partial'=>'Part paid','paid'=>'Paid'] as $key=>$label):?><option value="<?=e($key)?>" <?=$selectedRefill['payment_status']===$key?'selected':''?>><?=e($label)?></option><?php endforeach;?></select></label><button class="button primary">Update job</button></form></section><?php endif;?>
    <div class="filter-pills"><a class="<?=!$statusFilter?'active':''?>" href="index.php?page=refills">All</a><?php foreach($statuses as $key=>$label):?><a class="<?=$statusFilter===$key?'active':''?>" href="index.php?page=refills&status=<?=e($key)?>"><?=e($label)?> <b><?=e((string)($refillCounts[$key]??0))?></b></a><?php endforeach;?></div><section class="panel"><div class="table-wrap"><table><thead><tr><th>Reference</th><th>Customer</th><th>Cylinder</th><th>Due</th><th>Fulfilment</th><th>Status</th><th>Total</th><th></th></tr></thead><tbody><?php foreach($refills as $r):?><tr><td><strong><?=e($r['reference'])?></strong><br><small class="muted"><?=e(date('j M, H:i',strtotime($r['received_at'])))?></small></td><td><?=e($r['company_name']?:$r['customer'])?><br><small class="muted"><?=e($r['phone'])?></small></td><td><?=e(ucwords(str_replace('_',' ',$r['cylinder_type'])))?><br><small class="muted"><?=e((string)$r['quantity'])?> cylinder(s)</small></td><td class="<?=$r['due_at']&&$r['due_at']<date('Y-m-d H:i:s')&&!in_array($r['status'],['completed','cancelled'],true)?'due-late':''?>"><?=e($r['due_at']?date('j M, H:i',strtotime($r['due_at'])):'—')?></td><td><?=e($r['fulfilment_method'])?><?php if($r['delivery_fee']>0):?><br><small class="muted"><?=money($r['delivery_fee'])?> fee</small><?php endif;?></td><td><?=status_badge($r['status'])?></td><td><strong><?=money((float)$r['quantity']*(float)$r['unit_price']+(float)$r['delivery_fee'])?></strong><br><small class="muted"><?=e(ucfirst($r['payment_status']))?></small></td><td><a class="button secondary small" href="index.php?page=refills&id=<?=$r['id']?>">Open</a></td></tr><?php endforeach;?><?php if(!$refills):?><tr><td colspan="8" class="empty"><strong>No refill jobs</strong>Create the first overnight refill job.</td></tr><?php endif;?></tbody></table></div></section><?php render_footer();break;

case 'deliveries':
    $statuses=['pending'=>'Pending','scheduled'=>'Scheduled','out_for_delivery'=>'Out for delivery','delivered'=>'Delivered','failed'=>'Failed','cancelled'=>'Cancelled'];$statusFilter=(string)($_GET['status']??'');$where=in_array($statusFilter,array_keys($statuses),true)?'WHERE v.status=?':'';$stmt=db()->prepare('SELECT v.*,c.name customer,c.company_name,c.phone,c.whatsapp,d.number document_number,r.reference refill_reference FROM deliveries v JOIN customers c ON c.id=v.customer_id LEFT JOIN documents d ON d.id=v.document_id LEFT JOIN refill_jobs r ON r.id=v.refill_job_id '.$where.' ORDER BY FIELD(v.status,"out_for_delivery","scheduled","pending","failed","delivered","cancelled"),COALESCE(v.scheduled_for,"9999-12-31"),v.id DESC');$stmt->execute($where?[$statusFilter]:[]);$deliveries=$stmt->fetchAll();$customersList=db()->query('SELECT id,name,company_name,address,area FROM customers WHERE active=1 ORDER BY COALESCE(NULLIF(company_name,""),name)')->fetchAll();$opportunitiesList=db()->query('SELECT o.id,o.title,o.customer_id,c.name customer FROM crm_opportunities o JOIN customers c ON c.id=o.customer_id WHERE o.stage NOT IN ("completed","lost") ORDER BY o.updated_at DESC')->fetchAll();$documentsList=db()->query('SELECT d.id,d.number,d.customer_id,c.name customer FROM documents d JOIN customers c ON c.id=d.customer_id WHERE d.type="invoice" AND d.status<>"cancelled" ORDER BY d.id DESC LIMIT 200')->fetchAll();$counts=db()->query('SELECT status,COUNT(*) total FROM deliveries GROUP BY status')->fetchAll(PDO::FETCH_KEY_PAIR);$todayCount=(int)db()->query('SELECT COUNT(*) FROM deliveries WHERE DATE(scheduled_for)=CURDATE() AND status NOT IN ("delivered","cancelled")')->fetchColumn();$openFees=(float)db()->query('SELECT COALESCE(SUM(delivery_fee),0) FROM deliveries WHERE status NOT IN ("delivered","cancelled")')->fetchColumn();$selectedDelivery=null;if($id=(int)($_GET['id']??0)){$s=db()->prepare('SELECT v.*,c.name customer,c.company_name,c.phone,c.whatsapp,d.number document_number,r.reference refill_reference FROM deliveries v JOIN customers c ON c.id=v.customer_id LEFT JOIN documents d ON d.id=v.document_id LEFT JOIN refill_jobs r ON r.id=v.refill_job_id WHERE v.id=?');$s->execute([$id]);$selectedDelivery=$s->fetch();}render_header('Deliveries','deliveries');?>
    <div class="page-actions"><div><h2 class="register-title">Delivery operations</h2><p class="muted">Every delivery carries an agreed location-based fee and a trackable status.</p></div><a class="button primary" href="index.php?page=deliveries&new=1">+ Schedule delivery</a></div><section class="stats"><article class="stat"><span>Today</span><strong><?=$todayCount?></strong><small>Scheduled and outstanding</small></article><article class="stat"><span>Out for delivery</span><strong><?=e((string)($counts['out_for_delivery']??0))?></strong><small>Currently on the road</small></article><article class="stat"><span>Open delivery fees</span><strong><?=money($openFees)?></strong><small>Expected delivery income</small></article><article class="stat"><span>Delivered</span><strong><?=e((string)($counts['delivered']??0))?></strong><small>Completed records</small></article></section>
    <?php if(isset($_GET['new'])):$prefCustomer=(int)($_GET['customer_id']??0);$prefOpportunity=(int)($_GET['opportunity_id']??0);?><section class="panel"><div class="panel-head"><div><h2>Schedule delivery</h2><p>Confirm the fee with the customer before dispatch.</p></div><a class="button secondary small" href="index.php?page=deliveries">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_delivery"><label>Customer<select name="customer_id" required><option value="">Select customer</option><?php foreach($customersList as $c):?><option value="<?=$c['id']?>" <?=$prefCustomer===$c['id']?'selected':''?>><?=e($c['company_name']?:$c['name'])?></option><?php endforeach;?></select></label><label>Opportunity<select name="opportunity_id"><option value="">None</option><?php foreach($opportunitiesList as $o):?><option value="<?=$o['id']?>" <?=$prefOpportunity===$o['id']?'selected':''?>><?=e($o['customer'].' — '.$o['title'])?></option><?php endforeach;?></select></label><label>Invoice<select name="document_id"><option value="">None</option><?php foreach($documentsList as $d):?><option value="<?=$d['id']?>"><?=e($d['number'].' — '.$d['customer'])?></option><?php endforeach;?></select></label><label>Scheduled date and time<input type="datetime-local" name="scheduled_for" value="<?=date('Y-m-d\TH:i',strtotime('+1 day 09:00'))?>"></label><label class="full">Delivery address<input name="delivery_address" required></label><label>Area / suburb<input name="area"></label><label>Delivery fee<input type="number" min="0.01" step="0.01" name="delivery_fee" required></label><label>Driver name<input name="driver_name"></label><label>Driver phone<input name="driver_phone"></label><label class="full">Notes<textarea name="notes" rows="3"></textarea></label><button class="button primary">Schedule delivery</button></form></section><?php endif;?>
    <?php if($selectedDelivery):$wa=preg_replace('/\D+/','',(string)($selectedDelivery['whatsapp']?:$selectedDelivery['phone']));if(str_starts_with($wa,'0'))$wa='263'.substr($wa,1);?><section class="panel"><div class="panel-head"><div><h2><?=e($selectedDelivery['reference'])?></h2><p><?=e($selectedDelivery['company_name']?:$selectedDelivery['customer'])?> · <?=status_badge($selectedDelivery['status'])?></p></div><a class="button secondary small" href="index.php?page=deliveries">Close</a></div><div class="crm-detail-grid"><article><span>Scheduled</span><strong><?=e($selectedDelivery['scheduled_for']?date('j M Y, H:i',strtotime($selectedDelivery['scheduled_for'])):'Not scheduled')?></strong><small><?=e($selectedDelivery['area']?:'Area not set')?></small></article><article><span>Delivery fee</span><strong><?=money($selectedDelivery['delivery_fee'])?></strong><small>Location-based charge</small></article><article><span>Driver</span><strong><?=e($selectedDelivery['driver_name']?:'Not assigned')?></strong><small><?=e($selectedDelivery['driver_phone']?:'No driver phone')?></small></article><article><span>Linked record</span><strong><?=e($selectedDelivery['document_number']?:$selectedDelivery['refill_reference']?:'Standalone')?></strong><small><?=e($selectedDelivery['proof_reference']?:'No proof recorded')?></small></article></div><p class="delivery-address"><strong>Deliver to:</strong> <?=e($selectedDelivery['delivery_address'])?></p><div class="crm-action-row"><?php if($wa):?><a class="button green" target="_blank" rel="noopener" href="https://wa.me/<?=e($wa)?>?text=<?=rawurlencode('Hello '.$selectedDelivery['customer'].', your Soda ZW delivery '.$selectedDelivery['reference'].' is '.str_replace('_',' ',$selectedDelivery['status']).'. Delivery fee: '.money($selectedDelivery['delivery_fee']).'.')?>">WhatsApp update</a><?php endif;?></div><form method="post" class="crm-stage-form"><?=csrf_field()?><input type="hidden" name="action" value="update_delivery"><input type="hidden" name="id" value="<?=$selectedDelivery['id']?>"><label>Status<select name="status"><?php foreach($statuses as $key=>$label):?><option value="<?=e($key)?>" <?=$selectedDelivery['status']===$key?'selected':''?>><?=e($label)?></option><?php endforeach;?></select></label><label>Driver<input name="driver_name" value="<?=e($selectedDelivery['driver_name'])?>"></label><label>Driver phone<input name="driver_phone" value="<?=e($selectedDelivery['driver_phone'])?>"></label><label>Proof/reference<input name="proof_reference" value="<?=e($selectedDelivery['proof_reference'])?>" placeholder="Recipient or delivery note"></label><button class="button primary">Update delivery</button></form></section><?php endif;?>
    <?php if($selectedDelivery):?><section class="panel delivery-closeout"><div class="panel-head"><div><h2>Payment and delivery closeout</h2><p>Record who received the order and how the delivery charge was collected.</p></div></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="update_delivery"><input type="hidden" name="id" value="<?=$selectedDelivery['id']?>"><input type="hidden" name="status" value="<?=e($selectedDelivery['status'])?>"><input type="hidden" name="driver_name" value="<?=e($selectedDelivery['driver_name'])?>"><input type="hidden" name="driver_phone" value="<?=e($selectedDelivery['driver_phone'])?>"><input type="hidden" name="proof_reference" value="<?=e($selectedDelivery['proof_reference'])?>"><label>Delivery payment<select name="payment_status"><option value="unpaid" <?=$selectedDelivery['payment_status']==='unpaid'?'selected':''?>>Unpaid</option><option value="partial" <?=$selectedDelivery['payment_status']==='partial'?'selected':''?>>Part paid</option><option value="paid" <?=$selectedDelivery['payment_status']==='paid'?'selected':''?>>Paid</option></select></label><label>Payment method<select name="payment_method"><option value="">Not received</option><?php foreach(['Cash','EcoCash','Bank transfer','POS / Swipe','Paynow','OneMoney','InnBucks',"O'mari"] as $method):?><option <?=$selectedDelivery['payment_method']===$method?'selected':''?>><?=e($method)?></option><?php endforeach;?></select></label><label>Amount collected<input type="number" min="0" step="0.01" name="amount_collected" value="<?=e($selectedDelivery['amount_collected'])?>"></label><label>Received by<input name="recipient_name" value="<?=e($selectedDelivery['recipient_name'])?>" placeholder="Customer or authorised recipient"></label><label class="full">Failure reason, if unsuccessful<input name="failure_reason" value="<?=e($selectedDelivery['failure_reason'])?>"></label><label class="full">Internal note<input name="event_notes" placeholder="Payment reference or closeout note"></label><button class="button primary">Save closeout details</button></form></section><?php endif;?>
    <div class="filter-pills"><a class="<?=!$statusFilter?'active':''?>" href="index.php?page=deliveries">All</a><?php foreach($statuses as $key=>$label):?><a class="<?=$statusFilter===$key?'active':''?>" href="index.php?page=deliveries&status=<?=e($key)?>"><?=e($label)?> <b><?=e((string)($counts[$key]??0))?></b></a><?php endforeach;?></div><section class="panel"><div class="table-wrap"><table><thead><tr><th>Reference</th><th>Customer</th><th>Schedule</th><th>Location</th><th>Driver</th><th>Status</th><th>Fee</th><th></th></tr></thead><tbody><?php foreach($deliveries as $v):?><tr><td><strong><?=e($v['reference'])?></strong><br><small class="muted"><?=e($v['document_number']?:$v['refill_reference']?:'Standalone')?></small></td><td><?=e($v['company_name']?:$v['customer'])?><br><small class="muted"><?=e($v['phone'])?></small></td><td class="<?=$v['scheduled_for']&&$v['scheduled_for']<date('Y-m-d H:i:s')&&!in_array($v['status'],['delivered','cancelled'],true)?'due-late':''?>"><?=e($v['scheduled_for']?date('j M, H:i',strtotime($v['scheduled_for'])):'Not set')?></td><td><?=e($v['area']?:'—')?><br><small class="muted"><?=e($v['delivery_address'])?></small></td><td><?=e($v['driver_name']?:'Unassigned')?></td><td><?=status_badge($v['status'])?></td><td><strong><?=money($v['delivery_fee'])?></strong><br><small><?=e(ucfirst($v['payment_status']??'unpaid'))?></small></td><td><a class="button secondary small" href="index.php?page=deliveries&id=<?=$v['id']?>">Open</a></td></tr><?php endforeach;?><?php if(!$deliveries):?><tr><td colspan="8" class="empty"><strong>No deliveries yet</strong>Schedule a customer delivery with its agreed fee.</td></tr><?php endif;?></tbody></table></div></section><?php render_footer();break;

case 'dashboard':
    // The current dashboard is rendered by accounting-dashboard.php before this legacy block.
    // Ageing is derived from due dates and balances; opening a page must never mutate drafts.
    $sales=(float)db()->query('SELECT COALESCE(SUM(total),0) FROM documents WHERE type="invoice" AND status<>"cancelled" AND MONTH(issue_date)=MONTH(CURDATE()) AND YEAR(issue_date)=YEAR(CURDATE())')->fetchColumn();
    $collected=(float)db()->query('SELECT COALESCE(SUM(amount),0) FROM payments WHERE MONTH(payment_date)=MONTH(CURDATE()) AND YEAR(payment_date)=YEAR(CURDATE())')->fetchColumn();
    $outstandingRows=db()->query('SELECT x.currency_code,SUM(x.balance) total FROM (SELECT d.currency_code,GREATEST(d.total-COALESCE(SUM(p.amount),0),0) balance FROM documents d LEFT JOIN payments p ON p.invoice_id=d.id WHERE d.type="invoice" AND d.status<>"cancelled" GROUP BY d.id) x GROUP BY x.currency_code')->fetchAll();
    $customers=(int)db()->query('SELECT COUNT(*) FROM customers WHERE active=1')->fetchColumn();
    $lowStockCount=(int)db()->query('SELECT COUNT(*) FROM products WHERE active=1 AND track_stock=1 AND stock_quantity<=reorder_level')->fetchColumn();
    $billPayableRows=db()->query('SELECT x.currency_code,SUM(x.balance) total FROM (SELECT b.currency_code,GREATEST(b.total-COALESCE(SUM(bp.amount),0),0) balance FROM bills b LEFT JOIN bill_payments bp ON bp.bill_id=b.id WHERE b.status<>"cancelled" GROUP BY b.id) x GROUP BY x.currency_code')->fetchAll();$formatDashboardCurrencies=function(array $rows):string{if(!$rows)return 'USD 0.00';$parts=[];foreach($rows as $r)$parts[]=e($r['currency_code']).' '.number_format((float)$r['total'],2);return implode('<small class="currency-plus"> + </small>',$parts);};
    $recent=db()->query('SELECT d.*,c.name customer,COALESCE(SUM(p.amount),0) paid FROM documents d JOIN customers c ON c.id=d.customer_id LEFT JOIN payments p ON p.invoice_id=d.id GROUP BY d.id ORDER BY d.created_at DESC LIMIT 8')->fetchAll();
    $sendInvoices=db()->query('SELECT d.*,c.name customer,c.email,c.phone,c.whatsapp,COALESCE(SUM(p.amount),0) paid FROM documents d JOIN customers c ON c.id=d.customer_id LEFT JOIN payments p ON p.invoice_id=d.id WHERE d.type="invoice" AND d.status<>"cancelled" GROUP BY d.id ORDER BY d.created_at DESC LIMIT 8')->fetchAll();
    $setShareToken=db()->prepare('UPDATE documents SET share_token=? WHERE id=?');foreach($sendInvoices as &$sendInvoice){if(empty($sendInvoice['share_token'])){$sendInvoice['share_token']=bin2hex(random_bytes(32));$setShareToken->execute([$sendInvoice['share_token'],$sendInvoice['id']]);}}unset($sendInvoice);
    $shareBase=rtrim((string)($config['app']['base_url']??'https://billing.sodazw.co.zw'),'/');
    $whatsappNumber=function(?string $number):string{$digits=preg_replace('/\D+/','',(string)$number);if(str_starts_with($digits,'0'))$digits='263'.substr($digits,1);elseif(strlen($digits)===9&&str_starts_with($digits,'7'))$digits='263'.$digits;return $digits;};
    $overdue=db()->query('SELECT d.id,d.number,d.total,d.due_date,c.name, d.total-COALESCE(SUM(p.amount),0) balance FROM documents d JOIN customers c ON c.id=d.customer_id LEFT JOIN payments p ON p.invoice_id=d.id WHERE d.type="invoice" AND d.status="overdue" GROUP BY d.id ORDER BY d.due_date LIMIT 6')->fetchAll();
    $yearSales=(float)db()->query('SELECT COALESCE(SUM(total),0) FROM documents WHERE type="invoice" AND status<>"cancelled" AND YEAR(issue_date)=YEAR(CURDATE())')->fetchColumn();$yearExpenses=(float)db()->query('SELECT COALESCE((SELECT SUM(amount) FROM expenses WHERE YEAR(expense_date)=YEAR(CURDATE())),0)+COALESCE((SELECT SUM(total) FROM bills WHERE status<>"cancelled" AND YEAR(bill_date)=YEAR(CURDATE())),0)')->fetchColumn();
    $months=[];for($n=11;$n>=0;$n--){$key=date('Y-m',strtotime("-{$n} months"));$months[$key]=['label'=>date('M',strtotime($key.'-01')),'in'=>0.0,'out'=>0.0];}$flowIn=db()->query('SELECT DATE_FORMAT(payment_date,"%Y-%m") month,SUM(amount) total FROM payments WHERE payment_date>=DATE_SUB(CURDATE(),INTERVAL 11 MONTH) GROUP BY month')->fetchAll();foreach($flowIn as $r)if(isset($months[$r['month']]))$months[$r['month']]['in']=(float)$r['total'];$flowOut=db()->query('SELECT x.month,SUM(x.total) total FROM (SELECT DATE_FORMAT(expense_date,"%Y-%m") month,SUM(amount) total FROM expenses WHERE expense_date>=DATE_SUB(CURDATE(),INTERVAL 11 MONTH) GROUP BY month UNION ALL SELECT DATE_FORMAT(payment_date,"%Y-%m") month,SUM(amount) total FROM bill_payments WHERE payment_date>=DATE_SUB(CURDATE(),INTERVAL 11 MONTH) GROUP BY month) x GROUP BY x.month')->fetchAll();foreach($flowOut as $r)if(isset($months[$r['month']]))$months[$r['month']]['out']=(float)$r['total'];$maxFlow=max(1,...array_values(array_map(fn($m)=>max($m['in'],$m['out']),$months)));
    $channels=db()->query('SELECT COALESCE(a.name,p.method) channel,COALESCE(p.received_currency,d.currency_code) currency,SUM(COALESCE(p.received_amount,p.amount)) received,SUM(p.amount) applied FROM payments p JOIN documents d ON d.id=p.invoice_id LEFT JOIN payment_accounts a ON a.id=p.account_id WHERE p.payment_date>=DATE_SUB(CURDATE(),INTERVAL 12 MONTH) GROUP BY channel,currency ORDER BY applied DESC LIMIT 8')->fetchAll();
    $aging=db()->query('SELECT COALESCE(SUM(CASE WHEN age<=0 THEN balance ELSE 0 END),0) current_due,COALESCE(SUM(CASE WHEN age BETWEEN 1 AND 30 THEN balance ELSE 0 END),0) d30,COALESCE(SUM(CASE WHEN age BETWEEN 31 AND 60 THEN balance ELSE 0 END),0) d60,COALESCE(SUM(CASE WHEN age BETWEEN 61 AND 90 THEN balance ELSE 0 END),0) d90,COALESCE(SUM(CASE WHEN age>90 THEN balance ELSE 0 END),0) over90 FROM (SELECT DATEDIFF(CURDATE(),d.due_date) age,GREATEST(d.total-COALESCE(SUM(p.amount),0),0) balance FROM documents d LEFT JOIN payments p ON p.invoice_id=d.id WHERE d.type="invoice" AND d.status<>"cancelled" GROUP BY d.id) x')->fetch();
    $salesToday=(float)db()->query('SELECT COALESCE(SUM(total),0) FROM documents WHERE type="invoice" AND status<>"cancelled" AND issue_date=CURDATE()')->fetchColumn();$cashToday=(float)db()->query('SELECT COALESCE(SUM(amount),0) FROM payments WHERE payment_date=CURDATE()')->fetchColumn();
    $overdueCount=(int)db()->query('SELECT COUNT(*) FROM documents WHERE type="invoice" AND status="overdue"')->fetchColumn();$activeRefills=(int)db()->query('SELECT COUNT(*) FROM refill_jobs WHERE status NOT IN ("completed","cancelled")')->fetchColumn();$refillsDue=(int)db()->query('SELECT COUNT(*) FROM refill_jobs WHERE status NOT IN ("completed","cancelled") AND due_at<=DATE_ADD(NOW(),INTERVAL 1 DAY)')->fetchColumn();$openDeliveries=(int)db()->query('SELECT COUNT(*) FROM deliveries WHERE status NOT IN ("delivered","cancelled")')->fetchColumn();$newWebOrders=(int)db()->query('SELECT COUNT(*) FROM web_orders WHERE status="new"')->fetchColumn();$followupsDue=(int)db()->query('SELECT COUNT(*) FROM crm_activities WHERE completed_at IS NULL AND due_at IS NOT NULL AND due_at<=NOW()')->fetchColumn();$customerRefillReminders=(int)db()->query('SELECT COUNT(*) FROM customers WHERE active=1 AND next_refill_due IS NOT NULL AND next_refill_due<=CURDATE()')->fetchColumn();$attentionTotal=$overdueCount+$lowStockCount+$refillsDue+$openDeliveries+$followupsDue+$newWebOrders;
    $dashboardUser=user();$dashboardFirstName=trim(explode(' ',trim((string)$dashboardUser['name']))[0]??'');$dashboardHour=(int)date('G');$dashboardGreeting=$dashboardHour<12?'Good morning':($dashboardHour<17?'Good afternoon':'Good evening');
    render_header('Dashboard','dashboard');?>
    <section class="dashboard-hero"><div class="dashboard-hero-copy"><span class="dashboard-eyebrow">Soda ZW command centre</span><h2><?=e($dashboardGreeting)?><?= $dashboardFirstName!==''?', '.e($dashboardFirstName):''?></h2><p>See what was sold, what was paid and what needs action - without searching through reports.</p><div class="dashboard-hero-actions"><a class="button hero-primary" href="index.php?page=document-edit&type=invoice">+ Create invoice</a><a class="button hero-secondary" href="index.php?page=refills&new=1">New refill</a><a class="button hero-secondary" href="index.php?page=crm">Open CRM</a></div></div><div class="dashboard-today"><span>Today</span><strong><?=e(date('l'))?></strong><small><?=e(date('j F Y'))?></small><em class="<?=$attentionTotal?'needs-action':''?>"><i></i><?=$attentionTotal?$attentionTotal.' items need attention':'All operations clear'?></em></div></section>
    <section class="executive-metrics"><a href="index.php?page=invoices"><span>Sales today</span><strong><?=money($salesToday)?></strong><small>Invoices raised today</small><i class="metric-accent green"></i></a><a href="index.php?page=payments"><span>Cash received today</span><strong><?=money($cashToday)?></strong><small>Recorded customer payments</small><i class="metric-accent green"></i></a><a href="index.php?page=invoices&tab=unpaid"><span>Customer receivables</span><strong class="multi-currency"><?=$formatDashboardCurrencies($outstandingRows)?></strong><small><?=$overdueCount?> overdue invoice<?=$overdueCount===1?'':'s'?></small><i class="metric-accent orange"></i></a><a href="index.php?page=refills"><span>Refills due</span><strong><?=$refillsDue?></strong><small>Due within the next 24 hours</small><i class="metric-accent navy"></i></a></section>
    <section class="quick-strip accounting-shortcuts"><a href="index.php?page=document-edit&type=invoice"><b><?=app_nav_icon('invoices')?></b><span>Create invoice</span></a><a href="index.php?page=payments"><b><?=app_nav_icon('payments')?></b><span>Record payment</span></a><a href="index.php?page=whatsapp"><b><?=app_nav_icon('whatsapp')?></b><span>WhatsApp centre</span></a><a href="index.php?page=refills&new=1"><b><?=app_nav_icon('refills')?></b><span>New refill</span></a><a href="index.php?page=deliveries&new=1"><b><?=app_nav_icon('deliveries')?></b><span>Schedule delivery</span></a><a href="index.php?page=document-edit&type=quotation"><b><?=app_nav_icon('quotations')?></b><span>Create quotation</span></a></section>
    <section class="dashboard-command-grid"><article class="panel attention-centre"><div class="panel-head"><div><span class="section-kicker">PRIORITY QUEUE</span><h2>Needs attention</h2><p>Resolve the most important operational items first.</p></div><strong class="attention-total"><?=$attentionTotal?></strong></div><div class="attention-list"><a href="index.php?page=invoices&status=overdue"><i class="attention-dot danger"></i><span><b>Overdue invoices</b><small>Contact customers and record payments</small></span><strong><?=$overdueCount?></strong></a><a href="index.php?page=refills"><i class="attention-dot orange"></i><span><b>Refills due within 24 hours</b><small>Prepare overnight refill collections</small></span><strong><?=$refillsDue?></strong></a><a href="index.php?page=products"><i class="attention-dot orange"></i><span><b>Low-stock products</b><small>Review stock before confirming orders</small></span><strong><?=$lowStockCount?></strong></a><a href="index.php?page=crm"><i class="attention-dot navy"></i><span><b>CRM follow-ups due</b><small>Calls, WhatsApp messages and tasks</small></span><strong><?=$followupsDue?></strong></a></div></article><article class="panel operations-today"><div class="panel-head"><div><span class="section-kicker">LIVE OPERATIONS</span><h2>Today at Soda ZW</h2><p>Orders, deliveries and customer refill activity.</p></div></div><div class="operations-grid"><a href="index.php?page=web-orders"><b><?=app_nav_icon('web-orders')?></b><span>New website orders</span><strong><?=$newWebOrders?></strong></a><a href="index.php?page=deliveries"><b><?=app_nav_icon('deliveries')?></b><span>Open deliveries</span><strong><?=$openDeliveries?></strong></a><a href="index.php?page=refills"><b><?=app_nav_icon('refills')?></b><span>Refills in progress</span><strong><?=$activeRefills?></strong></a><a href="index.php?page=customers"><b><?=app_nav_icon('customers')?></b><span>Customer refill reminders</span><strong><?=$customerRefillReminders?></strong></a></div><a class="operations-link" href="index.php?page=crm">View customer pipeline <span>&rarr;</span></a></article></section>
    <section class="panel invoice-send-centre"><div class="panel-head"><div><h2>Send invoices</h2><p>Share a branded, secure customer copy by WhatsApp or email.</p></div><a class="button primary small" href="index.php?page=document-edit&type=invoice">+ Create invoice</a></div><div class="table-wrap"><table><thead><tr><th>Invoice</th><th>Customer</th><th>Balance</th><th>Customer link</th><th>Send</th></tr></thead><tbody><?php foreach($sendInvoices as $invoice):?><?php $balance=max(0,(float)$invoice['total']-(float)$invoice['paid']);$shareUrl=$shareBase.'/share.php?token='.$invoice['share_token'];$wa=$whatsappNumber($invoice['whatsapp']?:$invoice['phone']);$message='Hello '.$invoice['customer'].', your Soda ZW invoice '.$invoice['number'].' is ready. Total: '.money($invoice['total'],$invoice['currency_code']).'. Balance: '.money($balance,$invoice['currency_code']).'. View or save it here: '.$shareUrl;$subject='Soda ZW invoice '.$invoice['number'];?><tr><td><a href="index.php?page=document-view&id=<?=$invoice['id']?>"><strong><?=e($invoice['number'])?></strong></a><br><?=status_badge($invoice['status'])?></td><td><strong><?=e($invoice['customer'])?></strong><br><small class="muted"><?=e($invoice['email']?:$invoice['whatsapp']?:$invoice['phone']?:'No contact saved')?></small></td><td><strong><?=money($balance,$invoice['currency_code'])?></strong></td><td><?php if((int)$invoice['share_enabled']===1):?><span class="share-link-status enabled">Active</span><a class="copy-invoice-link" href="<?=e($shareUrl)?>" target="_blank" rel="noopener">Open link</a><?php else:?><span class="share-link-status disabled">Disabled</span><?php endif;?></td><td><div class="send-invoice-actions"><?php if((int)$invoice['share_enabled']===1):?><?php if($wa):?><a class="button green small" target="_blank" rel="noopener" href="https://wa.me/<?=e($wa)?>?text=<?=rawurlencode($message)?>">WhatsApp</a><?php endif;?><?php if($invoice['email']):?><a class="button secondary small" href="mailto:<?=e($invoice['email'])?>?subject=<?=rawurlencode($subject)?>&body=<?=rawurlencode($message."\n\nThank you,\nSoda ZW")?>">Email</a><?php endif;?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="toggle_invoice_share"><input type="hidden" name="id" value="<?=$invoice['id']?>"><input type="hidden" name="enabled" value="0"><button class="button link-button small" type="submit">Disable link</button></form><?php else:?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="toggle_invoice_share"><input type="hidden" name="id" value="<?=$invoice['id']?>"><input type="hidden" name="enabled" value="1"><button class="button secondary small" type="submit">Enable link</button></form><?php endif;?></div></td></tr><?php endforeach;?><?php if(!$sendInvoices):?><tr><td colspan="5" class="empty"><strong>No invoices yet</strong>Create an invoice and it will appear here ready to send.</td></tr><?php endif;?></tbody></table></div></section>
    <section class="stats accounting-stats"><article class="stat"><span>Sales this month</span><strong><?=money($sales)?></strong><small>Invoices excluding cancellations</small></article><article class="stat"><span>Payments received</span><strong><?=money($collected)?></strong><small>This calendar month</small></article><article class="stat"><span>Customer receivables</span><strong class="multi-currency"><?=$formatDashboardCurrencies($outstandingRows)?></strong><small>Money owed to Soda ZW</small></article><article class="stat payable-stat"><span>Supplier payables</span><strong class="multi-currency"><?=$formatDashboardCurrencies($billPayableRows)?></strong><small>Money Soda ZW owes</small></article><article class="stat <?=$lowStockCount?'stock-alert-stat':''?>"><span>Low stock</span><strong><?=$lowStockCount?></strong><small><a href="index.php?page=products">Review inventory</a></small></article><article class="stat"><span>Customers</span><strong><?=$customers?></strong><small>Active customer records</small></article></section>
    <section class="dashboard-grid"><div class="panel"><div class="panel-head"><div><h2>Overdue invoices</h2><p>Customers requiring follow-up</p></div><a class="button secondary small" href="index.php?page=invoices&status=overdue">View all</a></div><?php if($overdue):?><div class="account-list"><?php foreach($overdue as $d):?><a class="account-row" href="index.php?page=document-view&id=<?=$d['id']?>"><span><strong><?=e($d['name'])?></strong><br><small class="muted"><?=e($d['number'])?> · due <?=e($d['due_date'])?></small></span><b><?=money($d['balance'])?></b></a><?php endforeach;?></div><?php else:?><div class="empty"><strong>All clear</strong>No overdue invoices need attention.</div><?php endif;?></div><div class="panel"><div class="panel-head"><div><h2>Cash flow</h2><p>Payments in versus operating expenses</p></div><span class="chart-legend"><i></i> Inflow <i></i> Outflow</span></div><div class="cash-chart"><?php foreach($months as $m):?><div class="cash-month"><div class="cash-bars"><i style="height:<?=max(2,round($m['in']/$maxFlow*100))?>%" title="In <?=money($m['in'])?>"></i><b style="height:<?=max(2,round($m['out']/$maxFlow*100))?>%" title="Out <?=money($m['out'])?>"></b></div><span><?=e($m['label'])?></span></div><?php endforeach;?></div></div></section>
    <section class="dashboard-grid"><div class="panel"><div class="panel-head"><div><h2>Profit & loss snapshot</h2><p>Current calendar year</p></div><a class="button secondary small" href="index.php?page=reports">Full report</a></div><div class="pnl-lines"><div><span>Invoiced income</span><strong><?=money($yearSales)?></strong></div><div><span>Operating expenses</span><strong>-<?=money($yearExpenses)?></strong></div><div class="net"><span>Net contribution</span><strong><?=money($yearSales-$yearExpenses)?></strong></div></div></div><div class="panel"><div class="panel-head"><div><h2>Payments by channel</h2><p>Last 12 months</p></div></div><?php if($channels):?><div class="channel-list"><?php foreach($channels as $c):?><div><span><b><?=e($c['channel'])?></b><small><?=e($c['currency'])?> received</small></span><strong><?=e($c['currency'])?> <?=number_format((float)$c['received'],2)?></strong></div><?php endforeach;?></div><?php else:?><div class="empty">No payments recorded yet.</div><?php endif;?></div></section>
    <section class="panel"><div class="panel-head"><div><h2>Receivables ageing</h2><p>Outstanding invoice balances by age</p></div></div><div class="aging-grid"><div><span>Current</span><strong><?=money($aging['current_due'])?></strong></div><div><span>1–30 days</span><strong><?=money($aging['d30'])?></strong></div><div><span>31–60 days</span><strong><?=money($aging['d60'])?></strong></div><div><span>61–90 days</span><strong><?=money($aging['d90'])?></strong></div><div><span>90+ days</span><strong><?=money($aging['over90'])?></strong></div></div></section>
    <section class="panel"><div class="panel-head"><div><h2>Recent documents</h2><p>Your latest quotations and invoices</p></div><a class="button secondary small" href="index.php?page=invoices">View all</a></div><div class="table-wrap"><table><thead><tr><th>Number</th><th>Customer</th><th>Status</th><th>Total</th></tr></thead><tbody><?php foreach($recent as $d):?><tr><td><a href="index.php?page=document-view&id=<?=$d['id']?>"><strong><?=e($d['number'])?></strong></a><br><small class="muted"><?=e(document_label($d['type']))?></small></td><td><?=e($d['customer'])?></td><td><?=status_badge($d['status'])?></td><td><?=money($d['total'],$d['currency_code'])?></td></tr><?php endforeach;?><?php if(!$recent):?><tr><td colspan="4" class="empty">No documents yet.</td></tr><?php endif;?></tbody></table></div></section>
    <?php render_footer();break;

case 'customers':
    {
        $edit=null;if($id=(int)($_GET['edit']??0)){$s=db()->prepare('SELECT * FROM customers WHERE id=?');$s->execute([$id]);$edit=$s->fetch();}$q=trim($_GET['q']??'');$like='%'.$q.'%';$s=db()->prepare('SELECT c.* FROM customers c WHERE c.active=1 AND (c.name LIKE ? OR c.company_name LIKE ? OR c.phone LIKE ? OR c.email LIKE ?) ORDER BY c.name');$s->execute([$like,$like,$like,$like]);$rows=$s->fetchAll();
        $exposure=[];$invoiceStats=db()->query('SELECT d.customer_id,d.currency_code,d.due_date,d.status,'.finance_document_balance_sql('d').' balance FROM documents d WHERE d.type="invoice" AND d.status<>"cancelled"')->fetchAll();foreach($invoiceStats as $r){$balance=max(0,(float)$r['balance']);if($balance<=.009)continue;$cid=(int)$r['customer_id'];$cur=$r['currency_code'];if(!isset($exposure[$cid][$cur]))$exposure[$cid][$cur]=['balance'=>0.0,'overdue'=>0.0];$exposure[$cid][$cur]['balance']+=$balance;if($r['due_date']<date('Y-m-d'))$exposure[$cid][$cur]['overdue']+=$balance;}
        render_header('Customers','customers');?>
        <div class="page-actions"><div><h2 class="register-title">Customers</h2><p class="muted">Contacts, balances, statements and sales history in one place.</p></div><div class="button-row"><a class="button secondary" href="customer-cleanup.php">Merge duplicates</a><a class="button secondary" href="#customer-import">Import from CSV</a><a class="button primary" href="index.php?page=customers&edit=new">+ Add a customer</a></div></div>
        <form class="customer-search"><input type="hidden" name="page" value="customers"><input name="q" value="<?=e($q)?>" placeholder="Search by name, company, phone or email"><button class="button secondary">Search</button><span><b><?=count($rows)?></b> customers found</span><?php if($q):?><a href="index.php?page=customers">Clear</a><?php endif;?></form>
        <?php if(isset($_GET['edit'])):?><section class="panel customer-editor"><div class="panel-head"><div><h2><?=$edit?'Edit customer':'Add a customer'?></h2><p>Contact, CRM, refill and delivery information in one customer record.</p></div><a class="button secondary small" href="index.php?page=customers">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_customer"><input type="hidden" name="id" value="<?=e($edit['id']??'')?>"><label>Customer or contact name<input name="name" value="<?=e($edit['name']??'')?>" required></label><label>Company<input name="company_name" value="<?=e($edit['company_name']??'')?>"></label><label>Phone<input name="phone" value="<?=e($edit['phone']??'')?>"></label><label>WhatsApp<input name="whatsapp" value="<?=e($edit['whatsapp']??'')?>"></label><label>Email<input type="email" name="email" value="<?=e($edit['email']??'')?>"></label><label>Tax/VAT number<input name="tax_number" value="<?=e($edit['tax_number']??'')?>"></label><label>Customer type<select name="customer_type"><?php foreach(['Household','Corporate','Restaurant / Cafe','Hotel / Lodge','Retailer','Other'] as $type):?><option <?=$type===($edit['customer_type']??'Household')?'selected':''?>><?=e($type)?></option><?php endforeach;?></select></label><label>Area / suburb<input name="area" value="<?=e($edit['area']??'')?>"></label><label>Preferred payment<select name="preferred_payment"><option value="">Confirm when ordering</option><?php foreach(['Cash','EcoCash','Bank transfer','POS / Swipe','Paynow','OneMoney','InnBucks',"O'mari"] as $method):?><option <?=$method===($edit['preferred_payment']??'')?'selected':''?>><?=e($method)?></option><?php endforeach;?></select></label><label>Next expected refill<input type="date" name="next_refill_due" value="<?=e($edit['next_refill_due']??'')?>"></label><label class="full">Address<input name="address" value="<?=e($edit['address']??'')?>"></label><label>City<input name="city" value="<?=e($edit['city']??'')?>"></label><label class="check"><input type="checkbox" name="marketing_consent" value="1" <?=!empty($edit['marketing_consent'])?'checked':''?>> Customer permits promotional follow-ups</label><label class="full">Notes<textarea name="notes" rows="3"><?=e($edit['notes']??'')?></textarea></label><button class="button primary">Save customer</button></form></section><?php endif;?>
        <section class="panel customer-register"><div class="table-wrap"><table><thead><tr><th>Name</th><th>Type / Area</th><th>Contact</th><th>Next refill</th><th>Balance / Overdue</th><th>Actions</th></tr></thead><tbody><?php foreach($rows as $r):?><tr><td><a href="index.php?page=statement&customer_id=<?=$r['id']?>"><strong><?=e($r['company_name']?:$r['name'])?></strong></a><?php if($r['company_name']):?><br><small class="muted"><?=e($r['name'])?></small><?php endif;?></td><td><?=e($r['customer_type']?:'Household')?><br><small class="muted"><?=e($r['area']?:$r['city']?:'Area not set')?></small></td><td><?=e($r['phone']?:$r['whatsapp']?:'—')?><br><small class="muted"><?=e($r['email']?:$r['preferred_payment']?:'No email')?></small></td><td class="<?=$r['next_refill_due']&&$r['next_refill_due']<=date('Y-m-d')?'due-late':''?>"><?=e($r['next_refill_due']?date('j M Y',strtotime($r['next_refill_due'])):'—')?></td><td><?php if(!empty($exposure[$r['id']])):?><?php foreach($exposure[$r['id']] as $cur=>$values):?><span class="customer-balance"><b><?=e($cur)?> <?=number_format($values['balance'],2)?></b><?php if($values['overdue']>0):?><em><?=e($cur)?> <?=number_format($values['overdue'],2)?> overdue</em><?php endif;?></span><?php endforeach;?><?php else:?><span class="paid-clear">No balance</span><?php endif;?></td><td class="actions"><a class="button secondary small" href="index.php?page=crm&new=1">CRM</a><a class="button secondary small" href="index.php?page=statement&customer_id=<?=$r['id']?>">Statement</a><a class="button secondary small" href="index.php?page=document-edit&type=invoice&customer_id=<?=$r['id']?>">Invoice</a><a class="button secondary small" href="index.php?page=customers&edit=<?=$r['id']?>">Edit</a></td></tr><?php endforeach;?><?php if(!$rows):?><tr><td colspan="6" class="empty"><strong>No customers found</strong>Try another search or add your first customer.</td></tr><?php endif;?></tbody></table></div></section>
        <details class="panel import-panel" id="customer-import"><summary>Import or export customers</summary><p class="muted">Use the template so every column is recognised. Existing customers can be matched by email or phone.</p><div class="toolbar"><a class="button secondary small" href="index.php?page=customers&template=1">Download CSV template</a><a class="button secondary small" href="index.php?page=customers&export=1">Export customers</a></div><form method="post" enctype="multipart/form-data" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="import_customers"><label class="full">Customer CSV file<input type="file" name="customer_csv" accept=".csv,text/csv" required></label><label class="check full"><input type="checkbox" name="update_existing" value="1"> Update existing customers when email or phone matches</label><button class="button primary">Import customers</button></form></details>
        <?php render_footer();break;
    }
    $edit=null;if($id=(int)($_GET['edit']??0)){$s=db()->prepare('SELECT * FROM customers WHERE id=?');$s->execute([$id]);$edit=$s->fetch();}
    $q=trim($_GET['q']??'');$s=db()->prepare('SELECT c.*,COUNT(d.id) documents FROM customers c LEFT JOIN documents d ON d.customer_id=c.id WHERE c.active=1 AND (c.name LIKE ? OR c.company_name LIKE ? OR c.phone LIKE ?) GROUP BY c.id ORDER BY c.name');$like='%'.$q.'%';$s->execute([$like,$like,$like]);$rows=$s->fetchAll();render_header('Customers','customers');?>
    <div class="page-actions"><form class="toolbar"><input name="q" value="<?=e($q)?>" placeholder="Search customers"><button class="button secondary">Search</button></form><a class="button primary" href="index.php?page=customers&edit=new">+ Add customer</a></div>
    <details class="panel import-panel"><summary>Import existing customers from CSV</summary><p class="muted">Use the template so every column is recognised. Existing customers are matched by email first, then phone number.</p><div class="toolbar"><a class="button secondary small" href="index.php?page=customers&template=1">Download CSV template</a><a class="button secondary small" href="index.php?page=customers&export=1">Export current customers</a></div><form method="post" enctype="multipart/form-data" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="import_customers"><label class="full">Customer CSV file<input type="file" name="customer_csv" accept=".csv,text/csv" required></label><label class="check full"><input type="checkbox" name="update_existing" value="1"> Update existing customers when email or phone matches</label><button class="button primary" type="submit">Import customers</button></form></details>
    <?php if(isset($_GET['edit'])):?><section class="panel"><div class="panel-head"><h2><?=$edit?'Edit customer':'New customer'?></h2><a href="index.php?page=customers">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_customer"><input type="hidden" name="id" value="<?=e($edit['id']??'')?>"><label>Customer name<input name="name" value="<?=e($edit['name']??'')?>" required></label><label>Company<input name="company_name" value="<?=e($edit['company_name']??'')?>"></label><label>Phone<input name="phone" value="<?=e($edit['phone']??'')?>"></label><label>WhatsApp<input name="whatsapp" value="<?=e($edit['whatsapp']??'')?>"></label><label>Email<input type="email" name="email" value="<?=e($edit['email']??'')?>"></label><label>Tax/VAT number<input name="tax_number" value="<?=e($edit['tax_number']??'')?>"></label><label class="full">Address<input name="address" value="<?=e($edit['address']??'')?>"></label><label>City<input name="city" value="<?=e($edit['city']??'')?>"></label><label class="full">Notes<textarea name="notes" rows="3"><?=e($edit['notes']??'')?></textarea></label><button class="button primary" type="submit">Save customer</button></form></section><?php endif;?>
    <section class="panel"><div class="table-wrap"><table><thead><tr><th>Customer</th><th>Contact</th><th>Documents</th><th></th></tr></thead><tbody><?php foreach($rows as $r):?><tr><td><strong><?=e($r['name'])?></strong><br><small class="muted"><?=e($r['company_name'])?></small></td><td><?=e($r['phone'])?><br><small><?=e($r['email'])?></small></td><td><?=$r['documents']?></td><td class="actions"><a class="button secondary small" href="index.php?page=statement&customer_id=<?=$r['id']?>">Statement</a> <a class="button secondary small" href="index.php?page=customers&edit=<?=$r['id']?>">Edit</a></td></tr><?php endforeach;?><?php if(!$rows):?><tr><td colspan="4" class="empty">No customers found.</td></tr><?php endif;?></tbody></table></div></section><?php render_footer();break;

case 'products':
    {
        $edit=null;if($id=(int)($_GET['edit']??0)){$s=db()->prepare('SELECT * FROM products WHERE id=?');$s->execute([$id]);$edit=$s->fetch();}$rows=db()->query('SELECT * FROM products WHERE active=1 ORDER BY category,name')->fetchAll();$tracked=array_filter($rows,fn($r)=>(bool)$r['track_stock']);$lowStock=array_filter($tracked,fn($r)=>(float)$r['stock_quantity']<=(float)$r['reorder_level']);$stockValue=array_sum(array_map(fn($r)=>(float)$r['stock_quantity']*(float)$r['cost_price'],$tracked));$movements=db()->query('SELECT m.*,p.name product,u.name user_name FROM inventory_movements m JOIN products p ON p.id=m.product_id JOIN users u ON u.id=m.created_by ORDER BY m.movement_date DESC,m.id DESC LIMIT 25')->fetchAll();render_header('Products & stock','products');?>
        <div class="page-actions"><div><h2 class="register-title">Products & stock</h2><p class="muted">Machines, cylinders, bottles and syrups available to Soda ZW customers.</p></div><div class="button-row"><?php if(in_array((string)(user()['role']??''),['owner','admin'],true)):?><form method="post" onsubmit="return confirm('Update product names and selling prices from sodazw.co.zw? Existing cost prices and stock quantities will stay unchanged.')"><?=csrf_field()?><input type="hidden" name="action" value="sync_website_products"><button class="button secondary" type="submit">Sync from website</button></form><?php endif;?><a class="button secondary" href="#stock-adjustment">Update stock</a><a class="button primary" href="index.php?page=products&edit=new">+ Add product</a></div></div>
        <section class="stock-summary"><article><span>Active products</span><strong><?=count($rows)?></strong></article><article><span>Tracked products</span><strong><?=count($tracked)?></strong></article><article class="<?=$lowStock?'warning':''?>"><span>Low-stock items</span><strong><?=count($lowStock)?></strong></article><article><span>Stock cost value</span><strong><?=money($stockValue)?></strong></article></section>
        <section class="panel quick-stock-panel"><div class="panel-head"><div><h2>Quick stock count</h2><p>Enter the actual quantity physically available for each product, then save everything once.</p></div></div><form method="post" class="bulk-stock-form"><?=csrf_field()?><input type="hidden" name="action" value="bulk_set_stock_counts"><div class="stock-count-grid"><?php foreach($rows as $r):?><?php if($r['sku']==='SZW-CO2-REFILL')continue;?><label class="stock-count-card"><span><strong><?=e($r['name'])?></strong><small><?=e($r['sku'])?> &middot; Current <?=e($r['stock_quantity'])?></small></span><span class="stock-count-input">Stock on hand<input type="number" min="0" step="1" inputmode="numeric" name="stock_counts[<?=$r['id']?>]" value="<?=e($r['stock_quantity'])?>" required></span></label><?php endforeach;?></div><div class="bulk-stock-submit"><span>Only changed quantities are added to stock movement history.</span><button class="button primary" type="submit">Save all stock quantities</button></div></form></section>
        <?php if(isset($_GET['edit'])):?><section class="panel"><div class="panel-head"><div><h2><?=$edit?'Edit product':'Add product'?></h2><p>Costs support gross-margin reporting; they are never shown to customers.</p></div><a class="button secondary small" href="index.php?page=products">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_product"><input type="hidden" name="id" value="<?=e($edit['id']??'')?>"><label>Product name<input name="name" value="<?=e($edit['name']??'')?>" required></label><label>SKU<input name="sku" value="<?=e($edit['sku']??'')?>"></label><label>Category<select name="category"><?php foreach(['Sparkling Water Machines','CO2 Cylinders','Bottles','Flavours & Syrups','Other'] as $cat):?><option <?=$cat===($edit['category']??'Other')?'selected':''?>><?=e($cat)?></option><?php endforeach;?></select></label><label>Selling price<input type="number" step="0.01" min="0" name="unit_price" value="<?=e($edit['unit_price']??'0.00')?>" required></label><label>Cost price<input type="number" step="0.01" min="0" name="cost_price" value="<?=e($edit['cost_price']??'0.00')?>"></label><label>Tax rate %<input type="number" step="0.01" min="0" name="tax_rate" value="<?=e($edit['tax_rate']??settings()['default_tax_rate'])?>"></label><label>Current stock<input type="number" step="0.01" name="stock_quantity" value="<?=e($edit['stock_quantity']??'0')?>"></label><label>Low-stock level<input type="number" step="0.01" min="0" name="reorder_level" value="<?=e($edit['reorder_level']??'2')?>"></label><label class="check full"><input type="checkbox" name="track_stock" <?=(!$edit||$edit['track_stock'])?'checked':''?>> Track stock for this product</label><label class="full">Description<textarea name="description" rows="3"><?=e($edit['description']??'')?></textarea></label><button class="button primary">Save product</button></form></section><?php endif;?>
        <section class="panel product-stock-table"><div class="table-wrap"><table><thead><tr><th>Product</th><th>Category</th><th>Selling price</th><th>Cost</th><th>Margin</th><th>Stock</th><th>Actions</th></tr></thead><tbody><?php foreach($rows as $r):?><?php $margin=(float)$r['unit_price']-(float)$r['cost_price'];$isLow=$r['track_stock']&&(float)$r['stock_quantity']<=(float)$r['reorder_level'];?><tr><td><strong><?=e($r['name'])?></strong><br><small class="muted"><?=e($r['sku'])?></small></td><td><span class="category-pill"><?=e($r['category'])?></span></td><td><?=money($r['unit_price'])?></td><td><?=money($r['cost_price'])?></td><td><?=money($margin)?></td><td><?php if($r['track_stock']):?><strong class="<?=$isLow?'stock-low':'stock-ok'?>"><?=e($r['stock_quantity'])?></strong><?php if($isLow):?><small class="stock-warning">Reorder at <?=e($r['reorder_level'])?></small><?php endif;?><?php else:?><span class="muted">Not tracked</span><?php endif;?></td><td class="actions"><a class="button secondary small" href="index.php?page=products&edit=<?=$r['id']?>">Edit</a></td></tr><?php endforeach;?></tbody></table></div></section>
        <section class="dashboard-grid"><div class="panel" id="stock-adjustment"><div class="panel-head"><div><h2>Update stock</h2><p>Use positive quantities for stock received or cylinder returns; negative quantities for corrections.</p></div></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="adjust_stock"><label class="full">Product<select name="product_id" required><option value="">Select product</option><?php foreach($rows as $r):?><option value="<?=$r['id']?>"><?=e($r['name'])?> · current <?=e($r['stock_quantity'])?></option><?php endforeach;?></select></label><label>Movement<select name="movement_type"><option value="purchase">Stock received</option><option value="return">Cylinder/product returned</option><option value="opening">Opening stock</option><option value="adjustment">Stock correction</option></select></label><label>Quantity change<input type="number" step=".01" name="quantity_change" placeholder="e.g. 10 or -2" required></label><label class="full">Notes<input name="notes" placeholder="Supplier delivery, cylinder return, damaged unit…"></label><button class="button primary">Update stock</button></form></div><div class="panel"><div class="panel-head"><div><h2>Recent stock movements</h2><p>Latest inventory activity</p></div></div><div class="movement-list"><?php foreach($movements as $m):?><div><span><strong><?=e($m['product'])?></strong><small><?=e(ucfirst($m['movement_type']))?> · <?=e(date('d M Y',strtotime($m['movement_date'])))?></small></span><b class="<?=$m['quantity_change']<0?'movement-out':'movement-in'?>"><?=$m['quantity_change']>0?'+':''?><?=e($m['quantity_change'])?></b></div><?php endforeach;?><?php if(!$movements):?><div class="empty">No stock movements recorded yet.</div><?php endif;?></div></div></section>
        <?php render_footer();break;
    }
    $edit=null;if($id=(int)($_GET['edit']??0)){$s=db()->prepare('SELECT * FROM products WHERE id=?');$s->execute([$id]);$edit=$s->fetch();}$rows=db()->query('SELECT * FROM products WHERE active=1 ORDER BY name')->fetchAll();render_header('Products & pricing','products');?>
    <div class="page-actions"><p class="muted">Your Soda ZW catalogue is preloaded and can be updated here.</p><a class="button primary" href="index.php?page=products&edit=new">+ Add product</a></div><?php if(isset($_GET['edit'])):?><section class="panel"><div class="panel-head"><h2><?=$edit?'Edit product':'New product'?></h2><a href="index.php?page=products">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_product"><input type="hidden" name="id" value="<?=e($edit['id']??'')?>"><label>Product name<input name="name" value="<?=e($edit['name']??'')?>" required></label><label>SKU<input name="sku" value="<?=e($edit['sku']??'')?>"></label><label>Unit price<input type="number" step="0.01" name="unit_price" value="<?=e($edit['unit_price']??'0.00')?>" required></label><label>Tax rate %<input type="number" step="0.01" name="tax_rate" value="<?=e($edit['tax_rate']??settings()['default_tax_rate'])?>"></label><label>Stock quantity<input type="number" step="0.01" name="stock_quantity" value="<?=e($edit['stock_quantity']??'0')?>"></label><label class="check"><input type="checkbox" name="track_stock" <?=$edit&&$edit['track_stock']?'checked':''?>> Track stock level</label><label class="full">Description<textarea name="description" rows="3"><?=e($edit['description']??'')?></textarea></label><button class="button primary">Save product</button></form></section><?php endif;?><section class="panel"><div class="table-wrap"><table><thead><tr><th>SKU</th><th>Product</th><th>Price</th><th>Stock</th><th></th></tr></thead><tbody><?php foreach($rows as $r):?><tr><td><?=e($r['sku'])?></td><td><strong><?=e($r['name'])?></strong><br><small class="muted"><?=e($r['description'])?></small></td><td><?=money($r['unit_price'])?></td><td><?=$r['track_stock']?e($r['stock_quantity']):'—'?></td><td class="actions"><a class="button secondary small" href="index.php?page=products&edit=<?=$r['id']?>">Edit</a></td></tr><?php endforeach;?></tbody></table></div></section><?php render_footer();break;

case 'quotations':case 'invoices':
    if($page==='invoices'){
        $tab=in_array($_GET['tab']??'unpaid',['unpaid','overdue','due-soon','partial','draft','all'],true)?$_GET['tab']:'unpaid';
        $status=trim($_GET['status']??'');$customerId=(int)($_GET['customer_id']??0);$from=trim($_GET['from']??'');$to=trim($_GET['to']??'');$search=trim($_GET['q']??'');
        $params=[];$where=['d.type="invoice"'];
        if($customerId){$where[]='d.customer_id=?';$params[]=$customerId;}
        if($status!==''){$where[]='d.status=?';$params[]=$status;}
        if($from!==''){$where[]='d.issue_date>=?';$params[]=$from;}
        if($to!==''){$where[]='d.issue_date<=?';$params[]=$to;}
        if($search!==''){$where[]='(d.number LIKE ? OR c.name LIKE ? OR c.company_name LIKE ?)';$needle='%'.$search.'%';array_push($params,$needle,$needle,$needle);}
        if($tab==='draft')$where[]='d.status="draft"';
        if($tab==='unpaid')$where[]='d.status NOT IN ("paid","cancelled","draft")';
        if($tab==='overdue'){$where[]='d.status NOT IN ("paid","cancelled","draft")';$where[]='d.due_date<CURDATE()';}
        if($tab==='due-soon'){$where[]='d.status NOT IN ("paid","cancelled","draft")';$where[]='d.due_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(),INTERVAL 7 DAY)';}
        if($tab==='partial')$where[]='d.status NOT IN ("paid","cancelled","draft")';
        $sql='SELECT d.*,c.name customer,c.company_name,DATEDIFF(CURDATE(),d.due_date) due_days,(SELECT COALESCE(SUM(p.amount),0) FROM payments p WHERE p.invoice_id=d.id)-(SELECT COALESCE(SUM(r.amount),0) FROM payment_reversals r JOIN payments rp ON rp.id=r.payment_id WHERE rp.invoice_id=d.id) paid,(SELECT COALESCE(SUM(ca.amount),0) FROM credit_note_applications ca WHERE ca.invoice_id=d.id) credited FROM documents d JOIN customers c ON c.id=d.customer_id WHERE '.implode(' AND ',$where);
        if(in_array($tab,['unpaid','overdue','due-soon'],true))$sql.=' HAVING d.total-paid-credited>.009';
        if($tab==='partial')$sql.=' HAVING paid+credited>.009 AND d.total-paid-credited>.009';
        $sql.=' ORDER BY d.due_date ASC,d.id DESC';$s=db()->prepare($sql);$s->execute($params);$rows=$s->fetchAll();foreach($rows as &$invoiceRow)$invoiceRow['paid']=(float)$invoiceRow['paid']+(float)$invoiceRow['credited'];unset($invoiceRow);
        $all=db()->query('SELECT d.id,d.status,d.issue_date,d.due_date,d.total,d.currency_code,(SELECT COALESCE(SUM(p.amount),0) FROM payments p WHERE p.invoice_id=d.id)-(SELECT COALESCE(SUM(r.amount),0) FROM payment_reversals r JOIN payments rp ON rp.id=r.payment_id WHERE rp.invoice_id=d.id) paid,(SELECT COALESCE(SUM(ca.amount),0) FROM credit_note_applications ca WHERE ca.invoice_id=d.id) credited FROM documents d WHERE d.type="invoice"')->fetchAll();
        $overdueTotals=[];$upcomingTotals=[];$unpaidCount=0;$overdueTabCount=0;$dueSoonCount=0;$partialCount=0;$draftCount=0;$today=date('Y-m-d');$next7=date('Y-m-d',strtotime('+7 days'));$next30=date('Y-m-d',strtotime('+30 days'));
        foreach($all as $invoice){$settled=(float)$invoice['paid']+(float)$invoice['credited'];$balance=max(0,(float)$invoice['total']-$settled);$currency=$invoice['currency_code'];if($invoice['status']==='draft')$draftCount++;if(!in_array($invoice['status'],['paid','cancelled','draft'],true)&&$balance>.009){$unpaidCount++;if($settled>.009)$partialCount++;if($invoice['due_date']<$today){$overdueTabCount++;$overdueTotals[$currency]=($overdueTotals[$currency]??0)+$balance;}elseif($invoice['due_date']<=$next30)$upcomingTotals[$currency]=($upcomingTotals[$currency]??0)+$balance;if($invoice['due_date']>=$today&&$invoice['due_date']<=$next7)$dueSoonCount++;}}
        $formatTotals=function(array $totals):string{if(!$totals)return 'USD 0.00';$out=[];foreach($totals as $currency=>$amount)$out[]=e($currency).' '.number_format($amount,2);return implode(' + ',$out);};
        $averageDays=(float)db()->query('SELECT COALESCE(AVG(DATEDIFF(GREATEST(COALESCE((SELECT MAX(p.payment_date) FROM payments p WHERE p.invoice_id=d.id),d.issue_date),COALESCE((SELECT MAX(cn.issue_date) FROM credit_note_applications ca JOIN documents cn ON cn.id=ca.credit_note_id WHERE ca.invoice_id=d.id),d.issue_date)),d.issue_date)),0) FROM documents d WHERE d.type="invoice" AND d.status NOT IN ("draft","cancelled") AND d.total>0 AND '.finance_document_balance_sql('d').'<=.009')->fetchColumn();
        $customers=db()->query('SELECT id,name FROM customers WHERE active=1 ORDER BY name')->fetchAll();
        $isExpired=false; // Invoice rows use payment/aging status; quotation expiry is calculated separately.
        render_header('Invoices','invoices');?>
        <div class="page-actions"><div><h2 class="register-title">Accounts receivable</h2><p class="muted">Track invoices, collect overdue balances and record every customer payment.</p></div><div class="button-row"><a class="button command-payment" href="receive-payment.php"><?=app_nav_icon('payments')?> Receive payment</a><a class="button primary" href="quick-invoice.php">+ Create invoice</a></div></div>
        <section class="invoice-summary"><article><span>Overdue</span><strong><?=$formatTotals($overdueTotals)?></strong><small>Outstanding past the due date</small></article><article><span>Due within 30 days</span><strong><?=$formatTotals($upcomingTotals)?></strong><small>Upcoming customer payments</small></article><article><span>Average time to get paid</span><strong><?=number_format($averageDays,0)?> days</strong><small>Based on fully paid invoices</small></article></section>
        <form class="invoice-filters"><input type="hidden" name="page" value="invoices"><input type="hidden" name="tab" value="<?=e($tab)?>"><label>Customer<select name="customer_id"><option value="">All customers</option><?php foreach($customers as $c):?><option value="<?=$c['id']?>" <?=$customerId===$c['id']?'selected':''?>><?=e($c['name'])?></option><?php endforeach;?></select></label><label>Status<select name="status"><option value="">All statuses</option><?php foreach(['draft','sent','partial','paid','overdue','cancelled'] as $st):?><option value="<?=$st?>" <?=$status===$st?'selected':''?>><?=e(ucfirst($st))?></option><?php endforeach;?></select></label><label>From<input type="date" name="from" value="<?=e($from)?>"></label><label>To<input type="date" name="to" value="<?=e($to)?>"></label><label>Search<input name="q" value="<?=e($search)?>" placeholder="Invoice # or customer"></label><button class="button secondary">Apply filters</button><?php if($customerId||$status||$from||$to||$search):?><a class="button secondary" href="index.php?page=invoices&tab=<?=e($tab)?>">Clear</a><?php endif;?></form>
        <nav class="invoice-tabs" aria-label="Invoice work queues"><a class="<?=$tab==='unpaid'?'active':''?>" href="index.php?page=invoices&tab=unpaid" <?=$tab==='unpaid'?'aria-current="page"':''?>>Unpaid <b><?=$unpaidCount?></b></a><a class="<?=$tab==='overdue'?'active':''?>" href="index.php?page=invoices&tab=overdue" <?=$tab==='overdue'?'aria-current="page"':''?>>Overdue <b><?=$overdueTabCount?></b></a><a class="<?=$tab==='due-soon'?'active':''?>" href="index.php?page=invoices&tab=due-soon" <?=$tab==='due-soon'?'aria-current="page"':''?>>Due soon <b><?=$dueSoonCount?></b></a><a class="<?=$tab==='partial'?'active':''?>" href="index.php?page=invoices&tab=partial" <?=$tab==='partial'?'aria-current="page"':''?>>Part paid <b><?=$partialCount?></b></a><a class="<?=$tab==='draft'?'active':''?>" href="index.php?page=invoices&tab=draft" <?=$tab==='draft'?'aria-current="page"':''?>>Draft <b><?=$draftCount?></b></a><a class="<?=$tab==='all'?'active':''?>" href="index.php?page=invoices&tab=all" <?=$tab==='all'?'aria-current="page"':''?>>All <b><?=count($all)?></b></a></nav>
        <section class="panel invoice-register"><div class="table-wrap"><table><caption class="sr-only">Soda ZW invoice register</caption><thead><tr><th scope="col">Status</th><th scope="col">Due</th><th scope="col">Date</th><th scope="col">Number</th><th scope="col">Customer</th><th scope="col">Total</th><th scope="col">Amount due</th><th scope="col">Actions</th></tr></thead><tbody><?php foreach($rows as $r):?><?php $balance=max(0,(float)$r['total']-(float)$r['paid']);$displayStatus=$balance<=.009?'paid':((float)$r['paid']>.009?'partial':($r['status']==='draft'?'draft':($r['due_days']>0?'overdue':$r['status'])));?><tr><td><?=$isExpired?'<span class="status status-overdue">Expired</span>':status_badge($displayStatus)?></td><td class="<?=$r['due_days']>0&&$balance>.009?'due-late':''?>"><?php if($balance<=.009):?>Paid<?php elseif($r['status']==='draft'):?>Not sent<?php elseif($r['due_days']>0):?><?=$r['due_days']?> days overdue<?php elseif($r['due_days']==0):?>Due today<?php else:?>Due in <?=abs((int)$r['due_days'])?> days<?php endif;?></td><td><?=e(date('j M Y',strtotime($r['issue_date'])))?></td><td><a href="index.php?page=document-view&id=<?=$r['id']?>"><strong><?=e($r['number'])?></strong></a></td><td><strong><?=e($r['customer'])?></strong><?php if($r['company_name']):?><br><small class="muted"><?=e($r['company_name'])?></small><?php endif;?></td><td class="money-cell"><?=money($r['total'],$r['currency_code'])?></td><td class="money-cell"><strong><?=money($balance,$r['currency_code'])?></strong></td><td class="actions"><?php if($balance>.009&&$r['status']!=='draft'):?><a class="button command-payment small" href="receive-payment.php?invoice_id=<?=$r['id']?>">Receive payment</a><?php else:?><a class="button secondary small" href="index.php?page=document-view&id=<?=$r['id']?>">View</a><?php endif;?></td></tr><?php endforeach;?><?php if(!$rows):?><tr><td colspan="8" class="empty"><strong>No matching invoices</strong>Adjust the filters or create a new invoice.</td></tr><?php endif;?></tbody></table></div></section>
        <details class="panel import-panel"><summary>Import or export invoice records</summary><p class="muted">Import historical invoices as standard Soda ZW documents. Original invoice numbers, customers, line items and payments remain traceable.</p><div class="toolbar"><a class="button secondary small" href="index.php?page=invoices&template=1">Download invoice template</a><a class="button secondary small" href="index.php?page=invoices&export=1">Export current invoices</a></div><form method="post" enctype="multipart/form-data" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="import_invoices"><label class="full">Invoice CSV file<input type="file" name="invoice_csv" accept=".csv,text/csv" required></label><button class="button primary" type="submit">Import historical invoices</button></form></details>
        <?php render_footer();break;
    }
    $quoteTab=in_array($_GET['tab']??'active',['active','draft','awaiting','accepted','expired','closed','all'],true)?$_GET['tab']:'active';
    $quoteSearch=trim((string)($_GET['q']??''));$quoteParams=[];$quoteWhere=['d.type="quotation"'];
    if($quoteSearch!==''){$quoteWhere[]='(d.number LIKE ? OR d.customer_reference LIKE ? OR c.name LIKE ? OR c.company_name LIKE ?)';$quoteNeedle='%'.$quoteSearch.'%';array_push($quoteParams,$quoteNeedle,$quoteNeedle,$quoteNeedle,$quoteNeedle);}
    if($quoteTab==='active')$quoteWhere[]='(d.status="draft" OR (d.status="sent" AND (d.due_date IS NULL OR d.due_date>=CURDATE())))';
    elseif($quoteTab==='draft')$quoteWhere[]='d.status="draft"';
    elseif($quoteTab==='awaiting')$quoteWhere[]='d.status="sent" AND (d.due_date IS NULL OR d.due_date>=CURDATE())';
    elseif($quoteTab==='accepted')$quoteWhere[]='d.status="accepted"';
    elseif($quoteTab==='expired')$quoteWhere[]='d.status="sent" AND d.due_date<CURDATE()';
    elseif($quoteTab==='closed')$quoteWhere[]='d.status IN ("rejected","cancelled")';
    $quoteSql='SELECT d.*,c.name customer,c.company_name,qc.invoice_id converted_invoice_id FROM documents d JOIN customers c ON c.id=d.customer_id LEFT JOIN quotation_conversions qc ON qc.quotation_id=d.id WHERE '.implode(' AND ',$quoteWhere).' ORDER BY FIELD(d.status,"sent","draft","accepted","rejected","cancelled"),COALESCE(d.due_date,"9999-12-31"),d.id DESC';
    $quoteStmt=db()->prepare($quoteSql);$quoteStmt->execute($quoteParams);$rows=$quoteStmt->fetchAll();
    $allQuotes=db()->query('SELECT id,status,issue_date,due_date,total,currency_code FROM documents WHERE type="quotation" ORDER BY issue_date DESC,id DESC')->fetchAll();
    $quoteCounts=['active'=>0,'draft'=>0,'awaiting'=>0,'accepted'=>0,'expired'=>0,'closed'=>0,'all'=>count($allQuotes)];$openQuoteTotals=[];$acceptedQuoteTotals=[];$acceptedCount=0;$rejectedCount=0;$expiringSoon=0;$today=date('Y-m-d');$next7=date('Y-m-d',strtotime('+7 days'));
    foreach($allQuotes as $quote){$status=(string)$quote['status'];$due=(string)($quote['due_date']??'');$expired=$status==='sent'&&$due!==''&&$due<$today;$active=$status==='draft'||($status==='sent'&&!$expired);if($active){$quoteCounts['active']++;$currency=(string)$quote['currency_code'];$openQuoteTotals[$currency]=($openQuoteTotals[$currency]??0)+(float)$quote['total'];}if($status==='draft')$quoteCounts['draft']++;if($status==='sent'&&!$expired)$quoteCounts['awaiting']++;if($status==='accepted'){$quoteCounts['accepted']++;$acceptedCount++;$currency=(string)$quote['currency_code'];$acceptedQuoteTotals[$currency]=($acceptedQuoteTotals[$currency]??0)+(float)$quote['total'];}if($expired)$quoteCounts['expired']++;if(in_array($status,['rejected','cancelled'],true))$quoteCounts['closed']++;if($status==='rejected')$rejectedCount++;if($status==='sent'&&$due!==''&&$due>=$today&&$due<=$next7)$expiringSoon++;}
    $formatQuoteTotals=function(array $totals):string{if(!$totals)return e((string)(settings()['currency_code']??'USD')).' 0.00';$parts=[];foreach($totals as $currency=>$amount)$parts[]=e($currency).' '.number_format($amount,2);return implode(' + ',$parts);};
    $quoteWinRate=($acceptedCount+$rejectedCount)>0?round($acceptedCount/($acceptedCount+$rejectedCount)*100):0;
    $quoteTabUrl=function(string $tab)use($quoteSearch):string{$params=['page'=>'quotations','tab'=>$tab];if($quoteSearch!=='')$params['q']=$quoteSearch;return 'index.php?'.http_build_query($params);};
    render_header('Quotations','quotations');?>
    <div class="quotation-workbench">
    <div class="page-actions quotation-workbench-head"><div><span class="section-kicker">SALES WORKBENCH</span><h2 class="register-title">Quotation pipeline</h2><p class="muted">Prepare offers, follow up before expiry, and convert accepted work without losing context.</p></div><a class="button primary" href="quick-invoice.php?type=quotation">+ Create quotation</a></div>
    <section class="invoice-summary quotation-summary metric-grid" aria-label="Quotation overview"><article><span>Open pipeline</span><strong><?=$formatQuoteTotals($openQuoteTotals)?></strong><small><?=$quoteCounts['active']?> active quotation<?=$quoteCounts['active']===1?'':'s'?></small></article><article><span>Accepted value</span><strong><?=$formatQuoteTotals($acceptedQuoteTotals)?></strong><small><?=$acceptedCount?> won quotation<?=$acceptedCount===1?'':'s'?></small></article><article><span>Acceptance rate</span><strong><?=$quoteWinRate?>%</strong><small><?=$expiringSoon?> awaiting reply within 7 days</small></article></section>
    <form class="toolbar quotation-search" role="search"><input type="hidden" name="page" value="quotations"><input type="hidden" name="tab" value="<?=e($quoteTab)?>"><label>Search quotations<input type="search" name="q" value="<?=e($quoteSearch)?>" placeholder="Quotation number, reference, or customer"></label><button class="button secondary" type="submit">Search</button><?php if($quoteSearch!==''):?><a class="button secondary" href="index.php?page=quotations&tab=<?=e($quoteTab)?>">Clear</a><?php endif;?></form>
    <nav class="quotation-status-tabs quotation-tabs" aria-label="Quotation work queues"><?php foreach(['active'=>'Active','draft'=>'Drafts','awaiting'=>'Awaiting reply','accepted'=>'Accepted','expired'=>'Expired','closed'=>'Closed','all'=>'All'] as $key=>$label):?><a class="<?=$quoteTab===$key?'active':''?>" href="<?=e($quoteTabUrl($key))?>" <?=$quoteTab===$key?'aria-current="page"':''?>><?=e($label)?> <b><?=$quoteCounts[$key]?></b></a><?php endforeach;?></nav>
    <section class="panel invoice-register quotation-register"><div class="table-wrap"><table><caption class="sr-only">Soda ZW quotation workbench</caption><thead><tr><th scope="col">Status</th><th scope="col">Valid until</th><th scope="col">Quotation</th><th scope="col">Customer</th><th scope="col">Total</th><th scope="col">Next action</th></tr></thead><tbody>
    <?php foreach($rows as $r):?><?php $isExpired=$r['status']==='sent'&&!empty($r['due_date'])&&$r['due_date']<$today;$displayStatus=$isExpired?'expired':$r['status'];$validity='No expiry';if(!empty($r['due_date'])){$days=(int)floor((strtotime($r['due_date'])-strtotime($today))/86400);$validity=$isExpired?abs($days).' day'.(abs($days)===1?'':'s').' expired':($days===0?'Expires today':($days>0?'In '.$days.' day'.($days===1?'':'s'):date('j M Y',strtotime($r['due_date']))));}?>
      <tr><td><?=$isExpired?'<span class="status status-overdue">Expired</span>':status_badge($displayStatus)?></td><td class="<?=$isExpired?'due-late':''?>"><strong><?=e($validity)?></strong><br><small class="muted"><?=e(!empty($r['due_date'])?date('j M Y',strtotime($r['due_date'])):'Set when editing')?></small></td><td><a href="index.php?page=document-view&id=<?=$r['id']?>"><strong><?=e($r['number'])?></strong></a><br><small class="muted"><?=e(date('j M Y',strtotime($r['issue_date'])))?></small></td><td><strong><?=e($r['company_name']?:$r['customer'])?></strong><?php if($r['company_name']):?><br><small class="muted"><?=e($r['customer'])?></small><?php endif;?></td><td class="money-cell"><strong><?=money($r['total'],$r['currency_code'])?></strong></td><td class="actions"><div class="document-row-actions quote-row-actions"><a class="button secondary small" href="index.php?page=document-view&id=<?=$r['id']?>">Review</a><?php if($r['status']==='draft'):?><a class="button secondary small" href="index.php?page=document-edit&type=quotation&id=<?=$r['id']?>&customer_id=<?=$r['customer_id']?>">Edit</a><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="set_document_status"><input type="hidden" name="id" value="<?=$r['id']?>"><input type="hidden" name="status" value="sent"><button class="button primary small" type="submit">Issue</button></form><?php elseif($r['status']==='sent'&&!$isExpired):?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="set_document_status"><input type="hidden" name="id" value="<?=$r['id']?>"><input type="hidden" name="status" value="accepted"><button class="button primary small" type="submit">Accept</button></form><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="set_document_status"><input type="hidden" name="id" value="<?=$r['id']?>"><input type="hidden" name="status" value="rejected"><button class="button secondary small" type="submit" data-confirm="Mark this quotation as rejected?">Reject</button></form><?php elseif($r['status']==='accepted'):?><?php if(!empty($r['converted_invoice_id'])):?><a class="button primary small" href="index.php?page=document-view&id=<?=$r['converted_invoice_id']?>">Open invoice</a><?php else:?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="convert_quote"><input type="hidden" name="id" value="<?=$r['id']?>"><button class="button primary small" type="submit">Convert to invoice</button></form><?php endif;?><?php endif;?></div></td></tr>
    <?php endforeach;?><?php if(!$rows):?><tr><td colspan="6" class="empty"><strong>No quotations in this queue</strong><?=e($quoteSearch!==''?'Try another search or clear the filter.':'Create a quotation or choose another work queue.')?></td></tr><?php endif;?></tbody></table></div></section></div>
    <?php render_footer();break;
case 'document-view':
    $id=(int)($_GET['id']??0);$s=db()->prepare('SELECT d.*,c.name customer_name,c.company_name customer_company,c.email customer_email,c.phone customer_phone,c.whatsapp customer_whatsapp,c.address customer_address,c.city customer_city,c.tax_number customer_tax,qc.invoice_id converted_invoice_id,(SELECT COALESCE(SUM(p.amount),0) FROM payments p WHERE p.invoice_id=d.id)-(SELECT COALESCE(SUM(r.amount),0) FROM payment_reversals r JOIN payments rp ON rp.id=r.payment_id WHERE rp.invoice_id=d.id) paid,(SELECT COALESCE(SUM(ca.amount),0) FROM credit_note_applications ca WHERE ca.invoice_id=d.id) credited FROM documents d JOIN customers c ON c.id=d.customer_id LEFT JOIN quotation_conversions qc ON qc.quotation_id=d.id WHERE d.id=?');$s->execute([$id]);$d=$s->fetch();if(!$d){http_response_code(404);exit('Document not found.');}$i=db()->prepare('SELECT * FROM document_items WHERE document_id=? ORDER BY sort_order');$i->execute([$id]);$items=$i->fetchAll();$payments=[];$paymentAccounts=[];$paynowAttempt=null;if(in_array($d['type'],['invoice','quotation','credit_note'],true)&&empty($d['share_token'])){$d['share_token']=bin2hex(random_bytes(32));db()->prepare('UPDATE documents SET share_token=? WHERE id=?')->execute([$d['share_token'],$id]);}if($d['type']==='invoice'){$p=db()->prepare('SELECT p.*,COALESCE(a.name,p.method) account_name FROM payments p LEFT JOIN payment_accounts a ON a.id=p.account_id WHERE p.invoice_id=? ORDER BY p.payment_date DESC,p.id DESC');$p->execute([$id]);$payments=$p->fetchAll();$paymentAccounts=db()->query('SELECT * FROM payment_accounts WHERE active=1 ORDER BY currency_code,type,name')->fetchAll();$paynowStmt=db()->prepare('SELECT * FROM online_payment_attempts WHERE provider="paynow" AND invoice_id=? ORDER BY id DESC LIMIT 1');$paynowStmt->execute([$id]);$paynowAttempt=$paynowStmt->fetch()?:null;}$balance=max(0,(float)$d['total']-(float)$d['paid']-(float)$d['credited']);render_header($d['number'],$d['type']==='quotation'?'quotations':'invoices');?>
    <?php $isQuotation=$d['type']==='quotation';$isDraft=$d['status']==='draft';$isCancelled=$d['status']==='cancelled';$isIssued=$isQuotation?in_array($d['status'],['sent','accepted'],true):!in_array($d['status'],['draft','cancelled'],true);$sharePhone=preg_replace('/\D+/','',(string)($d['customer_whatsapp']?:$d['customer_phone']));if(str_starts_with($sharePhone,'0'))$sharePhone='263'.substr($sharePhone,1);elseif(strlen($sharePhone)===9&&str_starts_with($sharePhone,'7'))$sharePhone='263'.$sharePhone;$customerLink=$isIssued&&!empty($d['share_enabled'])&&!empty($d['share_token'])?rtrim((string)($config['app']['base_url']??'https://billing.sodazw.co.zw'),'/').'/share.php?token='.$d['share_token']:'';$shareText=rawurlencode('Hello '.$d['customer_name'].', your issued Soda ZW '.strtolower(document_label($d['type'])).' '.$d['number'].' is ready. Total: '.money($d['total'],$d['currency_code']).(!$isQuotation?'. Balance: '.money($balance,$d['currency_code']):'').($customerLink?'. View or save your secure copy here: '.$customerLink:'').'. Thank you.');$mailSubject=rawurlencode('Soda ZW '.document_label($d['type']).' '.$d['number']);$mailBody=rawurlencode('Hello '.$d['customer_name'].",\n\nYour issued Soda ZW ".strtolower(document_label($d['type'])).' '.$d['number'].' is ready. Total: '.money($d['total'],$d['currency_code']).(!$isQuotation?'. Balance: '.money($balance,$d['currency_code']):'').($customerLink?".\n\nView or save your secure copy here: ".$customerLink:'')."\n\nThank you,\nSoda ZW");?>
    <?php $paynowUrl=$paynowAttempt&&in_array($paynowAttempt['status'],['created','initiated','sent'],true)&&paynow_url_allowed((string)($paynowAttempt['browser_url']??''))?(string)$paynowAttempt['browser_url']:'';$paynowMessage=$paynowUrl?'Hello '.$d['customer_name'].', you can pay Soda ZW invoice '.$d['number'].' securely through Paynow. Amount due: '.money($balance,$d['currency_code']).'. Pay here: '.$paynowUrl.'. This link opens Paynow; please do not share it with anyone else.':'';$paynowSubject='Payment link for Soda ZW invoice '.$d['number'];?>
    <?php if(!$isQuotation&&$isIssued&&$balance>.009&&strtoupper((string)$d['currency_code'])==='USD'&&paynow_enabled()&&!$paynowUrl):?><form id="create-paynow-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="create_invoice_paynow_link"><input type="hidden" name="invoice_id" value="<?=$id?>"></form><?php endif;?>
    <?php if(!$isQuotation&&$balance>.009):?><section class="paynow-readiness" aria-label="Paynow payment availability"><div><span class="section-kicker">PAYNOW CHECKOUT</span><strong>Collect this invoice online</strong><small><?php if(!billing_schema_is_current()):?>Database upgrade required before Paynow invoice links can work.<?php elseif(!$isIssued):?>Issue this draft invoice first; payment links are never created for drafts.<?php elseif(strtoupper((string)$d['currency_code'])!=='USD'):?>This invoice is <?=e($d['currency_code'])?>. Paynow links are currently available for USD invoices.<?php elseif(!paynow_enabled()):?>Paynow configuration is incomplete. Ask the owner to confirm the Integration ID and rotated Integration Key in the protected config file.<?php elseif($paynowUrl):?>Payment link ready · <?=e(ucwords(str_replace('_',' ',(string)$paynowAttempt['status'])))?><?php else:?>Ready to create a secure link for <?=money($balance,$d['currency_code'])?>.<?php endif;?></small></div><?php if(!billing_schema_is_current()&&($u=user())&&$u['role']==='owner'):?><a class="button secondary" href="upgrade.php">Run database upgrade</a><?php elseif($isIssued&&strtoupper((string)$d['currency_code'])==='USD'&&paynow_enabled()):?><?php if($paynowUrl):?><a class="button primary" href="#paynow-payment">View and send Paynow link</a><?php else:?><button class="button primary" type="submit" form="create-paynow-<?=$id?>">Create Paynow payment link</button><?php endif;?><?php else:?><a class="button secondary" href="#paynow-payment">Why is it unavailable?</a><?php endif;?></section><?php endif;?>
    <?php if(!$isQuotation&&$isIssued&&$balance>.009):?><section class="panel paynow-invoice-panel" id="paynow-payment"><div class="panel-head"><div><span class="section-kicker">SECURE ONLINE PAYMENT</span><h2>Paynow invoice link</h2><p>Create a hosted payment link for the exact outstanding balance. Paynow confirms successful payment back to Billing automatically.</p></div><strong><?=money($balance,$d['currency_code'])?></strong></div><?php if(!paynow_enabled()):?><div class="alert error"><strong>Paynow is not enabled.</strong> The owner must add the Integration ID and a newly rotated Integration Key in the protected billing configuration.</div><?php elseif(strtoupper((string)$d['currency_code'])!=='USD'):?><div class="alert error">Paynow links are currently restricted to USD invoices.</div><?php elseif($paynowUrl):?><div class="paynow-link-box"><label>Customer payment link<input type="url" readonly value="<?=e($paynowUrl)?>" data-paynow-url></label><div class="button-row"><button class="button secondary" type="button" data-copy-paynow>Copy payment link</button><a class="button primary" target="_blank" rel="noopener noreferrer" href="<?=e($paynowUrl)?>">Open Paynow</a><?php if($sharePhone):?><a class="button green" target="_blank" rel="noopener" href="https://wa.me/<?=e($sharePhone)?>?text=<?=rawurlencode($paynowMessage)?>">Send on WhatsApp</a><?php endif;?><?php if($d['customer_email']):?><a class="button secondary" href="mailto:<?=e($d['customer_email'])?>?subject=<?=rawurlencode($paynowSubject)?>&body=<?=rawurlencode($paynowMessage."\n\nThank you,\nSoda ZW")?>">Send by email</a><?php endif;?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="refresh_invoice_paynow_status"><input type="hidden" name="invoice_id" value="<?=$id?>"><button class="button secondary" type="submit">Check payment status</button></form></div><small>Reference <?=e((string)$paynowAttempt['merchant_reference'])?> · Status <?=e(ucwords(str_replace('_',' ',(string)$paynowAttempt['status'])))?></small></div><?php else:?><form method="post" class="paynow-create-form"><?=csrf_field()?><input type="hidden" name="action" value="create_invoice_paynow_link"><input type="hidden" name="invoice_id" value="<?=$id?>"><button class="button primary" type="submit">Create secure Paynow link</button><small>The customer pays on Paynow’s hosted page. No card or wallet credentials are collected by Soda ZW Billing.</small></form><?php if($paynowAttempt&&$paynowAttempt['status']==='failed'&&!empty($paynowAttempt['error_message'])):?><p class="paynow-error"><?=e((string)$paynowAttempt['error_message'])?></p><?php endif;?><?php endif;?></section><?php endif;?>
    <?php if($isDraft):?><form id="issue-document-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="set_document_status"><input type="hidden" name="id" value="<?=$id?>"><input type="hidden" name="status" value="sent"></form><form id="cancel-document-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="set_document_status"><input type="hidden" name="id" value="<?=$id?>"><input type="hidden" name="status" value="cancelled"></form><?php endif;?>
    <?php if($isQuotation&&$d['status']==='sent'):?><form id="accept-quotation-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="set_document_status"><input type="hidden" name="id" value="<?=$id?>"><input type="hidden" name="status" value="accepted"></form><form id="reject-quotation-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="set_document_status"><input type="hidden" name="id" value="<?=$id?>"><input type="hidden" name="status" value="rejected"></form><form id="cancel-quotation-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="set_document_status"><input type="hidden" name="id" value="<?=$id?>"><input type="hidden" name="status" value="cancelled"></form><?php endif;?>
    <?php if($isQuotation&&$d['status']==='accepted'&&empty($d['converted_invoice_id'])):?><form id="convert-quotation-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="convert_quote"><input type="hidden" name="id" value="<?=$id?>"></form><?php endif;?>
    <?php if(!$isQuotation&&$isIssued):?><form id="toggle-share-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="toggle_invoice_share"><input type="hidden" name="id" value="<?=$id?>"><input type="hidden" name="enabled" value="<?=$d['share_enabled']?'0':'1'?>"></form><?php if(empty($d['stock_processed_at'])):?><form id="fulfil-invoice-<?=$id?>" method="post" hidden><?=csrf_field()?><input type="hidden" name="action" value="fulfil_invoice"><input type="hidden" name="id" value="<?=$id?>"></form><?php endif;?><?php endif;?>
    <div class="doc-actions document-state-actions" aria-label="<?=e(document_label($d['type']))?> actions"><a class="button secondary" target="_blank" rel="noopener" href="print.php?id=<?=$id?>">Print / Save PDF</a><?php if($isDraft):?><a class="button secondary" href="index.php?page=document-edit&type=<?=e($d['type'])?>&id=<?=$id?>&customer_id=<?=$d['customer_id']?>">Edit draft</a><button class="button primary" type="submit" form="issue-document-<?=$id?>">Issue <?=e(strtolower(document_label($d['type'])))?></button><button class="button danger" type="submit" form="cancel-document-<?=$id?>" data-confirm="Cancel this draft <?=e(strtolower(document_label($d['type'])))?>?">Cancel draft</button><?php elseif($isIssued):?><?php if($customerLink):?><a class="button secondary" target="_blank" rel="noopener" href="<?=e($customerLink)?>">Open secure customer copy</a><?php endif;?><?php if($isQuotation||$customerLink):?><?php if($sharePhone):?><a class="button green" target="_blank" rel="noopener" href="https://wa.me/<?=e($sharePhone)?>?text=<?=$shareText?>">Send issued <?=e(strtolower(document_label($d['type'])))?></a><?php endif;?><?php if($d['customer_email']):?><a class="button secondary" href="mailto:<?=e($d['customer_email'])?>?subject=<?=$mailSubject?>&body=<?=$mailBody?>">Email issued <?=e(strtolower(document_label($d['type'])))?></a><?php endif;?><?php endif;?><?php if(!$isQuotation):?><button class="button secondary" type="submit" form="toggle-share-<?=$id?>"><?=$d['share_enabled']?'Disable secure link':'Enable secure link'?></button><?php if($balance>.009):?><a class="button command-payment" href="receive-payment.php?invoice_id=<?=$id?>">Receive payment</a><?php endif;?><?php if(empty($d['stock_processed_at'])):?><button class="button primary" type="submit" form="fulfil-invoice-<?=$id?>" data-confirm="Confirm this order has been supplied? Tracked stock will be deducted once.">Fulfil order &amp; deduct stock</button><?php endif;?><?php elseif($d['status']==='sent'):?><button class="button primary" type="submit" form="accept-quotation-<?=$id?>">Mark accepted</button><button class="button secondary" type="submit" form="reject-quotation-<?=$id?>" data-confirm="Mark this quotation as rejected?">Mark rejected</button><button class="button danger" type="submit" form="cancel-quotation-<?=$id?>" data-confirm="Cancel this issued quotation?">Cancel quotation</button><?php elseif($d['status']==='accepted'):?><?php if(!empty($d['converted_invoice_id'])):?><a class="button primary" href="index.php?page=document-view&id=<?=$d['converted_invoice_id']?>">Open converted invoice</a><?php else:?><button class="button primary" type="submit" form="convert-quotation-<?=$id?>">Convert to invoice</button><?php endif;?><?php endif;?><?php endif;?></div>
    <article class="document-card"><div class="document-head"><div><img src="assets/sodazw-logo-invoice.png" alt="Soda ZW"><p><strong>Soda ZW</strong><br><?=nl2br(e(settings()['address']."\n".settings()['city']."\n".settings()['country']))?><br><?=e(settings()['phone'])?></p></div><div class="document-title"><h2><?=e(document_label($d['type']))?></h2><strong><?=e($d['number'])?></strong><p><?=status_badge($d['status'])?></p></div></div><div class="document-meta"><div><small class="muted">BILL TO</small><h3><?=e($d['customer_name'])?></h3><p><?=e($d['customer_company'])?><br><?=e($d['customer_address'])?><br><?=e($d['customer_city'])?><br><?=e($d['customer_phone'])?></p></div><div class="text-right"><p><b>Issue date:</b> <?=e($d['issue_date'])?><br><b><?=$d['type']==='quotation'?'Valid until':'Due date'?>:</b> <?=e($d['due_date'])?><br><b>Currency:</b> <?=e($d['currency_code'])?><?php if($d['customer_reference']):?><br><b>Source / reference:</b> <?=e($d['customer_reference'])?><?php endif;?></p></div></div><table class="document-items"><thead><tr><th>Description</th><th>Qty</th><th>Unit</th><th>Tax</th><th>Total</th></tr></thead><tbody><?php foreach($items as $it):?><tr><td><?=e($it['description'])?></td><td><?=e($it['quantity'])?></td><td><?=money($it['unit_price'],$d['currency_code'])?></td><td><?=e($it['tax_rate'])?>%</td><td><?=money($it['line_total'],$d['currency_code'])?></td></tr><?php endforeach;?></tbody></table><div class="document-summary"><div><span>Subtotal</span><b><?=money($d['subtotal'],$d['currency_code'])?></b></div><?php if((float)$d['discount_total']>0):?><div><span>Discount</span><b>-<?=money($d['discount_total'],$d['currency_code'])?></b></div><?php endif;?><?php if((float)$d['tax_total']>0):?><div><span>Tax</span><b><?=money($d['tax_total'],$d['currency_code'])?></b></div><?php endif;?><?php if((float)$d['delivery_fee']>0):?><div><span>Delivery</span><b><?=money($d['delivery_fee'],$d['currency_code'])?></b></div><?php endif;?><div class="total"><span>Total</span><b><?=money($d['total'],$d['currency_code'])?></b></div><?php if($d['type']==='invoice'):?><div><span>Paid</span><b><?=money($d['paid'],$d['currency_code'])?></b></div><div><span>Balance</span><b><?=money($balance,$d['currency_code'])?></b></div><?php endif;?></div><?php if($d['notes']):?><h4>Notes</h4><p><?=nl2br(e($d['notes']))?></p><?php endif;?><?php if($d['terms']):?><h4>Terms</h4><p class="muted"><?=nl2br(e($d['terms']))?></p><?php endif;?><p><?=nl2br(e(settings()['payment_instructions']))?></p></article>
    <?php if($d['type']==='invoice'):?><section class="panel" style="margin-top:20px"><div class="panel-head"><h2>Payments received</h2><strong>Outstanding: <?=money($balance,$d['currency_code'])?></strong></div><?php if($payments):?><div class="table-wrap"><table><thead><tr><th>Date</th><th>Received through</th><th>Reference</th><th>Received</th><th>Applied</th></tr></thead><tbody><?php foreach($payments as $p):?><tr><td><?=e($p['payment_date'])?></td><td><strong><?=e($p['account_name'])?></strong></td><td><?=e($p['reference'])?></td><td><?=e($p['received_currency']?:$d['currency_code'])?> <?=number_format((float)($p['received_amount']?:$p['amount']),2)?></td><td><?=money($p['amount'],$d['currency_code'])?></td></tr><?php endforeach;?></tbody></table></div><?php endif;?><?php if($balance>0&&$isIssued):?><form method="post" class="form-grid payment-form" style="margin-top:18px"><?=csrf_field()?><input type="hidden" name="action" value="record_payment"><input type="hidden" name="invoice_id" value="<?=$id?>"><input type="hidden" data-invoice-currency value="<?=e($d['currency_code'])?>"><label>Payment date<input type="date" name="payment_date" value="<?=date('Y-m-d')?>" required></label><label>Received through<select name="account_id" data-payment-account required><option value="">Select payment account</option><?php foreach($paymentAccounts as $a):?><option value="<?=$a['id']?>" data-currency="<?=e($a['currency_code'])?>"><?=e($a['name'])?> · <?=e($a['currency_code'])?></option><?php endforeach;?></select></label><label>Amount received<input type="number" step=".01" min=".01" name="received_amount" value="<?=number_format($balance,2,'.','')?>" data-received required></label><label>Exchange rate<input type="number" step=".000001" min=".000001" name="exchange_rate" value="1" data-exchange-rate required><small>Payment currency units for 1 <?=e($d['currency_code'])?></small></label><label>Transaction fee<input type="number" step=".01" min="0" name="fee_amount" value="0"></label><label>Reference / approval code<input name="reference" placeholder="EcoCash approval, bank reference…"></label><label class="full">Notes<input name="notes"></label><div class="payment-preview full">Applied to invoice: <strong><?=e($d['currency_code'])?> <span data-applied><?=number_format($balance,2)?></span></strong></div><button class="button green">Record payment received</button></form><?php endif;?></section><?php endif;?><?php render_footer();break;

case 'payments':
    $rows=db()->query('SELECT p.*,d.number,c.name customer,d.currency_code,COALESCE(a.name,p.method) account_name FROM payments p JOIN documents d ON d.id=p.invoice_id JOIN customers c ON c.id=d.customer_id LEFT JOIN payment_accounts a ON a.id=p.account_id ORDER BY p.payment_date DESC,p.id DESC')->fetchAll();render_header('Payments','payments');?><section class="panel"><div class="panel-head"><div><h2>Payment register</h2><p>Cash, EcoCash, bank, card and mobile-money receipts.</p></div></div><div class="table-wrap"><table><thead><tr><th>Date</th><th>Invoice</th><th>Customer</th><th>Channel</th><th>Reference</th><th>Received</th><th>Applied</th></tr></thead><tbody><?php foreach($rows as $r):?><tr><td><?=e($r['payment_date'])?></td><td><a href="index.php?page=document-view&id=<?=$r['invoice_id']?>"><?=e($r['number'])?></a></td><td><?=e($r['customer'])?></td><td><?=e($r['account_name'])?></td><td><?=e($r['reference'])?></td><td><?=e($r['received_currency']?:$r['currency_code'])?> <?=number_format((float)($r['received_amount']?:$r['amount']),2)?></td><td><?=money($r['amount'],$r['currency_code'])?></td></tr><?php endforeach;?><?php if(!$rows):?><tr><td colspan="7" class="empty">No payments recorded yet.</td></tr><?php endif;?></tbody></table></div></section><?php render_footer();break;

case 'suppliers':
    $edit=null;if($id=(int)($_GET['edit']??0)){$s=db()->prepare('SELECT * FROM suppliers WHERE id=?');$s->execute([$id]);$edit=$s->fetch();}$q=trim($_GET['q']??'');$like='%'.$q.'%';$s=db()->prepare('SELECT s.*,COUNT(DISTINCT b.id) bills FROM suppliers s LEFT JOIN bills b ON b.supplier_id=s.id WHERE s.active=1 AND (s.name LIKE ? OR s.contact_name LIKE ? OR s.phone LIKE ?) GROUP BY s.id ORDER BY s.name');$s->execute([$like,$like,$like]);$rows=$s->fetchAll();$supplierExposure=[];$payableRows=db()->query('SELECT b.supplier_id,b.currency_code,b.total,COALESCE(SUM(bp.amount),0) paid FROM bills b LEFT JOIN bill_payments bp ON bp.bill_id=b.id WHERE b.status<>"cancelled" GROUP BY b.id')->fetchAll();foreach($payableRows as $pr){$balance=max(0,(float)$pr['total']-(float)$pr['paid']);if($balance>.009)$supplierExposure[$pr['supplier_id']][$pr['currency_code']]=($supplierExposure[$pr['supplier_id']][$pr['currency_code']]??0)+$balance;}render_header('Suppliers','suppliers');?>
    <div class="page-actions"><div><h2 class="register-title">Suppliers</h2><p class="muted">Businesses and people Soda ZW purchases from.</p></div><a class="button primary" href="index.php?page=suppliers&edit=new">+ Add supplier</a></div><form class="customer-search"><input type="hidden" name="page" value="suppliers"><input name="q" value="<?=e($q)?>" placeholder="Search suppliers"><button class="button secondary">Search</button><span><b><?=count($rows)?></b> suppliers found</span></form>
    <?php if(isset($_GET['edit'])):?><section class="panel"><div class="panel-head"><h2><?=$edit?'Edit supplier':'Add supplier'?></h2><a class="button secondary small" href="index.php?page=suppliers">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_supplier"><input type="hidden" name="id" value="<?=e($edit['id']??'')?>"><label>Supplier name<input name="name" value="<?=e($edit['name']??'')?>" required></label><label>Contact person<input name="contact_name" value="<?=e($edit['contact_name']??'')?>"></label><label>Email<input type="email" name="email" value="<?=e($edit['email']??'')?>"></label><label>Phone<input name="phone" value="<?=e($edit['phone']??'')?>"></label><label>Tax/VAT number<input name="tax_number" value="<?=e($edit['tax_number']??'')?>"></label><label class="full">Address<input name="address" value="<?=e($edit['address']??'')?>"></label><label class="full">Notes<textarea name="notes" rows="3"><?=e($edit['notes']??'')?></textarea></label><button class="button primary">Save supplier</button></form></section><?php endif;?>
    <section class="panel customer-register"><div class="table-wrap"><table><thead><tr><th>Supplier</th><th>Contact</th><th>Bills</th><th>Payable</th><th>Actions</th></tr></thead><tbody><?php foreach($rows as $r):?><tr><td><strong><?=e($r['name'])?></strong><br><small class="muted"><?=e($r['contact_name'])?></small></td><td><?=e($r['phone']?:'—')?><br><small><?=e($r['email'])?></small></td><td><?=$r['bills']?></td><td><?php if(!empty($supplierExposure[$r['id']])):?><?php foreach($supplierExposure[$r['id']] as $cur=>$amount):?><span class="customer-balance"><b><?=e($cur)?> <?=number_format($amount,2)?></b></span><?php endforeach;?><?php else:?><span class="paid-clear">No balance</span><?php endif;?></td><td class="actions"><a class="button secondary small" href="index.php?page=bills&supplier_id=<?=$r['id']?>">Bills</a><a class="button secondary small" href="index.php?page=suppliers&edit=<?=$r['id']?>">Edit</a></td></tr><?php endforeach;?><?php if(!$rows):?><tr><td colspan="5" class="empty">No suppliers found.</td></tr><?php endif;?></tbody></table></div></section><?php render_footer();break;

case 'bills':
    $supplierId=(int)($_GET['supplier_id']??0);$status=trim($_GET['status']??'');$where=['1=1'];$params=[];if($supplierId){$where[]='b.supplier_id=?';$params[]=$supplierId;}if($status){$where[]='b.status=?';$params[]=$status;}$s=db()->prepare('SELECT b.*,s.name supplier,COALESCE(SUM(bp.amount),0) paid,DATEDIFF(CURDATE(),b.due_date) due_days FROM bills b JOIN suppliers s ON s.id=b.supplier_id LEFT JOIN bill_payments bp ON bp.bill_id=b.id WHERE '.implode(' AND ',$where).' GROUP BY b.id ORDER BY b.due_date,b.id DESC');$s->execute($params);$rows=$s->fetchAll();$suppliers=db()->query('SELECT id,name FROM suppliers WHERE active=1 ORDER BY name')->fetchAll();$accounts=db()->query('SELECT * FROM payment_accounts WHERE active=1 ORDER BY currency_code,name')->fetchAll();$totals=[];foreach($rows as $r){$balance=max(0,(float)$r['total']-(float)$r['paid']);$totals[$r['currency_code']]=($totals[$r['currency_code']]??0)+$balance;}render_header('Bills & payables','bills');?>
    <div class="page-actions"><div><h2 class="register-title">Bills & payables</h2><p class="muted">Track supplier bills, due dates and outgoing payments.</p></div><a class="button primary" href="index.php?page=bills&new=1">+ Add a bill</a></div><section class="payable-strip"><div><span>Total payable</span><strong><?php if($totals):?><?php foreach($totals as $cur=>$value):?><?=e($cur)?> <?=number_format($value,2)?> <?php endforeach;?><?php else:?>USD 0.00<?php endif;?></strong></div><div><span>Open bills</span><strong><?=count(array_filter($rows,fn($r)=>!in_array($r['status'],['paid','cancelled'],true)))?></strong></div><div><span>Overdue bills</span><strong><?=count(array_filter($rows,fn($r)=>$r['due_days']>0&&!in_array($r['status'],['paid','cancelled'],true)))?></strong></div></section>
    <form class="invoice-filters bill-filters"><input type="hidden" name="page" value="bills"><label>Supplier<select name="supplier_id"><option value="">All suppliers</option><?php foreach($suppliers as $sp):?><option value="<?=$sp['id']?>" <?=$supplierId===$sp['id']?'selected':''?>><?=e($sp['name'])?></option><?php endforeach;?></select></label><label>Status<select name="status"><option value="">All statuses</option><?php foreach(['unpaid','partial','paid','cancelled'] as $st):?><option value="<?=$st?>" <?=$status===$st?'selected':''?>><?=e(ucfirst($st))?></option><?php endforeach;?></select></label><button class="button secondary">Apply filters</button></form>
    <?php if(isset($_GET['new'])):?><section class="panel"><div class="panel-head"><h2>Record supplier bill</h2><a class="button secondary small" href="index.php?page=bills">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_bill"><label>Supplier<select name="supplier_id" required><option value="">Select supplier</option><?php foreach($suppliers as $sp):?><option value="<?=$sp['id']?>"><?=e($sp['name'])?></option><?php endforeach;?></select></label><label>Supplier bill number<input name="number" required></label><label>Bill date<input type="date" name="bill_date" value="<?=date('Y-m-d')?>" required></label><label>Due date<input type="date" name="due_date" value="<?=date('Y-m-d',strtotime('+14 days'))?>" required></label><label>Currency<select name="currency_code"><option>USD</option><option value="ZWG">ZiG (ZWG)</option></select></label><label>Total<input type="number" step=".01" min=".01" name="total" required></label><label class="full">Description<input name="description" placeholder="Stock, delivery, rent, services…" required></label><label>Reference<input name="reference"></label><label class="full">Notes<textarea name="notes" rows="3"></textarea></label><button class="button primary">Save bill</button></form></section><?php endif;?>
    <?php if($payId=(int)($_GET['pay']??0)):?><?php $bill=array_values(array_filter($rows,fn($r)=>(int)$r['id']===$payId))[0]??null;?><?php if($bill):?><section class="panel payment-panel"><div class="panel-head"><div><h2>Pay <?=e($bill['number'])?></h2><p><?=e($bill['supplier'])?> · outstanding <?=money(max(0,(float)$bill['total']-(float)$bill['paid']),$bill['currency_code'])?></p></div><a class="button secondary small" href="index.php?page=bills">Close</a></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="record_bill_payment"><input type="hidden" name="bill_id" value="<?=$bill['id']?>"><label>Payment date<input type="date" name="payment_date" value="<?=date('Y-m-d')?>" required></label><label>Paid from<select name="account_id" required><option value="">Select <?=e($bill['currency_code'])?> account</option><?php foreach($accounts as $a):?><?php if($a['currency_code']===$bill['currency_code']):?><option value="<?=$a['id']?>"><?=e($a['name'])?></option><?php endif;?><?php endforeach;?></select></label><label>Amount<input type="number" step=".01" min=".01" max="<?=e((string)max(0,(float)$bill['total']-(float)$bill['paid']))?>" name="amount" required></label><label>Reference<input name="reference" placeholder="Bank or EcoCash reference"></label><label class="full">Notes<input name="notes"></label><button class="button green">Record bill payment</button></form></section><?php endif;?><?php endif;?>
    <section class="panel invoice-register"><div class="table-wrap"><table><thead><tr><th>Status</th><th>Due</th><th>Supplier</th><th>Bill number</th><th>Description</th><th>Total</th><th>Balance</th><th>Actions</th></tr></thead><tbody><?php foreach($rows as $r):?><?php $balance=max(0,(float)$r['total']-(float)$r['paid']);?><tr><td><?=status_badge($r['status'])?></td><td class="<?=$r['due_days']>0&&$balance>.009?'due-late':''?>"><?=e($r['due_date'])?><?php if($r['due_days']>0&&$balance>.009):?><br><small><?=$r['due_days']?> days overdue</small><?php endif;?></td><td><strong><?=e($r['supplier'])?></strong></td><td><?=e($r['number'])?></td><td><?=e($r['description'])?></td><td><?=money($r['total'],$r['currency_code'])?></td><td><strong><?=money($balance,$r['currency_code'])?></strong></td><td class="actions"><?php if($balance>.009&&$r['status']!=='cancelled'):?><a class="button green small" href="index.php?page=bills&pay=<?=$r['id']?>">Record payment</a><?php else:?><span class="paid-clear">Settled</span><?php endif;?></td></tr><?php endforeach;?><?php if(!$rows):?><tr><td colspan="8" class="empty">No supplier bills found.</td></tr><?php endif;?></tbody></table></div></section><?php render_footer();break;

case 'expenses':
    require_permission('expenses.manage');
    $rows=db()->query('SELECT * FROM expenses ORDER BY expense_date DESC,id DESC LIMIT 250')->fetchAll();render_header('Expenses','expenses');?><section class="panel"><div class="panel-head"><div><h2>Record operating expense</h2><p>Expenses support a more useful profit summary.</p></div></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_expense"><label>Date<input type="date" name="expense_date" value="<?=date('Y-m-d')?>" required></label><label>Category<input name="category" placeholder="Delivery, stock, marketing…" required></label><label>Supplier<input name="supplier"></label><label>Amount<input type="number" step=".01" min=".01" name="amount" required></label><label class="full">Description<input name="description" required></label><label>Reference<input name="reference"></label><button class="button primary">Save expense</button></form></section><section class="panel"><table><thead><tr><th>Date</th><th>Category</th><th>Description</th><th>Supplier</th><th>Amount</th></tr></thead><tbody><?php foreach($rows as $r):?><tr><td><?=e($r['expense_date'])?></td><td><?=e($r['category'])?></td><td><?=e($r['description'])?></td><td><?=e($r['supplier'])?></td><td><?=money($r['amount'])?></td></tr><?php endforeach;?></tbody></table></section><?php render_footer();break;

case 'statement':
    {
        $customerId=(int)($_GET['customer_id']??0);$statementType=in_array($_GET['type']??'outstanding',['outstanding','all'],true)?$_GET['type']:'outstanding';$from=trim($_GET['from']??'');$to=trim($_GET['to']??'');
        $customers=db()->query('SELECT id,name FROM customers WHERE active=1 ORDER BY name')->fetchAll();$customer=null;$rows=[];$totals=[];
        if($customerId){
            $s=db()->prepare('SELECT * FROM customers WHERE id=? AND active=1');$s->execute([$customerId]);$customer=$s->fetch();
            if($customer){$params=[$customerId];$dateSql='';if($from!==''){$dateSql.=' AND d.issue_date>=?';$params[]=$from;}if($to!==''){$dateSql.=' AND d.issue_date<=?';$params[]=$to;}$s=db()->prepare('SELECT d.*,'.finance_document_balance_sql('d').' balance,COALESCE((SELECT SUM(p.amount) FROM payments p WHERE p.invoice_id=d.id),0)-COALESCE((SELECT SUM(r.amount) FROM payment_reversals r JOIN payments px ON px.id=r.payment_id WHERE px.invoice_id=d.id),0) paid,COALESCE((SELECT SUM(ca.amount) FROM credit_note_applications ca WHERE ca.invoice_id=d.id),0) credited FROM documents d WHERE d.customer_id=? AND d.type="invoice" AND d.status<>"cancelled"'.$dateSql.' ORDER BY d.issue_date,d.id');$s->execute($params);$rows=$s->fetchAll();if($statementType==='outstanding')$rows=array_values(array_filter($rows,fn($r)=>(float)$r['balance']>.009));foreach($rows as $r){$cur=$r['currency_code'];if(!isset($totals[$cur]))$totals[$cur]=['invoiced'=>0.0,'paid'=>0.0,'balance'=>0.0];$totals[$cur]['invoiced']+=(float)$r['total'];$totals[$cur]['paid']+=(float)$r['paid']+(float)$r['credited'];$totals[$cur]['balance']+=(float)$r['balance'];}}
        }
        $formatStatementTotals=function(string $key)use($totals):string{if(!$totals)return 'USD 0.00';$parts=[];foreach($totals as $currency=>$values)$parts[]=e($currency).' '.number_format($values[$key],2);return implode(' + ',$parts);};
        render_header('Customer statements','statement');?>
        <div class="page-actions"><div><h2 class="register-title">Customer statements</h2><p class="muted">Keep customers informed about invoices, payments and balances.</p></div></div>
        <form class="statement-builder"><input type="hidden" name="page" value="statement"><label>Customer<select name="customer_id" required><option value="">Select a customer</option><?php foreach($customers as $c):?><option value="<?=$c['id']?>" <?=$customerId===$c['id']?'selected':''?>><?=e($c['name'])?></option><?php endforeach;?></select></label><label>Statement type<select name="type"><option value="outstanding" <?=$statementType==='outstanding'?'selected':''?>>Outstanding invoices</option><option value="all" <?=$statementType==='all'?'selected':''?>>All invoice activity</option></select></label><label>From (optional)<input type="date" name="from" value="<?=e($from)?>"></label><label>To (optional)<input type="date" name="to" value="<?=e($to)?>"></label><button class="button primary">Create statement</button></form>
        <?php if($customer):?>
            <?php $phone=preg_replace('/\D+/','',$customer['phone']??'');if(str_starts_with($phone,'0'))$phone='263'.substr($phone,1);$shareMessage=rawurlencode('Hello '.$customer['name'].', your Soda ZW statement balance is '.$formatStatementTotals('balance').'. Please contact us if you need a copy or have any questions.');$emailSubject=rawurlencode('Soda ZW customer statement');$emailBody=rawurlencode('Hello '.$customer['name'].', your current Soda ZW statement balance is '.$formatStatementTotals('balance').'.');?>
            <div class="doc-actions statement-actions"><button type="button" class="button secondary" onclick="window.print()">Print / Save PDF</button><?php if($phone):?><a class="button green" target="_blank" rel="noopener" href="https://wa.me/<?=e($phone)?>?text=<?=$shareMessage?>">Share via WhatsApp</a><?php endif;?><?php if($customer['email']):?><a class="button secondary" href="mailto:<?=e($customer['email'])?>?subject=<?=$emailSubject?>&body=<?=$emailBody?>">Send by email</a><?php endif;?></div>
            <section class="statement-summary"><?php foreach($totals?:[settings()['currency_code']=>['invoiced'=>0,'paid'=>0,'balance'=>0]] as $currency=>$values):?><article><span><?=e($currency)?> invoiced</span><strong><?=e($currency)?> <?=number_format($values['invoiced'],2)?></strong></article><article><span><?=e($currency)?> paid</span><strong><?=e($currency)?> <?=number_format($values['paid'],2)?></strong></article><article class="balance"><span><?=e($currency)?> outstanding</span><strong><?=e($currency)?> <?=number_format($values['balance'],2)?></strong></article><?php endforeach;?></section>
            <section class="document-card statement-document"><div class="document-head"><div><img src="assets/sodazw-logo.png" alt="Soda ZW"><p><?=nl2br(e(settings()['address']."\n".settings()['city']."\n".settings()['country']))?><br><?=e(settings()['phone'])?></p></div><div class="document-title"><h2>Statement</h2><strong><?=e($statementType==='outstanding'?'Outstanding invoices':'Account activity')?></strong><p>As at <?=date('d M Y')?></p></div></div><div class="document-meta"><div><small class="muted">STATEMENT FOR</small><h3><?=e($customer['name'])?></h3><p><?=e($customer['company_name'])?><br><?=e($customer['address'])?><br><?=e($customer['city'])?><br><?=e($customer['phone'])?></p></div><div class="text-right"><b>Statement period</b><p><?=e($from?:'Beginning')?> to <?=e($to?:date('Y-m-d'))?></p><b>Outstanding balance</b><h3><?=$formatStatementTotals('balance')?></h3></div></div><div class="table-wrap"><table class="document-items"><thead><tr><th>Date</th><th>Invoice</th><th>Due date</th><th>Status</th><th>Total</th><th>Paid / credits</th><th>Balance</th></tr></thead><tbody><?php foreach($rows as $r):?><?php $balance=(float)$r['balance'];?><tr><td><?=e($r['issue_date'])?></td><td><?=e($r['number'])?></td><td><?=e($r['due_date'])?></td><td><?=e(ucfirst($r['status']))?></td><td><?=money($r['total'],$r['currency_code'])?></td><td><?=money((float)$r['paid']+(float)$r['credited'],$r['currency_code'])?></td><td><strong><?=money($balance,$r['currency_code'])?></strong></td></tr><?php endforeach;?><?php if(!$rows):?><tr><td colspan="7" class="empty">No invoices match this statement.</td></tr><?php endif;?></tbody></table></div><p class="statement-note">Thank you for choosing Soda ZW. Please quote the invoice number when making or querying a payment.</p></section>
        <?php else:?><section class="statement-empty"><div class="statement-art">S</div><h2>Keep customers informed</h2><p>Select a customer above to create a clear, branded statement of their invoices and payments.</p><ol><li>Select the customer and statement type.</li><li>Review the invoice balances.</li><li>Print, save as PDF, or share the balance through WhatsApp.</li></ol></section><?php endif;?>
        <?php render_footer();break;
    }
    $customerId=(int)($_GET['customer_id']??0);$customers=db()->query('SELECT id,name FROM customers WHERE active=1 ORDER BY name')->fetchAll();$customer=null;$rows=[];$total=0;$paid=0;if($customerId){$s=db()->prepare('SELECT * FROM customers WHERE id=?');$s->execute([$customerId]);$customer=$s->fetch();$s=db()->prepare('SELECT d.*,COALESCE(SUM(p.amount),0) paid FROM documents d LEFT JOIN payments p ON p.invoice_id=d.id WHERE d.customer_id=? AND d.type="invoice" AND d.status<>"cancelled" GROUP BY d.id ORDER BY d.issue_date');$s->execute([$customerId]);$rows=$s->fetchAll();foreach($rows as $r){$total+=(float)$r['total'];$paid+=(float)$r['paid'];}}render_header('Customer statement','customers');?><form class="toolbar"><input type="hidden" name="page" value="statement"><select name="customer_id" onchange="this.form.submit()"><option value="">Select customer</option><?php foreach($customers as $c):?><option value="<?=$c['id']?>" <?=$customerId===$c['id']?'selected':''?>><?=e($c['name'])?></option><?php endforeach;?></select><?php if($customer):?><button type="button" class="button secondary" onclick="window.print()">Print / Save PDF</button><?php endif;?></form><?php if($customer):?><section class="document-card"><div class="document-head"><img src="assets/sodazw-logo.png" alt="Soda ZW"><div class="document-title"><h2>Statement</h2><strong><?=e($customer['name'])?></strong><p>As at <?=date('d M Y')?></p></div></div><div class="document-meta"><div><b><?=e($customer['company_name'])?></b><p><?=e($customer['address'])?><br><?=e($customer['city'])?><br><?=e($customer['phone'])?></p></div><div class="text-right"><b>Outstanding balance</b><h2><?=money(max(0,$total-$paid))?></h2></div></div><table class="document-items"><thead><tr><th>Date</th><th>Invoice</th><th>Status</th><th>Total</th><th>Paid</th><th>Balance</th></tr></thead><tbody><?php foreach($rows as $r):?><tr><td><?=e($r['issue_date'])?></td><td><?=e($r['number'])?></td><td><?=e($r['status'])?></td><td><?=money($r['total'])?></td><td><?=money($r['paid'])?></td><td><?=money(max(0,(float)$r['total']-(float)$r['paid']))?></td></tr><?php endforeach;?></tbody></table></section><?php else:?><section class="panel empty"><strong>Select a customer</strong>Choose a customer to generate their full statement.</section><?php endif;?><?php render_footer();break;

case 'reports':
    $from=$_GET['from']??date('Y-01-01');$to=$_GET['to']??date('Y-m-d');$s=db()->prepare('SELECT COALESCE(SUM(total),0) FROM documents WHERE type="invoice" AND status<>"cancelled" AND issue_date BETWEEN ? AND ?');$s->execute([$from,$to]);$sales=(float)$s->fetchColumn();$s=db()->prepare('SELECT COALESCE(SUM(amount),0) FROM payments WHERE payment_date BETWEEN ? AND ?');$s->execute([$from,$to]);$received=(float)$s->fetchColumn();$s=db()->prepare('SELECT COALESCE(SUM(amount),0) FROM expenses WHERE expense_date BETWEEN ? AND ?');$s->execute([$from,$to]);$expenses=(float)$s->fetchColumn();$s=db()->prepare('SELECT DATE_FORMAT(issue_date,"%Y-%m") month,SUM(total) total FROM documents WHERE type="invoice" AND status<>"cancelled" AND issue_date BETWEEN ? AND ? GROUP BY month ORDER BY month');$s->execute([$from,$to]);$monthly=$s->fetchAll();$max=max(array_column($monthly,'total')?:[1]);$top=db()->prepare('SELECT p.name,SUM(i.quantity) quantity,SUM(i.line_total) sales FROM document_items i JOIN documents d ON d.id=i.document_id LEFT JOIN products p ON p.id=i.product_id WHERE d.type="invoice" AND d.status<>"cancelled" AND d.issue_date BETWEEN ? AND ? GROUP BY i.product_id,i.description ORDER BY sales DESC LIMIT 8');$top->execute([$from,$to]);render_header('Sales reports','reports');?>
    <form class="toolbar"><input type="hidden" name="page" value="reports"><input type="date" name="from" value="<?=e($from)?>"><input type="date" name="to" value="<?=e($to)?>"><button class="button secondary">Apply</button><a class="button primary" href="index.php?page=reports&export=sales&from=<?=e($from)?>&to=<?=e($to)?>">Export CSV</a></form><section class="stats"><article class="stat"><span>Invoiced sales</span><strong><?=money($sales)?></strong></article><article class="stat"><span>Payments received</span><strong><?=money($received)?></strong></article><article class="stat"><span>Operating expenses</span><strong><?=money($expenses)?></strong></article><article class="stat"><span>Cash contribution</span><strong><?=money($received-$expenses)?></strong><small>Payments less recorded expenses</small></article></section><section class="dashboard-grid"><div class="panel"><div class="panel-head"><h2>Monthly invoiced sales</h2></div><div class="bar-chart"><?php foreach($monthly as $m):?><div style="height:<?=max(4,round((float)$m['total']/$max*100))?>%" title="<?=money($m['total'])?>"><span><?=e(substr($m['month'],5))?></span></div><?php endforeach;?></div></div><div class="panel"><div class="panel-head"><h2>Top products</h2></div><table><thead><tr><th>Product</th><th>Qty</th><th>Sales</th></tr></thead><tbody><?php foreach($top as $r):?><tr><td><?=e($r['name']?:'Custom item')?></td><td><?=e($r['quantity'])?></td><td><?=money($r['sales'])?></td></tr><?php endforeach;?></tbody></table></div></section><?php render_footer();break;

case 'profile':
    $profileUser=user();render_header('My profile','profile');?>
    <div class="page-actions"><div><h2 class="register-title">Account security</h2><p class="muted">Manage your Soda ZW sign-in and keep your account protected.</p></div><span class="security-role"><?=e(ucfirst($profileUser['role']))?> account</span></div>
    <section class="security-layout"><article class="panel profile-card"><div class="profile-avatar"><?=e(strtoupper(substr((string)$profileUser['name'],0,1)))?></div><div><span>Signed in as</span><h2><?=e($profileUser['name'])?></h2><p><?=e($profileUser['email'])?></p><small>Role: <?=e(ucfirst($profileUser['role']))?></small></div></article><section class="panel password-card"><div class="panel-head"><div><h2>Change your password</h2><p>This works for every user, including the owner.</p></div><span class="security-lock"><?=app_nav_icon('settings')?></span></div><form method="post" class="form-grid password-form"><?=csrf_field()?><input type="hidden" name="action" value="change_own_password"><label class="full">Current password<input type="password" name="current_password" autocomplete="current-password" required></label><label>New password<input type="password" minlength="12" name="new_password" autocomplete="new-password" required><small>Use at least 12 characters.</small></label><label>Confirm new password<input type="password" minlength="12" name="confirm_password" autocomplete="new-password" required></label><button class="button primary">Update my password</button></form></section></section>
    <section class="panel security-advice"><div><strong>Use a unique password</strong><span>Do not reuse your email, cPanel or EcoCash password.</span></div><div><strong>Keep access personal</strong><span>Create a separate account for every staff member.</span></div><div><strong>Review team access</strong><span>Deactivate accounts immediately when someone leaves.</span></div></section>
    <?php render_footer();break;

case 'team':
    require_role(['owner','admin']);$users=db()->query('SELECT id,name,email,role,active,last_login_at FROM users ORDER BY active DESC,name')->fetchAll();render_header('Team members','team');?>
    <div class="page-actions"><div><h2 class="register-title">Users &amp; access</h2><p class="muted">Give every member their own secure login. Never share the owner password.</p></div><a class="button secondary" href="index.php?page=profile">Change my password</a></div>
    <section class="team-dashboard">
      <?php if(user()['role']==='owner'):?><article class="panel team-create-card"><div class="panel-head"><div><span class="section-kicker">NEW LOGIN</span><h2>Create a team account</h2><p>The member signs in at billing.sodazw.co.zw using this email and temporary password.</p></div></div><form method="post" class="form-grid add-user-form"><?=csrf_field()?><input type="hidden" name="action" value="add_user"><label>Full name<input name="name" autocomplete="name" required></label><label>Email / login<input type="email" name="email" autocomplete="email" required></label><label>Access level<select name="role"><option value="staff">Staff — daily sales and operations</option><option value="admin">Administrator — reports and settings</option><option value="owner">Owner — full control</option></select></label><label>Temporary password<input type="password" minlength="12" name="password" autocomplete="new-password" required><small>Use at least 12 characters and send it privately.</small></label><label>Confirm password<input type="password" minlength="12" name="confirm_password" autocomplete="new-password" required></label><button class="button primary">Create login account</button></form></article><?php endif;?>
      <article class="panel team-access"><div class="panel-head"><div><span class="section-kicker">CURRENT ACCESS</span><h2><?=count($users)?> team account<?=count($users)===1?'':'s'?></h2><p>Open a member to change their role, deactivate access or reset their password.</p></div></div><div class="team-user-list"><?php foreach($users as $teamUser):?><details class="team-user-card"><summary><span class="team-avatar"><?=e(strtoupper(substr($teamUser['name'],0,1)))?></span><span><strong><?=e($teamUser['name'])?></strong><small><?=e($teamUser['email'])?> &middot; <?=e(ucfirst($teamUser['role']))?></small></span><span class="team-state <?=$teamUser['active']?'active':'inactive'?>"><?=$teamUser['active']?'Active':'Inactive'?></span><small class="team-login"><?=e($teamUser['last_login_at']?'Last login '.date('j M Y, H:i',strtotime($teamUser['last_login_at'])):'Never signed in')?></small></summary><?php if(user()['role']==='owner'):?><div class="team-user-controls"><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="update_user"><input type="hidden" name="id" value="<?=$teamUser['id']?>"><label>Name<input name="name" value="<?=e($teamUser['name'])?>" required></label><label>Email<input type="email" name="email" value="<?=e($teamUser['email'])?>" required></label><label>Role<select name="role"><?php foreach(['staff'=>'Staff','admin'=>'Administrator','owner'=>'Owner'] as $roleKey=>$roleLabel):?><option value="<?=e($roleKey)?>" <?=$teamUser['role']===$roleKey?'selected':''?>><?=e($roleLabel)?></option><?php endforeach;?></select></label><label class="check user-active-check"><input type="checkbox" name="active" value="1" <?=$teamUser['active']?'checked':''?>> Account is active</label><button class="button secondary">Save access</button></form><?php if((int)$teamUser['id']!==(int)user()['id']):?><form method="post" class="reset-password-form"><?=csrf_field()?><input type="hidden" name="action" value="reset_user_password"><input type="hidden" name="id" value="<?=$teamUser['id']?>"><h3>Reset password</h3><label>New temporary password<input type="password" minlength="12" name="new_password" autocomplete="new-password" required></label><label>Confirm password<input type="password" minlength="12" name="confirm_password" autocomplete="new-password" required></label><button class="button secondary">Reset password</button></form><?php else:?><div class="reset-password-form self-password-note"><h3>Your password</h3><p>Use My profile to change your own password.</p><a class="button secondary" href="index.php?page=profile">Change my password</a></div><?php endif;?></div><?php endif;?></details><?php endforeach;?></div></article>
    </section>
    <?php render_footer();break;

case 'settings':
    require_role(['owner','admin']);$s=settings();$users=db()->query('SELECT id,name,email,role,active,last_login_at FROM users ORDER BY created_at')->fetchAll();$accounts=db()->query('SELECT * FROM payment_accounts WHERE active=1 ORDER BY currency_code,type,name')->fetchAll();render_header('Business settings','settings');?>
    <section class="panel"><div class="panel-head"><div><h2>Company & invoice details</h2><p>These details appear on quotations, invoices and statements.</p></div><?php if(user()['role']==='owner'):?><a class="button secondary small" href="backup.php">Download database backup</a><?php endif;?></div><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="save_settings"><label>Legal company name<input name="company_name" value="<?=e($s['company_name'])?>" required></label><label>Trading name<input name="trading_name" value="<?=e($s['trading_name'])?>" required></label><label>Business phone<input name="phone" value="<?=e($s['phone'])?>"></label><label>Business email<input type="email" name="email" value="<?=e($s['email'])?>"></label><label class="full">Address<input name="address" value="<?=e($s['address'])?>"></label><label>City<input name="city" value="<?=e($s['city'])?>"></label><label>Country<input name="country" value="<?=e($s['country'])?>"></label><label>Tax/VAT number<input name="tax_number" value="<?=e($s['tax_number'])?>"></label><label>Currency<input name="currency_code" maxlength="3" value="<?=e($s['currency_code'])?>"></label><h2>Numbering & defaults</h2><label>Invoice prefix<input name="invoice_prefix" value="<?=e($s['invoice_prefix'])?>"></label><label>Quotation prefix<input name="quote_prefix" value="<?=e($s['quote_prefix'])?>"></label><label>Default tax rate %<input type="number" step=".01" name="default_tax_rate" value="<?=e($s['default_tax_rate'])?>"></label><label>Default due days<input type="number" name="default_due_days" value="<?=e($s['default_due_days'])?>"></label><h2>Payment & terms</h2><label class="full">Bank or payment details<textarea name="bank_details" rows="4"><?=e($s['bank_details'])?></textarea></label><label class="full">Payment instructions<textarea name="payment_instructions" rows="4"><?=e($s['payment_instructions'])?></textarea></label><label class="full">Default invoice terms<textarea name="invoice_terms" rows="4"><?=e($s['invoice_terms'])?></textarea></label><button class="button primary">Save settings</button></form></section>
    <section class="panel"><div class="panel-head"><div><h2>Zimbabwe payment accounts</h2><p>Keep USD and ZiG channels separate for accurate cash-flow reporting.</p></div></div><div class="account-list"><?php foreach($accounts as $a):?><div class="account-row"><span><strong><?=e($a['name'])?></strong><br><small><?=e(ucwords(str_replace('_',' ',$a['type'])))?> · <?=e($a['currency_code'])?></small></span><small><?=e($a['details'])?></small></div><?php endforeach;?></div><form method="post" class="form-grid" style="margin-top:20px"><?=csrf_field()?><input type="hidden" name="action" value="save_payment_account"><label>Account name<input name="name" placeholder="e.g. CBZ Nostro USD" required></label><label>Type<select name="type"><option value="cash">Cash</option><option value="mobile_money">Mobile money</option><option value="bank">Bank transfer</option><option value="card">POS / Card</option><option value="online">Online gateway</option><option value="other">Other</option></select></label><label>Currency code<input name="currency_code" value="USD" maxlength="3" required></label><label>Account or wallet details<input name="details" placeholder="Optional internal description"></label><button class="button secondary">Add payment account</button></form></section>
    <section class="panel team-access"><div class="panel-head"><div><h2>Team access</h2><p>One secure account per person. Owners control roles, access and password resets.</p></div><a class="button secondary small" href="index.php?page=profile">Change my password</a></div>
      <div class="team-user-list"><?php foreach($users as $teamUser):?><details class="team-user-card"><summary><span class="team-avatar"><?=e(strtoupper(substr($teamUser['name'],0,1)))?></span><span><strong><?=e($teamUser['name'])?></strong><small><?=e($teamUser['email'])?> &middot; <?=e(ucfirst($teamUser['role']))?></small></span><span class="team-state <?=$teamUser['active']?'active':'inactive'?>"><?=$teamUser['active']?'Active':'Inactive'?></span><small class="team-login"><?=e($teamUser['last_login_at']?'Last login '.date('j M Y, H:i',strtotime($teamUser['last_login_at'])):'Never signed in')?></small></summary><?php if(user()['role']==='owner'):?><div class="team-user-controls"><form method="post" class="form-grid"><?=csrf_field()?><input type="hidden" name="action" value="update_user"><input type="hidden" name="id" value="<?=$teamUser['id']?>"><label>Name<input name="name" value="<?=e($teamUser['name'])?>" required></label><label>Email<input type="email" name="email" value="<?=e($teamUser['email'])?>" required></label><label>Role<select name="role"><?php foreach(['staff'=>'Staff','admin'=>'Administrator','owner'=>'Owner'] as $roleKey=>$roleLabel):?><option value="<?=e($roleKey)?>" <?=$teamUser['role']===$roleKey?'selected':''?>><?=e($roleLabel)?></option><?php endforeach;?></select></label><label class="check user-active-check"><input type="checkbox" name="active" value="1" <?=$teamUser['active']?'checked':''?>> Account is active</label><button class="button secondary">Save access</button></form><?php if((int)$teamUser['id']!==(int)user()['id']):?><form method="post" class="reset-password-form"><?=csrf_field()?><input type="hidden" name="action" value="reset_user_password"><input type="hidden" name="id" value="<?=$teamUser['id']?>"><h3>Reset password</h3><label>New password<input type="password" minlength="12" name="new_password" autocomplete="new-password" required></label><label>Confirm password<input type="password" minlength="12" name="confirm_password" autocomplete="new-password" required></label><button class="button secondary">Reset password</button></form><?php else:?><div class="reset-password-form self-password-note"><h3>Your password</h3><p>For security, confirm your current password on the My profile page.</p><a class="button secondary" href="index.php?page=profile">Change my password</a></div><?php endif;?></div><?php endif;?></details><?php endforeach;?></div>
      <?php if(user()['role']==='owner'):?><form method="post" class="form-grid add-user-form"><?=csrf_field()?><input type="hidden" name="action" value="add_user"><h2>Add a user</h2><label>Name<input name="name" required></label><label>Email<input type="email" name="email" required></label><label>Role<select name="role"><option value="staff">Staff</option><option value="admin">Administrator</option><option value="owner">Owner</option></select></label><label>Temporary password<input type="password" minlength="12" name="password" autocomplete="new-password" required><small>At least 12 characters.</small></label><label>Confirm password<input type="password" minlength="12" name="confirm_password" autocomplete="new-password" required></label><button class="button primary">Add team member</button></form><?php endif;?></section><?php render_footer();break;

default:http_response_code(404);render_header('Page not found');?><section class="panel empty"><strong>Page not found</strong><a href="index.php">Return to dashboard</a></section><?php render_footer();
}
