trustrender 0.2.0__tar.gz → 0.3.1__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.
- trustrender-0.3.1/PKG-INFO +263 -0
- trustrender-0.3.1/README.md +221 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/pyproject.toml +21 -11
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/__init__.py +109 -11
- trustrender-0.3.1/src/trustrender/adapters/__init__.py +14 -0
- trustrender-0.3.1/src/trustrender/adapters/stripe.py +163 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/cli.py +404 -104
- trustrender-0.3.1/src/trustrender/invoice_aliases.py +275 -0
- trustrender-0.3.1/src/trustrender/invoice_ingest.py +1002 -0
- trustrender-0.3.1/src/trustrender/invoice_schema.py +171 -0
- trustrender-0.3.1/src/trustrender/playground/assets/index-Bsf6YnD1.css +2 -0
- trustrender-0.3.1/src/trustrender/playground/assets/index-C6K0JsLw.js +97 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/playground/index.html +2 -2
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/server.py +42 -3
- trustrender-0.3.1/src/trustrender.egg-info/PKG-INFO +263 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/SOURCES.txt +11 -2
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/requires.txt +13 -5
- trustrender-0.3.1/tests/test_adapters.py +170 -0
- trustrender-0.3.1/tests/test_adversarial_corpus.py +278 -0
- trustrender-0.3.1/tests/test_invoice_ingest.py +646 -0
- trustrender-0.3.1/tests/test_real_payloads.py +101 -0
- trustrender-0.2.0/PKG-INFO +0 -274
- trustrender-0.2.0/README.md +0 -239
- trustrender-0.2.0/src/trustrender/playground/assets/index--cOwr7nw.css +0 -2
- trustrender-0.2.0/src/trustrender/playground/assets/index-CiBSP9E3.js +0 -81
- trustrender-0.2.0/src/trustrender.egg-info/PKG-INFO +0 -274
- {trustrender-0.2.0 → trustrender-0.3.1}/LICENSE +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/setup.cfg +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/einvoice.j2.typ +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/einvoice_data.json +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/invoice.j2.typ +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/invoice_data.json +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/letter.j2.typ +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/letter_data.json +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/receipt.j2.typ +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/receipt_data.json +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/report.j2.typ +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/report_data.json +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/statement.j2.typ +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/statement_data.json +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/contract.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/dashboard.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/doctor.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/engine.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/errors.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/filters.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fingerprint.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/Inter-Bold.ttf +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/Inter-BoldItalic.ttf +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/Inter-Italic.ttf +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/Inter-Regular.ttf +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/LICENSE.txt +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/playground/assets/pdf.worker-C1breYqn.mjs +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/playground/favicon.svg +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/playground/icons.svg +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/provenance.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/readiness.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/regression.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/semantic.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/templates.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/trace.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/zugferd.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/dependency_links.txt +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/entry_points.txt +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/top_level.txt +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_audit_e2e.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_cli.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_contract.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_doctor.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_engine.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_error_pipeline.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_errors.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_filters.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_fingerprint.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_fingerprint_stress.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_fonts.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_pagination.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_provenance.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_readiness.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_regression.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_regression_stress.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_render.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_semantic.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_semantic_stress.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_server.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_templates.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_trace.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_ugly_data.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_ugly_data_pressure.py +0 -0
- {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_zugferd.py +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trustrender
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Validate and normalize Stripe, Shopify, and custom billing data for Factur-X/ZUGFeRD
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://trustrender.dev
|
|
7
|
+
Project-URL: Documentation, https://trustrender.dev
|
|
8
|
+
Project-URL: Source, https://github.com/verityengine/trustrender
|
|
9
|
+
Project-URL: Issues, https://github.com/verityengine/trustrender/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/verityengine/trustrender/releases
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: drafthorse>=2024.0
|
|
24
|
+
Provides-Extra: render
|
|
25
|
+
Requires-Dist: typst>=0.14; extra == "render"
|
|
26
|
+
Requires-Dist: jinja2>=3.1; extra == "render"
|
|
27
|
+
Requires-Dist: pypdf>=4.0; extra == "render"
|
|
28
|
+
Provides-Extra: serve
|
|
29
|
+
Requires-Dist: starlette>=0.40; extra == "serve"
|
|
30
|
+
Requires-Dist: uvicorn>=0.30; extra == "serve"
|
|
31
|
+
Provides-Extra: zugferd
|
|
32
|
+
Requires-Dist: factur-x>=3.0; extra == "zugferd"
|
|
33
|
+
Provides-Extra: all
|
|
34
|
+
Requires-Dist: trustrender[render,serve,zugferd]; extra == "all"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
38
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
39
|
+
Requires-Dist: factur-x>=3.0; extra == "dev"
|
|
40
|
+
Requires-Dist: trustrender[all]; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# TrustRender
|
|
44
|
+
|
|
45
|
+
Validate and normalize billing data from Stripe, Shopify, and custom systems before Factur-X/ZUGFeRD embedding.
|
|
46
|
+
|
|
47
|
+
If you're bridging a non-compliant billing platform into EU e-invoicing, libraries like [factur-x](https://github.com/akretion/factur-x) and [drafthorse](https://github.com/pretix/python-drafthorse) assume your data is already correct. TrustRender catches arithmetic mismatches, field misalignment, missing required fields, and structural problems before compliant XML gets embedded.
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
pip install trustrender
|
|
51
|
+
trustrender validate invoice.json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What it does
|
|
55
|
+
|
|
56
|
+
Takes invoice JSON from Stripe, Shopify, custom billing APIs, or legacy exports and tells you whether it's safe to embed as Factur-X/ZUGFeRD.
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
$ trustrender validate quickbooks_invoice.json
|
|
60
|
+
|
|
61
|
+
Invoice: INV-2026-5541
|
|
62
|
+
From: Summit Analytics Co.
|
|
63
|
+
To: Horizon Financial
|
|
64
|
+
Items: 2
|
|
65
|
+
Total: $10,524.50
|
|
66
|
+
|
|
67
|
+
Normalizations (25):
|
|
68
|
+
DocNumber → invoice_number CompanyName → sender.name
|
|
69
|
+
TxnDate → invoice_date customer.Name → recipient.name
|
|
70
|
+
Line → items SubTotal → subtotal
|
|
71
|
+
... and 19 more
|
|
72
|
+
|
|
73
|
+
PASS — invoice data is valid
|
|
74
|
+
|
|
75
|
+
Safe to embed in Factur-X/ZUGFeRD PDF.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Bad data gets blocked:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
$ trustrender validate ocr_extracted_invoice.json
|
|
82
|
+
|
|
83
|
+
BLOCKED — 2 problem(s)
|
|
84
|
+
|
|
85
|
+
items[1] total is wrong
|
|
86
|
+
You entered $459.00 but math says $450.00
|
|
87
|
+
Fix the line total or the price/quantity.
|
|
88
|
+
|
|
89
|
+
Subtotal is wrong
|
|
90
|
+
Lines add up to $12,459.00 but you listed $12,450.00
|
|
91
|
+
|
|
92
|
+
This invoice cannot be processed until the problems above are fixed.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Install
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
pip install trustrender
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Core install requires only `drafthorse`. No Typst, no browser, no heavy deps.
|
|
102
|
+
|
|
103
|
+
Optional extras:
|
|
104
|
+
```
|
|
105
|
+
pip install "trustrender[zugferd]" # XSD/Schematron validation
|
|
106
|
+
pip install "trustrender[render]" # PDF rendering via Typst
|
|
107
|
+
pip install "trustrender[all]" # everything
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Requires Python 3.11+.
|
|
111
|
+
|
|
112
|
+
## Python API
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from trustrender import validate_invoice
|
|
116
|
+
|
|
117
|
+
result = validate_invoice({
|
|
118
|
+
"invoiceNo": "INV-001",
|
|
119
|
+
"vendor": {"companyName": "Acme Corp"},
|
|
120
|
+
"customer": {"Name": "Client Inc"},
|
|
121
|
+
"LineItems": [{"desc": "Widget", "qty": 2, "unitPrice": 50, "amount": 100}],
|
|
122
|
+
"SubTotal": 100,
|
|
123
|
+
"tax": 8.50,
|
|
124
|
+
"TotalAmt": 108.50,
|
|
125
|
+
}, zugferd=True)
|
|
126
|
+
|
|
127
|
+
if result["render_ready"] and result.get("zugferd_ready"):
|
|
128
|
+
# safe to call factur-x / drafthorse
|
|
129
|
+
print("All checks passed")
|
|
130
|
+
else:
|
|
131
|
+
for error in result["errors"]:
|
|
132
|
+
print(f"BLOCKED: {error['message']}")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`validate_invoice()` returns:
|
|
136
|
+
- `status`: "ready" | "ready_with_warnings" | "blocked"
|
|
137
|
+
- `render_ready`: bool
|
|
138
|
+
- `canonical`: normalized invoice dict (all fields in canonical names)
|
|
139
|
+
- `errors`: list of blocking issues with rule_id, path, expected/actual
|
|
140
|
+
- `warnings`: advisory issues
|
|
141
|
+
- `normalizations`: field-level provenance (what was renamed, coerced, computed)
|
|
142
|
+
- `zugferd_ready`: bool (if `zugferd=True`)
|
|
143
|
+
|
|
144
|
+
## Stripe adapter
|
|
145
|
+
|
|
146
|
+
Raw Stripe Invoice API responses use cents, Unix timestamps, and nested structures. The adapter handles all of it:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
trustrender validate stripe_invoice.json --source stripe --zugferd
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from trustrender import validate_invoice
|
|
154
|
+
from trustrender.adapters import from_stripe
|
|
155
|
+
|
|
156
|
+
result = validate_invoice(from_stripe(raw_stripe_response), zugferd=True)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The adapter converts cents to dollars, timestamps to dates, extracts line items from `lines.data[]`, and maps customer fields to recipient. Seller info is not included in Stripe invoices — TrustRender will flag it if required for ZUGFeRD compliance.
|
|
160
|
+
|
|
161
|
+
## What it normalizes
|
|
162
|
+
|
|
163
|
+
90+ vendor field aliases across QuickBooks, Xero, Stripe, and generic CSV/ERP formats:
|
|
164
|
+
|
|
165
|
+
| Source field | Canonical field |
|
|
166
|
+
|---|---|
|
|
167
|
+
| `DocNumber`, `invoiceNo`, `inv_no`, `ref` | `invoice_number` |
|
|
168
|
+
| `CompanyName`, `account_name`, `bill_from_name` | `sender.name` |
|
|
169
|
+
| `customer`, `billTo`, `Contact` | `recipient` |
|
|
170
|
+
| `Line`, `LineItems`, `entries`, `products` | `items` |
|
|
171
|
+
| `UnitPrice`, `cost`, `rate`, `unitCost` | `unit_price` |
|
|
172
|
+
| `Amount`, `LineAmount`, `line_total` | `line_total` |
|
|
173
|
+
| `SubTotal`, `net_total`, `sub_total` | `subtotal` |
|
|
174
|
+
| `TotalAmt`, `grand_total`, `amount_due` | `total` |
|
|
175
|
+
|
|
176
|
+
Plus: type coercion (`"$1,234.56"` → `1234.56`), date parsing (`"April 10, 2026"` → `2026-04-10`), computed defaults (missing `line_total` = `qty × price`), near-match typo detection (`invioce_number` → suggests `invoice_number`).
|
|
177
|
+
|
|
178
|
+
## What it checks
|
|
179
|
+
|
|
180
|
+
7 deterministic semantic checks, all arithmetic:
|
|
181
|
+
|
|
182
|
+
| Check | What it catches |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `identity.invoice_number` | Missing or empty invoice number |
|
|
185
|
+
| `identity.sender_name` | Missing vendor/sender name |
|
|
186
|
+
| `identity.recipient_name` | Missing recipient/buyer name |
|
|
187
|
+
| `items.non_empty` | No line items |
|
|
188
|
+
| `arithmetic.line_total` | line_total ≠ qty × unit_price |
|
|
189
|
+
| `arithmetic.subtotal` | subtotal ≠ sum of line_totals |
|
|
190
|
+
| `arithmetic.total` | total ≠ subtotal + tax_amount |
|
|
191
|
+
|
|
192
|
+
No AI. No heuristics. Every check is deterministic and objectively verifiable.
|
|
193
|
+
|
|
194
|
+
## CLI
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
trustrender validate <data.json> [--zugferd] [--format text|json]
|
|
198
|
+
trustrender ingest <data.json> [-o canonical.json]
|
|
199
|
+
trustrender render <template> <data.json> -o <output.pdf> [--zugferd en16931]
|
|
200
|
+
trustrender preflight <template> <data.json> [--zugferd en16931]
|
|
201
|
+
trustrender serve --templates <dir> [--port 8190]
|
|
202
|
+
trustrender doctor [--smoke]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Integration with factur-x / drafthorse
|
|
206
|
+
|
|
207
|
+
TrustRender validates and normalizes. You generate and embed with the library of your choice.
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from trustrender import validate_invoice
|
|
211
|
+
|
|
212
|
+
# Step 1: Validate with TrustRender
|
|
213
|
+
result = validate_invoice(messy_data, zugferd=True)
|
|
214
|
+
if not result["render_ready"] or not result["zugferd_ready"]:
|
|
215
|
+
raise ValueError(f"Invoice blocked: {result['errors']}")
|
|
216
|
+
|
|
217
|
+
# Step 2: Use the canonical payload with drafthorse or factur-x
|
|
218
|
+
canonical = result["canonical"]
|
|
219
|
+
# ... your existing ZUGFeRD generation code here
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## EN 16931 e-invoicing (narrow scope)
|
|
223
|
+
|
|
224
|
+
Catches many document-level and ZUGFeRD/EN 16931 readiness issues before embedding. Currently supports:
|
|
225
|
+
|
|
226
|
+
- **Domestic German B2B invoices** with standard VAT, EUR, SEPA payment
|
|
227
|
+
- Single or mixed VAT rates (7% + 19%)
|
|
228
|
+
- Invoice type 380 and credit note 381
|
|
229
|
+
- PDF/A-3b with embedded CII XML (requires `trustrender[render]`)
|
|
230
|
+
|
|
231
|
+
Not supported (fails loudly): reverse charge, cross-border, allowances/charges, non-EUR currencies.
|
|
232
|
+
|
|
233
|
+
See [docs/einvoice-scope.md](docs/einvoice-scope.md) for the full scope matrix.
|
|
234
|
+
|
|
235
|
+
## Optional: PDF rendering
|
|
236
|
+
|
|
237
|
+
If you also want TrustRender to generate PDFs (not just validate):
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
pip install "trustrender[render]"
|
|
241
|
+
trustrender render invoice.j2.typ data.json -o invoice.pdf --zugferd en16931
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Rendering uses Typst — no browser, no Chromium. Fast and deterministic.
|
|
245
|
+
|
|
246
|
+
## What this is not
|
|
247
|
+
|
|
248
|
+
- Not a full AP automation platform
|
|
249
|
+
- Not an e-invoice compliance certification
|
|
250
|
+
- Not an AI-powered data fixer (all corrections are deterministic)
|
|
251
|
+
- Not a replacement for factur-x or drafthorse — it's the validation layer you run before them
|
|
252
|
+
|
|
253
|
+
## Development
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
pip install -e ".[dev]"
|
|
257
|
+
trustrender doctor --smoke
|
|
258
|
+
pytest
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# TrustRender
|
|
2
|
+
|
|
3
|
+
Validate and normalize billing data from Stripe, Shopify, and custom systems before Factur-X/ZUGFeRD embedding.
|
|
4
|
+
|
|
5
|
+
If you're bridging a non-compliant billing platform into EU e-invoicing, libraries like [factur-x](https://github.com/akretion/factur-x) and [drafthorse](https://github.com/pretix/python-drafthorse) assume your data is already correct. TrustRender catches arithmetic mismatches, field misalignment, missing required fields, and structural problems before compliant XML gets embedded.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install trustrender
|
|
9
|
+
trustrender validate invoice.json
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+
Takes invoice JSON from Stripe, Shopify, custom billing APIs, or legacy exports and tells you whether it's safe to embed as Factur-X/ZUGFeRD.
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
$ trustrender validate quickbooks_invoice.json
|
|
18
|
+
|
|
19
|
+
Invoice: INV-2026-5541
|
|
20
|
+
From: Summit Analytics Co.
|
|
21
|
+
To: Horizon Financial
|
|
22
|
+
Items: 2
|
|
23
|
+
Total: $10,524.50
|
|
24
|
+
|
|
25
|
+
Normalizations (25):
|
|
26
|
+
DocNumber → invoice_number CompanyName → sender.name
|
|
27
|
+
TxnDate → invoice_date customer.Name → recipient.name
|
|
28
|
+
Line → items SubTotal → subtotal
|
|
29
|
+
... and 19 more
|
|
30
|
+
|
|
31
|
+
PASS — invoice data is valid
|
|
32
|
+
|
|
33
|
+
Safe to embed in Factur-X/ZUGFeRD PDF.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Bad data gets blocked:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
$ trustrender validate ocr_extracted_invoice.json
|
|
40
|
+
|
|
41
|
+
BLOCKED — 2 problem(s)
|
|
42
|
+
|
|
43
|
+
items[1] total is wrong
|
|
44
|
+
You entered $459.00 but math says $450.00
|
|
45
|
+
Fix the line total or the price/quantity.
|
|
46
|
+
|
|
47
|
+
Subtotal is wrong
|
|
48
|
+
Lines add up to $12,459.00 but you listed $12,450.00
|
|
49
|
+
|
|
50
|
+
This invoice cannot be processed until the problems above are fixed.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
pip install trustrender
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Core install requires only `drafthorse`. No Typst, no browser, no heavy deps.
|
|
60
|
+
|
|
61
|
+
Optional extras:
|
|
62
|
+
```
|
|
63
|
+
pip install "trustrender[zugferd]" # XSD/Schematron validation
|
|
64
|
+
pip install "trustrender[render]" # PDF rendering via Typst
|
|
65
|
+
pip install "trustrender[all]" # everything
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Requires Python 3.11+.
|
|
69
|
+
|
|
70
|
+
## Python API
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from trustrender import validate_invoice
|
|
74
|
+
|
|
75
|
+
result = validate_invoice({
|
|
76
|
+
"invoiceNo": "INV-001",
|
|
77
|
+
"vendor": {"companyName": "Acme Corp"},
|
|
78
|
+
"customer": {"Name": "Client Inc"},
|
|
79
|
+
"LineItems": [{"desc": "Widget", "qty": 2, "unitPrice": 50, "amount": 100}],
|
|
80
|
+
"SubTotal": 100,
|
|
81
|
+
"tax": 8.50,
|
|
82
|
+
"TotalAmt": 108.50,
|
|
83
|
+
}, zugferd=True)
|
|
84
|
+
|
|
85
|
+
if result["render_ready"] and result.get("zugferd_ready"):
|
|
86
|
+
# safe to call factur-x / drafthorse
|
|
87
|
+
print("All checks passed")
|
|
88
|
+
else:
|
|
89
|
+
for error in result["errors"]:
|
|
90
|
+
print(f"BLOCKED: {error['message']}")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`validate_invoice()` returns:
|
|
94
|
+
- `status`: "ready" | "ready_with_warnings" | "blocked"
|
|
95
|
+
- `render_ready`: bool
|
|
96
|
+
- `canonical`: normalized invoice dict (all fields in canonical names)
|
|
97
|
+
- `errors`: list of blocking issues with rule_id, path, expected/actual
|
|
98
|
+
- `warnings`: advisory issues
|
|
99
|
+
- `normalizations`: field-level provenance (what was renamed, coerced, computed)
|
|
100
|
+
- `zugferd_ready`: bool (if `zugferd=True`)
|
|
101
|
+
|
|
102
|
+
## Stripe adapter
|
|
103
|
+
|
|
104
|
+
Raw Stripe Invoice API responses use cents, Unix timestamps, and nested structures. The adapter handles all of it:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
trustrender validate stripe_invoice.json --source stripe --zugferd
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from trustrender import validate_invoice
|
|
112
|
+
from trustrender.adapters import from_stripe
|
|
113
|
+
|
|
114
|
+
result = validate_invoice(from_stripe(raw_stripe_response), zugferd=True)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The adapter converts cents to dollars, timestamps to dates, extracts line items from `lines.data[]`, and maps customer fields to recipient. Seller info is not included in Stripe invoices — TrustRender will flag it if required for ZUGFeRD compliance.
|
|
118
|
+
|
|
119
|
+
## What it normalizes
|
|
120
|
+
|
|
121
|
+
90+ vendor field aliases across QuickBooks, Xero, Stripe, and generic CSV/ERP formats:
|
|
122
|
+
|
|
123
|
+
| Source field | Canonical field |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `DocNumber`, `invoiceNo`, `inv_no`, `ref` | `invoice_number` |
|
|
126
|
+
| `CompanyName`, `account_name`, `bill_from_name` | `sender.name` |
|
|
127
|
+
| `customer`, `billTo`, `Contact` | `recipient` |
|
|
128
|
+
| `Line`, `LineItems`, `entries`, `products` | `items` |
|
|
129
|
+
| `UnitPrice`, `cost`, `rate`, `unitCost` | `unit_price` |
|
|
130
|
+
| `Amount`, `LineAmount`, `line_total` | `line_total` |
|
|
131
|
+
| `SubTotal`, `net_total`, `sub_total` | `subtotal` |
|
|
132
|
+
| `TotalAmt`, `grand_total`, `amount_due` | `total` |
|
|
133
|
+
|
|
134
|
+
Plus: type coercion (`"$1,234.56"` → `1234.56`), date parsing (`"April 10, 2026"` → `2026-04-10`), computed defaults (missing `line_total` = `qty × price`), near-match typo detection (`invioce_number` → suggests `invoice_number`).
|
|
135
|
+
|
|
136
|
+
## What it checks
|
|
137
|
+
|
|
138
|
+
7 deterministic semantic checks, all arithmetic:
|
|
139
|
+
|
|
140
|
+
| Check | What it catches |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `identity.invoice_number` | Missing or empty invoice number |
|
|
143
|
+
| `identity.sender_name` | Missing vendor/sender name |
|
|
144
|
+
| `identity.recipient_name` | Missing recipient/buyer name |
|
|
145
|
+
| `items.non_empty` | No line items |
|
|
146
|
+
| `arithmetic.line_total` | line_total ≠ qty × unit_price |
|
|
147
|
+
| `arithmetic.subtotal` | subtotal ≠ sum of line_totals |
|
|
148
|
+
| `arithmetic.total` | total ≠ subtotal + tax_amount |
|
|
149
|
+
|
|
150
|
+
No AI. No heuristics. Every check is deterministic and objectively verifiable.
|
|
151
|
+
|
|
152
|
+
## CLI
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
trustrender validate <data.json> [--zugferd] [--format text|json]
|
|
156
|
+
trustrender ingest <data.json> [-o canonical.json]
|
|
157
|
+
trustrender render <template> <data.json> -o <output.pdf> [--zugferd en16931]
|
|
158
|
+
trustrender preflight <template> <data.json> [--zugferd en16931]
|
|
159
|
+
trustrender serve --templates <dir> [--port 8190]
|
|
160
|
+
trustrender doctor [--smoke]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Integration with factur-x / drafthorse
|
|
164
|
+
|
|
165
|
+
TrustRender validates and normalizes. You generate and embed with the library of your choice.
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from trustrender import validate_invoice
|
|
169
|
+
|
|
170
|
+
# Step 1: Validate with TrustRender
|
|
171
|
+
result = validate_invoice(messy_data, zugferd=True)
|
|
172
|
+
if not result["render_ready"] or not result["zugferd_ready"]:
|
|
173
|
+
raise ValueError(f"Invoice blocked: {result['errors']}")
|
|
174
|
+
|
|
175
|
+
# Step 2: Use the canonical payload with drafthorse or factur-x
|
|
176
|
+
canonical = result["canonical"]
|
|
177
|
+
# ... your existing ZUGFeRD generation code here
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## EN 16931 e-invoicing (narrow scope)
|
|
181
|
+
|
|
182
|
+
Catches many document-level and ZUGFeRD/EN 16931 readiness issues before embedding. Currently supports:
|
|
183
|
+
|
|
184
|
+
- **Domestic German B2B invoices** with standard VAT, EUR, SEPA payment
|
|
185
|
+
- Single or mixed VAT rates (7% + 19%)
|
|
186
|
+
- Invoice type 380 and credit note 381
|
|
187
|
+
- PDF/A-3b with embedded CII XML (requires `trustrender[render]`)
|
|
188
|
+
|
|
189
|
+
Not supported (fails loudly): reverse charge, cross-border, allowances/charges, non-EUR currencies.
|
|
190
|
+
|
|
191
|
+
See [docs/einvoice-scope.md](docs/einvoice-scope.md) for the full scope matrix.
|
|
192
|
+
|
|
193
|
+
## Optional: PDF rendering
|
|
194
|
+
|
|
195
|
+
If you also want TrustRender to generate PDFs (not just validate):
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
pip install "trustrender[render]"
|
|
199
|
+
trustrender render invoice.j2.typ data.json -o invoice.pdf --zugferd en16931
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Rendering uses Typst — no browser, no Chromium. Fast and deterministic.
|
|
203
|
+
|
|
204
|
+
## What this is not
|
|
205
|
+
|
|
206
|
+
- Not a full AP automation platform
|
|
207
|
+
- Not an e-invoice compliance certification
|
|
208
|
+
- Not an AI-powered data fixer (all corrections are deterministic)
|
|
209
|
+
- Not a replacement for factur-x or drafthorse — it's the validation layer you run before them
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
pip install -e ".[dev]"
|
|
215
|
+
trustrender doctor --smoke
|
|
216
|
+
pytest
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
MIT
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "trustrender"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.3.1"
|
|
8
|
+
description = "Validate and normalize Stripe, Shopify, and custom billing data for Factur-X/ZUGFeRD"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
classifiers = [
|
|
11
11
|
"Development Status :: 3 - Alpha",
|
|
@@ -15,17 +15,14 @@ classifiers = [
|
|
|
15
15
|
"Programming Language :: Python :: 3.11",
|
|
16
16
|
"Programming Language :: Python :: 3.12",
|
|
17
17
|
"Topic :: Office/Business :: Financial",
|
|
18
|
-
"Topic ::
|
|
18
|
+
"Topic :: Office/Business :: Financial :: Accounting",
|
|
19
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
20
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
19
21
|
]
|
|
20
22
|
readme = "README.md"
|
|
21
23
|
requires-python = ">=3.11"
|
|
22
24
|
dependencies = [
|
|
23
|
-
"typst>=0.14",
|
|
24
|
-
"jinja2>=3.1",
|
|
25
|
-
"starlette>=0.40",
|
|
26
|
-
"uvicorn>=0.30",
|
|
27
25
|
"drafthorse>=2024.0",
|
|
28
|
-
"pypdf>=4.0",
|
|
29
26
|
]
|
|
30
27
|
|
|
31
28
|
[project.urls]
|
|
@@ -39,14 +36,27 @@ Changelog = "https://github.com/verityengine/trustrender/releases"
|
|
|
39
36
|
trustrender = "trustrender.cli:main"
|
|
40
37
|
|
|
41
38
|
[project.optional-dependencies]
|
|
39
|
+
render = [
|
|
40
|
+
"typst>=0.14",
|
|
41
|
+
"jinja2>=3.1",
|
|
42
|
+
"pypdf>=4.0",
|
|
43
|
+
]
|
|
44
|
+
serve = [
|
|
45
|
+
"starlette>=0.40",
|
|
46
|
+
"uvicorn>=0.30",
|
|
47
|
+
]
|
|
48
|
+
zugferd = [
|
|
49
|
+
"factur-x>=3.0",
|
|
50
|
+
]
|
|
51
|
+
all = [
|
|
52
|
+
"trustrender[render,serve,zugferd]",
|
|
53
|
+
]
|
|
42
54
|
dev = [
|
|
43
55
|
"pytest>=8.0",
|
|
44
56
|
"ruff>=0.4",
|
|
45
57
|
"httpx>=0.27",
|
|
46
58
|
"factur-x>=3.0",
|
|
47
|
-
]
|
|
48
|
-
zugferd = [
|
|
49
|
-
"factur-x>=3.0",
|
|
59
|
+
"trustrender[all]",
|
|
50
60
|
]
|
|
51
61
|
|
|
52
62
|
[tool.setuptools.packages.find]
|