pub enum TaxCategory {
Standard,
ZeroRated,
Exempt,
ReverseCharge,
IntraCommunity,
Export,
OutOfScope,
CanaryIslands,
CeutaMelilla,
SplitPayment,
}Expand description
EN 16931 BT-118 / UNTDID 5305 VAT category code.
The code tells a tax authority why a given base carries the rate it does — a 0% line is not self-explanatory, and “zero-rated”, “exempt”, “reverse charge” and “outside scope” have materially different legal meanings even though all four produce no tax.
This enum is deliberately not #[non_exhaustive]: it mirrors a closed,
externally-governed code list, and callers legitimately need exhaustive
matching when mapping to an output format. The list is fixed by rules
BR-CL-17 and BR-CL-18, which restrict BT-118 / BT-151 to exactly the
ten codes below — TaxCategory::ALL is that set, in the artefact’s order.
Variants§
Standard
S — standard rate.
ZeroRated
Z — zero-rated goods. Taxable at 0%; input tax remains deductible.
Exempt
E — exempt from VAT. Unlike zero-rating, input tax is generally not
deductible. Requires an exemption reason.
ReverseCharge
AE — VAT reverse charge: the recipient accounts for the tax
(§13b UStG, art. 194–199 of the VAT Directive).
IntraCommunity
K — VAT-exempt intra-Community supply of goods.
Export
G — free export item, VAT not charged.
OutOfScope
O — services outside the scope of VAT.
CanaryIslands
L — Canary Islands general indirect tax (IGIC).
CeutaMelilla
M — tax for production, services and importation in Ceuta and Melilla (IPSI).
SplitPayment
B — split payment (Italy, scissione dei pagamenti): the buyer remits
the VAT directly to the tax authority instead of paying it to the supplier.
Unlike the other “someone else pays” category (AE), the tax amount is
not zero — the supply is taxed at the normal rate and the tax is stated;
only the settlement route differs. The CEN artefacts make this observable by
omission: B is the one category with no BR-B-05, no BR-B-09 and no
BR-B-10, so nothing constrains its rate, forces BT-117 to zero, or requires
an exemption reason. It is therefore also the only category for which both
requires_exemption_reason and
forbids_exemption_reason are false.
Its two rules divide neatly, and the engine checks exactly the one it can:
- BR-B-01 — an invoice using
Bshall be a domestic Italian invoice. This needs the parties’ countries, which this crate never sees, so it stays with the caller. - BR-B-02 —
Bshall not appear in the same document asS. This is a property of the categories present, which the engine owns entirely, so it is checked: at breakdown level byBillingDocument::validateand at position level byverify_vat_attribution, exactly as BR-O-11 and BR-O-12/13/14 are.
Implementations§
Source§impl TaxCategory
impl TaxCategory
Sourcepub const ALL: [TaxCategory; 10]
pub const ALL: [TaxCategory; 10]
Every UNCL 5305 code EN 16931 permits, in the order BR-CL-17 lists them.
use billing::TaxCategory;
assert_eq!(TaxCategory::ALL.len(), 10);
// Every code round-trips through `from_code`.
for c in TaxCategory::ALL {
assert_eq!(TaxCategory::from_code(c.code()), Some(c));
}Sourcepub fn code(&self) -> &'static str
pub fn code(&self) -> &'static str
The UNTDID 5305 code as written in EN 16931 / UBL / CII documents.
use billing::TaxCategory;
assert_eq!(TaxCategory::Standard.code(), "S");
assert_eq!(TaxCategory::ReverseCharge.code(), "AE");
assert_eq!(TaxCategory::SplitPayment.code(), "B");Sourcepub fn from_code(code: &str) -> Option<TaxCategory>
pub fn from_code(code: &str) -> Option<TaxCategory>
Parse a UNTDID 5305 code (case-insensitive).
use billing::TaxCategory;
assert_eq!(TaxCategory::from_code("ae"), Some(TaxCategory::ReverseCharge));
assert_eq!(TaxCategory::from_code("B"), Some(TaxCategory::SplitPayment));
assert_eq!(TaxCategory::from_code("Q"), None);Sourcepub fn carries_tax(&self) -> bool
pub fn carries_tax(&self) -> bool
Whether this category actually levies tax.
True for S, L, M and B. For every other category EN 16931 requires
the category tax amount (BT-117) to be exactly zero — rules BR-Z-09,
BR-E-09, BR-AE-09, BR-IC-09, BR-G-09 and BR-O-09. There is deliberately no
BR-B-09: under split payment the supply is taxed normally and the tax is
stated, the buyer merely remits it to the authority rather than to the
supplier.
use billing::TaxCategory;
assert!(TaxCategory::Standard.carries_tax());
assert!(TaxCategory::SplitPayment.carries_tax());
assert!(!TaxCategory::ZeroRated.carries_tax());
assert!(!TaxCategory::ReverseCharge.carries_tax());Sourcepub fn requires_exemption_reason(&self) -> bool
pub fn requires_exemption_reason(&self) -> bool
Whether EN 16931 requires an exemption reason (BT-120/BT-121).
Required for E, AE, K, G and O (rules BR-E-10, BR-AE-10,
BR-IC-10, BR-G-10, BR-O-10).
Note the asymmetry that implementers most often get wrong: Z and E
both carry zero tax, but Z must not have an exemption reason and E
must. Zero-rating and exemption are legally distinct — input tax stays
deductible under Z but generally not under E.
Sourcepub fn forbids_exemption_reason(&self) -> bool
pub fn forbids_exemption_reason(&self) -> bool
Whether EN 16931 forbids an exemption reason for this category.
Forbidden for S (BR-S-10), Z (BR-Z-10), L (BR-AF-10) and M
(BR-AG-10): a taxed or zero-rated supply is not an exemption and needs no
justification.
B is neither required nor forbidden — the artefacts contain no BR-B-10 —
so it is the single category for which this and
requires_exemption_reason are both
false. Code that assumes exactly one of the two holds is wrong on B.
Sourcepub fn requires_positive_rate(&self) -> bool
pub fn requires_positive_rate(&self) -> bool
Whether EN 16931 requires a strictly positive line/allowance/charge VAT rate (BT-152 / BT-96 / BT-103) for this category.
True for S alone. BR-S-05 says the rate “shall be greater than zero”,
whereas the corresponding rules for L and M — BR-AF-05 and BR-AG-05 —
say “0 (zero) or greater than zero”, and B has no rule at all. Treating
all taxed categories alike would wrongly reject a lawful 0 % IGIC line.
use billing::TaxCategory;
assert!(TaxCategory::Standard.requires_positive_rate());
assert!(!TaxCategory::CanaryIslands.requires_positive_rate()); // BR-AF-05 allows 0
assert!(!TaxCategory::SplitPayment.requires_positive_rate()); // no BR-B-05Sourcepub fn requires_zero_rate(&self) -> bool
pub fn requires_zero_rate(&self) -> bool
Whether EN 16931 requires a zero line/allowance/charge VAT rate for this category — rules BR-Z-05, BR-E-05, BR-AE-05, BR-IC-05, BR-G-05 and BR-O-05.
For O this crate stores zero, but a consumer must omit the rate rather
than write 0 — see states_rate.
Sourcepub fn states_rate(&self) -> bool
pub fn states_rate(&self) -> bool
Whether a line, allowance or charge in this category may state its VAT rate — BT-152, BT-96, BT-103.
O is the only category where the answer is no, and the distinction is
not cosmetic. The other zero-tax categories say the rate “shall be 0
(zero)” — present, and zero (BR-Z-05, BR-E-05, BR-AE-05, BR-IC-05,
BR-G-05). O says the opposite:
[BR-O-05]An Invoice line (BG-25) where the VAT category code (BT-151) is “Not subject to VAT” shall not contain an Invoiced item VAT rate (BT-152).
BR-O-06 and BR-O-07 say the same for BT-96 and BT-103. Because
LineVat::rate is a plain Decimal rather than an Option, an O
position stores 0 — so a consumer emitting UBL or CII must suppress the
element for O instead of writing <cbc:Percent>0</cbc:Percent>, which
is a fatal violation. This predicate is that instruction, in code.
§It does not apply to BT-119
The VAT breakdown rate (TaxBreakdownEntry::rate) is a different term
and is governed by different rules. No BR-O rule suppresses it, and
XRechnung’s BR-DE-14 requires it unconditionally — “Das Element ‘VAT
category rate’ (BT-119) muss übermittelt werden”, fatal, with no category
exception. Applying this predicate to BG-23 would produce an invoice that
fails the KoSIT validator.
use billing::TaxCategory;
// Every other zero-tax category states an explicit 0 on its lines …
assert!(TaxCategory::ReverseCharge.states_rate());
assert!(TaxCategory::ZeroRated.states_rate());
// … while `O` must not state one at all (BR-O-05 / BR-O-06 / BR-O-07).
assert!(!TaxCategory::OutOfScope.states_rate());Trait Implementations§
Source§impl Clone for TaxCategory
impl Clone for TaxCategory
Source§fn clone(&self) -> TaxCategory
fn clone(&self) -> TaxCategory
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more