// lib/sars-runs.ts // Tiny wrapper around the sars_runs PocketBase collection. // // One record per SARS capture run, written at run start (status="running") // and patched at run end (status="completed"|"failed" + counts + result_path). // Per-row data writes to the per-return-type collection (e.g. // ysterbaard_SARS_EMP201_PAYE) are still a separate future sprint. // // The collection schema is documented in: // - the .env.local header comment // - the user memory entry "PocketBase auth gotcha" (refreshed 2026-09-10) // - the request to the user when the sars_runs collection was first created import "server-only"; import { createRecord, updateRecord, type PBRecord } from "./pb"; const COLLECTION = "sars_runs"; interface RunStartInput { run_id: string; started_at: string; // ISO timestamp return_type: string; period_start: string; period_end: string; periods_requested: string[]; companies: string[] | null; // null when allActive companies_count: number; created_by?: string; } interface RunEndInput { finished_at: string; status: "completed" | "failed"; result_path: string | null; ok_count: number; error_count: number; error_message?: string | null; } /** * Create a sars_runs record at the start of a run. Returns the PB * record id so we can patch it later. Throws on failure — the caller * is expected to handle the error and either abort the run or * continue without PB audit. */ export async function createRunRecord(input: RunStartInput): Promise { const record = await createRecord(COLLECTION, { run_id: input.run_id, started_at: input.started_at, return_type: input.return_type, period_start: input.period_start, period_end: input.period_end, periods_requested: input.periods_requested, companies: input.companies, companies_count: input.companies_count, status: "running", created_by: input.created_by ?? "sj", }); return record.id; } /** * Patch a sars_runs record at the end of a run. Best-effort: the * caller should log but not throw on failure (the .py already * produced the result file, and a missing audit row is recoverable * from the result_path on disk). */ export async function finishRunRecord(recordId: string, input: RunEndInput): Promise { await updateRecord(COLLECTION, recordId, { finished_at: input.finished_at, status: input.status, result_path: input.result_path, ok_count: input.ok_count, error_count: input.error_count, error_message: input.error_message ?? null, }); }