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.
- {quints-0.1.0 → quints-0.2.0}/PKG-INFO +5 -5
- {quints-0.1.0 → quints-0.2.0}/examples/.gitignore +3 -0
- {quints-0.1.0 → quints-0.2.0}/examples/AGENTS.md +21 -10
- quints-0.2.0/examples/accounts.bean +43 -0
- {quints-0.1.0 → quints-0.2.0}/examples/answers.toml +1 -0
- quints-0.2.0/examples/books/2026.bean +28 -0
- quints-0.2.0/examples/commodities.bean +9 -0
- quints-0.2.0/examples/main.bean +23 -0
- quints-0.2.0/examples/pyproject.toml +14 -0
- {quints-0.1.0 → quints-0.2.0}/examples/quints.toml +18 -3
- {quints-0.1.0 → quints-0.2.0}/pyproject.toml +5 -5
- {quints-0.1.0 → quints-0.2.0}/src/quints/cli.py +24 -2
- {quints-0.1.0 → quints-0.2.0}/src/quints/config.py +12 -0
- quints-0.2.0/src/quints/init.py +680 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/kmu.py +38 -9
- {quints-0.1.0 → quints-0.2.0}/src/quints/plugins/kmu.py +5 -3
- {quints-0.1.0 → quints-0.2.0}/src/quints/report_pdf.py +10 -5
- {quints-0.1.0 → quints-0.2.0}/tests/test_init.py +72 -5
- quints-0.1.0/examples/main.bean +0 -78
- quints-0.1.0/src/quints/init.py +0 -462
- {quints-0.1.0 → quints-0.2.0}/.gitignore +0 -0
- {quints-0.1.0 → quints-0.2.0}/LICENSE +0 -0
- {quints-0.1.0 → quints-0.2.0}/README.md +0 -0
- {quints-0.1.0 → quints-0.2.0}/examples/documents/.gitkeep +0 -0
- {quints-0.1.0 → quints-0.2.0}/examples/inbox/.gitkeep +0 -0
- {quints-0.1.0 → quints-0.2.0}/examples/invoicing/acme-2026-07.yaml +0 -0
- {quints-0.1.0 → quints-0.2.0}/examples/invoicing/issuer.yaml +0 -0
- {quints-0.1.0 → quints-0.2.0}/examples/prices.bean +0 -0
- {quints-0.1.0 → quints-0.2.0}/examples/staging/.gitkeep +0 -0
- {quints-0.1.0 → quints-0.2.0}/examples/statements/ubs-2026.mt940 +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/__init__.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/fava/__init__.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/fava/templates/QuintDashboard.html +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/fx.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/importing.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/inbox.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/__init__.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/draft.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/labels.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/model.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/qr.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/render.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/template.typ +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/vatid.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/invoice/verify.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/ledger.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/match.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/mwst.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/plugins/__init__.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/prices.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/receivables.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/report.typ +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/settlement.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/ui.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/src/quints/vat.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/fixtures/transactions.mt940 +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_cli_json.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_config.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_docs.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_fava_ext.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_fx.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_importing.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_inbox_match.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_invoice.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_kmu_plugin.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_kmu_report.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_ledger.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_mwst.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_prices_sync.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_receivables.py +0 -0
- {quints-0.1.0 → quints-0.2.0}/tests/test_report_pdf.py +0 -0
- {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.
|
|
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
|
|
@@ -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
|
|
11
|
-
"quints.plugins.kmu"` is enabled: every `*:CH:GmbH:*` account
|
|
12
|
-
opened with a four-digit `kmu:` code (Swiss KMU Kontenrahmen).
|
|
13
|
-
- `
|
|
14
|
-
- `
|
|
15
|
-
|
|
16
|
-
- `
|
|
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
|
|
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 `
|
|
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
|
|
@@ -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
|
|
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
|
|
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.
|
|
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,
|
|
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[/]
|
|
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")
|