实现企业银行账户与建行制单(corporate-bank-account-payment-order)
这个提交包含在:
@@ -0,0 +1,83 @@
|
||||
const XLSX = require("xlsx");
|
||||
|
||||
const { encryptStringOrThrow } = require("../../../src/services/crypto");
|
||||
const {
|
||||
HEADERS,
|
||||
PaymentOrderXlsError,
|
||||
buildCustomerSerial,
|
||||
generatePaymentOrderXls,
|
||||
} = require("../../../src/services/paymentOrderXls");
|
||||
|
||||
const FIELD_ENCRYPTION_KEY = Buffer.alloc(32, 7).toString("base64");
|
||||
const config = { fieldEncryptionKey: FIELD_ENCRYPTION_KEY };
|
||||
const encrypt = (value) => encryptStringOrThrow(config, value);
|
||||
|
||||
function buildBatch(overrides = {}) {
|
||||
return {
|
||||
batch_no: "PB-20260802-002",
|
||||
payment_account_snapshot_json: {
|
||||
bankInstitutionCode: "CCB",
|
||||
accountName: "合成测试公司",
|
||||
accountNumberEncrypted: encrypt("6217000000000012345"),
|
||||
},
|
||||
items: [
|
||||
{
|
||||
id: "b",
|
||||
entity_code: "BX-20260802-0002",
|
||||
amount: "2.00",
|
||||
snapshot_json: { payee: { name: "乙", bankName: "中国建设银行", bankInstitutionCode: "CCB", bankAccountEncrypted: encrypt("6222000000000056789") } },
|
||||
},
|
||||
{
|
||||
id: "a",
|
||||
entity_code: "BX-20260802-0001",
|
||||
amount: "1.20",
|
||||
snapshot_json: { payee: { name: "甲", bankName: "中国建设银行", bankInstitutionCode: "CCB", bankAccountEncrypted: encrypt("6222000000000012345") } },
|
||||
},
|
||||
],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("payment order BIFF8 generator", () => {
|
||||
it("writes the fixed workbook contract with typed string identifiers and stable ordering", () => {
|
||||
const first = generatePaymentOrderXls(config, buildBatch());
|
||||
const second = generatePaymentOrderXls(config, buildBatch());
|
||||
const workbook = XLSX.read(first.buffer, { type: "buffer", cellNF: true });
|
||||
const sheet = workbook.Sheets.Sheet1;
|
||||
|
||||
expect(first.buffer.subarray(0, 8).toString("hex")).toBe("d0cf11e0a1b11ae1");
|
||||
expect(workbook.SheetNames).toEqual(["Sheet1", "Sheet2", "Sheet3"]);
|
||||
expect(XLSX.utils.sheet_to_json(sheet, { header: 1 })[0]).toEqual(HEADERS);
|
||||
expect(sheet.B2).toMatchObject({ t: "s", v: "6217000000000012345" });
|
||||
expect(sheet.D2).toMatchObject({ t: "s", v: "01" });
|
||||
expect(sheet.E2).toMatchObject({ t: "s", v: "6222000000000012345" });
|
||||
expect(sheet.I2).toMatchObject({ t: "s", v: "PB-20260802-002-001" });
|
||||
expect(sheet.J2).toMatchObject({ t: "n", v: 1.2 });
|
||||
expect(sheet.M2).toMatchObject({ t: "s", v: "0" });
|
||||
expect(first.serials).toEqual(second.serials);
|
||||
expect(first.totalAmount).toBe("3.20");
|
||||
});
|
||||
|
||||
it("fails the whole export and reports every blocking payee issue", () => {
|
||||
const batch = buildBatch();
|
||||
batch.items[1].snapshot_json.payee.bankInstitutionCode = "OTHER";
|
||||
batch.items[0].snapshot_json.payee.bankAccountEncrypted = null;
|
||||
try {
|
||||
generatePaymentOrderXls(config, batch);
|
||||
throw new Error("expected payment order generation to fail");
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(PaymentOrderXlsError);
|
||||
expect(error.code).toBe("payee_bank_not_supported");
|
||||
expect(error.details.blockers).toEqual(expect.arrayContaining([
|
||||
{ code: "payee_bank_not_supported", entityCode: "BX-20260802-0001" },
|
||||
{ code: "payee_account_unavailable", entityCode: "BX-20260802-0002" },
|
||||
]));
|
||||
}
|
||||
});
|
||||
|
||||
it("validates customer serial length and character set without truncation", () => {
|
||||
expect(buildCustomerSerial("PB-20260802-002", 1)).toBe("PB-20260802-002-001");
|
||||
expect(() => buildCustomerSerial("pb invalid", 1)).toThrow(PaymentOrderXlsError);
|
||||
expect(() => buildCustomerSerial("PB-123456789012345678901234567890", 1)).toThrow(PaymentOrderXlsError);
|
||||
});
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户