quints 0.1.0__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.
Files changed (72) hide show
  1. {quints-0.1.0 → quints-0.2.0}/PKG-INFO +5 -5
  2. {quints-0.1.0 → quints-0.2.0}/examples/.gitignore +3 -0
  3. {quints-0.1.0 → quints-0.2.0}/examples/AGENTS.md +21 -10
  4. quints-0.2.0/examples/accounts.bean +43 -0
  5. {quints-0.1.0 → quints-0.2.0}/examples/answers.toml +1 -0
  6. quints-0.2.0/examples/books/2026.bean +28 -0
  7. quints-0.2.0/examples/commodities.bean +9 -0
  8. quints-0.2.0/examples/main.bean +23 -0
  9. quints-0.2.0/examples/pyproject.toml +14 -0
  10. {quints-0.1.0 → quints-0.2.0}/examples/quints.toml +18 -3
  11. {quints-0.1.0 → quints-0.2.0}/pyproject.toml +5 -5
  12. {quints-0.1.0 → quints-0.2.0}/src/quints/cli.py +24 -2
  13. {quints-0.1.0 → quints-0.2.0}/src/quints/config.py +12 -0
  14. quints-0.2.0/src/quints/init.py +680 -0
  15. {quints-0.1.0 → quints-0.2.0}/src/quints/kmu.py +38 -9
  16. {quints-0.1.0 → quints-0.2.0}/src/quints/plugins/kmu.py +5 -3
  17. {quints-0.1.0 → quints-0.2.0}/src/quints/report_pdf.py +10 -5
  18. {quints-0.1.0 → quints-0.2.0}/tests/test_init.py +72 -5
  19. quints-0.1.0/examples/main.bean +0 -78
  20. quints-0.1.0/src/quints/init.py +0 -462
  21. {quints-0.1.0 → quints-0.2.0}/.gitignore +0 -0
  22. {quints-0.1.0 → quints-0.2.0}/LICENSE +0 -0
  23. {quints-0.1.0 → quints-0.2.0}/README.md +0 -0
  24. {quints-0.1.0 → quints-0.2.0}/examples/documents/.gitkeep +0 -0
  25. {quints-0.1.0 → quints-0.2.0}/examples/inbox/.gitkeep +0 -0
  26. {quints-0.1.0 → quints-0.2.0}/examples/invoicing/acme-2026-07.yaml +0 -0
  27. {quints-0.1.0 → quints-0.2.0}/examples/invoicing/issuer.yaml +0 -0
  28. {quints-0.1.0 → quints-0.2.0}/examples/prices.bean +0 -0
  29. {quints-0.1.0 → quints-0.2.0}/examples/staging/.gitkeep +0 -0
  30. {quints-0.1.0 → quints-0.2.0}/examples/statements/ubs-2026.mt940 +0 -0
  31. {quints-0.1.0 → quints-0.2.0}/src/quints/__init__.py +0 -0
  32. {quints-0.1.0 → quints-0.2.0}/src/quints/fava/__init__.py +0 -0
  33. {quints-0.1.0 → quints-0.2.0}/src/quints/fava/templates/QuintDashboard.html +0 -0
  34. {quints-0.1.0 → quints-0.2.0}/src/quints/fx.py +0 -0
  35. {quints-0.1.0 → quints-0.2.0}/src/quints/importing.py +0 -0
  36. {quints-0.1.0 → quints-0.2.0}/src/quints/inbox.py +0 -0
  37. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/__init__.py +0 -0
  38. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/draft.py +0 -0
  39. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/labels.py +0 -0
  40. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/model.py +0 -0
  41. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/qr.py +0 -0
  42. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/render.py +0 -0
  43. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/template.typ +0 -0
  44. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/vatid.py +0 -0
  45. {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/verify.py +0 -0
  46. {quints-0.1.0 → quints-0.2.0}/src/quints/ledger.py +0 -0
  47. {quints-0.1.0 → quints-0.2.0}/src/quints/match.py +0 -0
  48. {quints-0.1.0 → quints-0.2.0}/src/quints/mwst.py +0 -0
  49. {quints-0.1.0 → quints-0.2.0}/src/quints/plugins/__init__.py +0 -0
  50. {quints-0.1.0 → quints-0.2.0}/src/quints/prices.py +0 -0
  51. {quints-0.1.0 → quints-0.2.0}/src/quints/receivables.py +0 -0
  52. {quints-0.1.0 → quints-0.2.0}/src/quints/report.typ +0 -0
  53. {quints-0.1.0 → quints-0.2.0}/src/quints/settlement.py +0 -0
  54. {quints-0.1.0 → quints-0.2.0}/src/quints/ui.py +0 -0
  55. {quints-0.1.0 → quints-0.2.0}/src/quints/vat.py +0 -0
  56. {quints-0.1.0 → quints-0.2.0}/tests/fixtures/transactions.mt940 +0 -0
  57. {quints-0.1.0 → quints-0.2.0}/tests/test_cli_json.py +0 -0
  58. {quints-0.1.0 → quints-0.2.0}/tests/test_config.py +0 -0
  59. {quints-0.1.0 → quints-0.2.0}/tests/test_docs.py +0 -0
  60. {quints-0.1.0 → quints-0.2.0}/tests/test_fava_ext.py +0 -0
  61. {quints-0.1.0 → quints-0.2.0}/tests/test_fx.py +0 -0
  62. {quints-0.1.0 → quints-0.2.0}/tests/test_importing.py +0 -0
  63. {quints-0.1.0 → quints-0.2.0}/tests/test_inbox_match.py +0 -0
  64. {quints-0.1.0 → quints-0.2.0}/tests/test_invoice.py +0 -0
  65. {quints-0.1.0 → quints-0.2.0}/tests/test_kmu_plugin.py +0 -0
  66. {quints-0.1.0 → quints-0.2.0}/tests/test_kmu_report.py +0 -0
  67. {quints-0.1.0 → quints-0.2.0}/tests/test_ledger.py +0 -0
  68. {quints-0.1.0 → quints-0.2.0}/tests/test_mwst.py +0 -0
  69. {quints-0.1.0 → quints-0.2.0}/tests/test_prices_sync.py +0 -0
  70. {quints-0.1.0 → quints-0.2.0}/tests/test_receivables.py +0 -0
  71. {quints-0.1.0 → quints-0.2.0}/tests/test_report_pdf.py +0 -0
  72. {quints-0.1.0 → quints-0.2.0}/tests/test_settlement.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quints
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Swiss VAT & accounting toolkit for plain-text (beancount) books: MWST reports and quarterly settlement, Bezugsteuer helpers, BAZG daily FX rates, QR-bill invoicing, KMU statutory statements, and statement importers (UBS MT940, Wise, Stripe) that draft into a staging area
5
5
  License: GPL-2.0-only
6
6
  License-File: LICENSE
@@ -8,10 +8,10 @@ Keywords: accounting,beancount,fava,invoice,kmu,mwst,plain-text-accounting,qr-bi
8
8
  Requires-Python: >=3.10
9
9
  Requires-Dist: babel>=2.13
10
10
  Requires-Dist: beancount>=3.2.0
11
- Requires-Dist: beangulp-mt940
12
- Requires-Dist: beangulp-stripe
13
- Requires-Dist: beangulp-wise[sca]
14
- Requires-Dist: beanprice-bazg
11
+ Requires-Dist: beangulp-mt940>=0.1.0
12
+ Requires-Dist: beangulp-stripe>=0.1.0
13
+ Requires-Dist: beangulp-wise[sca]>=0.1.0
14
+ Requires-Dist: beanprice-bazg>=0.1.0
15
15
  Requires-Dist: fava>=1.30
16
16
  Requires-Dist: pydantic>=2.7
17
17
  Requires-Dist: python-stdnum>=1.20
@@ -3,3 +3,6 @@
3
3
  .env
4
4
  __pycache__/
5
5
  *.pdf
6
+ .venv/
7
+ .DS_Store
8
+ .wise/
@@ -5,20 +5,31 @@ with [`quints`](https://github.com/sealambda/quints). **`quints` is a
5
5
  deterministic tool — you drive it, it never calls a model.** Your job is to
6
6
  extend and maintain the ledger; `quints` validates and reports on it.
7
7
 
8
+ ## Setup
9
+
10
+ `uv sync` once — it installs quints, which brings beancount and fava along.
11
+ Then `uv run quints check` (or activate the venv and call `quints` and the
12
+ standard beancount tools directly).
13
+
8
14
  ## Layout
9
15
 
10
- - `main.bean` — the ledger (accounts + transactions). `plugin
11
- "quints.plugins.kmu"` is enabled: every `*:CH:GmbH:*` account **must** be
12
- opened with a four-digit `kmu:` code (Swiss KMU Kontenrahmen).
13
- - `prices.bean` — FX rates; refresh with `quints prices sync`.
14
- - `quints.toml` — entity config (name, VAT, importer rules). VAT *rates* are
15
- law and live in code, not here.
16
- - `staging/` — importer drafts land here; `inbox/` — source documents.
16
+ - `main.bean` — options, plugins, includes; the entry point every tool loads.
17
+ `plugin "quints.plugins.kmu"` is enabled: every `*:CH:GmbH:*` account
18
+ **must** be opened with a four-digit `kmu:` code (Swiss KMU Kontenrahmen).
19
+ - `accounts.bean` — the chart of accounts (all `open` directives).
20
+ - `books/2026.bean` — transactions, one file per fiscal year. `main.bean`
21
+ includes `books/*.bean`, so a new year just needs a new file.
22
+ - `commodities.bean` — currencies; `prices.bean` — FX rates, refresh with
23
+ `quints prices sync`.
24
+ - `quints.toml` — entity config (name, legal form, VAT, importer rules). VAT
25
+ *rates* are law and live in code, not here.
26
+ - `staging/` — importer drafts land here; `inbox/` — incoming source
27
+ documents; `documents/` — filed documents, mirroring the account tree.
17
28
 
18
29
  ## Extending the chart of accounts (the part that needs judgement)
19
30
 
20
- Add income/expense sub-trees for this business as `open` directives, each with
21
- the KMU code it rolls up to, e.g.:
31
+ Add income/expense sub-trees for this business as `open` directives in
32
+ `accounts.bean`, each with the KMU code it rolls up to, e.g.:
22
33
 
23
34
  ```beancount
24
35
  2026-01-01 open Expenses:CH:GmbH:Marketing:Ads CHF
@@ -33,7 +44,7 @@ KMU Kontenrahmen; `quints check` fails on a `:CH:GmbH:` account with no valid
33
44
 
34
45
  1. Draft bank/PSP activity: `quints import ubs <file>` → `staging/`.
35
46
  2. Review, add the VAT decision (InputVAT / Bezugsteuer / none) and a linked
36
- document, then move drafts into `main.bean`.
47
+ document, then move drafts into `books/2026.bean`.
37
48
  3. **Always** `quints check` before you consider the books consistent.
38
49
 
39
50
  ## Machine-readable surfaces (prefer these over scraping text)
@@ -0,0 +1,43 @@
1
+ ; Chart of accounts — Swiss KMU Kontenrahmen. Every *:CH:GmbH:* account
2
+ ; must carry the four-digit kmu: code it rolls up to (enforced by
3
+ ; quints.plugins.kmu). Extend the chart by adding more open directives;
4
+ ; see AGENTS.md and `quints report konten`.
5
+
6
+ 2026-01-01 open Assets:CH:GmbH:Current:UBS:CHF CHF
7
+ kmu: "1020" ; Bank deposits
8
+ 2026-01-01 open Assets:CH:GmbH:Receivable:Trade
9
+ kmu: "1100" ; Trade receivables
10
+ 2026-01-01 open Assets:CH:GmbH:Tax:InputVAT CHF
11
+ kmu: "1170" ; Input VAT
12
+ 2026-01-01 open Liabilities:CH:GmbH:Tax:OutputVAT CHF
13
+ kmu: "2200" ; VAT due
14
+ 2026-01-01 open Liabilities:CH:GmbH:Tax:Bezugsteuer CHF
15
+ kmu: "2200" ; VAT due
16
+ 2026-01-01 open Liabilities:CH:GmbH:Tax:PayableVAT CHF
17
+ kmu: "2200" ; VAT due
18
+ 2026-01-01 open Equity:CH:GmbH:Capital:Share CHF
19
+ kmu: "2800" ; Share capital
20
+ 2026-01-01 open Income:CH:GmbH:Consulting:External:Domestic
21
+ kmu: "3400" ; Revenue from services
22
+ 2026-01-01 open Income:CH:GmbH:Consulting:External:Export
23
+ kmu: "3400" ; Revenue from services
24
+ 2026-01-01 open Expenses:CH:GmbH:IT:Hosting
25
+ kmu: "6570" ; IT expenses
26
+ 2026-01-01 open Expenses:CH:GmbH:FX:CurrencyLoss CHF
27
+ kmu: "6900" ; Financial expenses
28
+ 2026-01-01 open Income:CH:GmbH:Rounding CHF
29
+ kmu: "6950" ; Financial income
30
+ 2026-01-01 open Income:CH:GmbH:FX:CurrencyGain CHF
31
+ kmu: "6950" ; Financial income
32
+ 2026-01-01 open Assets:CH:GmbH:Current:Wise:CHF CHF
33
+ kmu: "1020" ; Bank deposits
34
+ 2026-01-01 open Assets:CH:GmbH:Current:Wise:EUR EUR
35
+ kmu: "1020" ; Bank deposits
36
+ 2026-01-01 open Assets:CH:GmbH:Current:Wise:USD USD
37
+ kmu: "1020" ; Bank deposits
38
+ 2026-01-01 open Expenses:CH:GmbH:BankFees:Wise
39
+ kmu: "6940" ; Bank charges
40
+ 2026-01-01 open Assets:CH:GmbH:Current:Stripe:EUR EUR
41
+ kmu: "1020" ; Bank deposits
42
+ 2026-01-01 open Expenses:CH:GmbH:BankFees:Stripe
43
+ kmu: "6940" ; Bank charges
@@ -8,6 +8,7 @@
8
8
  # never hand-maintained) and that the reporting commands agree on the numbers.
9
9
 
10
10
  entity_name = "Example GmbH"
11
+ legal_form = "gmbh" # or "ag" / "einzelfirma"
11
12
  vat_method = "effective"
12
13
  vat_registered_since = 2026-01-01
13
14
  operating_currency = "CHF"
@@ -0,0 +1,28 @@
1
+ ; 2026 — transactions for the fiscal year. Reviewed importer drafts
2
+ ; move here from staging/. main.bean includes books/*.bean, so a new
3
+ ; year just needs a new books/<year>.bean file.
4
+
5
+ ; ── sample activity (remove once you book your own) ───────────────────
6
+
7
+ 2026-01-02 * "Founders" "Share capital paid in"
8
+ Assets:CH:GmbH:Current:UBS:CHF 20000.00 CHF
9
+ Equity:CH:GmbH:Capital:Share -20000.00 CHF
10
+
11
+ 2026-07-02 * "Acme AG" "Consulting — July" ^INV2026014
12
+ Assets:CH:GmbH:Receivable:Trade 1081.00 CHF
13
+ Income:CH:GmbH:Consulting:External:Domestic -1000.00 CHF
14
+ Liabilities:CH:GmbH:Tax:OutputVAT -81.00 CHF
15
+
16
+ 2026-07-20 * "Acme AG" "Payment INV2026014" ^INV2026014
17
+ Assets:CH:GmbH:Current:UBS:CHF 1081.00 CHF
18
+ Assets:CH:GmbH:Receivable:Trade -1081.00 CHF
19
+
20
+ 2026-08-05 * "Globex Ltd" "Export consulting" ^INV2026015
21
+ Assets:CH:GmbH:Receivable:Trade 500.00 EUR
22
+ Income:CH:GmbH:Consulting:External:Export -500.00 EUR
23
+
24
+ 2026-08-12 * "Foreign SaaS" "Cloud hosting (reverse charge)"
25
+ Expenses:CH:GmbH:IT:Hosting 100.00 EUR
26
+ Assets:CH:GmbH:Tax:InputVAT 7.53 CHF @@ 8.10 EUR
27
+ Liabilities:CH:GmbH:Tax:Bezugsteuer -7.53 CHF @@ 8.10 EUR
28
+ Assets:CH:GmbH:Current:Wise:EUR -100.00 EUR
@@ -0,0 +1,9 @@
1
+ ; Currencies in use. Non-CHF commodities carry the beanprice source
2
+ ; (official BAZG/EZV daily CHF rates) that `quints prices sync` and
3
+ ; `bean-price` read.
4
+
5
+ 2026-01-01 commodity CHF
6
+ 2026-01-01 commodity EUR
7
+ price: "CHF:beanprice_bazg/EUR"
8
+ 2026-01-01 commodity USD
9
+ price: "CHF:beanprice_bazg/USD"
@@ -0,0 +1,23 @@
1
+ ; Example GmbH — books managed with quints (https://github.com/sealambda/quints)
2
+ ;
3
+ ; Generated by `quints init`. Layout:
4
+ ; accounts.bean chart of accounts (KMU Kontenrahmen)
5
+ ; commodities.bean currencies and their price sources
6
+ ; prices.bean FX rates — refresh with `quints prices sync`
7
+ ; books/2026.bean transactions, one file per fiscal year
8
+ ; Validate with `quints check`; after `uv sync`, plain
9
+ ; `bean-check main.bean` and `fava main.bean` work too.
10
+
11
+ plugin "quints.plugins.kmu" ":CH:GmbH:"
12
+ plugin "fava.plugins.link_documents"
13
+
14
+ 2026-01-01 custom "fava-extension" "quints.fava"
15
+
16
+ include "accounts.bean"
17
+ include "commodities.bean"
18
+ include "prices.bean"
19
+ include "books/*.bean"
20
+
21
+ option "title" "Example GmbH"
22
+ option "operating_currency" "CHF"
23
+ option "documents" "documents/"
@@ -0,0 +1,14 @@
1
+ # `uv sync` installs quints — which brings beancount, fava, the statement
2
+ # importers, and the BAZG price source along as its own dependencies — so
3
+ # the standard beancount toolchain (`bean-check main.bean`, `fava
4
+ # main.bean`, `bean-query`) works in this repo, not only the quints CLI.
5
+ [project]
6
+ name = "example-gmbh"
7
+ version = "0.1.0"
8
+ requires-python = ">=3.10"
9
+ dependencies = [
10
+ "quints",
11
+ ]
12
+
13
+ [tool.uv]
14
+ package = false
@@ -1,9 +1,9 @@
1
1
  # quints.toml — everything entity-specific lives here, next to main.bean.
2
- # Account names below are the built-in defaults (CH:GmbH namespacing);
3
- # uncomment and edit in quints.toml + main.bean together to change them.
2
+ # Account names mirror accounts.bean; change them in both files together.
4
3
 
5
4
  [entity]
6
5
  name = "Example GmbH"
6
+ legal_form = "gmbh" # gmbh | ag | einzelfirma
7
7
  vat_method = "effective" # "saldo" is not supported yet
8
8
  vat_registered_since = 2026-01-01 # earlier periods are pre-liability
9
9
  operating_currency = "CHF"
@@ -12,6 +12,21 @@ operating_currency = "CHF"
12
12
  main = "main.bean"
13
13
  prices = "prices.bean"
14
14
 
15
+ [accounts]
16
+ entity_marker = ":CH:GmbH:" # scopes KMU statements and the plugin
17
+ input_vat = "Assets:CH:GmbH:Tax:InputVAT"
18
+ output_vat = "Liabilities:CH:GmbH:Tax:OutputVAT"
19
+ bezugsteuer = "Liabilities:CH:GmbH:Tax:Bezugsteuer"
20
+ payable_vat = "Liabilities:CH:GmbH:Tax:PayableVAT"
21
+ receivable = "Assets:CH:GmbH:Receivable:Trade"
22
+ income_prefix = "Income:CH:GmbH"
23
+ export_marker = ":Export" # income sub-account marker → Ziffer 221
24
+ income_domestic = "Income:CH:GmbH:Consulting:External:Domestic"
25
+ income_export = "Income:CH:GmbH:Consulting:External:Export"
26
+ fx_gain = "Income:CH:GmbH:FX:CurrencyGain"
27
+ fx_loss = "Expenses:CH:GmbH:FX:CurrencyLoss"
28
+ rounding_income = "Income:CH:GmbH:Rounding"
29
+
15
30
  [report]
16
31
  language = "en" # or "de"; --lang overrides
17
32
 
@@ -42,7 +57,7 @@ USD = "Assets:CH:GmbH:Current:Wise:USD"
42
57
  [import.stripe]
43
58
  account_id = "acct_XXXXXXXXXXXX" # guard: refuse a key for another account
44
59
  fees_account = "Expenses:CH:GmbH:BankFees:Stripe"
45
- tax_account = "Assets:CH:GmbH:Tax:InputVAT" # VAT inside Stripe's monthly fee debits
60
+ tax_account = "Assets:CH:GmbH:Tax:InputVAT" # VAT inside Stripe's own fees
46
61
  rules = [
47
62
  ['\bpayout\b', "Assets:CH:GmbH:Current:Wise:EUR", "*"],
48
63
  ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "quints"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "Swiss VAT & accounting toolkit for plain-text (beancount) books: MWST reports and quarterly settlement, Bezugsteuer helpers, BAZG daily FX rates, QR-bill invoicing, KMU statutory statements, and statement importers (UBS MT940, Wise, Stripe) that draft into a staging area"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -12,10 +12,10 @@ keywords = [
12
12
  dependencies = [
13
13
  "babel>=2.13",
14
14
  "beancount>=3.2.0",
15
- "beangulp-mt940",
16
- "beangulp-stripe",
17
- "beangulp-wise[sca]",
18
- "beanprice-bazg",
15
+ "beangulp-mt940>=0.1.0",
16
+ "beangulp-stripe>=0.1.0",
17
+ "beangulp-wise[sca]>=0.1.0",
18
+ "beanprice-bazg>=0.1.0",
19
19
  "fava>=1.30",
20
20
  "pydantic>=2.7",
21
21
  "python-stdnum>=1.20",
@@ -801,6 +801,11 @@ def init(
801
801
  Path("."), help="Target project directory (created if missing)."
802
802
  ),
803
803
  name: str | None = typer.Option(None, "--name", help="Entity name, e.g. 'Acme GmbH'."),
804
+ legal_form: str | None = typer.Option(
805
+ None,
806
+ "--legal-form",
807
+ help="Legal form: gmbh, ag, or einzelfirma (sole proprietorship / freelancer).",
808
+ ),
804
809
  lang: str | None = typer.Option(None, "--lang", "-l", help="Report language: en or de."),
805
810
  importers: str | None = typer.Option(
806
811
  None, "--importers", help="Comma-separated: ubs, wise, stripe (default: none)."
@@ -815,7 +820,8 @@ def init(
815
820
  yes: bool = typer.Option(False, "--yes", "-y", help="Skip prompts; accept defaults."),
816
821
  as_json: bool = typer.Option(False, "--json", help="Machine-readable output."),
817
822
  ):
818
- """Scaffold a new quints project (chart of accounts, quints.toml, AGENTS.md).
823
+ """Scaffold a new quints project (chart of accounts, per-year books,
824
+ quints.toml, pyproject.toml, AGENTS.md).
819
825
 
820
826
  Deterministic: the same answers always produce the same files. Run
821
827
  interactively, or feed a TOML answer-file with --answers for CI/repeatable
@@ -829,6 +835,22 @@ def init(
829
835
  answers = init_mod.Answers()
830
836
 
831
837
  interactive = answers_file is None and not yes
838
+ if legal_form is not None:
839
+ answers = replace(answers, legal_form=legal_form.strip().lower())
840
+ elif interactive:
841
+ answers = replace(
842
+ answers,
843
+ legal_form=typer.prompt("Legal form (gmbh/ag/einzelfirma)", default=answers.legal_form)
844
+ .strip()
845
+ .lower(),
846
+ )
847
+ # An answer-file's entity name is authoritative; otherwise suggest one
848
+ # that matches the chosen legal form instead of the GmbH default.
849
+ example_names = {"gmbh": "Example GmbH", "ag": "Example AG", "einzelfirma": "Jane Doe"}
850
+ if answers_file is None and answers.entity_name == init_mod.Answers.entity_name:
851
+ answers = replace(
852
+ answers, entity_name=example_names.get(answers.legal_form, answers.entity_name)
853
+ )
832
854
  if name is not None:
833
855
  answers = replace(answers, entity_name=name)
834
856
  elif interactive:
@@ -875,7 +897,7 @@ def init(
875
897
  if result.written and not result.skipped:
876
898
  ui.console.print(
877
899
  f"\nScaffolded [b]{answers.entity_name}[/] in {directory}. "
878
- "Next: [b]quints check[/] then [b]quints mwst -q 2026-Q3[/]."
900
+ "Next: [b]uv sync[/], then [b]quints check[/] and [b]quints mwst -q 2026-Q3[/]."
879
901
  )
880
902
 
881
903
 
@@ -28,6 +28,16 @@ else: # pragma: no cover
28
28
 
29
29
  DEFAULT_PATH = Path("quints.toml")
30
30
 
31
+ # Legal form → account-name component (Assets:CH:<component>:…). The keys are
32
+ # the entity families the official KMU Kontenrahmen (veb.ch) prints Klasse-28
33
+ # variants for: juristische Personen (gmbh, ag) and Einzelunternehmen;
34
+ # Personengesellschaft (per-partner capital blocks) is not supported yet.
35
+ LEGAL_FORMS: dict[str, str] = {
36
+ "gmbh": "GmbH",
37
+ "ag": "AG",
38
+ "einzelfirma": "Einzelfirma",
39
+ }
40
+
31
41
 
32
42
  @dataclass(frozen=True)
33
43
  class UbsImport:
@@ -76,6 +86,7 @@ class StripeImport:
76
86
  class Config:
77
87
  # [entity]
78
88
  entity_name: str = "Example GmbH"
89
+ legal_form: str = "gmbh" # key into LEGAL_FORMS; picks the Klasse-28 equity variant
79
90
  vat_method: str = "effective" # "saldo" would need a different Form-310 mapping
80
91
  vat_registered_since: Date | None = None
81
92
  operating_currency: str = "CHF"
@@ -159,6 +170,7 @@ def _from_mapping(raw: dict) -> Config:
159
170
  updates[field] = cast(value) if cast else value
160
171
 
161
172
  take(entity, "name", "entity_name")
173
+ take(entity, "legal_form", "legal_form")
162
174
  take(entity, "vat_method", "vat_method")
163
175
  take(entity, "vat_registered_since", "vat_registered_since")
164
176
  take(entity, "operating_currency", "operating_currency")