(string) ($_POST['statement_date'] ?? ''), 'ending_balance' => (string) ($_POST['ending_balance'] ?? ''), 'notes' => (string) ($_POST['notes'] ?? ''), 'start_from' => (string) ($_POST['start_from'] ?? ''), 'start_date' => (string) ($_POST['start_date'] ?? ''), 'start_balance' => (string) ($_POST['start_balance'] ?? ''), ]; } /** "Operating Checking statement to Sep 30, 2026" */ function reconcileLabel(array $row) { return $row['account']['account_name'] . ' statement to ' . date('M j, Y', strtotime($row['reconciliation_statement_date'])); } if (isset($_POST['reconcile_start'])) { validateCSRFToken(); enforceUserPermission('module_financial', 2); $account_id = intval($_POST['account_id'] ?? 0); $had_opening = (bool) reconcileOpening($account_id); $id = reconcileStart($account_id, reconcilePostInput(), $error); if (!$id) { flashAlert(escapeHtml($error), 'error'); redirect("reconcile.php?account=$account_id"); } $row = reconcileRow($id); $totals = reconcileTotals($row); $opening = reconcileOpening($row['reconciliation_account_id']); if ($opening && !$had_opening) { logAudit("Reconciliation", "Create", "$session_name brought " . escapeSql($row['account']['account_name']) . " forward as reconciled to " . date('M j, Y', strtotime($opening['reconciliation_statement_date'])) . " at a bank balance of " . escapeSql(reconcileBalanceText($row['account'], $opening['reconciliation_ending_balance'])), 0, intval($opening['reconciliation_id'])); } logAudit("Reconciliation", "Create", "$session_name started reconciling the " . escapeSql(reconcileLabel($row)), 0, $id); flashAlert("Reconciling the " . escapeHtml(reconcileLabel($row)) . "" . ($totals['count'] ? " - {$totals['count']} line" . ($totals['count'] === 1 ? '' : 's') . ' from bank statements ticked already' : '')); redirect("reconcile.php?account=$account_id"); } if (isset($_POST['reconcile_update'])) { validateCSRFToken(); enforceUserPermission('module_financial', 2); $id = intval($_POST['reconciliation_id'] ?? 0); $before = reconcileRow($id); if (!reconcileUpdate($id, reconcilePostInput(), $error)) { flashAlert(escapeHtml($error), 'error'); redirect($before ? 'reconcile.php?account=' . intval($before['reconciliation_account_id']) : 'accounts.php'); } $row = reconcileRow($id); logAudit("Reconciliation", "Edit", "$session_name changed the statement being reconciled to the " . escapeSql(reconcileLabel($row)) . ", ending at " . escapeSql(reconcileBalanceText($row['account'], $row['reconciliation_ending_balance'])), 0, $id); flashAlert("Statement changed - " . escapeHtml(reconcileLabel($row)) . ""); redirect('reconcile.php?account=' . intval($row['reconciliation_account_id'])); } if (isset($_POST['reconcile_finish'])) { validateCSRFToken(); enforceUserPermission('module_financial', 2); $id = intval($_POST['reconciliation_id'] ?? 0); $row = reconcileRow($id); $totals = $row ? reconcileFinish($id, $error) : null; if (!$totals) { flashAlert(escapeHtml($row ? $error : 'That reconciliation no longer exists'), 'error'); redirect($row ? 'reconcile.php?account=' . intval($row['reconciliation_account_id']) : 'accounts.php'); } logAudit("Reconciliation", "Finish", "$session_name reconciled the " . escapeSql(reconcileLabel($row)) . " - {$totals['count']} lines cleared, ending at " . escapeSql(reconcileBalanceText($row['account'], $totals['ending'])), 0, $id); flashAlert("" . escapeHtml($row['account']['account_name']) . " is reconciled to " . escapeHtml(date('M j, Y', strtotime($row['reconciliation_statement_date']))) . " - {$totals['count']} line" . ($totals['count'] === 1 ? '' : 's') . " cleared"); redirect('reconcile.php?account=' . intval($row['reconciliation_account_id'])); } if (isset($_GET['reconcile_discard'])) { validateCSRFToken(); enforceUserPermission('module_financial', 2); $row = reconcileDiscard(intval($_GET['reconcile_discard']), $error); if (!$row) { flashAlert(escapeHtml($error), 'error'); redirect(); } logAudit("Reconciliation", "Delete", "$session_name discarded reconciling the " . escapeSql(reconcileLabel($row)), 0, intval($row['reconciliation_id'])); flashAlert("Reconciling the " . escapeHtml(reconcileLabel($row)) . " discarded - its ticks are off", 'error'); redirect('reconcile.php?account=' . intval($row['reconciliation_account_id'])); } if (isset($_GET['reconcile_undo'])) { validateCSRFToken(); enforceUserPermission('module_financial', 3); $row = reconcileUndo(intval($_GET['reconcile_undo']), $error); if (!$row) { flashAlert(escapeHtml($error), 'error'); redirect(); } if ($row['opening']) { logAudit("Reconciliation", "Undo", "$session_name took away " . escapeSql($row['account']['account_name']) . " being brought forward as reconciled to " . date('M j, Y', strtotime($row['reconciliation_statement_date'])) . (abs(floatval($row['reconciliation_adjustment'])) >= 0.005 ? ', the opening balance back to ' . escapeSql(billingMoney(accountRow($row['reconciliation_account_id'])['opening_balance'], $row['account']['account_currency_code'])) : ''), 0, intval($row['reconciliation_id'])); flashAlert("" . escapeHtml($row['account']['account_name']) . " is no longer brought forward - its next statement can start from either balance", 'warning'); redirect('reconcile.php?account=' . intval($row['reconciliation_account_id'])); } logAudit("Reconciliation", "Undo", "$session_name undid the reconciliation of the " . escapeSql(reconcileLabel($row)) . " - back to in progress", 0, intval($row['reconciliation_id'])); flashAlert("The " . escapeHtml(reconcileLabel($row)) . " is being reconciled again - its lines can change, and it can be finished or discarded", 'warning'); redirect('reconcile.php?account=' . intval($row['reconciliation_account_id'])); } /* * Correct an account's opening balance by how far ITFlow was off the bank on * the day it was brought forward, so the two match from then on. */ if (isset($_GET['reconcile_correct_opening'])) { validateCSRFToken(); enforceUserPermission('module_financial', 2); $row = reconcileRow(intval($_GET['reconcile_correct_opening'])); $gap = $row ? reconcileCorrectOpening($row['reconciliation_id'], $error) : null; if ($gap === null) { flashAlert(escapeHtml($row ? $error : 'That reconciliation no longer exists'), 'error'); redirect(); } $account = accountRow($row['reconciliation_account_id']); $moved = billingMoney($gap, $account['account_currency_code']); logAudit("Account", "Edit", "$session_name corrected the opening balance of " . escapeSql($account['account_name']) . " by " . escapeSql($moved) . ", to " . escapeSql(billingMoney($account['opening_balance'], $account['account_currency_code'])) . ", to match the bank on " . date('M j, Y', strtotime($row['reconciliation_statement_date'])), 0, intval($account['account_id'])); flashAlert("" . escapeHtml($account['account_name']) . "'s opening balance is corrected by " . escapeHtml($moved) . " - ITFlow matches the bank from " . escapeHtml(date('M j, Y', strtotime($row['reconciliation_statement_date']))) . " on"); redirect('reconcile.php?account=' . intval($account['account_id'])); }