MEGAINFRA-WEBAPP/scripts/dn-replay-review.mts
DEls ed69b517e8 Integrate agent work and add dashboard attention indicators
Remove repeated Plant, Financial and Workforce shortcut cards. Add site-scoped Plant and Financial sidebar attention status with automatic refresh.

Include accumulated delivery processing, purchase order recovery, supplier reconciliation, plant data and user management changes and audit records.
2026-10-04 21:53:32 +02:00

41 lines
3.3 KiB
TypeScript

/** Snapshot the current grouping/review queue. --run requires the saved snapshot
* and explicit operator approval to send the original images to MiniMax.
* This calls only non-financial ingestion; it never posts receipt lines. */
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { pbAdmin } from '../src/lib/pb';
import { runStagedBatch } from '../src/lib/dn-pipeline/run-staged';
import { displayBatchStage, displayCandidateStage } from '../src/lib/dn-pipeline/stages';
const directory='docs/audits/2026-10-03-delivery-replay';
const pb=await pbAdmin();
pb.autoCancellation(false);
await mkdir(directory,{recursive:true});
const run=process.argv.includes('--run');
if (!run) {
const [batches,notes]=await Promise.all([pb.collection('dn_batches').getFullList(),pb.collection('dn_batch_notes').getFullList()]);
const grouping=batches.filter(batch=>displayBatchStage(batch.status,batch.stage)==='split_review');
const complete=new Set(batches.filter(batch=>displayBatchStage(batch.status,batch.stage)==='complete').map(batch=>batch.id));
const review=notes.filter(note=>complete.has(note.batch) && ['needs_review','matching'].includes(displayCandidateStage(note.status,note.stage)));
const ids=[...new Set([...grouping.map(batch=>batch.id),...review.map(note=>note.batch)])];
const selected=ids.map(id=>({batchId:id,grouping:grouping.some(batch=>batch.id===id),noteIds:review.filter(note=>note.batch===id).map(note=>note.id)}));
const pages=await pb.collection('dn_batch_pages').getFullList({filter:ids.map(id=>`batch="${id}"`).join(' || ')});
const snapshot={createdAt:new Date().toISOString(),groupingCount:grouping.length,reviewCount:review.length,selected,batches:batches.filter(batch=>ids.includes(batch.id)),notes:notes.filter(note=>ids.includes(note.batch)),pages};
await writeFile(`${directory}/before.json`,JSON.stringify(snapshot,null,2));
console.log(JSON.stringify({groupingCount:grouping.length,reviewCount:review.length,batches:ids.length,selection:selected}));
} else {
const snapshot=JSON.parse(await readFile(`${directory}/before.json`,'utf8'));
for(const selection of snapshot.selected) {
const pages=selection.grouping ? snapshot.pages.filter((page:any)=>page.batch===selection.batchId).map((page:any)=>page.global_page) : snapshot.notes.filter((note:any)=>selection.noteIds.includes(note.id)).flatMap((note:any)=>Array.isArray(note.pages)?note.pages:[]);
console.log('START',selection.batchId,'fresh pages',pages.join(','));
try {
const result=await runStagedBatch(pb,selection.batchId,{freshPages:[...new Set(pages)] as number[]});
console.log('RESULT',selection.batchId,JSON.stringify(result));
} catch(error) {
console.log('FAILED',selection.batchId,error instanceof Error?error.message:String(error));
}
}
const ids=snapshot.selected.map((selection:any)=>selection.batchId);
const filter=ids.map((id:string)=>`batch="${id}"`).join(' || ');
const [batches,notes]=await Promise.all([Promise.all(ids.map((id:string)=>pb.collection('dn_batches').getOne(id))),pb.collection('dn_batch_notes').getFullList({filter})]);
await writeFile(`${directory}/after.json`,JSON.stringify({completedAt:new Date().toISOString(),batches,notes},null,2));
for(const note of notes) console.log('NOTE',note.delivery_note_number||note.doc_number||note.id,note.stage,note.checks?.filter((check:any)=>check.passed===false).map((check:any)=>check.reason).join('; '));
}