pyxaf 0.1.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.
Files changed (56) hide show
  1. pyxaf-0.1.0/CHANGELOG.md +65 -0
  2. pyxaf-0.1.0/LICENSE +21 -0
  3. pyxaf-0.1.0/PKG-INFO +366 -0
  4. pyxaf-0.1.0/README.md +314 -0
  5. pyxaf-0.1.0/pyproject.toml +287 -0
  6. pyxaf-0.1.0/pyproject.toml.orig +184 -0
  7. pyxaf-0.1.0/src/pyxaf/__init__.py +108 -0
  8. pyxaf-0.1.0/src/pyxaf/__main__.py +34 -0
  9. pyxaf-0.1.0/src/pyxaf/_adf.py +414 -0
  10. pyxaf-0.1.0/src/pyxaf/_arrow.py +239 -0
  11. pyxaf-0.1.0/src/pyxaf/_encoding.py +216 -0
  12. pyxaf-0.1.0/src/pyxaf/_normalize.py +676 -0
  13. pyxaf-0.1.0/src/pyxaf/_optional.py +20 -0
  14. pyxaf-0.1.0/src/pyxaf/_source.py +296 -0
  15. pyxaf-0.1.0/src/pyxaf/_xml.py +337 -0
  16. pyxaf-0.1.0/src/pyxaf/_xsd.py +147 -0
  17. pyxaf-0.1.0/src/pyxaf/catalogue/__init__.py +140 -0
  18. pyxaf-0.1.0/src/pyxaf/catalogue/_generated_clair2.py +98 -0
  19. pyxaf-0.1.0/src/pyxaf/catalogue/_generated_xaf30.py +228 -0
  20. pyxaf-0.1.0/src/pyxaf/catalogue/_generated_xaf31.py +229 -0
  21. pyxaf-0.1.0/src/pyxaf/catalogue/_generated_xaf32.py +308 -0
  22. pyxaf-0.1.0/src/pyxaf/catalogue/_generated_xaf321.py +308 -0
  23. pyxaf-0.1.0/src/pyxaf/catalogue/_generated_xaf40.py +121 -0
  24. pyxaf-0.1.0/src/pyxaf/cli.py +293 -0
  25. pyxaf-0.1.0/src/pyxaf/detect.py +314 -0
  26. pyxaf-0.1.0/src/pyxaf/errors.py +72 -0
  27. pyxaf-0.1.0/src/pyxaf/findings.py +326 -0
  28. pyxaf-0.1.0/src/pyxaf/formats.py +132 -0
  29. pyxaf-0.1.0/src/pyxaf/models.py +622 -0
  30. pyxaf-0.1.0/src/pyxaf/py.typed +0 -0
  31. pyxaf-0.1.0/src/pyxaf/raw.py +88 -0
  32. pyxaf-0.1.0/src/pyxaf/reader.py +842 -0
  33. pyxaf-0.1.0/src/pyxaf/rgs/__init__.py +571 -0
  34. pyxaf-0.1.0/src/pyxaf/rgs/_xlsx.py +483 -0
  35. pyxaf-0.1.0/src/pyxaf/schemas/NOTICE.md +13 -0
  36. pyxaf-0.1.0/src/pyxaf/schemas/XmlAuditfileFinancieel3.2.1.xsd +1144 -0
  37. pyxaf-0.1.0/src/pyxaf/schemas/XmlAuditfileFinancieel3.2.xsd +1183 -0
  38. pyxaf-0.1.0/src/pyxaf/schemas/XmlAuditfileFinancieel4.0.xsd +349 -0
  39. pyxaf-0.1.0/src/pyxaf/schemas/clair2-auditfile.xsd +547 -0
  40. pyxaf-0.1.0/src/pyxaf/tables.py +482 -0
  41. pyxaf-0.1.0/src/pyxaf/validate.py +1079 -0
  42. pyxaf-0.1.0/src/pyxaf/values.py +112 -0
  43. pyxaf-0.1.0/tests/conftest.py +18 -0
  44. pyxaf-0.1.0/tests/test_cli.py +91 -0
  45. pyxaf-0.1.0/tests/test_detect.py +131 -0
  46. pyxaf-0.1.0/tests/test_properties.py +52 -0
  47. pyxaf-0.1.0/tests/test_reader.py +358 -0
  48. pyxaf-0.1.0/tests/test_readme.py +65 -0
  49. pyxaf-0.1.0/tests/test_regressions.py +123 -0
  50. pyxaf-0.1.0/tests/test_rgs.py +553 -0
  51. pyxaf-0.1.0/tests/test_security.py +85 -0
  52. pyxaf-0.1.0/tests/test_tables.py +154 -0
  53. pyxaf-0.1.0/tests/test_validate.py +332 -0
  54. pyxaf-0.1.0/tests/test_values.py +69 -0
  55. pyxaf-0.1.0/tests/test_xsd_and_catalogue.py +69 -0
  56. pyxaf-0.1.0/tests/xafgen.py +647 -0
@@ -0,0 +1,65 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
6
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Until 1.0 the public API may
7
+ change in minor releases. Finding codes are stable: a code is never renumbered or reused.
8
+
9
+ ## [Unreleased]
10
+
11
+ ## [0.1.0] - 2026-10-01
12
+
13
+ Initial release.
14
+
15
+ ### Added
16
+
17
+ - **Reading every iteration** of the Dutch Auditfile Financieel: XML Auditfile Financieel 4.0,
18
+ 3.2.1, 3.2, 3.1 and 3.0, CLAIR2 and the fixed-width ADF (CLAIR1) format, through one
19
+ `pyxaf.open()` API with a normalized, typed data model (company, ledger accounts with RGS
20
+ references, customers/suppliers, VAT codes, periods, journals, transactions, lines, opening
21
+ balance). The original raw records stay available next to the parsed values.
22
+ - **Streaming**: transactions and lines are streamed, so memory does not grow with file size.
23
+ Multi-file sets are supported: continuation files ("Vervolgbestand x van y") are read as one
24
+ document, per-period files are merged (master data deduplicated, conflicts reported).
25
+ - **Robust input**: corrupt archives raise `CorruptArchiveError`; encodings Expat lacks
26
+ (windows-125x, UTF-32, …) are transcoded; Arrow-based exports take `on_inexact=`.
27
+ - **Raw access**: `af.raw.header`, `af.raw.company` and `af.raw.transactions()` stream the
28
+ lossless raw records without normalization (about twice as fast).
29
+ - **Exact values**: amounts are `decimal.Decimal` with the raw text kept; amounts with 0–2
30
+ decimals, negative amounts and opaque period numbers are handled. The opening balance is unified
31
+ from the `openingBalance` element or period-0/opening-journal transactions.
32
+ - **Input handling**: paths, bytes and binary streams; transparent gzip and single-member zip;
33
+ UTF-8 (with or without BOM), ISO-8859-1 and windows-1252, `encoding=` override and opt-in,
34
+ reported repairs (`control-chars`, `latin1-as-cp1252`, `bare-ampersand`). Encrypted
35
+ tax-authority containers are recognised and rejected with an explanatory error.
36
+ - **Version detection** (`pyxaf.detect()`) by scoring signals (namespace, vocabulary, ADF
37
+ signature, continuation comment) rather than namespace alone, with confidence, reasons and the
38
+ namespace status (official, documented variant, known bogus, none).
39
+ - **Validation** (`pyxaf.validate()`) with graded, coded findings (`XAF<nnnn>`, severities
40
+ ERROR/WARNING/INFO, documented in `pyxaf.CODES`): encoding, XML well-formedness, structure per
41
+ version (required elements, cardinality, lengths, code lists, patterns), references, control
42
+ totals and balance, uniqueness and data quality. The official XAF 4.0 consistency rules
43
+ [0001]–[0010] map to codes; rule sets `spec` and `vts`; per-code and overall finding limits.
44
+ Data problems never stop a file from loading.
45
+ - **Field catalogues** for every XML iteration, generated from the official XSDs
46
+ (`scripts/gen_catalogues.py`).
47
+ - **XSD validation** against the bundled official schemas (CLAIR2, 3.2, 3.2.1, 4.0) with the
48
+ `xsd` extra (lxml >= 6.1.3, hardened parser options).
49
+ - **Normalized tables and exports**: fixed-schema tables (`header`, `company`, `addresses`,
50
+ `accounts`, `relations`, `vat_codes`, `periods`, `journals`, `transactions`, `lines`,
51
+ `line_vat`, `opening_balance`); CSV and JSON Lines with the standard library; Arrow streams
52
+ with exact `decimal128` columns (`arrow` extra, nanoarrow); `to_polars()` (`polars` extra),
53
+ `to_pandas()` (`pandas` extra) and Parquet (`parquet` extra).
54
+ - **RGS** (`pyxaf.rgs`): load the official RGS Excel release with a small standard-library
55
+ `.xlsx` reader, resolve codes and their hierarchy, and check the RGS references of an auditfile.
56
+ No RGS data is bundled.
57
+ - **Command line** (`cli` extra): `pyxaf detect`, `info`, `validate` (text or JSON output,
58
+ `--strict`), `export` and `codes`, with documented exit codes. Without the extra, `pyxaf` prints
59
+ an install hint.
60
+ - **Security hardening** for untrusted input: DOCTYPE/ENTITY declarations are refused, no
61
+ network or entity resolution, limits on nesting depth, text-node size and decompressed size.
62
+ - Zero runtime dependencies in the core; fully typed (`py.typed`); Python 3.11–3.14.
63
+
64
+ [Unreleased]: https://github.com/SpireflyHQ/pyxaf/compare/v0.1.0...HEAD
65
+ [0.1.0]: https://github.com/SpireflyHQ/pyxaf/releases/tag/v0.1.0
pyxaf-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ben van der Burgh and the pyxaf contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pyxaf-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,366 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyxaf
3
+ Version: 0.1.0
4
+ Summary: Read and validate every iteration of the Dutch XML Auditfile Financieel (XAF 4.0, 3.2.1, 3.2, 3.1, 3.0, CLAIR2 and ADF).
5
+ Keywords: xaf,auditfile,auditfile financieel,accounting,audit,general ledger,rgs,belastingdienst,saf-t,xml
6
+ Author: Ben van der Burgh
7
+ Author-email: Ben van der Burgh <ben@spirefly.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Financial and Insurance Industry
13
+ Classifier: Natural Language :: English
14
+ Classifier: Natural Language :: Dutch
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
23
+ Classifier: Topic :: Text Processing :: Markup :: XML
24
+ Classifier: Typing :: Typed
25
+ Requires-Dist: pyxaf[xsd,arrow,polars,pandas,parquet,cli] ; extra == 'all'
26
+ Requires-Dist: nanoarrow>=0.7 ; extra == 'arrow'
27
+ Requires-Dist: typer>=0.16 ; extra == 'cli'
28
+ Requires-Dist: pandas>=2.2 ; extra == 'pandas'
29
+ Requires-Dist: pyarrow>=17 ; extra == 'pandas'
30
+ Requires-Dist: nanoarrow>=0.7 ; extra == 'pandas'
31
+ Requires-Dist: pyarrow>=17 ; extra == 'parquet'
32
+ Requires-Dist: nanoarrow>=0.7 ; extra == 'parquet'
33
+ Requires-Dist: polars>=1.44 ; extra == 'polars'
34
+ Requires-Dist: nanoarrow>=0.7 ; extra == 'polars'
35
+ Requires-Dist: lxml>=6.1.3 ; extra == 'xsd'
36
+ Maintainer: Ben van der Burgh
37
+ Maintainer-email: Ben van der Burgh <ben@spirefly.com>
38
+ Requires-Python: >=3.11
39
+ Project-URL: Homepage, https://github.com/SpireflyHQ/pyxaf
40
+ Project-URL: Documentation, https://spireflyhq.github.io/pyxaf/
41
+ Project-URL: Repository, https://github.com/SpireflyHQ/pyxaf
42
+ Project-URL: Issues, https://github.com/SpireflyHQ/pyxaf/issues
43
+ Project-URL: Changelog, https://github.com/SpireflyHQ/pyxaf/blob/main/CHANGELOG.md
44
+ Provides-Extra: all
45
+ Provides-Extra: arrow
46
+ Provides-Extra: cli
47
+ Provides-Extra: pandas
48
+ Provides-Extra: parquet
49
+ Provides-Extra: polars
50
+ Provides-Extra: xsd
51
+ Description-Content-Type: text/markdown
52
+
53
+ # 📒 pyxaf
54
+
55
+ [![CI](https://github.com/SpireflyHQ/pyxaf/actions/workflows/ci.yml/badge.svg)](https://github.com/SpireflyHQ/pyxaf/actions/workflows/ci.yml)
56
+ [![PyPI](https://img.shields.io/pypi/v/pyxaf)](https://pypi.org/project/pyxaf/)
57
+ [![Python](https://img.shields.io/pypi/pyversions/pyxaf)](https://pypi.org/project/pyxaf/)
58
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/SpireflyHQ/pyxaf/blob/main/LICENSE)
59
+
60
+ **Read and validate every version of the Dutch Auditfile Financieel in plain Python.**
61
+
62
+ The *Auditfile Financieel* is the standard export of a general ledger that Dutch accounting
63
+ software produces for tax inspectors and auditors. pyxaf opens all seven versions of it, from the
64
+ fixed-width files of 1999 to XAF 4.0, gives you one clean, typed model to work with, and tells
65
+ you exactly what is wrong with a file instead of choking on it. No runtime dependencies, no
66
+ upload to anyone's server, no gigabytes of RAM.
67
+
68
+ ```python
69
+ import pyxaf
70
+
71
+ with pyxaf.open("2024.xaf") as af:
72
+ print(af.version, af.company.name) # 4.0 Voorbeeld & Zonen B.V.
73
+ ```
74
+
75
+ ## 🧭 Table of contents
76
+
77
+ - [Why pyxaf](#-why-pyxaf)
78
+ - [Installation](#-installation)
79
+ - [Quickstart](#-quickstart)
80
+ - [1. Open the file](#1-open-the-file)
81
+ - [2. Walk through the transactions](#2-walk-through-the-transactions)
82
+ - [3. Validate it](#3-validate-it)
83
+ - [4. Or use the command line](#4-or-use-the-command-line)
84
+ - [More examples](#-more-examples)
85
+ - [Export to tables](#export-to-tables)
86
+ - [Query with DuckDB or pyarrow](#query-with-duckdb-or-pyarrow)
87
+ - [Check RGS codes](#check-rgs-codes)
88
+ - [Split files and problem files](#split-files-and-problem-files)
89
+ - [Supported versions](#-supported-versions)
90
+ - [Documentation](#-documentation)
91
+ - [Contributing](#-contributing)
92
+ - [License and attribution](#-license-and-attribution)
93
+
94
+ ## ✨ Why pyxaf
95
+
96
+ Real auditfiles are messy. Official test files break the official rules, vendors invent their own
97
+ namespaces, and an "ISO-8859-1" file turns out to be Windows-1252. pyxaf was built for those files.
98
+
99
+ - **All versions, one model.** XAF 4.0, 3.2.1, 3.2, 3.1, 3.0, CLAIR2 and the fixed-width ADF all
100
+ map onto the same classes: `Header`, `LedgerAccount`, `Transaction`, `Line` and friends. The
101
+ raw layer keeps every element exactly as written, vendor extensions included.
102
+ - **Lenient reading, honest reporting.** pyxaf never refuses a file because of bad data. Every
103
+ problem becomes a graded finding with a stable code, such as `XAF5010 ERROR`, so nothing is
104
+ silently guessed or dropped.
105
+ - **A validator on your own machine.** The official validation service is for subscribers and
106
+ stops at 5 MB. pyxaf checks files of any size: structure, references, control totals,
107
+ balance, uniqueness, data quality and RGS codes, including the official XAF 4.0 rules
108
+ [0001]–[0010].
109
+ - **Exact money.** Amounts are `decimal.Decimal`, never `float`, with the original text kept
110
+ next to every parsed value.
111
+ - **Streaming.** Master data is read when the file opens; transactions and lines are streamed,
112
+ so memory stays flat even for files of several gigabytes.
113
+ - **Lightweight.** The core uses only the standard library and is fully typed. pandas, polars,
114
+ Arrow, lxml and the command line are optional extras that are only imported when you use them.
115
+ - **Safe with untrusted files.** DOCTYPE and ENTITY declarations are refused (no "billion
116
+ laughs", no XXE), nesting, text and decompression sizes are capped, and there is no "recover"
117
+ mode that could quietly lose data.
118
+
119
+ ## 📦 Installation
120
+
121
+ pyxaf needs Python 3.11 or newer.
122
+
123
+ ```console
124
+ pip install pyxaf
125
+ ```
126
+
127
+ The core has no dependencies. Add an extra for each optional feature you need:
128
+
129
+ | Extra | Adds | Install |
130
+ |---|---|---|
131
+ | `cli` | the `pyxaf` command line | `pip install "pyxaf[cli]"` |
132
+ | `xsd` | validation against the official XSDs (lxml) | `pip install "pyxaf[xsd]"` |
133
+ | `polars` | `to_polars()`, without pyarrow | `pip install "pyxaf[polars]"` |
134
+ | `pandas` | `to_pandas()` with Arrow-backed types | `pip install "pyxaf[pandas]"` |
135
+ | `arrow` | Arrow streams for DuckDB, pyarrow and friends (nanoarrow) | `pip install "pyxaf[arrow]"` |
136
+ | `parquet` | Parquet export | `pip install "pyxaf[parquet]"` |
137
+ | `all` | everything above | `pip install "pyxaf[all]"` |
138
+
139
+ Using uv? `uv add pyxaf` works the same way, for example `uv add "pyxaf[cli,polars]"`.
140
+
141
+ ## 🚀 Quickstart
142
+
143
+ Grab an auditfile exported from your accounting software. The examples use `2024.xaf`, but any
144
+ version works, and so do `.gz` and `.zip` files.
145
+
146
+ ### 1. Open the file
147
+
148
+ ```python
149
+ import pyxaf
150
+
151
+ af = pyxaf.open("2024.xaf")
152
+ print(af.version, af.company.name, af.header.fiscal_year)
153
+ print(len(af.accounts), "ledger accounts")
154
+ ```
155
+
156
+ ```text
157
+ 4.0 Voorbeeld & Zonen B.V. 2024
158
+ 11 ledger accounts
159
+ ```
160
+
161
+ pyxaf detected the version and encoding, and read the master data: company, ledger accounts,
162
+ customers and suppliers, VAT codes and periods.
163
+
164
+ ### 2. Walk through the transactions
165
+
166
+ Transactions and their lines are streamed straight from the file, one at a time:
167
+
168
+ ```python
169
+ for tx in af.transactions():
170
+ print(tx.journal_id, tx.number, tx.date, tx.description, tx.balanced)
171
+ for line in tx.lines:
172
+ print(" ", line.account_id, line.side, line.amount)
173
+ ```
174
+
175
+ ```text
176
+ MEM 1 2024-01-05 Boeking 1 True
177
+ 1300 D 3612.16
178
+ 8000 C 2985.26
179
+ 1800 C 626.90
180
+ ...
181
+ ```
182
+
183
+ The opening balance gets the same treatment, whichever way the file stores it:
184
+
185
+ ```python
186
+ print(af.opening_balance().by_account()) # signed: debit positive, credit negative
187
+ af.close()
188
+ ```
189
+
190
+ ```text
191
+ {'1100': Decimal('10000.00'), '0100': Decimal('2500.50'), '0500': Decimal('-12500.50')}
192
+ ```
193
+
194
+ ### 3. Validate it
195
+
196
+ ```python
197
+ report = pyxaf.validate("2024.xaf")
198
+ print(report.ok)
199
+ for finding in report.errors:
200
+ print(finding.code, finding.line, finding.message)
201
+ ```
202
+
203
+ A clean file prints `True`. A file in which someone typed `3621.16` instead of `3612.16` prints:
204
+
205
+ ```text
206
+ False
207
+ XAF5007 None transactions totalDebit 38260.42 ≠ sum of debit lines 38269.42
208
+ XAF5010 223 transaction '1' in journal 'MEM' does not balance: debit 3621.16, credit 3612.16
209
+ ```
210
+
211
+ Every finding has a stable code, a severity (`ERROR`, `WARNING` or `INFO`) and, where possible,
212
+ a line number. The [finding-code reference](https://spireflyhq.github.io/pyxaf/reference/codes/)
213
+ explains each one.
214
+
215
+ ### 4. Or use the command line
216
+
217
+ With `pyxaf[cli]` installed:
218
+
219
+ ```console
220
+ $ pyxaf validate 2024.xaf
221
+ == 2024.xaf
222
+ 4.0 — 0 error(s), 0 warning(s), 0 info
223
+
224
+ $ pyxaf info 2024.xaf
225
+ version 4.0
226
+ company Voorbeeld & Zonen B.V.
227
+ fiscal year 2024
228
+ transactions 12
229
+ lines 36
230
+ total debit 38260.42
231
+ total credit 38260.42
232
+ ...
233
+ ```
234
+
235
+ `pyxaf validate` exits with 0 when the file is fine, 1 for warnings (with `--strict`), 2 for
236
+ errors and 3 when pyxaf itself could not do its job, so it slots straight into scripts and CI.
237
+
238
+ ## 🧰 More examples
239
+
240
+ ### Export to tables
241
+
242
+ pyxaf turns a file into twelve normalized tables, such as `accounts`, `transactions` and `lines`,
243
+ with the same columns for every version:
244
+
245
+ ```python
246
+ with pyxaf.open("2024.xaf") as af:
247
+ af.export("out/", format="csv") # standard library only; also "jsonl"
248
+ af.export("out/", format="parquet") # pyxaf[parquet]
249
+ frames = af.to_polars() # pyxaf[polars]: a dict of DataFrames
250
+ frames["lines"] # amounts as Decimal(20, 2), not float
251
+ ```
252
+
253
+ ### Query with DuckDB or pyarrow
254
+
255
+ Tables speak the Arrow PyCapsule interface, so Arrow-aware tools read them directly
256
+ (needs `pyxaf[arrow]`):
257
+
258
+ ```python
259
+ import duckdb
260
+
261
+ with pyxaf.open("2024.xaf") as af:
262
+ lines = af.tables["lines"]
263
+ duckdb.sql("select account_id, sum(signed_amount) from lines group by account_id").show()
264
+ ```
265
+
266
+ ### Check RGS codes
267
+
268
+ pyxaf reads each account's RGS (Referentie Grootboekschema) code. Load the official RGS Excel
269
+ release you downloaded, and the validator checks every code against it:
270
+
271
+ ```python
272
+ import pyxaf.rgs
273
+
274
+ schema = pyxaf.rgs.load_excel("RGS 3.8-def.xlsx")
275
+ report = pyxaf.validate("2024.xaf", rgs=schema)
276
+ ```
277
+
278
+ ### Split files and problem files
279
+
280
+ ```python
281
+ # a split auditfile ("Vervolgbestand 2 van 3") or one file per period, read as one
282
+ with pyxaf.open(["big.xaf", "big-2.xaf", "big-3.xaf"]) as af:
283
+ ...
284
+
285
+ # a file that lies about its encoding, with stray control characters and bare "&"
286
+ af = pyxaf.open("old.xaf", encoding="cp1252", repair={"control-chars", "bare-ampersand"})
287
+ ```
288
+
289
+ Repairs are opt-in and every repair shows up as a finding, so you always know what was changed.
290
+
291
+ <details>
292
+ <summary><b>Go deeper: raw records, detection details and the official XSDs</b></summary>
293
+
294
+ ```python
295
+ with pyxaf.open("2024.xaf") as af:
296
+ af.format # FormatInfo: version, namespace status, encoding, reasons
297
+ af.accounts["1000"].rgs # RgsRef(code='BLimKas', source='RGScode', ...)
298
+ for journal_id, record in af.raw.transactions():
299
+ ... # lossless raw records, about twice as fast
300
+
301
+ report = pyxaf.validate("2024.xaf", xsd=True) # also check the official XSD (pyxaf[xsd])
302
+ ```
303
+
304
+ </details>
305
+
306
+ ## 📜 Supported versions
307
+
308
+ | Version | Year | How pyxaf recognises it |
309
+ |---|---|---|
310
+ | XAF 4.0 (4.0.3) | 2025 | namespace (including known variants) and 4.0-only elements |
311
+ | XAF 3.2.1 | 2024 | its own ODB namespace |
312
+ | XAF 3.2 | 2014, 2017 | `http://www.auditfiles.nl/XAF/3.2` |
313
+ | XAF 3.1 and 3.0 | ~2010, ~2008 | namespace and vocabulary (no public XSD exists) |
314
+ | CLAIR2 | 2003 | `header/auditfileVersion` is `CLAIR2.00.00`, no namespace |
315
+ | ADF | 1999 | fixed-width ASCII starting with `CLAIR1.00.00` |
316
+
317
+ From 1 January 2027 the Belastingdienst only accepts XAF 4.0. Older files stay around for years
318
+ because of the seven-year retention period, which is why pyxaf reads them all.
319
+
320
+ ## 📚 Documentation
321
+
322
+ The full documentation lives at **<https://spireflyhq.github.io/pyxaf/>**:
323
+
324
+ - [Reading auditfiles](https://spireflyhq.github.io/pyxaf/guide/reading/): the data model,
325
+ streaming, opening balances, encodings and repairs
326
+ - [Versions](https://spireflyhq.github.io/pyxaf/guide/versions/): how the seven versions differ
327
+ and the field mapping across all of them
328
+ - [Validation](https://spireflyhq.github.io/pyxaf/guide/validation/) and the
329
+ [finding codes](https://spireflyhq.github.io/pyxaf/reference/codes/)
330
+ - [Interoperability](https://spireflyhq.github.io/pyxaf/guide/interop/),
331
+ [RGS](https://spireflyhq.github.io/pyxaf/guide/rgs/) and the
332
+ [command line](https://spireflyhq.github.io/pyxaf/guide/cli/)
333
+ - [Security](https://spireflyhq.github.io/pyxaf/security/) and
334
+ [versioning](https://spireflyhq.github.io/pyxaf/versioning/)
335
+
336
+ Release notes are in the [changelog](https://github.com/SpireflyHQ/pyxaf/blob/main/CHANGELOG.md).
337
+ For questions, see [SUPPORT.md](https://github.com/SpireflyHQ/pyxaf/blob/main/SUPPORT.md).
338
+
339
+ ## 🤝 Contributing
340
+
341
+ Contributions are very welcome, especially reports of files from software that pyxaf does not
342
+ handle well yet. Start with [CONTRIBUTING.md](https://github.com/SpireflyHQ/pyxaf/blob/main/CONTRIBUTING.md)
343
+ for the development setup, and [ARCHITECTURE.md](https://github.com/SpireflyHQ/pyxaf/blob/main/ARCHITECTURE.md)
344
+ for a map of the code. Issues labelled
345
+ [good first issue](https://github.com/SpireflyHQ/pyxaf/labels/good%20first%20issue) are a good
346
+ place to begin.
347
+
348
+ > **Never attach a real auditfile** to an issue or pull request. Auditfiles contain personal data
349
+ > and confidential financial records. The version, the exporting software, the finding codes and
350
+ > a small hand-made snippet are all we need.
351
+
352
+ Security problems are reported privately, as described in
353
+ [SECURITY.md](https://github.com/SpireflyHQ/pyxaf/blob/main/SECURITY.md).
354
+
355
+ ## 📄 License and attribution
356
+
357
+ pyxaf is released under the [MIT license](https://github.com/SpireflyHQ/pyxaf/blob/main/LICENSE).
358
+
359
+ The bundled XML schemas are published by the Belastingdienst (XAF 3.2.1 and 4.0), the former
360
+ auditfiles.nl platform (XAF 3.2) and SRA (CLAIR2). The XAF 3.0 and 3.1 element lists are derived
361
+ from the AnalyticsLibrary "XAF Mapping en Namen" table (Apache-2.0). RGS data is not bundled;
362
+ load the official Excel release yourself with `pyxaf.rgs.load_excel`.
363
+
364
+ pyxaf is an independent open-source project. It is not affiliated with or endorsed by the
365
+ Belastingdienst, the Taakgroep RGS or any software vendor, and a clean pyxaf report does not
366
+ guarantee that the Belastingdienst will accept a file.