Skip to content

Add Malta Regime - #956

Open
XiaoY-X wants to merge 3 commits into
invopop:mainfrom
XiaoY-X:new-regime-mt-xiaoyuan
Open

XiaoY-X wants to merge 3 commits into
invopop:mainfrom
XiaoY-X:new-regime-mt-xiaoyuan

Conversation

@XiaoY-X

@XiaoY-X XiaoY-X commented Sep 4, 2026 •

Copy link
Copy Markdown

Summary

  • New tax regime for Malta (MT), single VAT category:
    • general 18%: article 19(1), the residual rate
    • intermediate 12%: Eighth Schedule items 12-15, from 2024-01-01 per LN 231/2023
    • reduced 7%: items 1, 11, from 2011-01-01
    • super-reduced 5%: items 2-10
  • Historical rates back to the Act's commencement: 15% standard from 1999, rising to 18% in 2004. Dates from the Commission's VAT rates applied in the Member States, §VIII; TestRateHistory covers each transition.
  • No names for reduced rates: The Eighth Schedule is a bare description/rate table headed "Rate of tax", so the mapping onto GOBL's keys is by magnitude, and rate names follow the MTCA's own usage ("12% Rate"). The reasoning is in a comment above taxCategories. Names are English-only, since the VAT Act is enacted in English.
  • Tax identity: 8 digits, MOD 37 over the first six with weights [3, 4, 6, 7, 8, 9] and check against the last two digits. Only article 10 , 12 numbers are accepted. Article 13(3) gives those the MT prefix and says an article 11 small-enterprise number "shall not be treated for any purposes of this Act as a value added tax identification number". Article 50 agrees from the other side, since such a person issues fiscal receipts, never tax invoices.
  • The checksum is not published by the MTCA: no official source states a digit count, a weighting or a check rule. It follows [valvat](https://github.com/yolk/valvat), is verified against the published example 11679112, and is written out in a comment. Where valvat and python-stdnum diverge the stricter form is used.
  • Maltese law splits exemptions in two and only one half is GOBL's exempt. Fifth Schedule Part One is "exempt with credit" (no VAT charged, but input tax still recoverable under article 22(4)), mapping to zero, export and intra-community; Part Two is "exempt without credit", which is exempt. tax.GlobalVATKeys() is used unchanged.
  • Two example documents: a domestic B2B invoice across three rates, and an MT→IE intra-community supply.
  • Tests for all regime-specific logic, 95.0% statement coverage.

Not included

Each is noted in mt.go with its statutory basis rather than left silent.

  • Corrections left undefined: Twelfth Schedule item 1(2) treats any document unambiguously amending an earlier invoice as an invoice, and Eleventh Schedule item 1(1)(h) requires records of credit and debit notes.
  • No scenarios.go. The Twelfth Schedule transposes Directive 2006/112/EC verbatim, and item 1(1) accepts full Directive compliance as sufficient for a valid Maltese tax invoice.
  • No org_identities.go. A Maltese tax invoice carries no non-tax business identifier; Twelfth Schedule item 3 requires name, address and VAT number only.
  • No invoice rules. The only candidate, requiring the customer's VAT number, would reject the fiscal receipts article 51 provides for.
  • No addon. Malta has no e-invoicing mandate in either direction; contracting authorities must be able to receive EN 16931 over PEPPOL, but nobody is obliged to issue electronically. PEPPOL/EN 16931 is separate work.

Sources

The Act's rate table was cross-checked against the MTCA's published summary; the two agree item for item.

Notes

  • mage check passes: lint, generate, tests and a clean git state.

  • Two details could go either way and I'd welcome an opinion:

  1. whether rate names should follow the MTCA's usage as they do here (value based names) or GOBL's vocabulary,
  2. whether leading zeros in a VAT number should be accepted (valvat) or rejected (python-stdnum).

Pre-Review Checklist

  • Opened this PR as a draft
  • Read the CONTRIBUTING.md guide.
  • Performed a self-review of my code.
  • Added thorough tests with at least 90% code coverage.
  • Modified or created example GOBL documents to show my changes in use, if appropriate.
  • Added links to the source of the changes in tax regimes or addons, either structured or in the comments.
  • Run go generate . to ensure that the Schemas and Regime data are up to date.
  • Reviewed and fixed all linter warnings.
  • Been obsessive with pointer nil checks to avoid panics.
  • Added a changes/unreleased entry with an overview of my changes (CHANGELOG.md is generated from it).
  • Marked this PR as ready for review.

And if you are part of the org:

  • Requested a review from Copilot and fixed or dismissed (with a reason) all the feedback raised.
  • Requested a review from @samlown.

@codecov

codecov Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.87640% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.90%. Comparing base (765f238) to head (ed2e8cf).

Files with missing lines Patch % Lines
regimes/mt/tax_identity.go 95.65% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #956      +/-   ##
==========================================
+ Coverage   96.88%   96.90%   +0.01%     
==========================================
  Files         335      337       +2     
  Lines       15916    16005      +89     
==========================================
+ Hits        15421    15509      +88     
- Misses        494      495       +1     
  Partials        1        1              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@samlown samlown added the regime Related to a specific regime. label Sep 9, 2026
@cavalle
cavalle requested a lite review from Copilot September 9, 2026 11:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation and tests appear consistent with existing regime patterns, with only a minor comment wording fix requested.

Pull request overview

Adds first-class support for the Malta (MT) VAT regime to GOBL, including VAT rate definitions, tax identity validation/normalization, generated regime/rules artifacts, and example documents.

Changes:

  • Introduces regimes/mt with VAT categories/rates (including historical rate changes) and MT VAT number checksum validation.
  • Registers the MT regime and updates generated schema/rules/regime data to include MT.
  • Adds Malta invoice examples (domestic + intra-community) and updates the changelog.
File summaries
File Description
regimes/regimes.go Registers the MT regime package so it’s available at runtime.
regimes/mt/mt.go Defines the Malta regime metadata, sources, timezone, scenarios, and categories.
regimes/mt/tax_categories.go Adds MT VAT category with standard/reduced rate tiers and historical values.
regimes/mt/tax_identity.go Implements MT VAT number format + MOD 37 checksum validation rules.
regimes/mt/mt_test.go Tests regime construction and rate resolution (including history and commencements).
regimes/mt/tax_identity_test.go Tests normalization and validation edge cases for MT VAT identities.
data/schemas/tax/regime-code.json Adds MT to the regime-code schema enumeration.
data/rules/mt.json Generated rules output for MT tax identity validation.
data/regimes/mt.json Generated MT regime definition for distribution/consumption.
examples/mt/invoice-mt-en.yaml Adds a domestic MT invoice example exercising multiple positive VAT rates.
examples/mt/invoice-mt-intra-community.yaml Adds an MT→IE intra-community supply example using the correct VAT key.
examples/mt/out/invoice-mt-en.json Generated output envelope for the domestic MT example.
examples/mt/out/invoice-mt-intra-community.json Generated output envelope for the intra-community MT example.
data/schemas/pay/credit-transfer.json Normalizes schema formatting to match generator output (removes persistent diff).
CHANGELOG.md Documents the addition of the MT regime and VAT identity validation.
Review details
  • Files reviewed: 12/15 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread regimes/mt/tax_categories.go Outdated
Comment on lines +15 to +16
// Since versions a percentage, not the supplies it applies to: the Eighth Schedule items
// named below are today's and are not versioned.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ed2e8cf: the comment now reads "Since each rate's history versions a percentage, not the supplies it applies to, ..."

@alvarolivie
alvarolivie marked this pull request as ready for review September 21, 2026 15:12
Xiaoyuan and others added 2 commits September 25, 2026 00:10
CHANGELOG.md is now generated from the changes directory, so the Malta
entry moves to its own unreleased file. Also rewords the rate history
comment flagged in review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

regime Related to a specific regime.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants