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.
41 lines
3.3 KiB
TypeScript
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('; '));
|
|
}
|