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.
Files changed (90) hide show
  1. trustrender-0.3.1/PKG-INFO +263 -0
  2. trustrender-0.3.1/README.md +221 -0
  3. {trustrender-0.2.0 → trustrender-0.3.1}/pyproject.toml +21 -11
  4. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/__init__.py +109 -11
  5. trustrender-0.3.1/src/trustrender/adapters/__init__.py +14 -0
  6. trustrender-0.3.1/src/trustrender/adapters/stripe.py +163 -0
  7. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/cli.py +404 -104
  8. trustrender-0.3.1/src/trustrender/invoice_aliases.py +275 -0
  9. trustrender-0.3.1/src/trustrender/invoice_ingest.py +1002 -0
  10. trustrender-0.3.1/src/trustrender/invoice_schema.py +171 -0
  11. trustrender-0.3.1/src/trustrender/playground/assets/index-Bsf6YnD1.css +2 -0
  12. trustrender-0.3.1/src/trustrender/playground/assets/index-C6K0JsLw.js +97 -0
  13. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/playground/index.html +2 -2
  14. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/server.py +42 -3
  15. trustrender-0.3.1/src/trustrender.egg-info/PKG-INFO +263 -0
  16. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/SOURCES.txt +11 -2
  17. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/requires.txt +13 -5
  18. trustrender-0.3.1/tests/test_adapters.py +170 -0
  19. trustrender-0.3.1/tests/test_adversarial_corpus.py +278 -0
  20. trustrender-0.3.1/tests/test_invoice_ingest.py +646 -0
  21. trustrender-0.3.1/tests/test_real_payloads.py +101 -0
  22. trustrender-0.2.0/PKG-INFO +0 -274
  23. trustrender-0.2.0/README.md +0 -239
  24. trustrender-0.2.0/src/trustrender/playground/assets/index--cOwr7nw.css +0 -2
  25. trustrender-0.2.0/src/trustrender/playground/assets/index-CiBSP9E3.js +0 -81
  26. trustrender-0.2.0/src/trustrender.egg-info/PKG-INFO +0 -274
  27. {trustrender-0.2.0 → trustrender-0.3.1}/LICENSE +0 -0
  28. {trustrender-0.2.0 → trustrender-0.3.1}/setup.cfg +0 -0
  29. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/einvoice.j2.typ +0 -0
  30. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/einvoice_data.json +0 -0
  31. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/invoice.j2.typ +0 -0
  32. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/invoice_data.json +0 -0
  33. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/letter.j2.typ +0 -0
  34. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/letter_data.json +0 -0
  35. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/receipt.j2.typ +0 -0
  36. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/receipt_data.json +0 -0
  37. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/report.j2.typ +0 -0
  38. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/report_data.json +0 -0
  39. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/statement.j2.typ +0 -0
  40. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/builtin_templates/statement_data.json +0 -0
  41. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/contract.py +0 -0
  42. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/dashboard.py +0 -0
  43. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/doctor.py +0 -0
  44. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/engine.py +0 -0
  45. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/errors.py +0 -0
  46. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/filters.py +0 -0
  47. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fingerprint.py +0 -0
  48. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/Inter-Bold.ttf +0 -0
  49. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/Inter-BoldItalic.ttf +0 -0
  50. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/Inter-Italic.ttf +0 -0
  51. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/Inter-Regular.ttf +0 -0
  52. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/fonts/Inter/LICENSE.txt +0 -0
  53. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/playground/assets/pdf.worker-C1breYqn.mjs +0 -0
  54. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/playground/favicon.svg +0 -0
  55. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/playground/icons.svg +0 -0
  56. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/provenance.py +0 -0
  57. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/readiness.py +0 -0
  58. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/regression.py +0 -0
  59. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/semantic.py +0 -0
  60. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/templates.py +0 -0
  61. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/trace.py +0 -0
  62. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender/zugferd.py +0 -0
  63. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/dependency_links.txt +0 -0
  64. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/entry_points.txt +0 -0
  65. {trustrender-0.2.0 → trustrender-0.3.1}/src/trustrender.egg-info/top_level.txt +0 -0
  66. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_audit_e2e.py +0 -0
  67. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_cli.py +0 -0
  68. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_contract.py +0 -0
  69. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_doctor.py +0 -0
  70. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_engine.py +0 -0
  71. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_error_pipeline.py +0 -0
  72. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_errors.py +0 -0
  73. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_filters.py +0 -0
  74. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_fingerprint.py +0 -0
  75. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_fingerprint_stress.py +0 -0
  76. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_fonts.py +0 -0
  77. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_pagination.py +0 -0
  78. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_provenance.py +0 -0
  79. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_readiness.py +0 -0
  80. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_regression.py +0 -0
  81. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_regression_stress.py +0 -0
  82. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_render.py +0 -0
  83. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_semantic.py +0 -0
  84. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_semantic_stress.py +0 -0
  85. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_server.py +0 -0
  86. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_templates.py +0 -0
  87. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_trace.py +0 -0
  88. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_ugly_data.py +0 -0
  89. {trustrender-0.2.0 → trustrender-0.3.1}/tests/test_ugly_data_pressure.py +0 -0
  90. {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.2.0"
8
- description = "Fast, code-first PDF generation from structured data. No browser, no Chromium."
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 :: Text Processing :: Markup",
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]