commit d4b9a5e4a1f2ce8ce01e57349af3565455a91469
parent cf46a7bca60499922fbaaea0e8e05606a8c008d4
Author: Antoine A <>
Date: Wed, 14 Aug 2024 15:49:06 +0200
nexus: parse bank transaction code
Diffstat:
6 files changed, 578 insertions(+), 51 deletions(-)
diff --git a/Makefile b/Makefile
@@ -108,7 +108,7 @@ common-test: install-nobuild-files
./gradlew :common:test --tests $(test) -i
.PHONY: testbench-test
-integration-test: install-nobuild-files
+testbench-test: install-nobuild-files
./gradlew :testbench:test --tests $(test) -i
.PHONY: testbench
diff --git a/nexus/codegen.py b/nexus/codegen.py
@@ -8,7 +8,7 @@ import polars as pl
import requests
-def iso20022codegen():
+def iso20022codegenExternalCodeSet():
# Get XLSX zip file from server
r = requests.get(
"https://www.iso20022.org/sites/default/files/media/file/ExternalCodeSets_XLSX.zip"
@@ -33,10 +33,7 @@ def iso20022codegen():
(value, isoCode, description) = (
row["Code Value"],
row["Code Name"],
- row["Code Definition"]
- .split("\n", 1)[0]
- .rstrip()
- .replace("_x000D_", ""),
+ row["Code Definition"].split("\n", 1)[0].strip().replace("_x000D_", ""),
)
out += f'\n\t{value}("{isoCode}", "{description}"),'
@@ -76,8 +73,86 @@ package tech.libeufin.nexus
{extractCodeSet("ExternalReturnReason1Code", "ExternalReturnReasonCode")}
"""
- with open("src/main/kotlin/tech/libeufin/nexus/Iso20022CodeSets.kt", "w") as file1:
+ with open(
+ "src/main/kotlin/tech/libeufin/nexus/Iso20022ExternalCodeSets.kt", "w"
+ ) as file1:
file1.write(kt)
-iso20022codegen()
+def iso20022codegenBankTransactionCode():
+ # Get XLSX zip file from server
+ r = requests.get(
+ "https://www.iso20022.org/sites/default/files/media/file/BTC_Codification_21March2024.xlsx"
+ )
+ assert r.status_code == 200
+
+ # Get the XLSX file
+ bytes = r.content
+
+ # Parse excel
+ df = pl.read_excel(
+ bytes,
+ sheet_name="BTC_Codification",
+ read_options={"header_row": 2},
+ ).rename(lambda name: name.splitlines()[0])
+
+ def extractCodeSet(setName: str, className: str) -> str:
+ out = f"enum class {className}(val description: String) {{"
+ codeName = f"{setName} Code"
+ for row in (
+ df.group_by(codeName)
+ .agg(pl.col(setName).unique().sort())
+ .sort(codeName)
+ .rows(named=True)
+ ):
+ if len(row[setName]) > 1:
+ print(row)
+
+ (code, description) = (
+ row[codeName].strip().replace("\xa0", ""),
+ row[setName][0].strip(),
+ )
+ out += f'\n\t{code}("{description}"),'
+
+ out += "\n}"
+ return out
+
+ # Write kotlin file
+ kt = f"""/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2024 Taler Systems S.A.
+
+ * LibEuFin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3, or
+ * (at your option) any later version.
+
+ * LibEuFin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+ * Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public
+ * License along with LibEuFin; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>
+ */
+
+// THIS FILE IS GENERATED, DO NOT EDIT
+
+package tech.libeufin.nexus
+
+{extractCodeSet("Domain", "ExternalBankTransactionDomainCode")}
+
+{extractCodeSet("Family", "ExternalBankTransactionFamilyCode")}
+
+{extractCodeSet("SubFamily", "ExternalBankTransactionSubFamilyCode")}
+
+"""
+ with open(
+ "src/main/kotlin/tech/libeufin/nexus/Iso20022BankTransactionCode.kt", "w"
+ ) as file1:
+ file1.write(kt)
+
+
+iso20022codegenExternalCodeSet()
+iso20022codegenBankTransactionCode()
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
@@ -369,23 +369,23 @@ fun parseTx(
}
}
+ /** Parse amount */
fun XmlDestructor.amount(acceptedCurrency: String) = one("Amt") {
val currency = attr("Ccy")
/** FIXME: test by sending non-CHF to PoFi and see which currency gets here. */
if (currency != acceptedCurrency) throw Exception("Currency $currency not supported")
TalerAmount("$currency:${text()}")
}
-
- /** Check if transaction code is reversal */
- fun XmlDestructor.isReversalCode(): Boolean {
+
+ /** Parse bank transaction code */
+ fun XmlDestructor.bankTransactionCode(): BankTransactionCode {
return one("BkTxCd").one("Domn") {
- // TODO automate enum generation for all those code
- val domainCode = one("Cd").text()
+ val domain = one("Cd").enum<ExternalBankTransactionDomainCode>()
one("Fmly") {
- val familyCode = one("Cd").text()
- val subFamilyCode = one("SubFmlyCd").text()
+ val family = one("Cd").enum<ExternalBankTransactionFamilyCode>()
+ val subFamily = one("SubFmlyCd").enum<ExternalBankTransactionSubFamilyCode>()
- subFamilyCode == "RRTN" || subFamilyCode == "RPCR"
+ BankTransactionCode(domain, family, subFamily)
}
}
}
@@ -406,17 +406,19 @@ fun parseTx(
val kind = one("CdtDbtInd").enum<Kind>()
val amount = amount(acceptedCurrency)
one("NtryDtls").one("TxDtls") { // TODO handle batches
+ val code = bankTransactionCode()
+ if (!code.isPayment()) return@one
val txRef = opt("Refs")?.opt("AcctSvcrRef")?.text()
- val reversal = isReversalCode()
val nexusId = nexusId()
- if (reversal) {
+ if (code.isReversal()) {
if (kind == Kind.CRDT) {
val reason = returnReason()
txsInfo.add(TxInfo.CreditReversal(
ref = nexusId ?: txRef ?: entryRef,
bookDate = bookDate,
nexusId = nexusId,
- reason = reason
+ reason = reason,
+ code = code
))
}
} else {
@@ -431,7 +433,8 @@ fun parseTx(
bankId = bankId,
amount = amount,
subject = subject,
- debtorPayto = debtorPayto
+ debtorPayto = debtorPayto,
+ code = code
))
}
Kind.DBIT -> {
@@ -442,7 +445,8 @@ fun parseTx(
nexusId = nexusId,
amount = amount,
subject = subject,
- creditorPayto = creditorPayto
+ creditorPayto = creditorPayto,
+ code = code
))
}
}
@@ -465,7 +469,8 @@ fun parseTx(
}
each("Ntry") {
if (!isBooked()) return@each
- if (isReversalCode()) return@each
+ val code = bankTransactionCode()
+ if (code.isReversal() || !code.isPayment()) return@each
val entryRef = opt("AcctSvcrRef")?.text()
val bookDate = executionDate()
val kind = one("CdtDbtInd").enum<Kind>()
@@ -483,7 +488,8 @@ fun parseTx(
bankId = null,
amount = amount,
subject = subject,
- debtorPayto = debtorPayto
+ debtorPayto = debtorPayto,
+ code = code
))
}
}
@@ -503,8 +509,9 @@ fun parseTx(
}
each("Ntry") {
if (!isBooked()) return@each
+ val code = bankTransactionCode()
// Non reversal transaction are handled in camt.054
- if (!isReversalCode()) return@each
+ if (!code.isReversal() || !code.isPayment()) return@each
val entryRef = opt("AcctSvcrRef")?.text()
val bookDate = executionDate()
@@ -518,7 +525,8 @@ fun parseTx(
ref = nexusId ?: txRef ?: entryRef,
bookDate = bookDate,
nexusId = nexusId,
- reason = reason
+ reason = reason,
+ code = code
))
}
}
@@ -531,8 +539,9 @@ fun parseTx(
}
each("Ntry") {
if (!isBooked()) return@each
+ val code = bankTransactionCode()
// Reversal are handled from camt.053
- if (isReversalCode()) return@each
+ if (code.isReversal() || !code.isPayment()) return@each
val entryRef = opt("AcctSvcrRef")?.text()
val bookDate = executionDate()
@@ -551,7 +560,8 @@ fun parseTx(
bankId = bankId,
amount = amount,
subject = subject,
- debtorPayto = debtorPayto
+ debtorPayto = debtorPayto,
+ code = code
))
}
Kind.DBIT -> {
@@ -563,7 +573,8 @@ fun parseTx(
nexusId = nexusId,
amount = amount,
subject = subject,
- creditorPayto = creditorPayto
+ creditorPayto = creditorPayto,
+ code = code
))
}
}
@@ -589,9 +600,12 @@ private sealed interface TxInfo {
val ref: String?
// When was this transaction booked
val bookDate: Instant
+ // ISO20022 bank transaction code
+ val code: BankTransactionCode
data class CreditReversal(
override val ref: String?,
override val bookDate: Instant,
+ override val code: BankTransactionCode,
// Unique ID generated by libeufin-nexus
val nexusId: String?,
val reason: String?
@@ -599,6 +613,7 @@ private sealed interface TxInfo {
data class Credit(
override val ref: String?,
override val bookDate: Instant,
+ override val code: BankTransactionCode,
// Unique ID generated by payment provider
val bankId: String?,
val amount: TalerAmount,
@@ -608,6 +623,7 @@ private sealed interface TxInfo {
data class Debit(
override val ref: String?,
override val bookDate: Instant,
+ override val code: BankTransactionCode,
// Unique ID generated by libeufin-nexus
val nexusId: String?,
val amount: TalerAmount,
@@ -654,4 +670,23 @@ private fun parseTxLogic(info: TxInfo): TxNotification {
)
}
}
+}
+
+data class BankTransactionCode(
+ val domain: ExternalBankTransactionDomainCode,
+ val family: ExternalBankTransactionFamilyCode,
+ val subFamily: ExternalBankTransactionSubFamilyCode
+) {
+ fun isReversal(): Boolean = REVERSAL_CODE.contains(subFamily)
+ fun isPayment(): Boolean = domain == ExternalBankTransactionDomainCode.PMNT
+
+ override fun toString(): String =
+ "${domain.name} ${family.name} ${subFamily.name} - '${domain.description}' '${family.description}' '${subFamily.description}'"
+
+ companion object {
+ private val REVERSAL_CODE = setOf(
+ ExternalBankTransactionSubFamilyCode.RPCR,
+ ExternalBankTransactionSubFamilyCode.RRTN,
+ )
+ }
}
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022BankTransactionCode.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022BankTransactionCode.kt
@@ -0,0 +1,386 @@
+/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2024 Taler Systems S.A.
+
+ * LibEuFin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3, or
+ * (at your option) any later version.
+
+ * LibEuFin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+ * Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public
+ * License along with LibEuFin; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>
+ */
+
+// THIS FILE IS GENERATED, DO NOT EDIT
+
+package tech.libeufin.nexus
+
+enum class ExternalBankTransactionDomainCode(val description: String) {
+ ACMT("Account Management"),
+ CAMT("Cash Management"),
+ CMDT("Commodities"),
+ DERV("Derivatives"),
+ FORX("Foreign Exchange"),
+ LDAS("Loans, Deposits & Syndications"),
+ PMET("Precious Metal"),
+ PMNT("Payments"),
+ SECU("Securities"),
+ TRAD("Trade Services"),
+ XTND("Extended Domain"),
+}
+
+enum class ExternalBankTransactionFamilyCode(val description: String) {
+ ACCB("Account Balancing"),
+ ACOP("Additional Miscellaneous Credit Operations"),
+ ADOP("Additional Miscellaneous Debit Operations"),
+ BLOC("Blocked Transactions"),
+ CAPL("Cash Pooling"),
+ CASH("Miscellaneous Securities Operations"),
+ CCRD("Customer Card Transactions"),
+ CLNC("Clean Collection"),
+ CNTR("Counter Transactions"),
+ COLC("Custody Collection"),
+ COLL("Collateral Management"),
+ CORP("Corporate Action"),
+ CSLN("Consumer Loans"),
+ CUST("Custody"),
+ DCCT("Documentary Credit"),
+ DLVR("Delivery"),
+ DOCC("Documentary Collection"),
+ DRFT("Drafts"),
+ FTDP("Fixed Term Deposits"),
+ FTLN("Fixed Term Loans"),
+ FTUR("Futures"),
+ FWRD("Forwards"),
+ GUAR("Guarantees"),
+ ICCN("Issued Cash Concentration Transactions"),
+ ICDT("Issued Credit Transfers"),
+ ICHQ("Issued Cheques"),
+ IDDT("Issued Direct Debits"),
+ IRCT("Issued Real-Time Credit Transfers"),
+ LACK("Lack"),
+ LBOX("Lockbox Transactions"),
+ LFUT("Listed Derivatives - Futures"),
+ LOCT("Stand-By Letter Of Credit"),
+ LOPT("Listed Derivatives - Options"),
+ MCOP("Miscellaneous Credit Operations"),
+ MCRD("Merchant Card Transactions"),
+ MDOP("Miscellaneous Debit Operations"),
+ MGLN("Mortgage Loans"),
+ NDFX("Non Deliverable"),
+ NSET("Non Settled"),
+ NTAV("Not Available"),
+ NTDP("Notice Deposits"),
+ NTLN("Notice Loans"),
+ OBND("OTC Derivatives - Bonds"),
+ OCRD("OTC Derivatives - Credit"),
+ OEQT("OTC Derivatives - Equity"),
+ OIRT("OTC Derivatives - Interest Rates"),
+ OPCL("Opening & Closing"),
+ OPTN("Options"),
+ OSED("OTC Derivatives - Structured Exotic Derivatives"),
+ OSWP("OTC Derivatives – Swaps"),
+ OTHB("CSD Blocked transactions"),
+ OTHR("Other"),
+ RCCN("Received Cash Concentration Transactions"),
+ RCDT("Received Credit Transfers"),
+ RCHQ("Received Cheques"),
+ RDDT("Received Direct Debits"),
+ RRCT("Received Real-Time Credit Transfers"),
+ SETT("Trade, Clearing and Settlement"),
+ SPOT("Spots"),
+ SWAP("Swaps"),
+ SYDN("Syndications"),
+}
+
+enum class ExternalBankTransactionSubFamilyCode(val description: String) {
+ ACCC("Account Closing"),
+ ACCO("Account Opening"),
+ ACCT("Account Transfer"),
+ ACDT("ACH Credit"),
+ ACOR("ACH Corporate Trade"),
+ ADBT("ACH Debit"),
+ ADJT("Adjustments (Generic)"),
+ APAC("ACH Pre-Authorised"),
+ ARET("ACH Return"),
+ AREV("ACH Reversal"),
+ ARPD("ARP Debit"),
+ ASET("ACH Settlement"),
+ ATXN("ACH Transaction"),
+ AUTT("Automatic Transfer"),
+ BBDD("SEPA B2B Direct Debit"),
+ BCDP("Branch Deposit"),
+ BCHQ("Bank Cheque"),
+ BCKV("Back Value"),
+ BCWD("Branch Withdrawl"),
+ BFWD("Bond Forward"),
+ BIDS("Repurchase Offer/Issuer Bid/Reverse Rights."),
+ BKFE("Bank Fees"),
+ BONU("Bonus Issue/Capitalisation Issue"),
+ BOOK("Internal Book Transfer"),
+ BPUT("Put Redemption"),
+ BROK("Brokerage Fee"),
+ BSBC("Sell Buy Back"),
+ BSBO("Buy Sell Back"),
+ CAJT("Credit Adjustments (Generic)"),
+ CAPG("Capital Gains Distribution"),
+ CASH("Cash Letter"),
+ CCCH("Certified Customer Cheque"),
+ CCHQ("Cheque"),
+ CCIR("Cross Currency IRS"),
+ CCPC("CCP Cleared Initial Margin"),
+ CCPM("CCP Cleared Variation Margin"),
+ CCSM("CCP Cleared Segregated Initial Margin"),
+ CDIS("Controlled Disbursement"),
+ CDPT("Cash Deposit"),
+ CHAR("Charge/Fees"),
+ CHKD("Check Deposit"),
+ CHRG("Charges (Generic)"),
+ CLAI("Compensation/Claims"),
+ CLCQ("Circular Cheque"),
+ CMBO("Corporate Mark Broker Owned"),
+ CMCO("Corporate Mark Client Owned"),
+ COME("Commission Excluding Taxes (Generic)"),
+ COMI("Commission Including Taxes (Generic)"),
+ COMM("Commission (Generic)"),
+ COMT("Non Taxable Commissions (Generic)"),
+ CONV("Conversion"),
+ COVE("Cover Transaction"),
+ CPEN("Cash Penalties"),
+ CPRB("Corporate Rebate"),
+ CQRV("Cheque Reversal"),
+ CRCQ("Crossed Cheque"),
+ CRDS("Credit DefaultSwap"),
+ CROS("Cross Trade"),
+ CRPR("Cross Product"),
+ CRSP("Credit Support"),
+ CRTL("Credit Line"),
+ CSHA("Cash Letter Adjustment"),
+ CSLI("Cash In Lieu"),
+ CWDL("Cash Withdrawal"),
+ DAJT("Debit Adjustments (Generic)"),
+ DDFT("Discounted Draft"),
+ DDWN("Drawdown"),
+ DECR("Decrease in Value"),
+ DMCG("Draft Maturity Change"),
+ DMCT("Domestic Credit Transfer"),
+ DPST("Deposit"),
+ DRAW("Drawing"),
+ DRIP("Dividend Reinvestment"),
+ DSBR("Controlled Disbursement"),
+ DTCH("Dutch Auction"),
+ DVCA("Cash Dividend"),
+ DVOP("Dividend Option"),
+ ENCT("Nordic Payment Council Credit Transfer"),
+ EQBO("Equity Mark Broker Owned"),
+ EQCO("Equity Mark Client Owned"),
+ EQPT("Equity Option"),
+ EQUS("Equity Swap"),
+ ERTA("Exchange Rate Adjustment"),
+ ERWA("Lending Income"),
+ ERWI("Borrowing Fee"),
+ ESCT("SEPA Credit Transfer"),
+ ESDD("SEPA Core Direct Debit"),
+ EXOF("Exchange"),
+ EXPT("Exotic Option"),
+ EXRI("Call On Intermediate Securities"),
+ EXTD("Exchange Traded Derivatives"),
+ EXWA("Warrant Exercise/Warrant Conversion"),
+ FCDP("Foreign Currencies Deposit"),
+ FCTA("Factor Update"),
+ FCWD("Foreign Currencies Withdrawal"),
+ FEES("Fees (Generic)"),
+ FICT("Financial Institution Credit Transfer"),
+ FIDD("Financial Institution Direct Debit Payment"),
+ FIOA("Financial Institution Own Account Transfer"),
+ FIXI("Fixed Income"),
+ FLTA("Float Adjustment"),
+ FRZF("Freeze Of Funds"),
+ FUCO("Futures Commission"),
+ FUTU("Future Variation Margin"),
+ FWBC("Forwards Broker Owned Collateral"),
+ FWCC("Forwards Client Owned Collateral"),
+ FWSB("MFA Segregated Broker Cash Collateral"),
+ FWSC("MFA Segregated Client Cash Collateral"),
+ GEN1("Withdrawal/Distribution"),
+ GEN2("Deposit/Contribution"),
+ IADD("Invoice Accepted with Differed Due Date"),
+ INFD("Fixed Deposit Interest Amount"),
+ INSP("Inspeci/Share Exchange"),
+ INTR("Interest Payment"),
+ ISSU("Depositary Receipt Issue"),
+ LBCA("Credit Adjustment"),
+ LBDP("Deposit"),
+ LIQU("Liquidation Dividend / Liquidation Payment"),
+ MARG("Margin Payments"),
+ MBSB("Mortgage Back Segregated Broker Cash Collateral"),
+ MBSC("Mortgage Back Segregated Client Cash Collateral"),
+ MCAL("Full Call / Early Redemption"),
+ MGCC("Margin Client Owned Cash Collateral"),
+ MGSC("Initial Futures Margin Segregated Client Cash Collateral"),
+ MIXD("Mixed Deposit"),
+ MNFE("Management Fees"),
+ MRGR("Merger"),
+ MSCD("Miscellaneous Deposit"),
+ NETT("Netting"),
+ NPCC("Non Presented Circular Cheques"),
+ NSYN("Non Syndicated"),
+ NTAV("Not Available"),
+ NWID("New issue distribution"),
+ OCCC("Client owned OCC pledged collateral"),
+ ODFT("Overdraft"),
+ ODLT("Odd Lot Sale/Purchase"),
+ OODD("One-Off Direct Debit"),
+ OPBC("Option Broker Owned Collateral"),
+ OPCC("Option Client Owned Collateral"),
+ OPCQ("Open Cheque"),
+ OPSB("OTC Option Segregated Broker Cash Collateral"),
+ OPSC("OTC Option Segregated Client Cash Collateral"),
+ OPTN("FX Option"),
+ ORCQ("Order Cheque"),
+ OTCC("OTC CCP"),
+ OTCD("OTC Derivatives"),
+ OTCG("OTC"),
+ OTCN("OTC Non-CCP"),
+ OTHR("Other"),
+ OVCH("Overdraft Charge"),
+ OWNE("External Account Transfer"),
+ OWNI("Internal Account Transfer"),
+ PADD("Pre-Authorised Direct Debit"),
+ PAIR("Pair-Off"),
+ PCAL("Partial Redemption With Reduction Of Nominal Value"),
+ PLAC("Placement"),
+ PMDD("Direct Debit"),
+ PORT("Portfolio Move"),
+ POSC("Credit Card Payment"),
+ POSD("Point-of-Sale (POS) Payment - Debit Card"),
+ PPAY("Principal Payment"),
+ PRCT("Priority Credit Transfer"),
+ PRDD("Reversal Due To Payment Reversal"),
+ PRED("Partial Redemption Without Reduction Of Nominal Value"),
+ PRII("Interest Payment with Principles"),
+ PRIN("Interest Payment with Principles"),
+ PRIO("Priority Issue"),
+ PRUD("Principal Pay-Down/Pay-Up"),
+ PSTE("Posting Error"),
+ RCDD("Reversal Due To Payment Cancellation Request"),
+ RCOV("Reversal due to a Cover Transaction Return"),
+ REAA("Redemption Asset Allocation"),
+ REDM("Final Maturity"),
+ REPU("Repo"),
+ RESI("Futures Residual Amount"),
+ RHTS("Rights Issue/Subscription Rights/Rights Offer"),
+ RIMB("Reimbursement (Generic)"),
+ RNEW("Renewal"),
+ RPBC("Bi-lateral repo broker owned collateral"),
+ RPCC("Repo client owned collateral"),
+ RPCR("Reversal Due To Payment Cancellation Request"),
+ RPMT("Repayment"),
+ RPSB("Bi-lateral Repo Segregated Broker Cash Collateral"),
+ RPSC("Bi-lateral Repo Segregated Client Cash Collateral"),
+ RRTN("Reversal Due To Payment Return"),
+ RVPO("Reverse Repo"),
+ RWPL("Redemption Withdrawing Plan"),
+ SABG("Settlement Against Bank Guarantee"),
+ SALA("Payroll/Salary Payment"),
+ SBSC("Securities Buy Sell Sell Buy Back"),
+ SCIE("Single Currency IRS Exotic"),
+ SCIR("Single Currency IRS"),
+ SCRP("Securities Cross Products"),
+ SDVA("Same Day Value Credit Transfer"),
+ SECB("Securities Borrowing"),
+ SECL("Securities Lending"),
+ SHBC("Broker owned collateral Short Sale"),
+ SHCC("Client owned collateral Short Sale"),
+ SHPR("Equity Premium Reserve"),
+ SHSL("Short Sell"),
+ SLBC("Lending Broker Owned Cash Collateral"),
+ SLCC("Lending Client Owned Cash Collateral"),
+ SLEB("Securities Lending And Borrowing"),
+ SLOA("SecuredLoan"),
+ SOSE("Settlement Of Sight Export Document"),
+ SOSI("Settlement Of Sight Import Document"),
+ SSPL("Subscription Savings Plan"),
+ STAC("Settlement After Collection"),
+ STAM("Settlement At Maturity"),
+ STDO("Standing Order"),
+ STLM("Settlement"),
+ STLR("Settlement Under Reserve"),
+ STOD("Bill of Exchange Settlement on Demand"),
+ SUAA("Subscription Asset Allocation"),
+ SUBS("Subscription"),
+ SWAP("Swap Payment"),
+ SWBC("Swap Broker Owned Collateral"),
+ SWCC("Client Owned Collateral"),
+ SWEP("Sweep"),
+ SWFP("Final Payment"),
+ SWIC("Switch"),
+ SWPP("Partial Payment"),
+ SWPT("Swaption"),
+ SWRS("Reset Payment"),
+ SWSB("ISDA/CSA Segregated Broker Cash Collateral"),
+ SWSC("ISDA/CSA Segregated Client Cash Collateral"),
+ SWUF("Upfront Payment"),
+ SYND("Syndicated"),
+ TAXE("Taxes (Generic)"),
+ TBAC("TBA Closing"),
+ TBAS("To Be Announced"),
+ TBBC("TBA Broker owned cash collateral"),
+ TBCC("TBA Client owned cash collateral"),
+ TCDP("Travellers Cheques Deposit"),
+ TCWD("Travellers Cheques Withdrawal"),
+ TEND("Tender"),
+ TOPG("Topping"),
+ TOUT("Transfer Out"),
+ TRAD("Trade"),
+ TRCP("Treasury Cross Product"),
+ TREC("Tax Reclaim"),
+ TRFE("Transaction Fees"),
+ TRIN("Transfer In"),
+ TRPO("Triparty Repo"),
+ TRVO("Triparty Reverse Repo"),
+ TTLS("Treasury Tax And Loan Service"),
+ TURN("Turnaround"),
+ UDFT("Dishonoured/Unpaid Draft"),
+ UNCO("Underwriting Commission"),
+ UPCQ("Unpaid Cheque"),
+ UPCT("Unpaid Card Transaction"),
+ UPDD("Reversal Due To Return/Unpaid Direct Debit"),
+ URCQ("Cheque Under Reserve"),
+ URDD("Direct Debit Under Reserve"),
+ VALD("Value Date"),
+ VCOM("Credit Transfer With Agreed Commercial Information"),
+ WITH("Withholding Tax"),
+ XBCP("Cross-Border Credit Card Payment"),
+ XBCQ("Foreign Cheque"),
+ XBCT("Cross-Border Credit Transfer"),
+ XBCW("Cross-Border Cash Withdrawal"),
+ XBRD("Cross-Border"),
+ XBSA("Cross-Border Payroll/Salary Payment"),
+ XBST("Cross-Border Standing Order"),
+ XCHC("Exchange Traded CCP"),
+ XCHG("Exchange Traded"),
+ XCHN("Exchange Traded Non-CCP"),
+ XICT("Cross-Border Intra Company Transfer"),
+ XPCQ("Unpaid Foreign Cheque"),
+ XRCQ("Foreign Cheque Under Reserve"),
+ XRTN("Cross Border Reversal Due to Payment Return"),
+ YTDA("YTD Adjustment"),
+ ZABA("Zero Balancing"),
+ ACON("ACH Concentration"),
+ BACT("Branch Account Transfer"),
+ COAT("Corporate Own Account Transfer"),
+ ICCT("Intra Company Transfer"),
+ LBDB("Debit"),
+ POSP("Point-of-Sale (POS) Payment"),
+ SMCD("Smart-Card Payment"),
+ SMRT("Smart-Card Payment"),
+ XBDD("Cross-Border Direct Debit"),
+}
+
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022CodeSets.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022ExternalCodeSets.kt
diff --git a/testbench/src/test/kotlin/Iso20022Test.kt b/testbench/src/test/kotlin/Iso20022Test.kt
@@ -18,12 +18,10 @@
*/
import org.junit.Test
-import tech.libeufin.nexus.ebics.Dialect
-import tech.libeufin.nexus.nexusConfig
-import tech.libeufin.nexus.parseCustomerAck
-import tech.libeufin.nexus.parseCustomerPaymentStatusReport
-import tech.libeufin.nexus.parseTx
+import tech.libeufin.nexus.ebics.*
+import tech.libeufin.nexus.*
import java.nio.file.Files
+import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.exists
import kotlin.io.path.isDirectory
@@ -52,29 +50,62 @@ class Iso20022Test {
if (!root.exists()) return
for (platform in root.listDirectoryEntries()) {
if (!platform.isDirectory()) continue
+
+ // List logs
+ var logs = mutableListOf<Path>()
for (file in platform.listDirectoryEntries()) {
if (!file.isDirectory()) continue
- val fetch = file.resolve("fetch")
- if (!fetch.exists()) continue
- val nexusCfg = nexusConfig(platform.resolve("ebics.conf"))
- val cfg = nexusCfg.ebics
- val currency = cfg.currency
- val dialect = cfg.dialect
- for (log in fetch.listDirectoryEntries()) {
- val content = Files.newInputStream(log)
- val name = log.toString()
- println(name)
- if (name.contains("HAC")) {
- parseCustomerAck(content)
- } else if (name.contains("pain.002")) {
- parseCustomerPaymentStatusReport(content)
- } else if (
- !name.contains("camt.052") && !name.contains("_C52_") && !name.contains("_Z01_")
- ) {
- parseTx(content, currency, dialect)
+ when (file.fileName.toString()) {
+ "fetch" -> for (transaction in file.listDirectoryEntries()) {
+ if (transaction.isDirectory()) {
+ logs.addAll(transaction.listDirectoryEntries())
+ }
+ }
+ "submit" -> {}
+ else -> for (transaction in file.listDirectoryEntries()) {
+ when (transaction.fileName.toString()) {
+ "fetch" -> logs.addAll(transaction.listDirectoryEntries())
+ "submit" -> {}
+ else -> {
+ var payload = transaction.resolve("payload")
+ if (payload.exists()) {
+ logs.addAll(payload.listDirectoryEntries())
+ continue
+ }
+ payload = transaction.resolve("payload.xml")
+ if (payload.exists()) {
+ logs.add(payload)
+ }
+ }
+ }
}
}
}
+
+ // Load config
+ val nexusCfg = nexusConfig(platform.resolve("ebics.conf"))
+ val cfg = nexusCfg.ebics
+ val currency = cfg.currency
+ val dialect = cfg.dialect
+ // Parse logs
+ for (log in logs) {
+ val content = Files.newInputStream(log)
+ val name = log.toString()
+ println(name)
+ if (name.contains("wssparam")) {
+ // Skip
+ } else if (name.contains("HAC")) {
+ parseCustomerAck(content)
+ } else if (name.contains("HKD")) {
+ EbicsAdministrative.parseHKD(content)
+ } else if (name.contains("pain.002")) {
+ parseCustomerPaymentStatusReport(content)
+ } else if (
+ !name.contains("camt.052") && !name.contains("_C52_") && !name.contains("_Z01_")
+ ) {
+ parseTx(content, currency, dialect)
+ }
+ }
}
}
}
\ No newline at end of file