40 lines
2.8 KiB
TypeScript
40 lines
2.8 KiB
TypeScript
|
|
/** Run with npx tsx --env-file=.env.local scripts/backfill-retention-forecasts.ts [--apply]. Defaults to a read-only preview. */
|
||
|
|
import { pbAdmin } from "../src/lib/pb";
|
||
|
|
import { buildManagementBoard } from "../src/lib/management/model";
|
||
|
|
import { planRowFromRecord, projectPlanRowFromRecord, projectRowFromRecord, rowsToSnapshot } from "../src/lib/management/store";
|
||
|
|
import { matchRetentionForecasts } from "../src/lib/management/match-retention-forecasts";
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
const args = process.argv.slice(2);
|
||
|
|
if (args.some((arg) => arg !== "--apply")) throw new Error("Only --apply is supported");
|
||
|
|
const apply = args.includes("--apply");
|
||
|
|
const pb = await pbAdmin();
|
||
|
|
const [plans, projectRecords] = await Promise.all([pb.collection("management_plans").getFullList({ sort: "plan_year" }), pb.collection("management_projects").getFullList()]);
|
||
|
|
const projects = projectRecords.map((p) => projectRowFromRecord(p));
|
||
|
|
let changed = 0;
|
||
|
|
for (const record of plans) {
|
||
|
|
const plan = planRowFromRecord(record);
|
||
|
|
const records = await pb.collection("management_project_plans").getFullList({ filter: `plan = "${plan.id}"` });
|
||
|
|
const board = buildManagementBoard(rowsToSnapshot({ plan, projects, projectPlans: records.map((r) => projectPlanRowFromRecord(r)), retentions: [] }));
|
||
|
|
const result = matchRetentionForecasts(board, new Date().toISOString(), "Retention forecast backfill requested by user");
|
||
|
|
console.log(JSON.stringify({ year: plan.plan_year, retentionCells: result.audit.length, mode: apply ? "apply" : "preview" }));
|
||
|
|
if (!result.audit.length) continue;
|
||
|
|
changed += result.audit.length;
|
||
|
|
if (!apply) continue;
|
||
|
|
const fresh = await pb.collection("management_plans").getOne(plan.id);
|
||
|
|
if ((Number(fresh.revision) || 1) !== plan.revision) throw new Error(`Plan ${plan.plan_year} changed; no update made to that plan`);
|
||
|
|
const history = fresh.history && typeof fresh.history === "object" ? fresh.history : {};
|
||
|
|
await pb.collection("management_plans").update(plan.id, {
|
||
|
|
history: { ...history, retentionEdits: result.retentionEdits, retentionAudit: [...(plan.history.retentionAudit ?? []), ...result.audit] },
|
||
|
|
revision: plan.revision + 1,
|
||
|
|
});
|
||
|
|
const saved = planRowFromRecord(await pb.collection("management_plans").getOne(plan.id));
|
||
|
|
for (const entry of result.audit) {
|
||
|
|
const value = saved.history.retentionEdits?.[entry.project]?.[String(entry.month)];
|
||
|
|
if (!value || value.forecast !== entry.value.actual || value.actual !== entry.value.actual) throw new Error(`Could not verify ${plan.plan_year} retention edit`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
console.log(JSON.stringify({ totalRetentionCells: changed, applied: apply }));
|
||
|
|
}
|
||
|
|
main().catch(() => { console.error("Retention backfill failed. No income or cost records were changed."); process.exitCode = 1; });
|