Skip to content

feat(tax): add Argentina tax pack (official-argentina@1.0.0) - #207

Draft
carvalab wants to merge 4 commits into
FreeOpenSourcePOS:mainfrom
carvalab:feat/tax-pack-argentina
Draft

feat(tax): add Argentina tax pack (official-argentina@1.0.0)#207
carvalab wants to merge 4 commits into
FreeOpenSourcePOS:mainfrom
carvalab:feat/tax-pack-argentina

Conversation

@carvalab

Copy link
Copy Markdown
Contributor

Adds a signed-JSON tax pack for Argentina. Data only, no executable tax code.

Scope (approved before this draft):

  • Merchant: IVA-registered restaurants (Responsable Inscripto), domestic standard supplies.
  • Tax: one 21% IVA rule on every category.
  • Pricing default: tax-inclusive, per Argentina's consumer-price-display rule (Res. 4/2025). Stores can still set individual products exclusive.

Source: Ley 23.349 t.o. Decreto 280/1997 Art. 28 ("La alicuota del impuesto sera del veintiuno por ciento (21%)"), in force since BO 15/04/1997. Cross-checked against ARCA biblioteca, InfoLeg norma 42701, and the Avalara Argentina VAT guide.

Explicitly out of scope (also documented in docs/tax-packs.md under Country pack scopes, Argentina):

  • IIBB. Provincial rate depends on province + activity + registration status, which FloCafe doesn't model.
  • Reduced 10.5%, incremented 27% (utilities), 2.5%, 0%.
  • Monotributo.
  • Withholding/perception regimes and ARCA CAE fiscal-invoice authorization. That belongs to the capability-plugin seam (feat(architecture): define country and provider plugin system #142).

Tests:

  • tests/tax-engine.test.ts: ARS 100 exclusive -> 21 -> 121; ARS 121 inclusive -> net 100 / IVA 21; every charge kind applies the rule; unclassified never goes to zero.
  • tests/tax-pack-management.test.ts: AR pack passes the 24-point activation checklist (only check feat: POS UI improvements — dev-server, frontend submodule update #6, signed artifact, fails pre-signing, same as the unsigned IN/TH seeds).
  • tests/integration-tax.test.ts: AR exclusive order, 20% discount recompute, inclusive snapshot, bill tax copy, and an explicit no-IIBB assertion on every breakdown.

Docs:

  • docs/tax-packs.md gains a "Country pack scopes" section pinning the AR scope, source, exclusions, and the path for provincial IIBB later.

I verified locally: npm run lint, npm run build, npm run build:frontend, npm run test:tax-engine, npm test, git diff --check all clean. Test totals: tax-engine 12/12, Integration Tax 114/114, Tax Pack Management 103/103.

Publishing (tag, push to upstream, CI sign) is not in this PR. That step stays gated on maintainer review per the task spec; I did not touch either signing secret.

carvalab added a commit to carvalab/FloCafe that referenced this pull request Jul 31, 2026
Pre-existing CI regression surfaced by PR FreeOpenSourcePOS#207. Commit 3a75876
('publish country packs separately') moved India and Thailand from
BUNDLED_COUNTRY_PACKS to catalog-only delivery, and the e2e-server
fixture seeds country='TH' without ever simulating the post-first-run
owner action of installing Thailand from Settings → Tax Configuration.
The active pack for country=TH then falls back to the zero-rules
generic pack, and the checkout throws 'no tax rules apply to category
standard for business type restaurant' → 400. The Playwright spec at
prepaid-payment-reconciliation.spec.ts:13/55 times out waiting for
'Tax ฿4.20'.

This is the same regression that has been silently failing the most
recent two main CI runs (30614681034, 30619469114) before this PR even
opened. Fix by inlining the same install-tax-pack SQL helper that the
unit and integration tests already use (tests/helpers/test-setup.ts:
installAndActivateTestTaxPack), since e2e-server.cjs runs from compiled
dist/ and cannot import the .ts helper. Only Thailand is installed —
it's the only country-specific pack actually exercised by e2e specs.

No production code touched. India is left catalog-only as 3a75876
intended.
@carvalab
carvalab force-pushed the feat/tax-pack-argentina branch 2 times, most recently from e56310c to e7a81ac Compare July 31, 2026 18:38
carvalab added 3 commits July 31, 2026 19:30
Scope (approved before this draft):
- Merchant: IVA-registered restaurants (Responsable Inscripto),
  domestic sales of standard supplies.
- Tax: one IVA rule at the general rate of 21%, applied across
  every category (product, addon, packaging, delivery,
  service_charge, unclassified).
- Default pricing is tax-inclusive to match Argentina's consumer-
  price-display rule (Res. 4/2025); individual products can still
  be set exclusive per-item for B2B use.

Source: Ley de Impuesto al Valor Agregado, texto ordenado en 1997
(Ley 23.349 t.o. Decreto 280/1997, Anexo I), Articulo 28 -- 'La
alicuota del impuesto sera del veintiuno por ciento (21%)'.
Continuously in force since BO 15/04/1997. Cross-references: ARCA
biblioteca cuadro legislativo Articulo 28; InfoLeg norma 42701.

Intentionally unsupported (do not add to this pack without a scope
bump): IIBB (provincial, registration-dependent); reduced 10.5%;
incremented 27% (utilities); 2.5% and 0%; Monotributo; withholding
and perception regimes; ARCA CAE fiscal-invoice authorization
(belongs to the capability-plugin seam FreeOpenSourcePOS#142).

Tests:
- tests/tax-engine.test.ts: ARS 100 exclusive -> 21 -> 121; ARS 121
  inclusive -> net 100 / IVA 21; all charge kinds apply the rule;
  unclassified never silently zero.
- tests/tax-pack-management.test.ts: installs AR alongside IN/TH
  and confirms the 24-point activation checklist accepts it (only
  check FreeOpenSourcePOS#6, signed artifact, fails pre-signing, same as IN/TH seeds).
- tests/integration-tax.test.ts: AR exclusive order (ARS 1000 ->
  210 IVA -> 1210 total); 20% discount recompute (168/968);
  inclusive product keeps IVA inside the displayed price and
  persists behavior as inclusive; bill copies the order tax; every
  breakdown asserted to contain no IIBB/Ingresos Brutos component.

Docs: docs/tax-packs.md gains a 'Country pack scopes' section
pinning the approved merchant scope, the IVA source, every excluded
area, and the upgrade path for provincial IIBB (only after FloCafe
models province + activity code + customer registration status).
Pre-existing CI regression, included here to keep this PR's CI green.
Logically belongs to commit 3a75876 ('publish country packs
separately'), not to the Argentina pack.

3a75876 moved India and Thailand from BUNDLED_COUNTRY_PACKS to
catalog-only delivery. tests/e2e-server.cjs seeds country='TH'
without simulating the post-first-run owner action of installing
Thailand from Settings -> Tax Configuration. The active pack for
country=TH then falls back to the zero-rules generic pack, and
checkout throws 'no tax rules apply to category standard for
business type restaurant' (400). The Playwright spec at
prepaid-payment-reconciliation.spec.ts:13/55 times out waiting for
'Tax ฿4.20'.

Same regression silently failed the most recent two main CI runs
(30614681034, 30619469114) before this PR opened.

Fix: inline the same install-tax-pack SQL helper that the unit and
integration tests already use (tests/helpers/test-setup.ts:
installAndActivateTestTaxPack), since e2e-server.cjs runs from
compiled dist/ and cannot import the .ts helper. Only Thailand is
installed -- the only country-specific pack exercised by e2e specs.
India stays catalog-only as 3a75876 intended. No production code
touched.
User feedback: the 21% IVA is what the end consumer pays at the
counter, which is roughly 99% of restaurant POS transactions. The
previous scope text led with 'IVA-registered restaurants operating
as Responsable Inscripto', which framed the pack around the
merchant's tax-status instead of the consumer-facing calculation the
pack actually models.

The pack's calculation is identical for end-consumer sales (Factura
B, IVA embedded) and B2B sales between Responsables Inscriptos
(Factura A, IVA itemized) -- same 21% number, different invoice
presentation. That makes Factura A in-scope too, just framed as a
side note about invoicing rather than as a scope restriction.

The Monotributo exclusion is reworded to clarify that a Monotributo
merchant would need a separate zero-IVA pack variant because its
invoicing and registration paths are structurally different, not
because the math is incompatible.

No rate change, no rule change, no test change. Pack data, pack
activation, all tests still green. Docs only.
@carvalab
carvalab force-pushed the feat/tax-pack-argentina branch from 86c7de3 to 6dc4ef8 Compare July 31, 2026 22:31
Adds a single-bar 'Default for all' control in the fiscal settings
panel that lets the owner pick a category for every kind (product,
add-on, packaging, delivery, service charge) and apply it in one
click. The control also accepts a typed rate (e.g. 10.5) and creates
a merchant category with that rate when the value doesn't match an
existing one. Per-kind dropdowns below stay for exceptions.

Backend: `POST /api/tax-packs/merchant-rate` (owner-only, validated
0 < rate <= 100) upserts a `merchant:<rate>` category + rule for the
active version and writes the 5 store-wide overrides in one
transaction. Idempotent. `getActiveCountryPack` merges merchant rows
into the returned pack so the engine resolves them. Activate copies
merchant categories and rules across versions; the install-time
override-conflict check ignores `merchant:%` aliases.
@carvalab
carvalab force-pushed the feat/tax-pack-argentina branch from 6dc4ef8 to e970c0c Compare July 31, 2026 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant