import fs from'node:fs';import assert from'node:assert/strict';import{createHash}from'node:crypto';import{session}from'./sgf-single-http.mjs'; const run=JSON.parse(fs.readFileSync('C:/laragon/data/sgf-tempo/run/modular-regression-current.json')),root=run.root+'/private/receipt-composition-cc50a276c10c',inputs=JSON.parse(fs.readFileSync(root+'/single-document-inputs.json')),file=root+'/service-documents-http-20261110.json',p=fs.existsSync(file)?JSON.parse(fs.readFileSync(file)):{operations:{},checks:[],keys:{}},save=()=>fs.writeFileSync(file,JSON.stringify(p,null,2)); const c={cp:await session('sgf:commercial_prepare','commercial'),cr:await session('sgf:commercial_review','commercial'),fp:await session('sgf:finance_prepare','finance'),fr:await session('sgf:finance_review','finance'),dp:await session('sgf:finance_prepare','commercial'),dr:await session('sgf:finance_review','commercial'),read:await session('sgf:read_only','commercial'),ap:await session('sgf:accounting_prepare','accounting'),ar:await session('sgf:accounting_review','accounting')}; const ok=(v,label)=>{assert(v,label);if(!p.checks.includes(label))p.checks.push(label);save();}; async function post(name,role,path,body){let o=p.operations[name];if(!o){o=p.operations[name]={role,path,body,key:crypto.randomUUID()};save();}assert.deepEqual({role,path,body},{role:o.role,path:o.path,body:o.body});if(o.result)return o.result;const r=await c[role].command(path,body,o.key);assert.equal(r.status,200,name+': '+JSON.stringify(r.value));o.result=r.value;save();return o.result;} async function doc(name,role,kind,action,key,version,data={}){const o=await post(name+'-preview',role,'/api/service-documents/preview',{kind,action,document_key:key,version,data,reason:'Decisão individual QA na8085: '+name+'.'});const result=await post(name+'-confirm',role,'/api/service-documents/confirm',{preview:o.preview});const again=await c[role].command('/api/service-documents/confirm',{preview:o.preview});ok(again.status===200&&again.value.replayed&&again.value.id===result.id,name+' recovers same committed version');return result;} let r=await c.cp.get('/commercial/documents?kind=receipt',{headers:{Accept:'text/html'}});ok(r.status===200&&(await r.text()).includes('sgf-service-documents.mjs'),'authenticated document screen available'); const src=inputs.credit_invoice,review=await post('invoice-review','cp','/api/service-invoices/drafts/'+src.draft_id+'/review',{series_id:src.series_id,rule_id:src.rule_id});ok(review.ready,'new1000 invoice ready under original writer');const invoice=await post('invoice-issue','cp','/api/service-invoices/drafts/'+src.draft_id+'/issue',{series_id:src.series_id,rule_id:src.rule_id,review_hash:review.review_hash}); const ob=await post('obligation-prepare','fp','/api/service-receivables/proposals',{kind:'obligation',invoice_id:invoice.id,currency:'MZN',source:'SIM-SD-8085',external_id:'OB-'+invoice.id,revision:0});await post('obligation-review','fr','/api/service-receivables/'+ob.id+'/decision',{version:1,revision:0,decision:'approved',reason:'Reconhecimento distinto da nova factura de teste1000MZN.'});const rc=await post('receipt-prepare','fp','/api/service-receivables/proposals',{kind:'receipt',invoice_id:invoice.id,obligation_id:ob.id,currency:'MZN',amount:'400.00',source:'SIM-SD-8085',external_id:'RC-'+invoice.id,revision:1,date:'2026-11-10',evidence:'Comprovativo QA400 da nova factura, sem dinheiro real.'});await post('receipt-review','fr','/api/service-receivables/'+rc.id+'/decision',{version:1,revision:1,decision:'approved',reason:'Conferência distinta do recebimento fictício400MZN.'});const allocation=await post('allocate400','fp','/api/service-receivables/'+rc.id+'/allocate',{amount:'400.00',version:1,revision:2}); for(const kind of ['receipt','credit_note','invoice_receipt']){p.keys[kind]??=crypto.randomUUID();save();}const receiptKey=p.keys.receipt;await doc('receipt-prepare','cp','receipt','prepare',receiptKey,0,{invoice_id:invoice.id,allocation_id:allocation.id});await doc('receipt-review','cr','receipt','review',receiptKey,1,{decision:'approved'});const receipt=await doc('receipt-issue','cp','receipt','issue',receiptKey,2); const creditKey=p.keys.credit_note;await doc('credit-prepare','dp','credit_note','prepare',creditKey,0,{invoice_id:invoice.id,amount:'100.00',explanation:'Redução nominal QA100 da nova factura, sem reembolso ou efeito físico.'});await doc('credit-review','dr','credit_note','review',creditKey,1,{decision:'approved'});const credit=await doc('credit-issue','dr','credit_note','issue',creditKey,2); const frKey=p.keys.invoice_receipt,frsrc=inputs.invoice_receipt;await doc('fr-prepare','dp','invoice_receipt','prepare',frKey,0,{draft_id:frsrc.draft_id,series_id:frsrc.series_id,rule_id:frsrc.rule_id,payment_date:'2026-11-10',evidence:'Intenção fictícia QA1000MZN, aprovada antes da conclusão.'});await doc('fr-review','dr','invoice_receipt','review',frKey,1,{decision:'approved'});const fr=await doc('fr-issue','dr','invoice_receipt','issue',frKey,2); for(const [kind,out]of [['receipt',receipt],['credit_note',credit],['invoice_receipt',fr]]){r=await c.read.get('/api/service-documents?'+new URLSearchParams({kind,document_key:p.keys[kind],per_page:100}));const h=await r.json();ok(r.status===200&&h.documents.data.length===3,kind+' retains three audited versions');const row=h.documents.data.find(x=>x.version===3);r=await c.read.get('/api/service-documents/'+out.output_id+'/pdf');const bytes=Buffer.from(await r.arrayBuffer());ok(r.status===200&&bytes.subarray(0,5).toString()==='%PDF-'&&createHash('sha256').update(bytes).digest('hex')===row.output.pdf_sha256,kind+' frozen PDF hash matches stored output');} r=await c.dr.get('/api/service-documents/options?view=allocations&invoice_id='+invoice.id);const options=await r.json();ok(r.status===200&&options.records.data.length===1&&options.records.data[0].id===allocation.id,'bounded selector returns actual400 allocation');const denied=await c.read.command('/api/service-documents/preview',{kind:'receipt',action:'prepare',document_key:crypto.randomUUID(),version:0,data:{},reason:'Tentativa não autorizada de consulta para escrita.'});ok(denied.status===403,'read-only document writer403'); r=await c.cp.get('/api/service-invoices');const invoices=await r.json();const frInvoice=invoices.invoices.find(x=>x.draft_id===frsrc.draft_id);assert(frInvoice); for(const [kind,id]of [['sales.invoice',invoice.id],['receivable.allocated',allocation.id],['receivable.reduced',credit.effect_id],['sales.invoice',frInvoice.id],['receivable.allocated',fr.effect_id]]){const key=kind+'-'+id,proposal=await post(key+'-prepare','ap','/api/general-ledger/effect-prepare',{effect_type:kind,source_id:id,journal_on:'2026-11-10',period_key:'QA-2026-INTEGRADO',rule_key:kind,reason:'Origem documental QA seleccionada individualmente: '+kind+'.'});await post(key+'-review','ar','/api/general-ledger/validate',{proposal_id:proposal.proposal_id,revision:proposal.revision,decision:'approved',reason:'Conferência contabilística distinta da origem e montantes de teste.'});await post(key+'-post','ap','/api/general-ledger/post',{proposal_id:proposal.proposal_id,revision:proposal.revision,reason:'Aplicação única da origem documental QA no mesmo diário.'});} r=await c.ap.get('/api/general-ledger');const ledger=await r.json();ok(r.status===200&&ledger.journals.length===25,'same ledger25 journals after five new documentary facts');ok(ledger.totals.debit_minor===ledger.totals.credit_minor,'integrated ledger remains balanced');p.passed=true;p.invoices={credit:invoice.id,invoice_receipt:frInvoice.id};p.obligation=ob.id;p.expected_balances={credit_invoice:'500.00',invoice_receipt:'0.00'};p.browser_visual_review=false;save();console.log(JSON.stringify({passed:true,checks:p.checks.length,proof:file}));