diff --git a/docs/index.md b/docs/index.md index 46e007e..63b3038 100644 --- a/docs/index.md +++ b/docs/index.md @@ -61,6 +61,12 @@ for transaction in transactions: print(transaction) ``` +**Note**: Each transaction includes remittance information in two fields: +- `RemittanceInformation`: Contains only the first unstructured remittance line +- `RemittanceInformationFull`: Contains all unstructured remittance lines joined with spaces + +This is particularly useful for international transactions that may contain multiple lines of remittance information. + ### Extracting Statement Information To get basic statement information like IBAN and opening/closing balance: diff --git a/pycamt/parser.py b/pycamt/parser.py index 2d1b4e3..1475397 100644 --- a/pycamt/parser.py +++ b/pycamt/parser.py @@ -258,6 +258,9 @@ def _extract_transaction_details(self, tx_detail): ------- dict Detailed information extracted from the transaction detail element. + Includes RemittanceInformation (first Ustrd) and RemittanceInformationFull + (all Ustrd elements joined with spaces) for backward compatibility and + comprehensive remittance data capture. """ data = { @@ -306,6 +309,11 @@ def _extract_transaction_details(self, tx_detail): if tx_detail.find(".//RmtInf//Ustrd", self.namespaces) is not None else None ), + "RemittanceInformationFull": ( + " ".join(rinfo.text.strip() for rinfo in tx_detail.findall(".//RmtInf//Ustrd", self.namespaces) if rinfo.text) + if tx_detail.findall(".//RmtInf//Ustrd", self.namespaces) + else None + ), } structured_remittance_elem = tx_detail.find(".//RmtInf//Strd", self.namespaces) diff --git a/tests/test_parser.py b/tests/test_parser.py index 6b4e7ad..af424fc 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -88,3 +88,167 @@ def test_get_statement_info(self, parser): "ClosingBalanceDate": None }] assert parser.get_statement_info() == expected + + def test_multiple_remittance_information(self): + """Test that multiple Ustrd elements in RemittanceInformation are captured""" + xml_data = """ + + + + ABC123 + 2020-06-23T18:56:25.64Z + + + + + GB33BUKB20201555555555 + + + + 69.06 + CRDT + +
2020-07-07
+
+ +
2020-07-07
+
+ 123 + + + + ENDTOENDID123 + + + Ref.. 1234567890123456 + Betrag EUR 69,06 + /ADRS CH/Basel,1234 + A12345/ Best 00123456 + 7185) Peter Pan + Urspr. EUR 69,06 + ADRS Re xxxxx,4 CH/Basel + Land,1234/ + ERST RAIFCH12345 + + + +
+
+
+
+ """ + parser = Camt053Parser(xml_data) + transactions = parser.get_transactions() + + assert len(transactions) == 1 + transaction = transactions[0] + + # Current behavior: only first Ustrd element + assert transaction["RemittanceInformation"] == "Ref.. 1234567890123456" + + # New behavior: all Ustrd elements joined + expected_full = ( + "Ref.. 1234567890123456 Betrag EUR 69,06 " + "/ADRS CH/Basel,1234 A12345/ Best 00123456 7185) Peter Pan " + "Urspr. EUR 69,06 ADRS Re xxxxx,4 CH/Basel " + "Land,1234/ ERST RAIFCH12345" + ) + assert "RemittanceInformationFull" in transaction + assert transaction["RemittanceInformationFull"] == expected_full + + def test_single_remittance_information_backward_compatibility(self): + """Test that single Ustrd element works for both fields""" + xml_data = """ + + + + ABC123 + 2020-06-23T18:56:25.64Z + + + + + GB33BUKB20201555555555 + + + + 500.00 + CRDT + +
2020-06-23
+
+ +
2020-06-23
+
+ 123 + + + + ENDTOENDID123 + + + Single remittance info + + + +
+
+
+
+ """ + parser = Camt053Parser(xml_data) + transactions = parser.get_transactions() + + assert len(transactions) == 1 + transaction = transactions[0] + + # Both fields should have the same value for single Ustrd + assert transaction["RemittanceInformation"] == "Single remittance info" + assert transaction["RemittanceInformationFull"] == "Single remittance info" + + def test_no_remittance_information(self): + """Test that transactions without RemittanceInformation still work""" + xml_data = """ + + + + ABC123 + 2020-06-23T18:56:25.64Z + + + + + GB33BUKB20201555555555 + + + + 500.00 + CRDT + +
2020-06-23
+
+ +
2020-06-23
+
+ 123 + + + + ENDTOENDID123 + + + +
+
+
+
+ """ + parser = Camt053Parser(xml_data) + transactions = parser.get_transactions() + + assert len(transactions) == 1 + transaction = transactions[0] + + # Both fields should be None when no remittance info exists + assert "RemittanceInformation" not in transaction # Filtered out by return statement + assert "RemittanceInformationFull" not in transaction # Filtered out by return statement