Module: Ksef::FA3::VatRate
- Defined in:
- lib/ksef/fa3/vat_rate.rb
Overview
Maps a TStawkaPodatku rate code to its numeric percentage and its summary bucket.
The bucket mapping is read from the XSD's own documentation and recorded in docs/REFERENCE.md §8.1a. It is not guessable: bucket 4 is the passenger-taxi flat rate and bucket 5 a special procedure, so the buckets are not simply "rates in descending order".
Constant Summary collapse
- BUCKETS =
Rate code => [net element, tax element or nil]
The zero-rated and exempt buckets have no tax element. There is no amount to report and the schema provides nowhere to put one, so a summary must not invent a paired field for them.
Buckets pair a current rate with the historical one it replaced — 23/22, 8/7, and 4/3. Until 2026-08-24 code
"3"was mapped to bucket five, which is not a rate bucket at all:P_13_5is the special procedure of "dział XII rozdział 6a" andP_14_5is "kwota podatku od wartości dodanej" — foreign VAT under OSS, whose per-line rate lives inP_12_XII(a percentage) and not inP_12at all. A domestic 3% sale was therefore declared as OSS foreign VAT, on a document the XSD accepts. Found by comparing againstksef-pdf-generator, whose summary labels bucket 4 "4% lub 3%" and bucket 5 "OSS" (docs/REFERENCE.md §8.1a).np Iandnp IIare different buckets, and the schema says so twice. Until 2026-08-26 both mapped toP_13_8. Butnp IIis "świadczenie usług o których mowa w art. 100 ust. 1 pkt 4 ustawy" andP_13_9is "suma wartości świadczenia usług, o których mowa w art. 100 ust. 1 pkt 4 ustawy" — the same scope word for word — whileP_13_8reads "z wyłączeniem kwot wykazanych w polach P_13_5 i P_13_9" andnp Iexcludes those same transactions. SoP_13_8is the one bucketnp IImay not go in.P_13_9feeds intra-EU services reporting, so the mistake misstated a category on an XSD-valid document — the third instance of this class after the shared-bucket bug and rate code3(docs/REFERENCE.md §8.1a).No
P_12code maps to bucket 5 or to bucket 11, and that is correct rather than an omission. { "23" => %w[P_13_1 P_14_1], "22" => %w[P_13_1 P_14_1], "8" => %w[P_13_2 P_14_2], "7" => %w[P_13_2 P_14_2], "5" => %w[P_13_3 P_14_3], "4" => %w[P_13_4 P_14_4], "3" => %w[P_13_4 P_14_4], "0 KR" => ["P_13_6_1", nil], "0 WDT" => ["P_13_6_2", nil], "0 EX" => ["P_13_6_3", nil], "zw" => ["P_13_7", nil], "np I" => ["P_13_8", nil], "np II" => ["P_13_9", nil], "oo" => ["P_13_10", nil] }.freeze
- PERCENTAGES =
Only the codes that denote an actual percentage. The rest are procedures, not rates, and carry no tax.
{ "23" => 23, "22" => 22, "8" => 8, "7" => 7, "5" => 5, "4" => 4, "3" => 3 }.freeze
Class Method Summary collapse
-
.bucket(code) ⇒ Array(String, String officially nil)
The net and tax element names.
-
.percentage(code) ⇒ Integer?
Nil for a non-numeric code such as "zw" or "oo".
-
.unmapped_codes ⇒ Array<String>
Cross-check against the generated enum, so a schema revision that adds or renames a rate code surfaces here rather than in a KSeF rejection.
-
.unreachable_elements ⇒ Array<String>
The summary elements no rate code reports into.
Class Method Details
.bucket(code) ⇒ Array(String, String officially nil)
Returns the net and tax element names.
66 67 68 69 70 71 |
# File 'lib/ksef/fa3/vat_rate.rb', line 66 def bucket(code) BUCKETS.fetch(code.to_s) do raise ValidationError, "Unknown VAT rate code #{code.inspect}. Permitted: #{BUCKETS.keys.map(&:inspect).join(", ")}" end end |
.percentage(code) ⇒ Integer?
Returns nil for a non-numeric code such as "zw" or "oo".
62 |
# File 'lib/ksef/fa3/vat_rate.rb', line 62 def percentage(code) = PERCENTAGES[code.to_s] |
.unmapped_codes ⇒ Array<String>
Cross-check against the generated enum, so a schema revision that adds or renames a rate code surfaces here rather than in a KSeF rejection.
88 89 90 |
# File 'lib/ksef/fa3/vat_rate.rb', line 88 def unmapped_codes (Generated::Enums.values_for("TStawkaPodatku") || []) - BUCKETS.keys end |
.unreachable_elements ⇒ Array<String>
The summary elements no rate code reports into. Exposed so the omission is
assertable rather than merely commented — an incomplete list here reads as a
deliberate gap, which is how P_13_9 stayed miscategorised.
Bucket 5 is the OSS special procedure, whose lines carry P_12_XII — a percentage —
instead of a P_12 code. P_13_11 is the margin scheme of art. 119/120, which a
document declares through Adnotacje/PMarzy rather than through a rate.
81 82 83 |
# File 'lib/ksef/fa3/vat_rate.rb', line 81 def unreachable_elements %w[P_13_5 P_14_5 P_13_11] end |