cbpr-usage-rules 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/CHANGELOG.md +10 -0
- {cbpr_usage_rules-0.1.2/src/cbpr_usage_rules.egg-info → cbpr_usage_rules-0.1.3}/PKG-INFO +2 -2
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/README.md +1 -1
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/__init__.py +1 -1
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/registry.py +55 -6
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/camt_052.py +1 -29
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/camt_054.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_002.py +1 -5
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_004.py +1 -27
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_008.py +1 -9
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_008_stp.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_009.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_009_adv.py +1 -9
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_009_cov.py +1 -9
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pain_001.py +1 -26
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/camt_052.py +0 -14
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/camt_054.py +1 -14
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_002.py +1 -10
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_004.py +1 -27
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_008.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_008_stp.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_009.py +1 -10
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_009_adv.py +1 -10
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_009_cov.py +1 -9
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pain_001.py +1 -19
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3/src/cbpr_usage_rules.egg-info}/PKG-INFO +2 -2
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/LICENSE +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/MANIFEST.in +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/pyproject.toml +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/setup.cfg +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/cli.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/engine.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/helpers.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/loader.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/message.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/models.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/py.typed +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/reference/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/reference/countries.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/reference/currencies.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/schema.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/validators/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/validators/bic.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/validators/country.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/validators/currency.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/validators/iban.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/validators/lei.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/SOURCES.txt +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/dependency_links.txt +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/entry_points.txt +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/requires.txt +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/top_level.txt +0 -0
|
@@ -4,6 +4,16 @@ All notable changes to this project are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres
|
|
5
5
|
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.1.3]
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Country (`VAL-CTRY`) and currency (`VAL-CCY`) validation are now single universal
|
|
11
|
+
rules, like IBAN/LEI. `VAL-CTRY` checks every `<Ctry>` element; `VAL-CCY` checks
|
|
12
|
+
every `Ccy` attribute and `<Ccy>` element, document-wide, for all message types.
|
|
13
|
+
This closes gaps where country was unvalidated (e.g. pacs.008 2025, the pacs.009
|
|
14
|
+
family) and currency was unvalidated (pacs.002 2025), and replaces the ~13
|
|
15
|
+
per-module `VAL-CTRY` and ~19 per-module `VAL-CCY` rules.
|
|
16
|
+
|
|
7
17
|
## [0.1.2]
|
|
8
18
|
|
|
9
19
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cbpr-usage-rules
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Validate ISO 20022 CBPR+ XML messages against SWIFT usage (business) rules.
|
|
5
5
|
Author: Pete Houghton
|
|
6
6
|
License: MIT License
|
|
@@ -141,7 +141,7 @@ Both `validate_file` and `validate_string` return a dictionary:
|
|
|
141
141
|
"message_type": "pacs.008", # the rule set that was applied
|
|
142
142
|
"detected_message_type": "pacs.008",# auto-detected from the Document namespace
|
|
143
143
|
"year": 2025,
|
|
144
|
-
"rules_evaluated":
|
|
144
|
+
"rules_evaluated": 87,
|
|
145
145
|
"violations": [
|
|
146
146
|
{
|
|
147
147
|
"rule_number": "pacs.008:R41", # unique within the message type
|
|
@@ -86,7 +86,7 @@ Both `validate_file` and `validate_string` return a dictionary:
|
|
|
86
86
|
"message_type": "pacs.008", # the rule set that was applied
|
|
87
87
|
"detected_message_type": "pacs.008",# auto-detected from the Document namespace
|
|
88
88
|
"year": 2025,
|
|
89
|
-
"rules_evaluated":
|
|
89
|
+
"rules_evaluated": 87,
|
|
90
90
|
"violations": [
|
|
91
91
|
{
|
|
92
92
|
"rule_number": "pacs.008:R41", # unique within the message type
|
|
@@ -4,7 +4,7 @@ This package is AI generated. See the README for details and caveats.
|
|
|
4
4
|
"""
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
__version__ = "0.1.
|
|
7
|
+
__version__ = "0.1.3"
|
|
8
8
|
|
|
9
9
|
from .models import Rule, Severity, Violation
|
|
10
10
|
from .engine import validate_file, validate_string, list_rules, available
|
|
@@ -11,7 +11,7 @@ import pkgutil
|
|
|
11
11
|
from typing import Callable, Dict, List, Tuple
|
|
12
12
|
|
|
13
13
|
from .models import Rule, Severity, Violation
|
|
14
|
-
from .validators import is_valid_iban, is_valid_lei
|
|
14
|
+
from .validators import is_valid_country, is_valid_currency, is_valid_iban, is_valid_lei
|
|
15
15
|
|
|
16
16
|
# (year, message_type) -> list[Rule]
|
|
17
17
|
_REGISTRY: Dict[Tuple[int, str], List[Rule]] = {}
|
|
@@ -99,24 +99,32 @@ def _discover(year: int) -> None:
|
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
# Cross-cutting datatype validations applied to every message type. In ISO 20022
|
|
102
|
-
# these
|
|
102
|
+
# these are self-validating datatypes wherever they appear, so a single
|
|
103
103
|
# document-wide scan covers all message types (and all party positions - Debtor,
|
|
104
104
|
# Creditor, Agent, Intermediary) correctly:
|
|
105
105
|
# <IBAN> is IBAN2007Identifier (ISO 7064 mod-97);
|
|
106
|
-
# <LEI> is LEIIdentifier (ISO 17442, 18 alphanumerics + 2 ISO 7064 check digits)
|
|
106
|
+
# <LEI> is LEIIdentifier (ISO 17442, 18 alphanumerics + 2 ISO 7064 check digits);
|
|
107
|
+
# <Ctry> is CountryCode (ISO 3166-1 alpha-2).
|
|
107
108
|
# (number, element, validator, name, description)
|
|
108
|
-
|
|
109
|
+
_UNIVERSAL_ELEMENT = [
|
|
109
110
|
("VAL-IBAN", "IBAN", is_valid_iban, "CBPR_Valid_IBAN",
|
|
110
111
|
"Every IBAN must be structurally valid and pass the ISO 7064 mod-97 check."),
|
|
111
112
|
("VAL-LEI", "LEI", is_valid_lei, "CBPR_Valid_LEI",
|
|
112
113
|
"Every LEI must be a structurally valid ISO 17442 identifier with correct check digits."),
|
|
114
|
+
("VAL-CTRY", "Ctry", is_valid_country, "CBPR_Valid_Country",
|
|
115
|
+
"Every Country must be a valid ISO 3166-1 alpha-2 country code."),
|
|
113
116
|
]
|
|
114
117
|
|
|
118
|
+
# Currency is carried as the ``Ccy`` XML attribute on amount elements
|
|
119
|
+
# (ActiveCurrencyAndAmount etc.) and occasionally as a ``<Ccy>`` element (account
|
|
120
|
+
# currency). Both must be a valid ISO 4217 code.
|
|
121
|
+
_CCY_DESC = "Every currency must be a valid ISO 4217 currency code."
|
|
122
|
+
|
|
115
123
|
|
|
116
124
|
def _universal_rules(msgtype: str) -> List[Rule]:
|
|
117
|
-
"""Build the cross-cutting datatype rules (IBAN, LEI
|
|
125
|
+
"""Build the cross-cutting datatype rules (IBAN, LEI, country, currency)."""
|
|
118
126
|
rules: List[Rule] = []
|
|
119
|
-
for number, element, validator, name, description in
|
|
127
|
+
for number, element, validator, name, description in _UNIVERSAL_ELEMENT:
|
|
120
128
|
rule_id = f"{msgtype}:{number}"
|
|
121
129
|
|
|
122
130
|
def check(msg, _el=element, _val=validator, _id=rule_id, _name=name, _desc=description):
|
|
@@ -138,9 +146,50 @@ def _universal_rules(msgtype: str) -> List[Rule]:
|
|
|
138
146
|
return out
|
|
139
147
|
|
|
140
148
|
rules.append(Rule(rule_id, name, description, Severity.VIOLATION, check))
|
|
149
|
+
|
|
150
|
+
rules.append(_currency_rule(msgtype))
|
|
141
151
|
return rules
|
|
142
152
|
|
|
143
153
|
|
|
154
|
+
def _currency_rule(msgtype: str) -> Rule:
|
|
155
|
+
rule_id = f"{msgtype}:VAL-CCY"
|
|
156
|
+
|
|
157
|
+
def check(msg) -> List[Violation]:
|
|
158
|
+
out: List[Violation] = []
|
|
159
|
+
|
|
160
|
+
def flag(el, code):
|
|
161
|
+
out.append(
|
|
162
|
+
Violation(
|
|
163
|
+
rule_number=rule_id,
|
|
164
|
+
name="CBPR_Valid_Currency",
|
|
165
|
+
description=_CCY_DESC,
|
|
166
|
+
detail=f"invalid currency '{code}'",
|
|
167
|
+
found=msg.snippet_of(el),
|
|
168
|
+
xpath=msg.xpath_of(el),
|
|
169
|
+
line=msg.line_of(el),
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Every ``Ccy`` attribute, anywhere in the message.
|
|
174
|
+
for root in (msg.bah, msg.document):
|
|
175
|
+
if root is None:
|
|
176
|
+
continue
|
|
177
|
+
for el in root.iter():
|
|
178
|
+
if not isinstance(el.tag, str):
|
|
179
|
+
continue
|
|
180
|
+
code = el.get("Ccy")
|
|
181
|
+
if code and not is_valid_currency(code):
|
|
182
|
+
flag(el, code)
|
|
183
|
+
# Standalone ``<Ccy>`` elements (e.g. account currency).
|
|
184
|
+
for el in msg.iter_local("Ccy"):
|
|
185
|
+
code = msg.text_of(el)
|
|
186
|
+
if code and not is_valid_currency(code):
|
|
187
|
+
flag(el, code)
|
|
188
|
+
return out
|
|
189
|
+
|
|
190
|
+
return Rule(rule_id, "CBPR_Valid_Currency", _CCY_DESC, Severity.VIOLATION, check)
|
|
191
|
+
|
|
192
|
+
|
|
144
193
|
def load_rules(year: int, msgtype: str) -> List[Rule]:
|
|
145
194
|
_discover(int(year))
|
|
146
195
|
rules = list(_REGISTRY.get(_key(year, msgtype), []))
|
|
@@ -11,7 +11,7 @@ rules are surfaced as advisory guidance. Algorithmic field validations
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
from ...registry import advisory, rule
|
|
14
|
-
from ...validators import is_valid_bic
|
|
14
|
+
from ...validators import is_valid_bic
|
|
15
15
|
from ...helpers import (
|
|
16
16
|
amount_equals_sum,
|
|
17
17
|
business_msg_id_carries_group_id,
|
|
@@ -117,34 +117,6 @@ reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
|
117
117
|
_val_bic)
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
def _val_ctry(msg, report):
|
|
121
|
-
for node in msg.find(RPT + "/Acct/Svcr/FinInstnId/PstlAdr/Ctry"):
|
|
122
|
-
val = msg.text_of(node)
|
|
123
|
-
if val and not is_valid_country(val):
|
|
124
|
-
report(node, detail=f"invalid country '{val}'")
|
|
125
|
-
for node in msg.find(RPT + "/Acct/Ownr/PstlAdr/Ctry"):
|
|
126
|
-
val = msg.text_of(node)
|
|
127
|
-
if val and not is_valid_country(val):
|
|
128
|
-
report(node, detail=f"invalid country '{val}'")
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
reg("VAL-CTRY", "CBPR_Valid_Country",
|
|
132
|
-
"Every PostalAddress Country must be a valid ISO 3166 alpha-2 code.",
|
|
133
|
-
_val_ctry)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _val_ccy(msg, report):
|
|
137
|
-
for p in (RPT + "/Bal/Amt", RPT + "/Ntry/Amt"):
|
|
138
|
-
for el, ccy in msg.attr_nodes(p, "Ccy"):
|
|
139
|
-
if ccy and not is_valid_currency(ccy):
|
|
140
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
reg("VAL-CCY", "CBPR_Valid_Amount_Currency",
|
|
144
|
-
"Balance and Entry amount currency must be a valid ISO 4217 code.",
|
|
145
|
-
_val_ccy)
|
|
146
|
-
|
|
147
|
-
|
|
148
120
|
# ---------------------------------------------------------------------------
|
|
149
121
|
# Advisory textual rules (not mechanically enforceable - surfaced as guidance)
|
|
150
122
|
# ---------------------------------------------------------------------------
|
|
@@ -8,7 +8,7 @@ mechanizable textual rules are enforced, the rest are surfaced as advisories.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
from ...registry import advisory, rule
|
|
11
|
-
from ...validators import is_valid_bic
|
|
11
|
+
from ...validators import is_valid_bic
|
|
12
12
|
from ...helpers import (
|
|
13
13
|
business_msg_id_carries_group_id,
|
|
14
14
|
code_in,
|
|
@@ -76,22 +76,10 @@ reg("R4", "CBPR_Message_Definition_Identifier_TextualRule",
|
|
|
76
76
|
# ---------------------------------------------------------------------------
|
|
77
77
|
# Algorithmic field validations (brief), only for fields present in camt.054
|
|
78
78
|
# ---------------------------------------------------------------------------
|
|
79
|
-
reg("VAL-CCY", "CBPR_Valid_Entry_Currency",
|
|
80
|
-
"Entry Amount currency must be a valid ISO 4217 code.",
|
|
81
|
-
lambda msg, report: [
|
|
82
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
83
|
-
for el, ccy in msg.attr_nodes(ENTRY + "/Amt", "Ccy")
|
|
84
|
-
if ccy and not is_valid_currency(ccy)
|
|
85
|
-
])
|
|
86
|
-
|
|
87
79
|
reg("VAL-BIC", "CBPR_Valid_Account_Servicer_BIC",
|
|
88
80
|
"Account Servicer BICFI must be a structurally valid BIC.",
|
|
89
81
|
each_value_valid(NTFCTN + "/Acct/Svcr/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
90
82
|
|
|
91
|
-
reg("VAL-CTRY", "CBPR_Valid_Account_Owner_Country",
|
|
92
|
-
"Account Owner postal address Country must be a valid ISO 3166 code.",
|
|
93
|
-
each_value_valid(NTFCTN + "/Acct/Ownr/PstlAdr/Ctry", is_valid_country, "country"))
|
|
94
|
-
|
|
95
83
|
|
|
96
84
|
# ---------------------------------------------------------------------------
|
|
97
85
|
# Advisory textual / guideline rules (not mechanically enforceable)
|
|
@@ -12,7 +12,7 @@ Paths are the short ISO 20022 tags from the guideline's XML Path column.
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
from ...registry import advisory, rule
|
|
15
|
-
from ...validators import is_valid_bic
|
|
15
|
+
from ...validators import is_valid_bic
|
|
16
16
|
from ...helpers import (
|
|
17
17
|
address_hybrid,
|
|
18
18
|
address_lines_max_length,
|
|
@@ -172,10 +172,6 @@ reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
|
172
172
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
173
173
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
174
174
|
|
|
175
|
-
reg("VAL-CTRY", "CBPR_Valid_Originator_Country",
|
|
176
|
-
"Originator Postal Address Country must be a valid ISO 3166 country code.",
|
|
177
|
-
each_value_valid(ORGTR + "/PstlAdr/Ctry", is_valid_country, "country"))
|
|
178
|
-
|
|
179
175
|
|
|
180
176
|
# ---------------------------------------------------------------------------
|
|
181
177
|
# Advisory textual rules (not mechanically enforceable - surfaced as guidance)
|
|
@@ -12,7 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
import re
|
|
13
13
|
|
|
14
14
|
from ...registry import advisory, rule
|
|
15
|
-
from ...validators import is_valid_bic
|
|
15
|
+
from ...validators import is_valid_bic
|
|
16
16
|
from ...helpers import (
|
|
17
17
|
address_hybrid,
|
|
18
18
|
address_lines_max_length,
|
|
@@ -691,23 +691,6 @@ def _run_all(*checks):
|
|
|
691
691
|
return check
|
|
692
692
|
|
|
693
693
|
|
|
694
|
-
def _valid_ccy(path):
|
|
695
|
-
def check(msg, report):
|
|
696
|
-
for el, ccy in msg.attr_nodes(path, "Ccy"):
|
|
697
|
-
if ccy and not is_valid_currency(ccy):
|
|
698
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
699
|
-
return check
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
703
|
-
"Settlement amount currencies must be valid ISO 4217 codes.",
|
|
704
|
-
_run_all(
|
|
705
|
-
_valid_ccy(TX + "/OrgnlIntrBkSttlmAmt"),
|
|
706
|
-
_valid_ccy(TX + "/RtrdIntrBkSttlmAmt"),
|
|
707
|
-
_valid_ccy(TX + "/RtrdInstdAmt"),
|
|
708
|
-
_valid_ccy(OTR + "/IntrBkSttlmAmt"),
|
|
709
|
-
))
|
|
710
|
-
|
|
711
694
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
712
695
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
713
696
|
_run_all(
|
|
@@ -715,15 +698,6 @@ reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
|
715
698
|
each_value_valid(TX + "/InstdAgt/FinInstnId/BICFI", is_valid_bic, "BIC"),
|
|
716
699
|
))
|
|
717
700
|
|
|
718
|
-
reg("VAL-CTRY", "CBPR_Valid_Country",
|
|
719
|
-
"Every Country code must be a valid ISO 3166 country code.",
|
|
720
|
-
_run_all(
|
|
721
|
-
each_value_valid(RC + "/Dbtr/Pty/PstlAdr/Ctry", is_valid_country, "country"),
|
|
722
|
-
each_value_valid(RC + "/Cdtr/Pty/PstlAdr/Ctry", is_valid_country, "country"),
|
|
723
|
-
each_value_valid(OTR + "/Dbtr/Pty/PstlAdr/Ctry", is_valid_country, "country"),
|
|
724
|
-
each_value_valid(OTR + "/Cdtr/Pty/PstlAdr/Ctry", is_valid_country, "country"),
|
|
725
|
-
))
|
|
726
|
-
|
|
727
701
|
# ---------------------------------------------------------------------------
|
|
728
702
|
# Advisory textual / guideline rules (not mechanically enforceable)
|
|
729
703
|
# ---------------------------------------------------------------------------
|
|
@@ -11,7 +11,7 @@ XML paths are the short ISO 20022 tags from its Full_View / XML Path column.
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
from ...registry import advisory, rule
|
|
14
|
-
from ...validators import is_valid_bic
|
|
14
|
+
from ...validators import is_valid_bic
|
|
15
15
|
from ...helpers import (
|
|
16
16
|
address_hybrid,
|
|
17
17
|
address_lines_max_length,
|
|
@@ -302,14 +302,6 @@ reg("R44", "CBPR_DEBT_Rule_1_TextualRule",
|
|
|
302
302
|
|
|
303
303
|
# Specific validations required by the brief (algorithmic), applied to the
|
|
304
304
|
# fields where these data types appear in pacs.008.
|
|
305
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
306
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
307
|
-
lambda msg, report: [
|
|
308
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
309
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
310
|
-
if ccy and not is_valid_currency(ccy)
|
|
311
|
-
])
|
|
312
|
-
|
|
313
305
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
314
306
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
315
307
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_008_stp.py
RENAMED
|
@@ -13,7 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
import re as _re
|
|
14
14
|
|
|
15
15
|
from ...registry import advisory, rule
|
|
16
|
-
from ...validators import is_valid_bic
|
|
16
|
+
from ...validators import is_valid_bic
|
|
17
17
|
from ...helpers import (
|
|
18
18
|
address_hybrid,
|
|
19
19
|
address_lines_max_length,
|
|
@@ -251,22 +251,10 @@ reg("R8", "CBPR_Business_Service_Usage_TextualRule",
|
|
|
251
251
|
code_in("/AppHdr/BizSvc", ["swift.cbprplus.stp.03"]))
|
|
252
252
|
|
|
253
253
|
|
|
254
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
255
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
256
|
-
lambda msg, report: [
|
|
257
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
258
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
259
|
-
if ccy and not is_valid_currency(ccy)
|
|
260
|
-
])
|
|
261
|
-
|
|
262
254
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
263
255
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
264
256
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
265
257
|
|
|
266
|
-
reg("VAL-CTRY", "CBPR_Valid_Country",
|
|
267
|
-
"Every Postal Address Country must be a valid ISO 3166 country code.",
|
|
268
|
-
each_value_valid(TX + "/Dbtr/PstlAdr/Ctry", is_valid_country, "country"))
|
|
269
|
-
|
|
270
258
|
|
|
271
259
|
# ---------------------------------------------------------------------------
|
|
272
260
|
# Promoted from advisory: mechanizable textual rules now enforced.
|
|
@@ -11,7 +11,7 @@ Name/PostalAddress follow the *agent* (present-together) rule via FinInstnId.
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
from ...registry import advisory, rule
|
|
14
|
-
from ...validators import is_valid_bic
|
|
14
|
+
from ...validators import is_valid_bic
|
|
15
15
|
from ...helpers import (
|
|
16
16
|
address_hybrid,
|
|
17
17
|
address_lines_max_length,
|
|
@@ -199,22 +199,10 @@ reg("R28", "CBPR_Duplication_Postal_Address_TextualRule",
|
|
|
199
199
|
no_postal_address_duplication())
|
|
200
200
|
|
|
201
201
|
# Algorithmic validations required by the brief, for fields present in pacs.009.
|
|
202
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
203
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
204
|
-
lambda msg, report: [
|
|
205
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
206
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
207
|
-
if ccy and not is_valid_currency(ccy)
|
|
208
|
-
])
|
|
209
|
-
|
|
210
202
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
211
203
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
212
204
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
213
205
|
|
|
214
|
-
reg("VAL-CTRY", "CBPR_Valid_Postal_Country",
|
|
215
|
-
"Every Postal Address Country must be a valid ISO 3166 country code.",
|
|
216
|
-
each_value_valid(TX + "/CdtrAgt/FinInstnId/PstlAdr/Ctry", is_valid_country, "country"))
|
|
217
|
-
|
|
218
206
|
|
|
219
207
|
# ---------------------------------------------------------------------------
|
|
220
208
|
# Advisory textual rules (not mechanically enforceable - surfaced as guidance)
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_009_adv.py
RENAMED
|
@@ -12,7 +12,7 @@ Name/PostalAddress + grace-period rules (not the customer party rules).
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
from ...registry import advisory, rule
|
|
15
|
-
from ...validators import is_valid_bic
|
|
15
|
+
from ...validators import is_valid_bic
|
|
16
16
|
from ...helpers import (
|
|
17
17
|
address_hybrid,
|
|
18
18
|
address_lines_max_length,
|
|
@@ -198,14 +198,6 @@ reg("R20", "CBPR_Duplication_Postal_Address_TextualRule",
|
|
|
198
198
|
# ---------------------------------------------------------------------------
|
|
199
199
|
# Specific algorithmic validations required by the brief (fields present here).
|
|
200
200
|
# ---------------------------------------------------------------------------
|
|
201
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
202
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
203
|
-
lambda msg, report: [
|
|
204
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
205
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
206
|
-
if ccy and not is_valid_currency(ccy)
|
|
207
|
-
])
|
|
208
|
-
|
|
209
201
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
210
202
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
211
203
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2025/pacs_009_cov.py
RENAMED
|
@@ -10,7 +10,7 @@ are the short ISO 20022 tags from its Full_View / XML Path column.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
from ...registry import advisory, rule
|
|
13
|
-
from ...validators import is_valid_bic
|
|
13
|
+
from ...validators import is_valid_bic
|
|
14
14
|
from ...helpers import (
|
|
15
15
|
address_hybrid,
|
|
16
16
|
address_lines_max_length,
|
|
@@ -235,14 +235,6 @@ reg("R8", "CBPR_Business_Service_Usage_TextualRule",
|
|
|
235
235
|
code_in("/AppHdr/BizSvc", ["swift.cbprplus.cov.03"]))
|
|
236
236
|
|
|
237
237
|
# Algorithmic validations (brief), for fields present in pacs.009 COV.
|
|
238
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
239
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
240
|
-
lambda msg, report: [
|
|
241
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
242
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
243
|
-
if ccy and not is_valid_currency(ccy)
|
|
244
|
-
])
|
|
245
|
-
|
|
246
238
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
247
239
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
248
240
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
@@ -9,7 +9,7 @@ module (``pacs_008``): combinator-built rules go through ``reg``/``_agent_block`
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
from ...registry import advisory, rule
|
|
12
|
-
from ...validators import is_valid_bic
|
|
12
|
+
from ...validators import is_valid_bic
|
|
13
13
|
from ...helpers import (
|
|
14
14
|
address_hybrid,
|
|
15
15
|
address_lines_max_length,
|
|
@@ -141,14 +141,6 @@ reg("R69", "CBPR_Structured_Remittance_Information_TextualRule",
|
|
|
141
141
|
# ---------------------------------------------------------------------------
|
|
142
142
|
# Algorithmic field validation (brief), only for fields present in pain.001.
|
|
143
143
|
# ---------------------------------------------------------------------------
|
|
144
|
-
reg("VAL-CCY", "CBPR_Valid_Instructed_Amount_Currency",
|
|
145
|
-
"Instructed Amount currency must be a valid ISO 4217 code.",
|
|
146
|
-
lambda msg, report: [
|
|
147
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
148
|
-
for el, ccy in msg.attr_nodes(TX + "/Amt/InstdAmt", "Ccy")
|
|
149
|
-
if ccy and not is_valid_currency(ccy)
|
|
150
|
-
])
|
|
151
|
-
|
|
152
144
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
153
145
|
"Every Financial Institution BICFI must be a structurally valid BIC.",
|
|
154
146
|
lambda msg, report: [
|
|
@@ -164,23 +156,6 @@ reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
|
164
156
|
if msg.text_of(node) and not is_valid_bic(msg.text_of(node))
|
|
165
157
|
])
|
|
166
158
|
|
|
167
|
-
reg("VAL-CTRY", "CBPR_Valid_Country",
|
|
168
|
-
"Every Country must be a valid ISO 3166-1 alpha-2 code.",
|
|
169
|
-
lambda msg, report: [
|
|
170
|
-
report(node, detail=f"invalid country '{msg.text_of(node)}'")
|
|
171
|
-
for path in (
|
|
172
|
-
PMTINF + "/Dbtr/PstlAdr/Ctry",
|
|
173
|
-
PMTINF + "/DbtrAgt/FinInstnId/PstlAdr/Ctry",
|
|
174
|
-
TX + "/Cdtr/PstlAdr/Ctry",
|
|
175
|
-
TX + "/CdtrAgt/FinInstnId/PstlAdr/Ctry",
|
|
176
|
-
TX + "/IntrmyAgt1/FinInstnId/PstlAdr/Ctry",
|
|
177
|
-
TX + "/IntrmyAgt2/FinInstnId/PstlAdr/Ctry",
|
|
178
|
-
TX + "/IntrmyAgt3/FinInstnId/PstlAdr/Ctry",
|
|
179
|
-
)
|
|
180
|
-
for node in msg.find(path)
|
|
181
|
-
if msg.text_of(node) and not is_valid_country(msg.text_of(node))
|
|
182
|
-
])
|
|
183
|
-
|
|
184
159
|
|
|
185
160
|
# ---------------------------------------------------------------------------
|
|
186
161
|
# Advisory textual rules (not mechanically enforceable - surfaced as guidance)
|
|
@@ -11,8 +11,6 @@ from __future__ import annotations
|
|
|
11
11
|
from ...registry import advisory, rule
|
|
12
12
|
from ...validators import (
|
|
13
13
|
is_valid_bic,
|
|
14
|
-
is_valid_country,
|
|
15
|
-
is_valid_currency,
|
|
16
14
|
)
|
|
17
15
|
from ...helpers import (
|
|
18
16
|
amount_equals_sum,
|
|
@@ -89,22 +87,10 @@ reg("R13", "CBPR_Original_Instruction_Identification_FormalRule",
|
|
|
89
87
|
# ---------------------------------------------------------------------------
|
|
90
88
|
# Algorithmic field validations (brief), for fields present in camt.052
|
|
91
89
|
# ---------------------------------------------------------------------------
|
|
92
|
-
reg("VAL-CCY", "CBPR_Valid_Balance_Currency",
|
|
93
|
-
"Balance Amount currency must be a valid ISO 4217 code.",
|
|
94
|
-
lambda msg, report: [
|
|
95
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
96
|
-
for el, ccy in msg.attr_nodes(RPT + "/Bal/Amt", "Ccy")
|
|
97
|
-
if ccy and not is_valid_currency(ccy)
|
|
98
|
-
])
|
|
99
|
-
|
|
100
90
|
reg("VAL-BIC", "CBPR_Valid_Account_Servicer_BIC",
|
|
101
91
|
"Account Servicer BICFI must be a structurally valid BIC.",
|
|
102
92
|
each_value_valid(RPT + "/Acct/Svcr/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
103
93
|
|
|
104
|
-
reg("VAL-CTRY", "CBPR_Valid_Account_Owner_Country",
|
|
105
|
-
"Account Owner Postal Address Country must be a valid ISO 3166 code.",
|
|
106
|
-
each_value_valid(RPT + "/Acct/Ownr/PstlAdr/Ctry", is_valid_country, "Country"))
|
|
107
|
-
|
|
108
94
|
|
|
109
95
|
# ---------------------------------------------------------------------------
|
|
110
96
|
# Mechanizable textual rules promoted from advisory to enforced.
|
|
@@ -11,7 +11,7 @@ rules are surfaced via ``advisory``.
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
from ...registry import advisory, rule
|
|
14
|
-
from ...validators import is_valid_bic
|
|
14
|
+
from ...validators import is_valid_bic
|
|
15
15
|
from ...helpers import (
|
|
16
16
|
business_msg_id_carries_group_id,
|
|
17
17
|
each_value_valid,
|
|
@@ -97,23 +97,10 @@ reg("R13", "CBPR_Original_Instruction_Identification_FormalRule",
|
|
|
97
97
|
# Algorithmic field validation (brief-required VAL-* checks), applied to the
|
|
98
98
|
# fields where these data types appear in camt.054.
|
|
99
99
|
# ---------------------------------------------------------------------------
|
|
100
|
-
reg("VAL-CCY", "CBPR_Valid_Entry_Amount_Currency",
|
|
101
|
-
"Entry Amount currency must be a valid ISO 4217 code.",
|
|
102
|
-
lambda msg, report: [
|
|
103
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
104
|
-
for el, ccy in msg.attr_nodes(ENTRY + "/Amt", "Ccy")
|
|
105
|
-
if ccy and not is_valid_currency(ccy)
|
|
106
|
-
])
|
|
107
|
-
|
|
108
100
|
reg("VAL-BIC", "CBPR_Valid_Account_Servicer_BIC",
|
|
109
101
|
"Account Servicer BICFI must be a structurally valid BIC.",
|
|
110
102
|
each_value_valid(NTFCTN + "/Acct/Svcr/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
111
103
|
|
|
112
|
-
reg("VAL-CTRY", "CBPR_Valid_Account_Servicer_Country",
|
|
113
|
-
"Account Servicer postal address Country must be a valid ISO 3166 code.",
|
|
114
|
-
each_value_valid(NTFCTN + "/Acct/Svcr/FinInstnId/PstlAdr/Ctry",
|
|
115
|
-
is_valid_country, "country"))
|
|
116
|
-
|
|
117
104
|
|
|
118
105
|
# ---------------------------------------------------------------------------
|
|
119
106
|
# Mechanizable textual rules promoted from advisory to enforced. Both are
|
|
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
import re as _re
|
|
11
11
|
|
|
12
12
|
from ...registry import advisory, rule
|
|
13
|
-
from ...validators import is_valid_bic
|
|
13
|
+
from ...validators import is_valid_bic
|
|
14
14
|
from ...helpers import (
|
|
15
15
|
business_msg_id_carries_group_id,
|
|
16
16
|
header_msg_def_id_matches,
|
|
@@ -141,15 +141,6 @@ reg("R20", "CBPR_Party_Name_Any_BIC_FormalRule",
|
|
|
141
141
|
# ---------------------------------------------------------------------------
|
|
142
142
|
# Algorithmic field validation (project brief) - only fields present here.
|
|
143
143
|
# ---------------------------------------------------------------------------
|
|
144
|
-
reg("VAL-CCY", "CBPR_Valid_Original_Interbank_Settlement_Currency",
|
|
145
|
-
"Original Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
146
|
-
lambda msg, report: [
|
|
147
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
148
|
-
for el, ccy in msg.attr_nodes(TX + "/OrgnlTxRef/IntrBkSttlmAmt", "Ccy")
|
|
149
|
-
if ccy and not is_valid_currency(ccy)
|
|
150
|
-
])
|
|
151
|
-
|
|
152
|
-
|
|
153
144
|
@rule(MT, YEAR, "VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
154
145
|
"Every Instructing/Instructed Agent BICFI must be a structurally valid BIC.")
|
|
155
146
|
def _val_bic(msg, report):
|
|
@@ -10,7 +10,7 @@ Structure mirrors the reference module ``y2025/pacs_008.py``.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
from ...registry import advisory, rule
|
|
13
|
-
from ...validators import is_valid_bic
|
|
13
|
+
from ...validators import is_valid_bic
|
|
14
14
|
from ...helpers import (
|
|
15
15
|
bic_presence_exclusive,
|
|
16
16
|
business_msg_id_carries_group_id,
|
|
@@ -258,23 +258,6 @@ def _run_all(*checks):
|
|
|
258
258
|
return check
|
|
259
259
|
|
|
260
260
|
|
|
261
|
-
def _valid_ccy(path: str):
|
|
262
|
-
def check(msg, report):
|
|
263
|
-
for el, ccy in msg.attr_nodes(path, "Ccy"):
|
|
264
|
-
if ccy and not is_valid_currency(ccy):
|
|
265
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
266
|
-
return check
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
270
|
-
"Settlement amount currencies must be valid ISO 4217 codes.",
|
|
271
|
-
_run_all(
|
|
272
|
-
_valid_ccy(TX + "/OrgnlIntrBkSttlmAmt"),
|
|
273
|
-
_valid_ccy(TX + "/RtrdIntrBkSttlmAmt"),
|
|
274
|
-
_valid_ccy(TX + "/RtrdInstdAmt"),
|
|
275
|
-
_valid_ccy(OTR + "/IntrBkSttlmAmt"),
|
|
276
|
-
))
|
|
277
|
-
|
|
278
261
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
279
262
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
280
263
|
_run_all(
|
|
@@ -282,15 +265,6 @@ reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
|
282
265
|
each_value_valid(TX + "/InstdAgt/FinInstnId/BICFI", is_valid_bic, "BIC"),
|
|
283
266
|
))
|
|
284
267
|
|
|
285
|
-
reg("VAL-CTRY", "CBPR_Valid_Country",
|
|
286
|
-
"Every Country code must be a valid ISO 3166 country code.",
|
|
287
|
-
_run_all(
|
|
288
|
-
each_value_valid(RC + "/Dbtr/Pty/PstlAdr/Ctry", is_valid_country, "country"),
|
|
289
|
-
each_value_valid(RC + "/Cdtr/Pty/PstlAdr/Ctry", is_valid_country, "country"),
|
|
290
|
-
each_value_valid(OTR + "/Dbtr/Pty/PstlAdr/Ctry", is_valid_country, "country"),
|
|
291
|
-
each_value_valid(OTR + "/Cdtr/Pty/PstlAdr/Ctry", is_valid_country, "country"),
|
|
292
|
-
))
|
|
293
|
-
|
|
294
268
|
|
|
295
269
|
# ---------------------------------------------------------------------------
|
|
296
270
|
# Mechanizable textual rules + advisory textual rules.
|
|
@@ -9,7 +9,7 @@ registered as advisories.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
from ...registry import advisory, rule
|
|
12
|
-
from ...validators import is_valid_bic
|
|
12
|
+
from ...validators import is_valid_bic
|
|
13
13
|
from ...helpers import (
|
|
14
14
|
bic_presence_exclusive,
|
|
15
15
|
business_msg_id_carries_group_id,
|
|
@@ -307,22 +307,10 @@ reg("R63", "CBPR_Structured_RemittanceInformation_TextualRule",
|
|
|
307
307
|
# ---------------------------------------------------------------------------
|
|
308
308
|
# Algorithmic field validation required by the project brief
|
|
309
309
|
# ---------------------------------------------------------------------------
|
|
310
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
311
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
312
|
-
lambda msg, report: [
|
|
313
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
314
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
315
|
-
if ccy and not is_valid_currency(ccy)
|
|
316
|
-
])
|
|
317
|
-
|
|
318
310
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
319
311
|
"Every BICFI in the message must be a structurally valid BIC.",
|
|
320
312
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
321
313
|
|
|
322
|
-
reg("VAL-CTRY", "CBPR_Valid_Country",
|
|
323
|
-
"Every Country code must be a valid ISO 3166 code.",
|
|
324
|
-
each_value_valid(TX + "/Dbtr/PstlAdr/Ctry", is_valid_country, "Country"))
|
|
325
|
-
|
|
326
314
|
|
|
327
315
|
# ---------------------------------------------------------------------------
|
|
328
316
|
# Advisory textual rules (not mechanically enforceable - surfaced as guidance)
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_008_stp.py
RENAMED
|
@@ -11,7 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
import re
|
|
12
12
|
|
|
13
13
|
from ...registry import advisory, rule
|
|
14
|
-
from ...validators import is_valid_bic
|
|
14
|
+
from ...validators import is_valid_bic
|
|
15
15
|
from ...helpers import (
|
|
16
16
|
bic_presence_exclusive,
|
|
17
17
|
business_msg_id_carries_group_id,
|
|
@@ -309,22 +309,10 @@ def _r20(msg, report):
|
|
|
309
309
|
# ---------------------------------------------------------------------------
|
|
310
310
|
# Algorithmic field validation (VAL-*) - only for fields present in this message
|
|
311
311
|
# ---------------------------------------------------------------------------
|
|
312
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
313
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
314
|
-
lambda msg, report: [
|
|
315
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
316
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
317
|
-
if ccy and not is_valid_currency(ccy)
|
|
318
|
-
])
|
|
319
|
-
|
|
320
312
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
321
313
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
322
314
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
323
315
|
|
|
324
|
-
reg("VAL-CTRY", "CBPR_Valid_Country",
|
|
325
|
-
"Every PostalAddress Country must be a valid ISO 3166 alpha-2 code.",
|
|
326
|
-
each_value_valid(TX + "/Dbtr/PstlAdr/Ctry", is_valid_country, "country"))
|
|
327
|
-
|
|
328
316
|
|
|
329
317
|
# ---------------------------------------------------------------------------
|
|
330
318
|
# Advisory textual rules (not mechanically enforceable - surfaced as guidance)
|
|
@@ -9,7 +9,7 @@ otherwise surfaced as advisories.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
from ...registry import advisory, rule
|
|
12
|
-
from ...validators import is_valid_bic
|
|
12
|
+
from ...validators import is_valid_bic
|
|
13
13
|
from ...helpers import (
|
|
14
14
|
business_msg_id_carries_group_id,
|
|
15
15
|
header_msg_def_id_matches,
|
|
@@ -161,15 +161,6 @@ for _num, _path in _AGENT_NAME_ADR.items():
|
|
|
161
161
|
# ---------------------------------------------------------------------------
|
|
162
162
|
# Algorithmic field validation (brief), only for fields present in pacs.009.
|
|
163
163
|
# ---------------------------------------------------------------------------
|
|
164
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
165
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
166
|
-
lambda msg, report: [
|
|
167
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
168
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
169
|
-
if ccy and not is_valid_currency(ccy)
|
|
170
|
-
])
|
|
171
|
-
|
|
172
|
-
|
|
173
164
|
@rule(MT, YEAR, "VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
174
165
|
"Every Agent BICFI in the message must be a structurally valid BIC.")
|
|
175
166
|
def _val_bic(msg, report):
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_009_adv.py
RENAMED
|
@@ -9,7 +9,7 @@ otherwise surfaced as advisories.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
from ...registry import advisory, rule
|
|
12
|
-
from ...validators import is_valid_bic
|
|
12
|
+
from ...validators import is_valid_bic
|
|
13
13
|
from ...helpers import (
|
|
14
14
|
not_matching_pattern,
|
|
15
15
|
presence_together,
|
|
@@ -167,15 +167,6 @@ for _num, _path in _AGENT_NAME_ADR.items():
|
|
|
167
167
|
# ---------------------------------------------------------------------------
|
|
168
168
|
# Algorithmic field validation (brief), only for fields present in pacs.009 ADV.
|
|
169
169
|
# ---------------------------------------------------------------------------
|
|
170
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
171
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
172
|
-
lambda msg, report: [
|
|
173
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
174
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
175
|
-
if ccy and not is_valid_currency(ccy)
|
|
176
|
-
])
|
|
177
|
-
|
|
178
|
-
|
|
179
170
|
@rule(MT, YEAR, "VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
180
171
|
"Every Agent BICFI in the message must be a structurally valid BIC.")
|
|
181
172
|
def _val_bic(msg, report):
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_rules/rules/y2026/pacs_009_cov.py
RENAMED
|
@@ -10,7 +10,7 @@ are the short ISO 20022 tags from its Full_View / XML Path column.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
from ...registry import advisory, rule
|
|
13
|
-
from ...validators import is_valid_bic
|
|
13
|
+
from ...validators import is_valid_bic
|
|
14
14
|
from ...helpers import (
|
|
15
15
|
bic_presence_exclusive,
|
|
16
16
|
business_msg_id_carries_group_id,
|
|
@@ -210,14 +210,6 @@ _party_name_adr("R59", UND + "/UltmtCdtr")
|
|
|
210
210
|
# ---------------------------------------------------------------------------
|
|
211
211
|
# Algorithmic field validation (brief), for fields present in pacs.009 COV.
|
|
212
212
|
# ---------------------------------------------------------------------------
|
|
213
|
-
reg("VAL-CCY", "CBPR_Valid_Settlement_Currency",
|
|
214
|
-
"Interbank Settlement Amount currency must be a valid ISO 4217 code.",
|
|
215
|
-
lambda msg, report: [
|
|
216
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
217
|
-
for el, ccy in msg.attr_nodes(TX + "/IntrBkSttlmAmt", "Ccy")
|
|
218
|
-
if ccy and not is_valid_currency(ccy)
|
|
219
|
-
])
|
|
220
|
-
|
|
221
213
|
reg("VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
222
214
|
"Instructing/Instructed Agent BICFI must be a structurally valid BIC.",
|
|
223
215
|
each_value_valid(TX + "/InstgAgt/FinInstnId/BICFI", is_valid_bic, "BIC"))
|
|
@@ -9,7 +9,7 @@ small helpers, and cross-field / cross-schema logic is written as bespoke
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
from ...registry import advisory, rule
|
|
12
|
-
from ...validators import is_valid_bic
|
|
12
|
+
from ...validators import is_valid_bic
|
|
13
13
|
from ...helpers import (
|
|
14
14
|
business_msg_id_carries_group_id,
|
|
15
15
|
header_msg_def_id_matches,
|
|
@@ -182,14 +182,6 @@ _CTRY_PATHS = [
|
|
|
182
182
|
]
|
|
183
183
|
|
|
184
184
|
|
|
185
|
-
@rule(MT, YEAR, "VAL-CCY", "CBPR_Valid_Instructed_Amount_Currency",
|
|
186
|
-
"Instructed Amount currency must be a valid ISO 4217 code.")
|
|
187
|
-
def _val_ccy(msg, report):
|
|
188
|
-
for el, ccy in msg.attr_nodes(TX + "/Amt/InstdAmt", "Ccy"):
|
|
189
|
-
if ccy and not is_valid_currency(ccy):
|
|
190
|
-
report(el, detail=f"invalid currency '{ccy}'")
|
|
191
|
-
|
|
192
|
-
|
|
193
185
|
@rule(MT, YEAR, "VAL-BIC", "CBPR_Valid_Agent_BIC",
|
|
194
186
|
"Every Financial Institution BICFI must be a structurally valid BIC.")
|
|
195
187
|
def _val_bic(msg, report):
|
|
@@ -200,16 +192,6 @@ def _val_bic(msg, report):
|
|
|
200
192
|
report(node, detail=f"invalid BIC: '{val}'")
|
|
201
193
|
|
|
202
194
|
|
|
203
|
-
@rule(MT, YEAR, "VAL-CTRY", "CBPR_Valid_Country",
|
|
204
|
-
"Every Country code in a Postal Address must be a valid ISO 3166 code.")
|
|
205
|
-
def _val_ctry(msg, report):
|
|
206
|
-
for path in _CTRY_PATHS:
|
|
207
|
-
for node in msg.find(path):
|
|
208
|
-
val = msg.text_of(node)
|
|
209
|
-
if val and not is_valid_country(val):
|
|
210
|
-
report(node, detail=f"invalid country: '{val}'")
|
|
211
|
-
|
|
212
|
-
|
|
213
195
|
# ---------------------------------------------------------------------------
|
|
214
196
|
# Promoted from advisory: mechanizable header / address / remittance rules.
|
|
215
197
|
# Each combinator is conservative: it skips when its inputs are absent or
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cbpr-usage-rules
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Validate ISO 20022 CBPR+ XML messages against SWIFT usage (business) rules.
|
|
5
5
|
Author: Pete Houghton
|
|
6
6
|
License: MIT License
|
|
@@ -141,7 +141,7 @@ Both `validate_file` and `validate_string` return a dictionary:
|
|
|
141
141
|
"message_type": "pacs.008", # the rule set that was applied
|
|
142
142
|
"detected_message_type": "pacs.008",# auto-detected from the Document namespace
|
|
143
143
|
"year": 2025,
|
|
144
|
-
"rules_evaluated":
|
|
144
|
+
"rules_evaluated": 87,
|
|
145
145
|
"violations": [
|
|
146
146
|
{
|
|
147
147
|
"rule_number": "pacs.008:R41", # unique within the message type
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/requires.txt
RENAMED
|
File without changes
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.1.3}/src/cbpr_usage_rules.egg-info/top_level.txt
RENAMED
|
File without changes
|