Skip to main content

TaxCategory

Enum TaxCategory 

Source
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 B shall be a domestic Italian invoice. This needs the parties’ countries, which this crate never sees, so it stays with the caller.
  • BR-B-02B shall not appear in the same document as S. This is a property of the categories present, which the engine owns entirely, so it is checked: at breakdown level by BillingDocument::validate and at position level by verify_vat_attribution, exactly as BR-O-11 and BR-O-12/13/14 are.

Implementations§

Source§

impl TaxCategory

Source

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));
}
Source

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");
Source

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);
Source

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());
Source

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.

Source

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.

Source

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-05
Source

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.

Source

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

Source§

fn clone(&self) -> TaxCategory

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for TaxCategory

Source§

impl Debug for TaxCategory

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for TaxCategory

Source§

fn deserialize<__D>( __deserializer: __D, ) -> Result<TaxCategory, <__D as Deserializer<'de>>::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for TaxCategory

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Honours width, fill and alignment.

Source§

impl Eq for TaxCategory

Source§

impl Hash for TaxCategory

Source§

fn hash<__H>(&self, state: &mut __H)
where __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for TaxCategory

Source§

fn cmp(&self, other: &TaxCategory) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 (const: unstable) · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 (const: unstable) · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 (const: unstable) · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
Source§

fn clamp_to<R>(self, range: R) -> Self
where Self: Sized, R: ClampBounds<Self>,

🔬This is a nightly-only experimental API. (clamp_to)
Restrict a value to a certain range. Read more
Source§

impl PartialEq for TaxCategory

Source§

fn eq(&self, other: &TaxCategory) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl PartialOrd for TaxCategory

Source§

fn partial_cmp(&self, other: &TaxCategory) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 (const: unstable) · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 (const: unstable) · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 (const: unstable) · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 (const: unstable) · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl Serialize for TaxCategory

Source§

fn serialize<__S>( &self, __serializer: __S, ) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for TaxCategory

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.