Re-link invoice lines whose target asset a seed replaced
`booked_asset` stores an asset record ID, and `npm run seed` deletes and re-creates every asset — so the id a line was booked against stops existing after a seed. The line then points at nothing and silently drops out of monthly_income, which is exactly the reported symptom: booked yesterday, unbooked today, with the line still carrying the correct code. The booking step now re-derives the pointer from the register code the line already carries (`asset_code`, falling back to the machine code) whenever the stored target is empty or no longer exists. It only ever points a line at the asset its own code names, so it never invents a match and never clears a site booking. A line with no matching code stays unbooked exactly as before. This makes the documented run order self-healing: `npm run seed && node scripts/book-invoice.mjs` now restores the links, so a reset cannot silently unbook the register. Verified by running a full seed and re-booking: 29 pointers were invalidated by the seed and all 29 were re-linked, giving 45 posted rows and 3 unbooked lines — the two MPLANT05 containers and one WT02 trailer, which have no matching plant item.
This commit is contained in:
parent
b5f6c0e16b
commit
f7ce429c04
1 changed files with 34 additions and 0 deletions
|
|
@ -74,6 +74,40 @@ if (!targets.length) {
|
|||
const allLines = await pb.collection("invoice_lines").getFullList({ sort: "line_no" });
|
||||
const existingIncome = await pb.collection("monthly_income").getFullList();
|
||||
|
||||
// ---- Repair stale booking pointers ------------------------------------------------
|
||||
// `booked_asset` stores an asset RECORD ID, and `npm run seed` deletes and re-creates
|
||||
// every asset — so the id a line was booked against stops existing after a seed. The line
|
||||
// then points at nothing and silently drops out of monthly_income, which is exactly the
|
||||
// "it booked yesterday and is unbooked today" symptom.
|
||||
//
|
||||
// The invoice line already carries the register code it was matched on (`asset_code`),
|
||||
// so the pointer can be re-derived rather than retyped. This ONLY ever points a line at
|
||||
// the asset its own code names; it never invents a match and never clears one, so a line
|
||||
// with no code stays unbooked exactly as before.
|
||||
const allAssets = await pb.collection("assets").getFullList();
|
||||
const assetIds = new Set(allAssets.map((a) => a.id));
|
||||
const idByCode = new Map(allAssets.filter((a) => a.code).map((a) => [String(a.code).trim().toUpperCase(), a.id]));
|
||||
const codeFromMachine = (code) => {
|
||||
const m = /^([A-Za-z]+)[- ]?([0-9]{1,3})/.exec(String(code || "").trim());
|
||||
return m ? `${m[1].toUpperCase()}${m[2]}` : "";
|
||||
};
|
||||
|
||||
let repaired = 0;
|
||||
if (!dryRun) {
|
||||
for (const l of allLines) {
|
||||
if (l.booked_site) continue; // booked to a site — a different, deliberate choice
|
||||
const fresh = (String(l.asset_code || "").trim().toUpperCase() || codeFromMachine(l.machine_code))
|
||||
&& idByCode.get(String(l.asset_code || "").trim().toUpperCase() || codeFromMachine(l.machine_code));
|
||||
// Only act when the pointer is genuinely gone: empty, or pointing at a deleted asset.
|
||||
const needsRepair = !l.booked_asset || !assetIds.has(l.booked_asset);
|
||||
if (!needsRepair || !fresh || fresh === l.booked_asset) continue;
|
||||
await pb.collection("invoice_lines").update(l.id, { booked_asset: fresh });
|
||||
l.booked_asset = fresh;
|
||||
repaired++;
|
||||
}
|
||||
}
|
||||
if (repaired) console.log(` · re-linked ${repaired} line(s) whose target asset had been replaced by a seed`);
|
||||
|
||||
// Remove previously invoice-derived rows for the target invoices only.
|
||||
const targetIds = new Set(targets.map((i) => i.id));
|
||||
const linesOfTargets = allLines.filter((l) => targetIds.has(l.invoice));
|
||||
|
|
|
|||
Loading…
Reference in a new issue