<?php
declare(strict_types=1);

/*
 * Soda ZW Accounting Command Centre.
 * All headline figures are deliberately limited to the configured base
 * currency so unlike currencies are never silently added together.
 */
$baseCurrency = (string)(settings()['currency_code'] ?? 'USD');
$period = in_array($_GET['period'] ?? 'month', ['30days', 'month', 'quarter', 'year'], true)
    ? (string)$_GET['period']
    : 'month';

$today = date('Y-m-d');
switch ($period) {
    case '30days':
        $periodStart = date('Y-m-d', strtotime('-29 days'));
        $periodLabel = 'Last 30 days';
        break;
    case 'quarter':
        $quarterMonth = ((int)floor(((int)date('n') - 1) / 3) * 3) + 1;
        $periodStart = date('Y-') . str_pad((string)$quarterMonth, 2, '0', STR_PAD_LEFT) . '-01';
        $periodLabel = 'This quarter';
        break;
    case 'year':
        $periodStart = date('Y-01-01');
        $periodLabel = 'This year';
        break;
    default:
        $periodStart = date('Y-m-01');
        $periodLabel = 'This month';
}
$periodEnd = $today;
$periodDays = max(1, (int)((strtotime($periodEnd) - strtotime($periodStart)) / 86400) + 1);
$previousEnd = date('Y-m-d', strtotime($periodStart . ' -1 day'));
$previousStart = date('Y-m-d', strtotime($previousEnd . ' -' . ($periodDays - 1) . ' days'));

$scalar = static function (string $sql, array $params = []): float {
    $stmt = db()->prepare($sql);
    $stmt->execute($params);
    return (float)$stmt->fetchColumn();
};
$rows = static function (string $sql, array $params = []): array {
    $stmt = db()->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll();
};
$trend = static function (float $current, float $previous): array {
    if (abs($previous) < 0.009) {
        return ['label' => abs($current) < 0.009 ? 'No change' : 'New activity', 'class' => $current >= 0 ? 'up' : 'down'];
    }
    $change = (($current - $previous) / abs($previous)) * 100;
    return ['label' => ($change >= 0 ? '+' : '') . number_format($change, 1) . '%', 'class' => $change >= 0 ? 'up' : 'down'];
};

$salesSql = 'SELECT COALESCE(SUM(total),0) FROM documents WHERE type="invoice" AND status NOT IN ("draft","cancelled") AND currency_code=? AND issue_date BETWEEN ? AND ?';
$cashSql = 'SELECT COALESCE(SUM(p.amount),0) FROM payments p JOIN documents d ON d.id=p.invoice_id WHERE d.type="invoice" AND d.status<>"cancelled" AND d.currency_code=? AND p.payment_date BETWEEN ? AND ?';
$creditsSql = 'SELECT COALESCE(SUM(total),0) FROM documents WHERE type="credit_note" AND status NOT IN ("draft","cancelled") AND currency_code=? AND issue_date BETWEEN ? AND ?';
$refundsSql = 'SELECT COALESCE(SUM(r.amount),0) FROM payment_reversals r JOIN payments p ON p.id=r.payment_id JOIN documents d ON d.id=p.invoice_id WHERE d.currency_code=? AND r.reversal_date BETWEEN ? AND ?';
$sales = $scalar($salesSql, [$baseCurrency, $periodStart, $periodEnd]);
$salesPrevious = $scalar($salesSql, [$baseCurrency, $previousStart, $previousEnd]);
$cashReceived = $scalar($cashSql, [$baseCurrency, $periodStart, $periodEnd]);
$cashPrevious = $scalar($cashSql, [$baseCurrency, $previousStart, $previousEnd]);
$sales -= $scalar($creditsSql, [$baseCurrency, $periodStart, $periodEnd]);
$salesPrevious -= $scalar($creditsSql, [$baseCurrency, $previousStart, $previousEnd]);
$cashReceived -= $scalar($refundsSql, [$baseCurrency, $periodStart, $periodEnd]);
$cashPrevious -= $scalar($refundsSql, [$baseCurrency, $previousStart, $previousEnd]);
$expenses = $scalar('SELECT COALESCE(SUM(amount),0) FROM expenses WHERE currency_code=? AND expense_date BETWEEN ? AND ?', [$baseCurrency, $periodStart, $periodEnd]);
$billPayments = $scalar('SELECT COALESCE(SUM(bp.amount),0) FROM bill_payments bp JOIN bills b ON b.id=bp.bill_id WHERE b.currency_code=? AND bp.payment_date BETWEEN ? AND ?', [$baseCurrency, $periodStart, $periodEnd]);
$netCash = $cashReceived - $expenses - $billPayments;

$balanceExpression=finance_document_balance_sql('d');
$receivables = $scalar(
    'SELECT COALESCE(SUM(x.balance),0) FROM (SELECT '.$balanceExpression.' balance FROM documents d WHERE d.type="invoice" AND d.currency_code=? AND d.status NOT IN ("cancelled","draft")) x',
    [$baseCurrency]
);
$payables = $scalar(
    'SELECT COALESCE(SUM(x.balance),0) FROM (SELECT 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.currency_code=? AND b.status<>"cancelled" GROUP BY b.id) x',
    [$baseCurrency]
);

$overdueInvoices = $rows(
    'SELECT d.id,d.number,d.due_date,c.name,c.company_name,'.$balanceExpression.' balance,DATEDIFF(CURDATE(),d.due_date) days_late FROM documents d JOIN customers c ON c.id=d.customer_id WHERE d.type="invoice" AND d.currency_code=? AND d.status NOT IN ("cancelled","draft","paid") AND d.due_date<CURDATE() HAVING balance>.009 ORDER BY d.due_date ASC LIMIT 6',
    [$baseCurrency]
);
$overdueCount = (int)$scalar(
    'SELECT COUNT(*) FROM (SELECT d.id,'.$balanceExpression.' balance FROM documents d WHERE d.type="invoice" AND d.currency_code=? AND d.status NOT IN ("cancelled","draft","paid") AND d.due_date<CURDATE() HAVING balance>.009) x',
    [$baseCurrency]
);
$dueBills = $rows(
    'SELECT b.id,b.number,b.due_date,s.name supplier,GREATEST(b.total-COALESCE(SUM(bp.amount),0),0) balance,DATEDIFF(CURDATE(),b.due_date) days_late FROM bills b JOIN suppliers s ON s.id=b.supplier_id LEFT JOIN bill_payments bp ON bp.bill_id=b.id WHERE b.currency_code=? AND b.status<>"cancelled" AND b.due_date<=DATE_ADD(CURDATE(),INTERVAL 7 DAY) GROUP BY b.id HAVING balance>.009 ORDER BY b.due_date ASC LIMIT 5',
    [$baseCurrency]
);
$dueBillsCount = (int)$scalar(
    'SELECT COUNT(*) FROM (SELECT b.id,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.currency_code=? AND b.status<>"cancelled" AND b.due_date<=DATE_ADD(CURDATE(),INTERVAL 7 DAY) GROUP BY b.id HAVING balance>.009) due_bills',
    [$baseCurrency]
);

$aging = $rows(
    '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,'.$balanceExpression.' balance FROM documents d WHERE d.type="invoice" AND d.currency_code=? AND d.status NOT IN ("cancelled","draft")) x',
    [$baseCurrency]
)[0] ?? ['current_due' => 0, 'd30' => 0, 'd60' => 0, 'd90' => 0, 'over90' => 0];

$buildCashMovement = static function (string $view) use ($rows, $baseCurrency): array {
    $periods = [];
    if ($view === 'daily') {
        for ($offset = 13; $offset >= 0; $offset--) {
            $key = date('Y-m-d', strtotime('-' . $offset . ' days'));
            $periods[$key] = [
                'label' => date('D j', strtotime($key)),
                'full_label' => date('l, j F Y', strtotime($key)),
                'in' => 0.0,
                'out' => 0.0,
            ];
        }
        $paymentPeriodSql = 'DATE(p.payment_date)';
        $expensePeriodSql = 'DATE(expense_date)';
        $billPeriodSql = 'DATE(bp.payment_date)';
        $refundPeriodSql = 'DATE(r.reversal_date)';
        $title = 'Daily cash movement';
        $description = 'Day-by-day inflows, outflows and net movement for the last 14 days.';
        $aria = 'Daily cash movement for the last fourteen days. Each day shows cash in, cash out and net movement.';
    } elseif ($view === 'weekly') {
        $currentWeekStart = date('Y-m-d', strtotime('monday this week'));
        for ($offset = 7; $offset >= 0; $offset--) {
            $key = date('Y-m-d', strtotime($currentWeekStart . ' -' . $offset . ' weeks'));
            $end = date('Y-m-d', strtotime($key . ' +6 days'));
            $periods[$key] = [
                'label' => date('j M', strtotime($key)),
                'full_label' => date('j M', strtotime($key)) . ' – ' . date('j M Y', strtotime($end)),
                'in' => 0.0,
                'out' => 0.0,
            ];
        }
        $paymentPeriodSql = 'DATE(DATE_SUB(p.payment_date,INTERVAL WEEKDAY(p.payment_date) DAY))';
        $expensePeriodSql = 'DATE(DATE_SUB(expense_date,INTERVAL WEEKDAY(expense_date) DAY))';
        $billPeriodSql = 'DATE(DATE_SUB(bp.payment_date,INTERVAL WEEKDAY(bp.payment_date) DAY))';
        $refundPeriodSql = 'DATE(DATE_SUB(r.reversal_date,INTERVAL WEEKDAY(r.reversal_date) DAY))';
        $title = 'Weekly cash movement';
        $description = 'Week-by-week inflows, outflows and net movement across the last 8 weeks.';
        $aria = 'Weekly cash movement for the last eight weeks. Each week shows cash in, cash out and net movement.';
    } else {
        for ($offset = 5; $offset >= 0; $offset--) {
            $monthStart = date('Y-m-01', strtotime('-' . $offset . ' months'));
            $key = date('Y-m', strtotime($monthStart));
            $periods[$key] = [
                'label' => date('M', strtotime($monthStart)),
                'full_label' => date('F Y', strtotime($monthStart)),
                'in' => 0.0,
                'out' => 0.0,
            ];
        }
        $paymentPeriodSql = 'DATE_FORMAT(p.payment_date,"%Y-%m")';
        $expensePeriodSql = 'DATE_FORMAT(expense_date,"%Y-%m")';
        $billPeriodSql = 'DATE_FORMAT(bp.payment_date,"%Y-%m")';
        $refundPeriodSql = 'DATE_FORMAT(r.reversal_date,"%Y-%m")';
        $title = 'Monthly cash movement';
        $description = 'Month-by-month inflows, outflows and net movement across the last 6 months.';
        $aria = 'Monthly cash movement for the last six months. Each month shows cash in, cash out and net movement.';
    }
    $flowStart = (string)array_key_first($periods);
    $queryStart = $view === 'monthly' ? $flowStart . '-01' : $flowStart;
    foreach ($rows('SELECT '.$paymentPeriodSql.' period_key,SUM(p.amount) total FROM payments p JOIN documents d ON d.id=p.invoice_id WHERE d.type="invoice" AND d.status<>"cancelled" AND d.currency_code=? AND p.payment_date>=? GROUP BY period_key', [$baseCurrency, $queryStart]) as $item) {
        if (isset($periods[$item['period_key']])) $periods[$item['period_key']]['in'] = (float)$item['total'];
    }
    $outRows = $rows(
        'SELECT x.period_key,SUM(x.total) total FROM (' .
        'SELECT '.$expensePeriodSql.' period_key,SUM(amount) total FROM expenses WHERE currency_code=? AND expense_date>=? GROUP BY period_key ' .
        'UNION ALL SELECT '.$billPeriodSql.' period_key,SUM(bp.amount) total FROM bill_payments bp JOIN bills b ON b.id=bp.bill_id WHERE b.currency_code=? AND bp.payment_date>=? GROUP BY period_key ' .
        'UNION ALL SELECT '.$refundPeriodSql.' period_key,SUM(r.amount) total FROM payment_reversals r JOIN payments p ON p.id=r.payment_id JOIN documents d ON d.id=p.invoice_id WHERE d.type="invoice" AND d.status<>"cancelled" AND d.currency_code=? AND r.reversal_date>=? GROUP BY period_key' .
        ') x GROUP BY x.period_key',
        [$baseCurrency, $queryStart, $baseCurrency, $queryStart, $baseCurrency, $queryStart]
    );
    foreach ($outRows as $item) {
        if (isset($periods[$item['period_key']])) $periods[$item['period_key']]['out'] = (float)$item['total'];
    }
    foreach ($periods as &$flowPeriod) $flowPeriod['net'] = $flowPeriod['in'] - $flowPeriod['out'];
    unset($flowPeriod);
    $values = array_values($periods);
    $cashIn = array_sum(array_column($values, 'in'));
    $cashOut = array_sum(array_column($values, 'out'));
    $net = $cashIn - $cashOut;
    $best = $values[0];
    $lowest = $values[0];
    foreach ($values as $flowPeriod) {
        if ($flowPeriod['net'] > $best['net']) $best = $flowPeriod;
        if ($flowPeriod['net'] < $lowest['net']) $lowest = $flowPeriod;
    }
    $latest = $values[count($values) - 1];
    $previous = $values[max(0, count($values) - 2)];
    $latestChange = $latest['net'] - $previous['net'];
    $latestChangePercent = abs($previous['net']) > 0.009 ? ($latestChange / abs($previous['net'])) * 100 : null;
    return [
        'title' => $title,
        'description' => $description,
        'aria' => $aria,
        'periods' => $periods,
        'max' => max(1.0, ...array_map(static fn(array $flowPeriod): float => max($flowPeriod['in'], $flowPeriod['out']), $values)),
        'cash_in' => $cashIn,
        'cash_out' => $cashOut,
        'net' => $net,
        'average_net' => $net / max(1, count($values)),
        'positive_periods' => count(array_filter($values, static fn(array $flowPeriod): bool => $flowPeriod['net'] >= 0)),
        'best' => $best,
        'lowest' => $lowest,
        'latest' => $latest,
        'previous' => $previous,
        'latest_change' => $latestChange,
        'latest_change_percent' => $latestChangePercent,
    ];
};
$dailyCashMovement = $buildCashMovement('daily');
$weeklyCashMovement = $buildCashMovement('weekly');
$monthlyCashMovement = $buildCashMovement('monthly');
if (isset($_GET['cash_export'])) {
    require_permission('reports.view');
    $exportView = in_array($_GET['cash_export'], ['daily','weekly','monthly'], true) ? (string)$_GET['cash_export'] : 'monthly';
    $exportMovement = ['daily'=>$dailyCashMovement,'weekly'=>$weeklyCashMovement,'monthly'=>$monthlyCashMovement][$exportView];
    audit('cash_movement_exported', 'report', null, $exportView);
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="sodazw-'.$exportView.'-cash-movement-'.date('Y-m-d').'.csv"');
    $output = fopen('php://output', 'wb');
    fputcsv($output, ['Soda ZW '.ucfirst($exportView).' Cash Movement', 'Cash in', 'Cash out', 'Net movement', 'Currency']);
    foreach ($exportMovement['periods'] as $flowPeriod) {
        fputcsv($output, [$flowPeriod['full_label'], number_format($flowPeriod['in'],2,'.',''), number_format($flowPeriod['out'],2,'.',''), number_format($flowPeriod['net'],2,'.',''), $baseCurrency]);
    }
    fputcsv($output, []);
    fputcsv($output, ['TOTAL', number_format($exportMovement['cash_in'],2,'.',''), number_format($exportMovement['cash_out'],2,'.',''), number_format($exportMovement['net'],2,'.',''), $baseCurrency]);
    fclose($output);
    exit;
}

$paymentChannels = $rows(
    'SELECT COALESCE(NULLIF(a.name,""),NULLIF(p.method,""),"Other") channel,SUM(p.amount) total,COUNT(*) transactions FROM payments p JOIN documents d ON d.id=p.invoice_id LEFT JOIN payment_accounts a ON a.id=p.account_id WHERE d.type="invoice" AND d.status<>"cancelled" AND d.currency_code=? AND p.payment_date BETWEEN ? AND ? GROUP BY channel ORDER BY total DESC LIMIT 5',
    [$baseCurrency, $periodStart, $periodEnd]
);
$recentDocuments = $rows(
    'SELECT d.id,d.number,d.type,d.status,d.issue_date,d.due_date,d.total,c.name customer,c.company_name,(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 d.currency_code=? ORDER BY d.created_at DESC LIMIT 7',
    [$baseCurrency]
);
$recentPayments = $rows(
    'SELECT p.payment_date,p.amount,p.reference,d.id invoice_id,d.number,c.name customer,COALESCE(NULLIF(a.name,""),p.method) channel 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 WHERE d.currency_code=? ORDER BY p.payment_date DESC,p.id DESC LIMIT 5',
    [$baseCurrency]
);

$lowStock = (int)$scalar('SELECT COUNT(*) FROM products WHERE active=1 AND track_stock=1 AND stock_quantity<=reorder_level');
$refillsDue = (int)$scalar('SELECT COUNT(*) FROM refill_jobs WHERE status NOT IN ("completed","cancelled") AND due_at<=DATE_ADD(NOW(),INTERVAL 1 DAY)');
$deliveriesOpen = (int)$scalar('SELECT COUNT(*) FROM deliveries WHERE status NOT IN ("delivered","cancelled")');
$webOrdersNew = (int)$scalar('SELECT COUNT(*) FROM web_orders WHERE status="new"');
$followupsDue = (int)$scalar('SELECT COUNT(*) FROM crm_activities WHERE completed_at IS NULL AND due_at IS NOT NULL AND due_at<=NOW()');
$attentionTotal = $overdueCount + $dueBillsCount + $lowStock + $refillsDue + $followupsDue + $webOrdersNew;

$salesTrend = $trend($sales, $salesPrevious);
$cashTrend = $trend($cashReceived, $cashPrevious);
$dashboardUser = user();
$firstName = trim(explode(' ', trim((string)($dashboardUser['name'] ?? '')))[0] ?? '');
$hour = (int)date('G');
$greeting = $hour < 12 ? 'Good morning' : ($hour < 17 ? 'Good afternoon' : 'Good evening');

render_header('Business overview', 'dashboard');
?>
<section class="accounting-command-hero">
    <div class="accounting-command-copy">
        <span class="command-kicker">Soda ZW Business Command Centre</span>
        <h2><?=e($greeting)?><?=$firstName !== '' ? ', ' . e($firstName) : ''?></h2>
        <p>A clear view of cash, customer balances and the work that needs your attention.</p>
        <div class="command-actions">
            <a class="button primary" href="quick-invoice.php">+ New invoice</a>
            <a class="button command-payment" href="receive-payment.php"><?=app_nav_icon('payments')?> Receive payment</a>
            <a class="button secondary" href="quick-invoice.php?type=quotation">New quotation</a>
        </div>
    </div>
    <form class="period-control" method="get">
        <input type="hidden" name="page" value="dashboard">
        <label for="dashboard-period">Reporting period</label>
        <select id="dashboard-period" name="period" onchange="this.form.submit()">
            <option value="30days" <?=$period === '30days' ? 'selected' : ''?>>Last 30 days</option>
            <option value="month" <?=$period === 'month' ? 'selected' : ''?>>This month</option>
            <option value="quarter" <?=$period === 'quarter' ? 'selected' : ''?>>This quarter</option>
            <option value="year" <?=$period === 'year' ? 'selected' : ''?>>This year</option>
        </select>
        <small><?=e(date('j M Y', strtotime($periodStart)))?> – <?=e(date('j M Y', strtotime($periodEnd)))?> · <?=e($baseCurrency)?> view</small>
    </form>
</section>

<section class="accounting-kpis" aria-label="Financial overview">
    <a href="index.php?page=payments">
        <span>Cash received</span>
        <strong><?=money($cashReceived, $baseCurrency)?></strong>
        <small class="trend <?=$cashTrend['class']?>"><?=$cashTrend['label']?> vs previous period</small>
        <i class="kpi-icon cash"><?=app_nav_icon('payments')?></i>
    </a>
    <a href="index.php?page=invoices">
        <span>Invoiced sales</span>
        <strong><?=money($sales, $baseCurrency)?></strong>
        <small class="trend <?=$salesTrend['class']?>"><?=$salesTrend['label']?> vs previous period</small>
        <i class="kpi-icon sales"><?=app_nav_icon('invoices')?></i>
    </a>
    <a href="index.php?page=invoices&tab=overdue">
        <span>Customer receivables</span>
        <strong><?=money($receivables, $baseCurrency)?></strong>
        <small class="<?=$overdueCount ? 'needs-attention' : ''?>"><?=$overdueCount?> overdue invoice<?=$overdueCount === 1 ? '' : 's'?></small>
        <i class="kpi-icon receivables"><?=app_nav_icon('customers')?></i>
    </a>
    <a href="index.php?page=bills">
        <span>Supplier payables</span>
        <strong><?=money($payables, $baseCurrency)?></strong>
        <small><?=$dueBillsCount?> due or due within 7 days</small>
        <i class="kpi-icon payables"><?=app_nav_icon('bills')?></i>
    </a>
</section>

<section class="accounting-primary-grid priority-section">
    <article class="panel priority-workbench">
        <div class="panel-head">
            <div><span class="section-kicker">PRIORITY WORKBENCH</span><h2>What needs action</h2><p>Work from the highest financial impact down.</p></div>
            <strong class="attention-bubble"><?=$attentionTotal?></strong>
        </div>
        <div class="priority-list">
            <a href="index.php?page=invoices&tab=overdue"><i class="priority-mark urgent"></i><span><b>Collect overdue invoices</b><small>Customer balances past their due date</small></span><strong><?=$overdueCount?></strong><em>&rarr;</em></a>
            <a href="index.php?page=bills"><i class="priority-mark warning"></i><span><b>Review supplier bills</b><small>Due now or within the next 7 days</small></span><strong><?=$dueBillsCount?></strong><em>&rarr;</em></a>
            <a href="index.php?page=refills"><i class="priority-mark brand"></i><span><b>Protect the overnight refill promise</b><small>Refill jobs due within 24 hours</small></span><strong><?=$refillsDue?></strong><em>&rarr;</em></a>
            <a href="index.php?page=products"><i class="priority-mark warning"></i><span><b>Replenish low stock</b><small>Products at or below reorder level</small></span><strong><?=$lowStock?></strong><em>&rarr;</em></a>
            <a href="index.php?page=crm"><i class="priority-mark neutral"></i><span><b>Complete customer follow-ups</b><small>Calls, WhatsApp messages and tasks due</small></span><strong><?=$followupsDue?></strong><em>&rarr;</em></a>
        </div>
    </article>

</section>

<section class="cash-movement-section">
    <article class="panel command-cashflow cash-movement-dashboard">
        <div class="panel-head">
            <div><span class="section-kicker">CASH MOVEMENT DASHBOARD</span><h2>Daily and weekly cash movement</h2><p>Understand when money enters the business, when it leaves, and whether each reporting period generated or consumed cash.</p></div>
            <span class="command-legend descriptive-legend"><i></i> Cash in <b></b> Cash out <em></em> Net positive</span>
        </div>
        <div class="cash-definition"><strong>What the graph measures</strong><span><b>Cash in</b> is customer invoice payments. <b>Cash out</b> combines operating expenses, supplier bill payments and payment reversals. <b>Net movement</b> is cash in minus cash out.</span></div>
        <div class="cash-view-controls">
            <div class="cash-view-tabs" role="tablist" aria-label="Cash movement period">
                <button type="button" role="tab" id="cash-tab-daily" aria-controls="cash-panel-daily" aria-selected="true" data-cash-view="daily"><strong>Daily</strong><span>Last 14 days</span></button>
                <button type="button" role="tab" id="cash-tab-weekly" aria-controls="cash-panel-weekly" aria-selected="false" tabindex="-1" data-cash-view="weekly"><strong>Weekly</strong><span>Last 8 weeks</span></button>
                <button type="button" role="tab" id="cash-tab-monthly" aria-controls="cash-panel-monthly" aria-selected="false" tabindex="-1" data-cash-view="monthly"><strong>Monthly</strong><span>Last 6 months</span></button>
            </div>
            <?php if (has_permission('reports.view')):?><a class="button secondary cash-export" data-cash-export href="index.php?page=dashboard&amp;period=<?=e($period)?>&amp;cash_export=daily">Download Daily CSV</a><?php endif;?>
        </div>
        <div class="cash-movement-graphs" data-cash-dashboard>
            <?php foreach (['daily' => $dailyCashMovement, 'weekly' => $weeklyCashMovement, 'monthly' => $monthlyCashMovement] as $movementView => $movement):?>
            <section class="cash-movement-card" id="cash-panel-<?=e($movementView)?>" role="tabpanel" aria-labelledby="cash-tab-<?=e($movementView)?>" data-cash-panel="<?=e($movementView)?>" <?=$movementView === 'weekly' ? 'hidden' : ''?>>
                <header><div><span><?=e($movement['title'])?></span><p><?=e($movement['description'])?></p></div><strong class="<?=$movement['net'] < 0 ? 'negative' : 'positive'?>"><?=($movement['net'] >= 0 ? '+' : '')?><?=money($movement['net'], $baseCurrency)?><small>Total net</small></strong></header>
                <div class="cash-movement-scroll">
                <div class="command-chart-v7 descriptive-cash-chart" style="--flow-columns:<?=count($movement['periods'])?>" role="img" aria-label="<?=e($movement['aria'])?>">
                    <?php foreach ($movement['periods'] as $flowPeriod):?>
                    <div class="cashflow-month-v7 <?=$flowPeriod['net'] < 0 ? 'net-negative' : 'net-positive'?>" title="<?=e($flowPeriod['full_label'])?> — Cash in: <?=e(money($flowPeriod['in'],$baseCurrency))?>; cash out: <?=e(money($flowPeriod['out'],$baseCurrency))?>; net: <?=e(money($flowPeriod['net'],$baseCurrency))?>">
                        <div class="cashflow-bars-v7" aria-hidden="true"><span class="cashflow-bar-v7 cash-in" style="--cash-height:<?=max(3,round($flowPeriod['in']/$movement['max']*100))?>%"></span><span class="cashflow-bar-v7 cash-out" style="--cash-height:<?=max(3,round($flowPeriod['out']/$movement['max']*100))?>%"></span></div>
                        <span><?=e($flowPeriod['label'])?></span>
                        <b class="period-net"><?=($flowPeriod['net'] >= 0 ? '+' : '')?><?=e(money($flowPeriod['net'],$baseCurrency))?></b>
                        <small class="sr-only"><?=e($flowPeriod['full_label'])?>. Cash in <?=money($flowPeriod['in'], $baseCurrency)?>, cash out <?=money($flowPeriod['out'], $baseCurrency)?>, net movement <?=money($flowPeriod['net'], $baseCurrency)?>.</small>
                    </div>
                    <?php endforeach;?>
                </div>
                </div>
                <div class="cash-summary descriptive-summary">
                    <div><span>Total cash in</span><strong><?=money($movement['cash_in'], $baseCurrency)?></strong></div>
                    <div><span>Total cash out</span><strong><?=money($movement['cash_out'], $baseCurrency)?></strong></div>
                    <div class="<?=$movement['net'] < 0 ? 'negative' : 'positive'?>"><span>Net movement</span><strong><?=money($movement['net'], $baseCurrency)?></strong></div>
                    <div><span>Average net / period</span><strong><?=money($movement['average_net'], $baseCurrency)?></strong></div>
                </div>
                <div class="cash-insights">
                    <span><b>Strongest period</b><?=e($movement['best']['full_label'])?> · <?=($movement['best']['net'] >= 0 ? '+' : '')?><?=money($movement['best']['net'], $baseCurrency)?></span>
                    <span><b>Lowest period</b><?=e($movement['lowest']['full_label'])?> · <?=($movement['lowest']['net'] >= 0 ? '+' : '')?><?=money($movement['lowest']['net'], $baseCurrency)?></span>
                    <span><b>Positive movement</b><?=$movement['positive_periods']?> of <?=count($movement['periods'])?> periods</span>
                    <span><b>Latest vs previous</b><?=($movement['latest_change'] >= 0 ? '+' : '')?><?=money($movement['latest_change'], $baseCurrency)?><?=$movement['latest_change_percent'] !== null ? ' · '.($movement['latest_change_percent'] >= 0 ? '+' : '').number_format($movement['latest_change_percent'],1).'%' : ' · new activity'?></span>
                </div>
            </section>
            <?php endforeach;?>
        </div>
    </article>
</section>

<section class="accounting-secondary-grid">
    <article class="panel ageing-workbench">
        <div class="panel-head"><div><span class="section-kicker">ACCOUNTS RECEIVABLE</span><h2>Receivables ageing</h2><p>Outstanding <?=e($baseCurrency)?> balances by days overdue.</p></div><a href="index.php?page=invoices&tab=unpaid">Open invoices</a></div>
        <div class="command-ageing">
            <?php foreach ([['Current', $aging['current_due'], 'current'], ['1–30 days', $aging['d30'], 'watch'], ['31–60 days', $aging['d60'], 'warning'], ['61–90 days', $aging['d90'], 'danger'], ['90+ days', $aging['over90'], 'critical']] as [$label, $amount, $class]):?>
                <a class="<?=$class?>" href="index.php?page=invoices&tab=<?=$class === 'current' ? 'unpaid' : 'overdue'?>"><span><?=e($label)?></span><strong><?=money((float)$amount, $baseCurrency)?></strong></a>
            <?php endforeach;?>
        </div>
        <?php if ($overdueInvoices):?><div class="collection-list">
            <?php foreach ($overdueInvoices as $invoice):?><a href="index.php?page=document-view&id=<?=$invoice['id']?>"><span><b><?=e($invoice['company_name'] ?: $invoice['name'])?></b><small><?=e($invoice['number'])?> · <?=$invoice['days_late']?> days overdue</small></span><strong><?=money($invoice['balance'], $baseCurrency)?></strong></a><?php endforeach;?>
        </div><?php else:?><div class="command-empty"><b>No overdue invoices</b><span>Customer accounts are currently up to date.</span></div><?php endif;?>
    </article>

    <article class="panel payment-mix">
        <div class="panel-head"><div><span class="section-kicker">PAYMENT CHANNELS</span><h2>How customers paid</h2><p><?=e($periodLabel)?> · <?=e($baseCurrency)?></p></div><a href="index.php?page=payments">Register</a></div>
        <?php $channelMax = max(1.0, ...array_map(static fn(array $channel): float => (float)$channel['total'], $paymentChannels ?: [['total' => 1]]));?>
        <div class="payment-channel-list">
            <?php foreach ($paymentChannels as $channel):?><div><span><b><?=e($channel['channel'])?></b><small><?=$channel['transactions']?> transaction<?=$channel['transactions'] == 1 ? '' : 's'?></small></span><i><b style="width:<?=round((float)$channel['total'] / $channelMax * 100)?>%"></b></i><strong><?=money($channel['total'], $baseCurrency)?></strong></div><?php endforeach;?>
            <?php if (!$paymentChannels):?><div class="command-empty"><b>No payments in this period</b><span>Received payments will appear here.</span></div><?php endif;?>
        </div>
        <a class="receive-payment-card" href="receive-payment.php"><i><?=app_nav_icon('payments')?></i><span><b>Receive a payment</b><small>Find an invoice and record Cash, EcoCash, transfer or POS.</small></span><em>&rarr;</em></a>
    </article>
</section>

<section class="accounting-activity-grid">
    <article class="panel recent-financials">
        <div class="panel-head"><div><span class="section-kicker">RECENT ACTIVITY</span><h2>Invoices and quotations</h2></div><a href="index.php?page=invoices">View all</a></div>
        <div class="financial-list">
            <?php foreach ($recentDocuments as $document):?><?php $settled=(float)$document['paid']+(float)$document['credited'];$balance = max(0, (float)$document['total'] - $settled);$paymentDetail = $document['type'] === 'invoice' ? invoice_payment_detail((float)$document['total'], $settled, $document['due_date']) : ucwords(str_replace('_', ' ', (string)$document['status']));?>
                <a href="index.php?page=document-view&id=<?=$document['id']?>"><i class="doc-kind <?=$document['type']?>"><?=app_nav_icon($document['type'] === 'quotation' ? 'quotations' : 'invoices')?></i><span><b><?=e($document['number'])?> · <?=e($document['company_name'] ?: $document['customer'])?></b><small><?=e(date('j M Y', strtotime($document['issue_date'])))?> · <?=e($paymentDetail)?></small></span><strong><?=money($document['type'] === 'invoice' ? $balance : $document['total'], $baseCurrency)?></strong><em>&rarr;</em></a>
            <?php endforeach;?>
            <?php if (!$recentDocuments):?><div class="command-empty"><b>No documents yet</b><span>Create your first Soda ZW invoice or quotation.</span></div><?php endif;?>
        </div>
    </article>
    <article class="panel recent-receipts">
        <div class="panel-head"><div><span class="section-kicker">LATEST RECEIPTS</span><h2>Payments received</h2></div><a href="receive-payment.php">Record payment</a></div>
        <div class="receipt-list">
            <?php foreach ($recentPayments as $payment):?><a href="index.php?page=document-view&id=<?=$payment['invoice_id']?>"><span><b><?=e($payment['customer'])?></b><small><?=e($payment['channel'])?> · <?=e(date('j M Y', strtotime($payment['payment_date'])))?></small></span><strong><?=money($payment['amount'], $baseCurrency)?></strong></a><?php endforeach;?>
            <?php if (!$recentPayments):?><div class="command-empty"><b>No payments recorded</b><span>Your latest customer receipts will appear here.</span></div><?php endif;?>
        </div>
        <div class="operations-mini">
            <a href="index.php?page=web-orders"><span>New web orders</span><strong><?=$webOrdersNew?></strong></a>
            <a href="index.php?page=deliveries"><span>Open deliveries</span><strong><?=$deliveriesOpen?></strong></a>
            <a href="index.php?page=refills"><span>Refills due</span><strong><?=$refillsDue?></strong></a>
        </div>
    </article>
</section>
<?php render_footer();
