|
| 1 | +import { parseCamt052 } from "../camt052-parser"; |
| 2 | + |
| 3 | +const parse = (xml: string) => parseCamt052(xml) as any[]; |
| 4 | + |
| 5 | +const MINIMAL_XML = `<Document> |
| 6 | + <BkToCstmrAcctRpt> |
| 7 | + <Rpt> |
| 8 | + <Acct><Id><IBAN>DE27100777770209299700</IBAN></Id></Acct> |
| 9 | + <Bal> |
| 10 | + <Tp><CdOrPrtry><Cd>OPBD</Cd></CdOrPrtry></Tp> |
| 11 | + <Amt Ccy="EUR">1000.00</Amt> |
| 12 | + <Dt><Dt>2024-01-01</Dt></Dt> |
| 13 | + </Bal> |
| 14 | + <Bal> |
| 15 | + <Tp><CdOrPrtry><Cd>CLBD</Cd></CdOrPrtry></Tp> |
| 16 | + <Amt Ccy="EUR">950.00</Amt> |
| 17 | + <Dt><Dt>2024-01-02</Dt></Dt> |
| 18 | + </Bal> |
| 19 | + <Ntry> |
| 20 | + <Amt Ccy="EUR">100.00</Amt> |
| 21 | + <CdtDbtInd>CRDT</CdtDbtInd> |
| 22 | + <AcctSvcrRef>TXN-CREDIT</AcctSvcrRef> |
| 23 | + <BookgDt><Dt>2024-01-02</Dt></BookgDt> |
| 24 | + <ValDt><Dt>2024-01-02</Dt></ValDt> |
| 25 | + <NtryDtls> |
| 26 | + <TxDtls> |
| 27 | + <Dbtr><Nm>Jane Sender</Nm></Dbtr> |
| 28 | + <DbtrAcct><Id><IBAN>DE12345678901234567890</IBAN></Id></DbtrAcct> |
| 29 | + <DbtrAgt><FinInstnId><BICFI>TESTBIC0</BICFI></FinInstnId></DbtrAgt> |
| 30 | + <RmtInf><Ustrd>Invoice 42</Ustrd></RmtInf> |
| 31 | + <Refs> |
| 32 | + <EndToEndId>E2E-001</EndToEndId> |
| 33 | + <MndtId>MNDT-001</MndtId> |
| 34 | + </Refs> |
| 35 | + </TxDtls> |
| 36 | + </NtryDtls> |
| 37 | + </Ntry> |
| 38 | + <Ntry> |
| 39 | + <Amt Ccy="EUR">150.00</Amt> |
| 40 | + <CdtDbtInd>DBIT</CdtDbtInd> |
| 41 | + <AcctSvcrRef>TXN-DEBIT</AcctSvcrRef> |
| 42 | + <BookgDt><Dt>2024-01-02</Dt></BookgDt> |
| 43 | + <ValDt><Dt>2024-01-02</Dt></ValDt> |
| 44 | + <NtryDtls> |
| 45 | + <TxDtls> |
| 46 | + <Cdtr><Nm>ACME Corp</Nm></Cdtr> |
| 47 | + <CdtrAcct><Id><IBAN>DE98765432109876543210</IBAN></Id></CdtrAcct> |
| 48 | + <CdtrAgt><FinInstnId><BICFI>ACMEBIC0</BICFI></FinInstnId></CdtrAgt> |
| 49 | + <RmtInf><Ustrd>Monthly fee</Ustrd></RmtInf> |
| 50 | + </TxDtls> |
| 51 | + </NtryDtls> |
| 52 | + </Ntry> |
| 53 | + </Rpt> |
| 54 | + </BkToCstmrAcctRpt> |
| 55 | +</Document>`; |
| 56 | + |
| 57 | +describe("parseCamt052", () => { |
| 58 | + test("returns empty array for XML without Rpt elements", () => { |
| 59 | + expect(parse("<Document></Document>")).toEqual([]); |
| 60 | + }); |
| 61 | + |
| 62 | + test("parses account identification from IBAN tag", () => { |
| 63 | + const [statement] = parse(MINIMAL_XML) as any[]; |
| 64 | + expect(statement.accountIdentification).toBe("DE27100777770209299700"); |
| 65 | + }); |
| 66 | + |
| 67 | + test("parses opening and closing balances", () => { |
| 68 | + const [statement] = parse(MINIMAL_XML); |
| 69 | + expect(statement.openingBalance?.value).toBe(1000.0); |
| 70 | + expect(statement.openingBalance?.currency).toBe("EUR"); |
| 71 | + expect(statement.closingBalance?.value).toBe(950.0); |
| 72 | + }); |
| 73 | + |
| 74 | + test("parses correct number of transactions", () => { |
| 75 | + const [statement] = parse(MINIMAL_XML); |
| 76 | + expect(statement.transactions).toHaveLength(2); |
| 77 | + }); |
| 78 | + |
| 79 | + describe("credit transaction", () => { |
| 80 | + test("sets isCredit and isExpense correctly", () => { |
| 81 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 82 | + const tx = transactions[0]; |
| 83 | + expect(tx.isCredit).toBe(true); |
| 84 | + expect(tx.isExpense).toBe(false); |
| 85 | + }); |
| 86 | + |
| 87 | + test("sets amount and currency", () => { |
| 88 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 89 | + const tx = transactions[0]; |
| 90 | + expect(tx.amount).toBe(100.0); |
| 91 | + expect(tx.currency).toBe("EUR"); |
| 92 | + }); |
| 93 | + |
| 94 | + test("sets id from AcctSvcrRef", () => { |
| 95 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 96 | + expect(transactions[0].id).toBe("TXN-CREDIT"); |
| 97 | + }); |
| 98 | + |
| 99 | + test("sets name from debtor for credit transactions", () => { |
| 100 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 101 | + expect(transactions[0].name).toBe("Jane Sender"); |
| 102 | + }); |
| 103 | + |
| 104 | + test("sets description from Ustrd", () => { |
| 105 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 106 | + expect(transactions[0].description).toBe("Invoice 42"); |
| 107 | + }); |
| 108 | + |
| 109 | + test("sets bankReference equal to description", () => { |
| 110 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 111 | + const tx = transactions[0]; |
| 112 | + expect(tx.bankReference).toBe(tx.description); |
| 113 | + }); |
| 114 | + |
| 115 | + test("populates descriptionStructured", () => { |
| 116 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 117 | + const structured = transactions[0].descriptionStructured; |
| 118 | + expect(structured.name).toBe("Jane Sender"); |
| 119 | + expect(structured.text).toBe("Invoice 42"); |
| 120 | + expect(structured.iban).toBe("DE12345678901234567890"); |
| 121 | + expect(structured.bic).toBe("TESTBIC0"); |
| 122 | + expect(structured.reference.endToEndRef).toBe("E2E-001"); |
| 123 | + expect(structured.reference.mandateRef).toBe("MNDT-001"); |
| 124 | + }); |
| 125 | + }); |
| 126 | + |
| 127 | + describe("debit transaction", () => { |
| 128 | + test("sets isCredit and isExpense correctly", () => { |
| 129 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 130 | + const tx = transactions[1]; |
| 131 | + expect(tx.isCredit).toBe(false); |
| 132 | + expect(tx.isExpense).toBe(true); |
| 133 | + }); |
| 134 | + |
| 135 | + test("sets name from creditor for debit transactions", () => { |
| 136 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 137 | + expect(transactions[1].name).toBe("ACME Corp"); |
| 138 | + }); |
| 139 | + |
| 140 | + test("sets counterparty IBAN from creditor account", () => { |
| 141 | + const [{ transactions }] = parse(MINIMAL_XML); |
| 142 | + const structured = transactions[1].descriptionStructured; |
| 143 | + expect(structured.iban).toBe("DE98765432109876543210"); |
| 144 | + expect(structured.bic).toBe("ACMEBIC0"); |
| 145 | + }); |
| 146 | + }); |
| 147 | + |
| 148 | + test("handles namespaced XML tags", () => { |
| 149 | + const xml = `<ns2:Document xmlns:ns2="urn:iso:std:iso:20022:tech:xsd:camt.052.001.08"> |
| 150 | + <ns2:BkToCstmrAcctRpt> |
| 151 | + <ns2:Rpt> |
| 152 | + <ns2:Acct><ns2:Id><ns2:IBAN>DE99000000000000000000</ns2:IBAN></ns2:Id></ns2:Acct> |
| 153 | + <ns2:Ntry> |
| 154 | + <ns2:Amt Ccy="EUR">50.00</ns2:Amt> |
| 155 | + <ns2:CdtDbtInd>CRDT</ns2:CdtDbtInd> |
| 156 | + <ns2:AcctSvcrRef>NS-TXN</ns2:AcctSvcrRef> |
| 157 | + <ns2:BookgDt><ns2:Dt>2024-03-01</ns2:Dt></ns2:BookgDt> |
| 158 | + </ns2:Ntry> |
| 159 | + </ns2:Rpt> |
| 160 | + </ns2:BkToCstmrAcctRpt> |
| 161 | + </ns2:Document>`; |
| 162 | + const [statement] = parse(xml); |
| 163 | + expect(statement.accountIdentification).toBe("DE99000000000000000000"); |
| 164 | + expect(statement.transactions).toHaveLength(1); |
| 165 | + expect(statement.transactions[0].amount).toBe(50.0); |
| 166 | + expect(statement.transactions[0].id).toBe("NS-TXN"); |
| 167 | + }); |
| 168 | +}); |
0 commit comments