跳至內容
Astera Technology

一行拒收為何留下半份匯入文件?

完整文件連明細用同一儲存點,另一份獨立成功文件則保留。

下載完整個案資料及參考答案 ↓

先核來源與已訂規則

本例各份獨立文件可分別完成。D 有標頭、L1=2、L2=−1,E 另有有效行 3。每條已存在明細須有非 NULL 正數數量;D 標頭與明細須一同成功。每份一儲存點,行錯便移除整份 D 暫建。此處明准只得標頭的草稿。

已填假設輸入
紀錄輸入 JSON
原例輸入{"documents":[{"id":"D","lines":[{"item":"L1","quantity":2},{"item":"L2","quantity":-1}]},{"id":"E","lines":[{"item":"L1","quantity":3}]}]}
下載可編輯 CSV ↓

比較兩條實際路徑

更正保 E 及其明細,留 D 的 275 拒收,沒有 D 碎片。逐行捕錯則會留 D 標頭及 L1。

錯誤與更正路徑的輸出
路徑輸出 JSON
錯誤路徑{"errors":[{"document":"D","code":275}],"documents":[{"id":"D"},{"id":"E"}],"lines":[{"document_id":"D","item":"L1","quantity":2},{"document_id":"E","item":"L1","quantity":3}]}
更正路徑{"errors":[{"document":"D","code":275}],"documents":[{"id":"E"}],"lines":[{"document_id":"E","item":"L1","quantity":3}]}
下載可編輯 CSV ↓
本地 Node.js 程序:22.23.3 或 24.14.1
import {DatabaseSync} from 'node:sqlite';
import {TextDecoder} from 'node:util';
import {inflateSync} from 'node:zlib';
import {posix} from 'node:path';
import {createHash} from 'node:crypto';
const rows=(db,sql)=>db.prepare(sql).all().map(r=>({...r}));
const failed=(action)=>{try{action();return null;}catch(e){return {code:e.errcode??e.code??e.name};}};

import assert from 'node:assert/strict';
const run=function run(i,corrected=true){
    const db=new DatabaseSync(':memory:');try{db.exec('PRAGMA foreign_keys=ON;CREATE TABLE document(id TEXT PRIMARY KEY);CREATE TABLE line(document_id TEXT REFERENCES document(id),item TEXT,quantity INTEGER NOT NULL CHECK(quantity>0));');const errors=[];
      for(const d of i.documents){if(corrected)db.exec('SAVEPOINT family;');db.prepare('INSERT INTO document VALUES(?)').run(d.id);
        let error=null;for(const l of d.lines){error=failed(()=>db.prepare('INSERT INTO line VALUES(?,?,?)').run(d.id,l.item,l.quantity));if(error){errors.push({document:d.id,code:error.code});if(corrected)break;}}
        if(corrected){if(error)db.exec('ROLLBACK TO family;');db.exec('RELEASE family;');}}
      return {errors,documents:rows(db,'SELECT * FROM document ORDER BY id'),lines:rows(db,'SELECT * FROM line ORDER BY document_id,item')};
    }finally{db.close();}
  };
const cases=[{"label":"misleading base","input":{"documents":[{"id":"D","lines":[{"item":"L1","quantity":2},{"item":"L2","quantity":-1}]},{"id":"E","lines":[{"item":"L1","quantity":3}]}]},"expected":{"errors":[{"document":"D","code":275}],"documents":[{"id":"D"},{"id":"E"}],"lines":[{"document_id":"D","item":"L1","quantity":2},{"document_id":"E","item":"L1","quantity":3}]},"corrected":false},{"label":"corrected base","input":{"documents":[{"id":"D","lines":[{"item":"L1","quantity":2},{"item":"L2","quantity":-1}]},{"id":"E","lines":[{"item":"L1","quantity":3}]}]},"expected":{"errors":[{"document":"D","code":275}],"documents":[{"id":"E"}],"lines":[{"document_id":"E","item":"L1","quantity":3}]},"corrected":true},{"label":"complete entity commits","input":{"documents":[{"id":"D","lines":[{"item":"L1","quantity":2},{"item":"L2","quantity":1}]}]},"expected":{"errors":[],"documents":[{"id":"D"}],"lines":[{"document_id":"D","item":"L1","quantity":2},{"document_id":"D","item":"L2","quantity":1}]},"corrected":true},{"label":"permitted header-only draft","input":{"documents":[{"id":"D","lines":[]}]},"expected":{"errors":[],"documents":[{"id":"D"}],"lines":[]},"corrected":true},{"label":"missing line quantity holds its complete family","input":{"documents":[{"id":"D","lines":[{"item":"L1","quantity":2},{"item":"L2","quantity":null}]},{"id":"E","lines":[{"item":"L1","quantity":3}]}]},"expected":{"errors":[{"document":"D","code":1299}],"documents":[{"id":"E"}],"lines":[{"document_id":"E","item":"L1","quantity":3}]},"corrected":true}];
for(const test of cases){
  const actual=run(test.input,test.corrected);
  assert.deepEqual(actual,test.expected,test.label);
  console.log(test.label,actual);
}
下載可執行例子 ↓

下次載入前核邊界

完整有效 D 可提交;明准只得標頭 D 則留標頭、沒有明細。D 已存在明細若欠數量,NOT NULL1299 會令整份 D 回復,獨立有效 E 照留。

可編輯邊界紀錄及預期輸出
情況輸入 JSON預期輸出 JSON
同份文件明細全有效{"documents":[{"id":"D","lines":[{"item":"L1","quantity":2},{"item":"L2","quantity":1}]}]}{"errors":[],"documents":[{"id":"D"}],"lines":[{"document_id":"D","item":"L1","quantity":2},{"document_id":"D","item":"L2","quantity":1}]}
明准只得標頭草稿{"documents":[{"id":"D","lines":[]}]}{"errors":[],"documents":[{"id":"D"}],"lines":[]}
明細欠數量,整份文件暫停{"documents":[{"id":"D","lines":[{"item":"L1","quantity":2},{"item":"L2","quantity":null}]},{"id":"E","lines":[{"item":"L1","quantity":3}]}]}{"errors":[{"document":"D","code":1299}],"documents":[{"id":"E"}],"lines":[{"document_id":"E","item":"L1","quantity":3}]}
下載可編輯 CSV ↓

獨立 D 失敗,E 必須一同回復嗎?

查看參考答案

按此文件家族約定不用。整份 D 回復,完整 E 保留,D 未解錯誤另記。

相關資源