Skip to content

Instantly share code, notes, and snippets.

@eerohele
Last active April 14, 2017 19:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eerohele/00cb0988546aea37e490abcc5ca60786 to your computer and use it in GitHub Desktop.
Save eerohele/00cb0988546aea37e490abcc5ca60786 to your computer and use it in GitHub Desktop.
Example of a Clojure Schematron implementation
(ns ^:no-doc clojutron.examples.pain-mdr
(:require [clojure.java.io :as io]
[clojure.data.zip.xml :refer [xml1-> attr text]]
[clojutron.schema :refer :all]
[clojure.edn :as edn])
(:refer-clojure :exclude [assert])
(:import (org.iban4j BicUtil IbanUtil)))
(def iso4217-currency-codes
"Read the set of valid ISO 4217 currency codes from an EDN file."
(-> "ccy.edn" io/resource slurp edn/read-string delay))
(defn has-valid-currency-code?
"Check whether a payments initiation message amount has a valid ISO4216
currency code."
[amount]
(boolean (some #{(xml1-> amount (attr :Ccy))} @iso4217-currency-codes)))
(defn has-valid-iban-number?
"Check whether the given IBAN is valid."
[iban]
(IbanUtil/validate (xml1-> iban text)))
(defn has-valid-bic-code?
"Check whether the given BIC is valid."
[bic]
(BicUtil/validate (xml1-> bic text)))
(def pain-mdr-schema
"A Schematron-like schema for validating a ISO20022 Payments Initiation XML message."
(schema "ISO20022 Payments Initiation Message Schema"
{:let {:CdtTrfTxInf "Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf"}}
(pattern "Currency"
{:id :Ccy}
(rule "*[@Ccy]" of
(assert "The currency code is a valid active or historic currency code."
has-valid-currency-code?)))
(pattern "BIC"
{:id :BIC}
(rule "BIC" of
(assert "The BIC is a valid ISO9362 Business Identifier Code."
has-valid-bic-code?)))
(pattern "IBAN"
{:id :IBAN}
(rule "IBAN" of
(assert "The IBAN is a valid ISO13616 International Bank Account Number."
has-valid-iban-number?)))
(pattern "Group Header"
{:id :GrpHdr}
(rule "GrpHdr/NbOfTxs" of
(assert "The reported number of transactions matches the actual number of transactions in the message."
"xs:int(.) eq count($CdtTrfTxInf)"))
(rule "GrpHdr/CtrlSum" of
(assert "The reported control sum matches the actual total sum of all transactions in the message."
"xs:double(.) eq sum($CdtTrfTxInf/Amt/*)")))
(pattern "Charge Bearer"
{:id :ChrgBr}
(rule "PmtInf[ChrgBr]" of
(assert "If the payment defines a charge bearer, the transaction must not define it."
"empty(CdtTrfTxInf/ChrgBr)"))
(rule "CdtTrfTxInf[ChrgBr]" of
(assert "If the transaction defines a charge bearer, the payment must not define it."
"empty(../ChrgBr)")))
(pattern "Charges Account"
{:id :ChrgsAcct}
(rule "ChrgsAcctAgt" of
(assert "If charges account agent is present, then charges account must be present."
"exists(../ChrgsAcct)")))
(pattern "Cheque"
{:id :Chq}
(rule "PmtInf[PmtMtd eq 'CHK']" of
(assert "If payment method is CHK, then transaction creditor account is not allowed."
"empty(CdtTrfTxInf/CdtrAcct)")))
(pattern "Credit Note"
{:id :CREN}
(rule "RmtInf[Strd/RfrdDocInf/Tp/CdOrPrtry/Cd = 'CREN']" of
(assert "The sum total amount of all credit notes within a transaction must be less than or equal to the sum total amount of the credit invoices within that transaction."
"sum(Strd[RfrdDocInf/Tp/CdOrPrtry/Cd = 'CREN']/RfrdDocAmt/CdtNote) le sum(Strd[RfrdDocInf/Tp/CdOrPrtry/Cd = 'CINV']/RfrdDocAmt/*)")))))
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>ABC/090928/CCT001</MsgId>
<CreDtTm>2009-09-28T14:07:00</CreDtTm>
<NbOfTxs>3</NbOfTxs>
<CtrlSum>112500001</CtrlSum>
<InitgPty>
<Nm>ABC Corporation</Nm>
<PstlAdr>
<StrtNm>Times Square</StrtNm>
<BldgNb>7</BldgNb>
<PstCd>NY 10036</PstCd>
<TwnNm>New York</TwnNm>
<Ctry>US</Ctry>
</PstlAdr>
</InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>ABC/086</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<BtchBookg>false</BtchBookg>
<NbOfTxs>3</NbOfTxs>
<CtrlSum>11250000</CtrlSum>
<ReqdExctnDt>2009-09-29</ReqdExctnDt>
<Dbtr>
<Nm>ABC Corporation</Nm>
<PstlAdr>
<StrtNm>Times Square</StrtNm>
<BldgNb>7</BldgNb>
<PstCd>NY 10036</PstCd>
<TwnNm>New York</TwnNm>
<Ctry>US</Ctry>
</PstlAdr>
</Dbtr>
<DbtrAcct>
<Id>
<Othr>
<Id>00125574999</Id>
</Othr>
</Id>
</DbtrAcct>
<DbtrAgt>
<FinInstnId>
<BIC>BBBBUS33</BIC>
</FinInstnId>
</DbtrAgt>
<CdtTrfTxInf>
<PmtId>
<InstrId>ABC/090928/CCT001/01</InstrId>
<EndToEndId>ABC/4562/2009-09-08</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="JPY">10000000</InstdAmt>
</Amt>
<ChrgBr>SHAR</ChrgBr>
<CdtrAgt>
<FinInstnId>
<BIC>AAAAGB2L</BIC>
</FinInstnId>
</CdtrAgt>
<Cdtr>
<Nm>DEF Electronics</Nm>
<PstlAdr>
<AdrLine>Corn Exchange 5th Floor</AdrLine>
<AdrLine>Mark Lane 55</AdrLine>
<AdrLine>EC3R7NE London</AdrLine>
<AdrLine>GB</AdrLine>
</PstlAdr>
</Cdtr>
<CdtrAcct>
<Id>
<Othr>
<Id>23683707994125</Id>
</Othr>
</Id>
</CdtrAcct>
<Purp>
<Cd>CINV</Cd>
</Purp>
<RmtInf>
<Strd>
<RfrdDocInf>
<Nb>4562</Nb>
<RltdDt>2009-09-08</RltdDt>
</RfrdDocInf>
</Strd>
</RmtInf>
</CdtTrfTxInf>
<CdtTrfTxInf>
<PmtId>
<InstrId>ABC/090628/CCT001/2</InstrId>
<EndToEndId>ABC/ABC-13679/2009-09-15</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="EUR">250000</InstdAmt>
</Amt>
<ChrgBr>CRED</ChrgBr>
<CdtrAgt>
<FinInstnId>
<BIC>DDDDBEBB</BIC>
</FinInstnId>
</CdtrAgt>
<Cdtr>
<Nm>GHI Semiconductors</Nm>
<PstlAdr>
<StrtNm>Avenue Brugmann</StrtNm>
<BldgNb>415</BldgNb>
<PstCd>1180</PstCd>
<TwnNm>Brussels</TwnNm>
</PstlAdr>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>BE30001216371411</IBAN>
</Id>
</CdtrAcct>
<InstrForCdtrAgt>
<Cd>PHOB</Cd>
<InstrInf>+32/2/2222222</InstrInf>
</InstrForCdtrAgt>
<Purp>
<Cd>GDDS</Cd>
</Purp>
<RmtInf>
<Strd>
<RfrdDocInf>
<Tp>
<CdOrPrtry>
<Cd>CINV</Cd>
</CdOrPrtry>
</Tp>
<Nb>ABC-13679</Nb>
<RltdDt>2009-09-15</RltdDt>
</RfrdDocInf>
<RfrdDocAmt>
<RmtdAmt Ccy="EUR">500000</RmtdAmt>
</RfrdDocAmt>
</Strd>
<Strd>
<RfrdDocInf>
<Tp>
<CdOrPrtry>
<Cd>CREN</Cd>
</CdOrPrtry>
</Tp>
<Nb>ABC-22173</Nb>
<RltdDt>2009-09-16</RltdDt>
</RfrdDocInf>
<RfrdDocAmt>
<CdtNoteAmt Ccy="EUR">250000</CdtNoteAmt>
</RfrdDocAmt>
</Strd>
</RmtInf>
</CdtTrfTxInf>
<CdtTrfTxInf>
<PmtId>
<InstrId>ABC/090928/CCT001/3</InstrId>
<EndToEndId>ABC/987-AC/2009-09-27</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="USD">1000000</InstdAmt>
</Amt>
<ChrgBr>SHAR</ChrgBr>
<CdtrAgt>
<FinInstnId>
<BIC>BBBBUS66</BIC>
</FinInstnId>
</CdtrAgt>
<Cdtr>
<Nm>ABC Corporation</Nm>
<PstlAdr>
<Dept>Treasury department</Dept>
<StrtNm>Bush Street</StrtNm>
<BldgNb>13</BldgNb>
<PstCd>CA 94108</PstCd>
<TwnNm>San Francisco</TwnNm>
<Ctry>US</Ctry>
</PstlAdr>
</Cdtr>
<CdtrAcct>
<Id>
<Othr>
<Id>4895623</Id>
</Othr>
</Id>
</CdtrAcct>
<Purp>
<Cd>INTC</Cd>
</Purp>
<RmtInf>
<Strd>
<RfrdDocInf>
<Tp>
<CdOrPrtry>
<Cd>CINV</Cd>
</CdOrPrtry>
</Tp>
<Nb>987-AC</Nb>
<RltdDt>2009-09-27</RltdDt>
</RfrdDocInf>
</Strd>
</RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment