$account_id, 'method' => $method, 'file_name' => mb_substr(basename((string) $file['name']), 0, 120), 'header' => $read['header'], 'rows' => $read['rows'], 'map' => $map, 'guess' => $map ?: expenseImportGuess($read['header'], $read['rows']), 'remembered' => $map !== null, ]; redirect('expense_import.php?step=' . ($map ? 'review' : 'columns')); } if (isset($_POST['expense_import_columns'])) { validateCSRFToken(); enforceUserPermission('module_financial', 2); $import = $_SESSION['expense_import'] ?? null; if (!$import) { flashAlert('Upload the statement again - the one you started is gone', 'error'); redirect('expense_import.php'); } $map = expenseImportMapClean($_POST, count($import['header'])); if (!$map) { flashAlert('Pick the columns with the date, the description and the amount', 'error'); redirect('expense_import.php?step=columns'); } $lines = expenseImportLines($import['rows'], $map); if (!array_filter($lines, function ($line) { return $line['problem'] === ''; })) { $_SESSION['expense_import']['guess'] = $map; flashAlert("None of the lines read with those columns - check the date format and the amount", 'error'); redirect('expense_import.php?step=columns'); } $_SESSION['expense_import']['map'] = $map; $_SESSION['expense_import']['guess'] = $map; $_SESSION['expense_import']['remembered'] = false; expenseImportSaveMap($import['account_id'], $import['header'], $map); redirect('expense_import.php?step=review'); } if (isset($_POST['expense_import_save'])) { validateCSRFToken(); enforceUserPermission('module_financial', 2); $import = $_SESSION['expense_import'] ?? null; if (!$import || !$import['map']) { flashAlert('Upload the statement again - the one you started is gone', 'error'); redirect('expense_import.php'); } $picks = []; foreach ((array) json_decode((string) ($_POST['picks'] ?? ''), true) as $pick) { if (is_array($pick) && isset($pick['n'])) { $picks[intval($pick['n'])] = [ 'take' => !empty($pick['take']), 'vendor' => (string) ($pick['vendor'] ?? ''), 'new_vendor' => (string) ($pick['new_vendor'] ?? ''), 'category' => intval($pick['category'] ?? 0), 'client' => intval($pick['client'] ?? 0), 'billable' => !empty($pick['billable']), ]; } } $result = expenseImportSave($import, $picks, $error); if (!$result) { flashAlert(escapeHtml($error), 'error'); redirect('expense_import.php?step=review'); } unset($_SESSION['expense_import']); $account_name = (string) getFieldById('accounts', intval($import['account_id']), 'account_name'); $currency = (string) getFieldById('accounts', intval($import['account_id']), 'account_currency_code'); logAudit("Expense", "Import", "$session_name imported " . $result['imported'] . " expense" . ($result['imported'] === 1 ? '' : 's') . " from " . escapeSql($import['file_name'] . " for $account_name") . ($result['vendors'] ? ", adding " . $result['vendors'] . " vendor" . ($result['vendors'] === 1 ? '' : 's') : '')); $notes = []; if ($result['vendors']) { $notes[] = $result['vendors'] . ' new vendor' . ($result['vendors'] === 1 ? '' : 's'); } if ($result['linked']) { $notes[] = $result['linked'] . ' matched to ' . ($result['linked'] === 1 ? 'an expense' : 'expenses') . ' entered already'; } if ($result['skipped']) { $notes[] = $result['skipped'] . ' imported before'; } flashAlert('' . $result['imported'] . ' expense' . ($result['imported'] === 1 ? '' : 's') . ' imported - ' . escapeHtml(billingMoney($result['total'], $currency ?: $session_company_currency)) . ($notes ? ' · ' . escapeHtml(implode(', ', $notes)) : '')); if (!$result['imported']) { redirect('expenses.php'); } redirect('expenses.php?' . http_build_query(['account' => intval($import['account_id']), 'canned_date' => 'custom', 'dtf' => $result['from'], 'dtt' => $result['to']])); } /* Start over - the statement in progress is dropped. */ if (isset($_GET['expense_import_discard'])) { validateCSRFToken(); unset($_SESSION['expense_import']); redirect('expense_import.php'); } /* Forget what a merchant was matched to. */ if (isset($_GET['expense_import_rule_delete'])) { validateCSRFToken(); enforceUserPermission('module_financial', 2); $rule_id = intval($_GET['expense_import_rule_delete']); $match = getFieldById('expense_import_rules', $rule_id, 'import_rule_match'); mysqli_query($mysqli, "DELETE FROM expense_import_rules WHERE import_rule_id = $rule_id"); if ($match !== null) { logAudit("Expense", "Import Rule Delete", "$session_name removed the statement match for " . escapeSql($match)); } flashAlert($match !== null ? 'Forgotten - ' . escapeHtml($match) . ' will be matched afresh next time' : 'That match was already gone', $match !== null ? 'success' : 'error'); redirect(); }