cbpr-usage-rules 0.1.2__tar.gz → 0.2.0__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.2.0}/CHANGELOG.md +19 -0
- {cbpr_usage_rules-0.1.2/src/cbpr_usage_rules.egg-info → cbpr_usage_rules-0.2.0}/PKG-INFO +21 -4
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/README.md +20 -3
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/__init__.py +1 -1
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/cli.py +54 -2
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/registry.py +55 -6
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/camt_052.py +1 -29
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/camt_054.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_002.py +1 -5
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_004.py +1 -27
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_008.py +1 -9
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_008_stp.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_009.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_009_adv.py +1 -9
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_009_cov.py +1 -9
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pain_001.py +1 -26
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/camt_052.py +0 -14
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/camt_054.py +1 -14
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_002.py +1 -10
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_004.py +1 -27
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_008.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_008_stp.py +1 -13
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_009.py +1 -10
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_009_adv.py +1 -10
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_009_cov.py +1 -9
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pain_001.py +1 -19
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0/src/cbpr_usage_rules.egg-info}/PKG-INFO +21 -4
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/LICENSE +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/MANIFEST.in +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/pyproject.toml +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/setup.cfg +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/engine.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/helpers.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/loader.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/message.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/models.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/py.typed +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/reference/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/reference/countries.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/reference/currencies.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/schema.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/__init__.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/bic.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/country.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/currency.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/iban.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/lei.py +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/SOURCES.txt +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/dependency_links.txt +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/entry_points.txt +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/requires.txt +0 -0
- {cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/top_level.txt +0 -0
|
@@ -4,6 +4,25 @@ 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.2.0]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `--format {text,compact,json}` CLI option. The new `compact` format prints one
|
|
11
|
+
finding per line, compiler/linter-style (`file:line: severity [rule] message | at
|
|
12
|
+
xpath`) with a stable `VALID:`/`INVALID:` summary line — easy for tools and coding
|
|
13
|
+
agents to parse. `--json` is now an alias for `--format json`; the default rich
|
|
14
|
+
text output is unchanged.
|
|
15
|
+
|
|
16
|
+
## [0.1.3]
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Country (`VAL-CTRY`) and currency (`VAL-CCY`) validation are now single universal
|
|
20
|
+
rules, like IBAN/LEI. `VAL-CTRY` checks every `<Ctry>` element; `VAL-CCY` checks
|
|
21
|
+
every `Ccy` attribute and `<Ccy>` element, document-wide, for all message types.
|
|
22
|
+
This closes gaps where country was unvalidated (e.g. pacs.008 2025, the pacs.009
|
|
23
|
+
family) and currency was unvalidated (pacs.002 2025), and replaces the ~13
|
|
24
|
+
per-module `VAL-CTRY` and ~19 per-module `VAL-CCY` rules.
|
|
25
|
+
|
|
7
26
|
## [0.1.2]
|
|
8
27
|
|
|
9
28
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cbpr-usage-rules
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Validate ISO 20022 CBPR+ XML messages against SWIFT usage (business) rules.
|
|
5
5
|
Author: Pete Houghton
|
|
6
6
|
License: MIT License
|
|
@@ -112,8 +112,11 @@ result = cbpr_rules.validate_string(xml_text, year=2026)
|
|
|
112
112
|
# Human-readable (default). Exit code is non-zero if there are violations.
|
|
113
113
|
cbpr-validate payment.xml --year 2025
|
|
114
114
|
|
|
115
|
-
#
|
|
116
|
-
cbpr-validate payment.xml --year 2025 --
|
|
115
|
+
# Compact, one finding per line (compiler-style) — for tools and coding agents
|
|
116
|
+
cbpr-validate payment.xml --year 2025 --format compact
|
|
117
|
+
|
|
118
|
+
# JSON output (full machine-readable result)
|
|
119
|
+
cbpr-validate payment.xml --year 2025 --json # same as --format json
|
|
117
120
|
|
|
118
121
|
# Read from stdin
|
|
119
122
|
cat payment.xml | cbpr-validate --year 2026
|
|
@@ -127,6 +130,20 @@ cbpr-validate payment.xml --year 2025 --xsd pacs.008.001.08.xsd
|
|
|
127
130
|
|
|
128
131
|
Each reported violation shows the rule, **why** this instance failed (`Problem:`),
|
|
129
132
|
the **offending XML** from your file (`Found:`), and its line + xpath (`At:`).
|
|
133
|
+
|
|
134
|
+
**For tools and coding agents**, prefer `--format compact` (one finding per line,
|
|
135
|
+
parseable like a compiler/linter) or `--json` (the full structured result):
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
$ cbpr-validate payment.xml --year 2025 --format compact
|
|
139
|
+
payment.xml:7: violation [pacs.008:R2] From vs Instructing Agent: ABCDGB2LXXX != GGGGGB2LXXX | at /AppHdr/Fr/FIId/FinInstnId/BICFI
|
|
140
|
+
payment.xml:63: violation [pacs.008:VAL-IBAN] invalid IBAN: 'GB29NWBK60161331926818' | at /Document/.../DbtrAcct/Id/IBAN
|
|
141
|
+
INVALID: 5 violations (87 rules)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Each line is `file:line: severity [rule] message | at xpath`, and the final
|
|
145
|
+
`VALID:` / `INVALID:` line is a stable summary to grep for. With `--xsd`, schema
|
|
146
|
+
errors appear as `schema-error` lines and are counted in the summary.
|
|
130
147
|
Advisory rules are summarised as a count by default; pass `--advisory` to list them.
|
|
131
148
|
|
|
132
149
|
---
|
|
@@ -141,7 +158,7 @@ Both `validate_file` and `validate_string` return a dictionary:
|
|
|
141
158
|
"message_type": "pacs.008", # the rule set that was applied
|
|
142
159
|
"detected_message_type": "pacs.008",# auto-detected from the Document namespace
|
|
143
160
|
"year": 2025,
|
|
144
|
-
"rules_evaluated":
|
|
161
|
+
"rules_evaluated": 87,
|
|
145
162
|
"violations": [
|
|
146
163
|
{
|
|
147
164
|
"rule_number": "pacs.008:R41", # unique within the message type
|
|
@@ -57,8 +57,11 @@ result = cbpr_rules.validate_string(xml_text, year=2026)
|
|
|
57
57
|
# Human-readable (default). Exit code is non-zero if there are violations.
|
|
58
58
|
cbpr-validate payment.xml --year 2025
|
|
59
59
|
|
|
60
|
-
#
|
|
61
|
-
cbpr-validate payment.xml --year 2025 --
|
|
60
|
+
# Compact, one finding per line (compiler-style) — for tools and coding agents
|
|
61
|
+
cbpr-validate payment.xml --year 2025 --format compact
|
|
62
|
+
|
|
63
|
+
# JSON output (full machine-readable result)
|
|
64
|
+
cbpr-validate payment.xml --year 2025 --json # same as --format json
|
|
62
65
|
|
|
63
66
|
# Read from stdin
|
|
64
67
|
cat payment.xml | cbpr-validate --year 2026
|
|
@@ -72,6 +75,20 @@ cbpr-validate payment.xml --year 2025 --xsd pacs.008.001.08.xsd
|
|
|
72
75
|
|
|
73
76
|
Each reported violation shows the rule, **why** this instance failed (`Problem:`),
|
|
74
77
|
the **offending XML** from your file (`Found:`), and its line + xpath (`At:`).
|
|
78
|
+
|
|
79
|
+
**For tools and coding agents**, prefer `--format compact` (one finding per line,
|
|
80
|
+
parseable like a compiler/linter) or `--json` (the full structured result):
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
$ cbpr-validate payment.xml --year 2025 --format compact
|
|
84
|
+
payment.xml:7: violation [pacs.008:R2] From vs Instructing Agent: ABCDGB2LXXX != GGGGGB2LXXX | at /AppHdr/Fr/FIId/FinInstnId/BICFI
|
|
85
|
+
payment.xml:63: violation [pacs.008:VAL-IBAN] invalid IBAN: 'GB29NWBK60161331926818' | at /Document/.../DbtrAcct/Id/IBAN
|
|
86
|
+
INVALID: 5 violations (87 rules)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Each line is `file:line: severity [rule] message | at xpath`, and the final
|
|
90
|
+
`VALID:` / `INVALID:` line is a stable summary to grep for. With `--xsd`, schema
|
|
91
|
+
errors appear as `schema-error` lines and are counted in the summary.
|
|
75
92
|
Advisory rules are summarised as a count by default; pass `--advisory` to list them.
|
|
76
93
|
|
|
77
94
|
---
|
|
@@ -86,7 +103,7 @@ Both `validate_file` and `validate_string` return a dictionary:
|
|
|
86
103
|
"message_type": "pacs.008", # the rule set that was applied
|
|
87
104
|
"detected_message_type": "pacs.008",# auto-detected from the Document namespace
|
|
88
105
|
"year": 2025,
|
|
89
|
-
"rules_evaluated":
|
|
106
|
+
"rules_evaluated": 87,
|
|
90
107
|
"violations": [
|
|
91
108
|
{
|
|
92
109
|
"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.
|
|
7
|
+
__version__ = "0.2.0"
|
|
8
8
|
|
|
9
9
|
from .models import Rule, Severity, Violation
|
|
10
10
|
from .engine import validate_file, validate_string, list_rules, available
|
|
@@ -26,7 +26,18 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
26
26
|
help="Message type override, e.g. pacs.008 or pacs.008_stp "
|
|
27
27
|
"(auto-detected from the Document namespace if omitted).",
|
|
28
28
|
)
|
|
29
|
-
p.add_argument(
|
|
29
|
+
p.add_argument(
|
|
30
|
+
"--format",
|
|
31
|
+
choices=("text", "compact", "json"),
|
|
32
|
+
default="text",
|
|
33
|
+
help="Output format: text (rich, default), compact (one finding per line, "
|
|
34
|
+
"compiler-style — for tools/agents), or json (full machine-readable).",
|
|
35
|
+
)
|
|
36
|
+
p.add_argument(
|
|
37
|
+
"--json",
|
|
38
|
+
action="store_true",
|
|
39
|
+
help="Alias for --format json.",
|
|
40
|
+
)
|
|
30
41
|
p.add_argument(
|
|
31
42
|
"--xsd",
|
|
32
43
|
action="append",
|
|
@@ -125,6 +136,43 @@ def _format_text(result: dict, show_advisory: bool = False) -> str:
|
|
|
125
136
|
return "\n".join(lines)
|
|
126
137
|
|
|
127
138
|
|
|
139
|
+
def _format_compact(result: dict, source: str) -> str:
|
|
140
|
+
"""One finding per line, compiler/linter-style, for tools and coding agents.
|
|
141
|
+
|
|
142
|
+
``<source>:<line>: <severity> [<rule>] <message> | at <xpath>`` per violation,
|
|
143
|
+
followed by a single greppable ``VALID:``/``INVALID:`` summary line. Advisory
|
|
144
|
+
(non-enforced) rules are omitted; use --json or text --advisory for those.
|
|
145
|
+
"""
|
|
146
|
+
lines: List[str] = []
|
|
147
|
+
for v in result["violations"]:
|
|
148
|
+
line = v["line"] if v["line"] is not None else "?"
|
|
149
|
+
message = " ".join((v.get("detail") or v["description"] or "").split())
|
|
150
|
+
suffix = f" | at {v['xpath']}" if v.get("xpath") else ""
|
|
151
|
+
lines.append(f"{source}:{line}: {v['severity']} [{v['rule_number']}] {message}{suffix}")
|
|
152
|
+
|
|
153
|
+
xsd = result.get("xsd")
|
|
154
|
+
schema_errors = 0
|
|
155
|
+
if xsd is not None:
|
|
156
|
+
for s in xsd["schemas"]:
|
|
157
|
+
if s.get("load_error"):
|
|
158
|
+
schema_errors += 1
|
|
159
|
+
lines.append(f"{s['file']}: schema-error could not load schema: {s['load_error']}")
|
|
160
|
+
for e in s["errors"]:
|
|
161
|
+
schema_errors += 1
|
|
162
|
+
eline = e["line"] if e.get("line") is not None else "?"
|
|
163
|
+
msg = " ".join((e.get("message") or "").split())
|
|
164
|
+
lines.append(f"{s['file']}:{eline}: schema-error {msg}")
|
|
165
|
+
|
|
166
|
+
n = len(result["violations"])
|
|
167
|
+
verdict = "VALID" if result["valid"] else "INVALID"
|
|
168
|
+
parts = [f"{n} violation{'s' if n != 1 else ''}"]
|
|
169
|
+
if xsd is not None:
|
|
170
|
+
parts.append(f"{schema_errors} schema error{'s' if schema_errors != 1 else ''}")
|
|
171
|
+
summary = f"{verdict}: {', '.join(parts)} ({result['rules_evaluated']} rules)"
|
|
172
|
+
lines.append(summary)
|
|
173
|
+
return "\n".join(lines)
|
|
174
|
+
|
|
175
|
+
|
|
128
176
|
def main(argv: Optional[List[str]] = None) -> int:
|
|
129
177
|
args = _build_parser().parse_args(argv)
|
|
130
178
|
|
|
@@ -153,6 +201,7 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
153
201
|
print("error: --year is required to validate", file=sys.stderr)
|
|
154
202
|
return 2
|
|
155
203
|
|
|
204
|
+
source = args.file or "<stdin>"
|
|
156
205
|
try:
|
|
157
206
|
if args.file:
|
|
158
207
|
result = validate_file(args.file, args.year, args.msgtype, xsd=args.xsd)
|
|
@@ -163,8 +212,11 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
163
212
|
print(f"error: {exc}", file=sys.stderr)
|
|
164
213
|
return 2
|
|
165
214
|
|
|
166
|
-
if args.json
|
|
215
|
+
fmt = "json" if args.json else args.format
|
|
216
|
+
if fmt == "json":
|
|
167
217
|
print(json.dumps(result, indent=2))
|
|
218
|
+
elif fmt == "compact":
|
|
219
|
+
print(_format_compact(result, source))
|
|
168
220
|
else:
|
|
169
221
|
print(_format_text(result, show_advisory=args.advisory))
|
|
170
222
|
|
|
@@ -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.2.0}/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.2.0}/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.2.0}/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.2.0}/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.2.0}/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.2.0}/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.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Validate ISO 20022 CBPR+ XML messages against SWIFT usage (business) rules.
|
|
5
5
|
Author: Pete Houghton
|
|
6
6
|
License: MIT License
|
|
@@ -112,8 +112,11 @@ result = cbpr_rules.validate_string(xml_text, year=2026)
|
|
|
112
112
|
# Human-readable (default). Exit code is non-zero if there are violations.
|
|
113
113
|
cbpr-validate payment.xml --year 2025
|
|
114
114
|
|
|
115
|
-
#
|
|
116
|
-
cbpr-validate payment.xml --year 2025 --
|
|
115
|
+
# Compact, one finding per line (compiler-style) — for tools and coding agents
|
|
116
|
+
cbpr-validate payment.xml --year 2025 --format compact
|
|
117
|
+
|
|
118
|
+
# JSON output (full machine-readable result)
|
|
119
|
+
cbpr-validate payment.xml --year 2025 --json # same as --format json
|
|
117
120
|
|
|
118
121
|
# Read from stdin
|
|
119
122
|
cat payment.xml | cbpr-validate --year 2026
|
|
@@ -127,6 +130,20 @@ cbpr-validate payment.xml --year 2025 --xsd pacs.008.001.08.xsd
|
|
|
127
130
|
|
|
128
131
|
Each reported violation shows the rule, **why** this instance failed (`Problem:`),
|
|
129
132
|
the **offending XML** from your file (`Found:`), and its line + xpath (`At:`).
|
|
133
|
+
|
|
134
|
+
**For tools and coding agents**, prefer `--format compact` (one finding per line,
|
|
135
|
+
parseable like a compiler/linter) or `--json` (the full structured result):
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
$ cbpr-validate payment.xml --year 2025 --format compact
|
|
139
|
+
payment.xml:7: violation [pacs.008:R2] From vs Instructing Agent: ABCDGB2LXXX != GGGGGB2LXXX | at /AppHdr/Fr/FIId/FinInstnId/BICFI
|
|
140
|
+
payment.xml:63: violation [pacs.008:VAL-IBAN] invalid IBAN: 'GB29NWBK60161331926818' | at /Document/.../DbtrAcct/Id/IBAN
|
|
141
|
+
INVALID: 5 violations (87 rules)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Each line is `file:line: severity [rule] message | at xpath`, and the final
|
|
145
|
+
`VALID:` / `INVALID:` line is a stable summary to grep for. With `--xsd`, schema
|
|
146
|
+
errors appear as `schema-error` lines and are counted in the summary.
|
|
130
147
|
Advisory rules are summarised as a count by default; pass `--advisory` to list them.
|
|
131
148
|
|
|
132
149
|
---
|
|
@@ -141,7 +158,7 @@ Both `validate_file` and `validate_string` return a dictionary:
|
|
|
141
158
|
"message_type": "pacs.008", # the rule set that was applied
|
|
142
159
|
"detected_message_type": "pacs.008",# auto-detected from the Document namespace
|
|
143
160
|
"year": 2025,
|
|
144
|
-
"rules_evaluated":
|
|
161
|
+
"rules_evaluated": 87,
|
|
145
162
|
"violations": [
|
|
146
163
|
{
|
|
147
164
|
"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
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/requires.txt
RENAMED
|
File without changes
|
{cbpr_usage_rules-0.1.2 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/top_level.txt
RENAMED
|
File without changes
|