Skip to content

Add Slovakia (SK) tax regime - #922

Open
Liuck27 wants to merge 4 commits into
invopop:mainfrom
Liuck27:add-slovakia-regime
Open

Add Slovakia (SK) tax regime#922
Liuck27 wants to merge 4 commits into
invopop:mainfrom
Liuck27:add-slovakia-regime

Conversation

@Liuck27

@Liuck27 Liuck27 commented Aug 6, 2026

Copy link
Copy Markdown

Summary

Adds the Slovak (SK) tax regime, structured on regimes/fi.

  • VAT with the three rates in force since the 2025 reform: standard 23%, reduced 19%, super-reduced 5% (all since 2025-01-01), plus the prior 20% standard rate (since 2011-01-01) so earlier invoices still resolve. Zero, exempt, reverse-charge, export and outside-scope keys come from tax.GlobalVATKeys(), as in other regimes.
  • IČ DPH tax identity: normalization strips the SK prefix, spaces and hyphens; validation requires 10 digits with a non-zero first digit, the whole number divisible by 11.
  • EUR currency, Europe/Bratislava time zone, Slovak (sk) translations alongside English.
  • Example invoice exercising all three rates, with items matching each rate's documented scope (software services, electricity, books).
  • Tests, generated regime/rules data, CHANGELOG.

Decisions

Rate history covers the current structure plus the prior standard rate only. Before 2025 there was a single 10% reduced rate, covering the goods listed in annex 7 of the VAT act (medicines, books). The 2025 reform abolished that tier rather than renaming it, introducing 19% over a different set of goods, while the old 10% scope maps more closely to today's 5%. Attaching that history to either current rate would misstate which goods were taxed at which rate, so it is left out. Invoices dated before 2025-01-01 using reduced or super-reduced therefore fail with rate value unavailable; the standard rate resolves back to 2011. Several regimes have the same shape: fr (reduced tiers only from 2014), se (from 1996), ie (general only from 2012 while reduced reaches 2003).

Why the 20% rate cites two laws. Slovakia's VAT act (no. 222/2004) normally states its rates in section 27, but the 20% rate did not start there. A 2010 amendment (no. 490/2010) introduced it in section 85j instead, as a temporary measure due to expire once the national deficit fell below 3%. So between 2011 and 2014, section 27 still read 19% while the rate actually charged was 20%. The deficit condition was never met, and from 2015 the 20% was written into section 27 itself, where it stayed until the 2025 reform. The rate therefore ran unbroken at 20% from 2011 to 2024, and only the part of the law holding it changed. Citing section 27 alone would not support the 2011 start date, so both laws are listed as sources.

Tax identity validation stops short of the third-digit rule. The reference implementation also restricts the third digit to [2346-9]. That is not adopted here: sources disagree on it, the algorithm is not published by the tax authority, and wrongly rejecting a valid VAT number blocks a real business from invoicing whereas wrongly accepting one is only a data-quality issue. The non-zero first digit is adopted, since without it 0000000000 validates (0 % 11 == 0).

Deliberately out of scope

Regime only: no addon, no format conversion, no tax-authority transmission, no invoice chaining. Slovakia's e-invoicing requirements belong in an addon rather than the regime. There are also no supplier-specific invoice rules and no requirement for a tax ID on invoices, since Slovakia has a VAT-registration threshold below which a business has none and the regime resolves from tax_id.country alone.

Open question

The tax authority does not publish the IČ DPH check algorithm. The modulo-11 rule is consistently documented across independent validators and holds for every valid identifier tested, but it is not an official specification. This is flagged in a code comment, following the precedent in regimes/sg.

How to test

go generate .
go test ./...
go test ./regimes/sk/ -cover   # 100%

Sources

All sources are also recorded in code as cbc.Source entries with retrieval dates.

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.
  • Updated the CHANGELOG.md with an overview of my changes.
  • 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.

Implements Slovakia's VAT regime with:
- VAT categories and rates (standard 23%, reduced 19%, super-reduced 5% since
  2025-01-01, plus the prior 20% standard rate since 2011-01-01)
- IČ DPH tax identity validation: 10 digits, first non-zero, modulo-11 checksum
- Tax identity normalization stripping the SK prefix, spaces and hyphens
- EUR currency and Europe/Bratislava time zone
- Slovak (sk) translations alongside English
- Table-driven tests for validation and normalization
- Example invoice exercising all three VAT rates

Reduced and super-reduced rates are not backfilled before 2025: the reform
restructured which goods fall in each tier, so a pre-2025 lineage would
misrepresent which goods were taxed at which rate.

Sources:
- Slov-Lex, zákon č. 222/2004 Z. z. (§ 27): https://www.slov-lex.sk/ezbierky/pravne-predpisy/SK/ZZ/2004/222/
- Slov-Lex, zákon č. 490/2010 Z. z. (§ 85j): https://www.slov-lex.sk/ezbierky/pravne-predpisy/SK/ZZ/2010/490/
- Finančná správa - daň z pridanej hodnoty: https://www.financnasprava.sk/sk/podnikatelia/dane/dan-z-pridanej-hodnoty
@Liuck27
Liuck27 marked this pull request as ready for review August 6, 2026 21:26

@samlown samlown left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good thanks! I'd recommend changing the tax ID validations to more specific rules.

Comment thread regimes/sk/tax_identity.go Outdated
Comment on lines +22 to +23
rules.AssertIfPresent("01", "tax identity code for SK must be 10 digits starting with 1-9 and divisible by 11",
is.Func("valid", validateTaxCode),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Preference here is to split into specific rules rather than a is.Func catch all. For example, there could be a specific assertion around the pattern matching of the string, and a second for the checksum.

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.

Done. Split into two assertions: IDENTITY-01 for the 10-digit pattern and IDENTITY-02 for the modulo-11 checksum, each with its own message. Since assertions are evaluated independently, the checksum no longer depends on the pattern rule having run first, so it now parses with strconv.ParseInt and rejects non-digit input on its own. Dropping the is.Func wrapper also removed the cbc.Code type assertion, which took the package to 100% coverage.

Review feedback: a single is.Func catch-all reported the same fault whether
the code had a malformed pattern or a failed checksum.

- IDENTITY-01 now asserts the format only, via is.MatchesRegexp.
- IDENTITY-02 asserts the modulo-11 checksum, via is.StringFunc.
- is.StringFunc receives the string directly, which removes the cbc.Code
  type assertion and the cbc import.
- Assertions are evaluated independently, so the checksum no longer relies
  on the pattern rule having run first; strconv.ParseInt rejects non-digits.
- Tests assert the exact set of fault codes each input produces, including
  that the other code is absent.

Follows the pattern already used by regimes/gb.
@Liuck27
Liuck27 requested a review from samlown August 12, 2026 10:54
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.

2 participants