Class: Einvoicing::Tax

Inherits:
Object
  • Object
show all
Defined in:
lib/einvoicing/tax.rb

Overview

VAT breakdown entry (BG-23) for a single rate/category pair.

Constant Summary collapse

CATEGORY_CODES =

EN 16931 BT-118 — the UNCL5305 subset the standard allows.

{
  standard:        "S",   # Standard rate
  zero_rated:      "Z",   # Zero rated goods
  exempt:          "E",   # Exempt from VAT
  reverse_charge:  "AE",  # VAT reverse charge
  intra_community: "K",   # Intra-Community supply
  export:          "G",   # Export outside the EU
  not_subject:     "O"    # Services outside scope of tax
}.freeze
EXEMPTION_REQUIRED_CODES =

BR-E-10, BR-AE-10, BR-IC-10, BR-G-10, BR-O-10: these categories must carry a VAT exemption reason (BT-120) or reason code (BT-121). BR-Z-10 and the standard category must NOT carry one.

%w[E AE K G O].freeze
ZERO_VAT_CODES =

Categories that net out to 0 % VAT whatever rate the caller passed in.

%w[Z E AE K G O].freeze
DEFAULT_EXEMPTIONS =

Defaults from the EN 16931 VATEX code list. "E" is deliberately absent: the reason depends on which exemption is invoked, so the caller supplies it (e.g. VATEX_FR_FRANCHISE for the French art. 293 B franchise).

{
  "AE" => [ "VATEX-EU-AE", "Reverse charge" ],
  "K"  => [ "VATEX-EU-IC", "Intra-Community supply" ],
  "G"  => [ "VATEX-EU-G",  "Export outside the EU" ],
  "O"  => [ "VATEX-EU-O",  "Not subject to VAT" ]
}.freeze
VATEX_FR_FRANCHISE =

France, art. 293 B du CGI — franchise en base de TVA.

"VATEX-FR-FRANCHISE"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rate:, taxable_amount:, tax_amount:, category: nil, exemption_reason: nil, exemption_reason_code: nil) ⇒ Tax

Returns a new instance of Tax.

Parameters:

  • rate (Numeric)

    e.g. 0.20 for 20% VAT; 0 for zero-rated or exempt

  • taxable_amount (Numeric)

    net amount subject to this rate (BT-116)

  • tax_amount (Numeric)

    VAT amount for this rate (BT-117)

  • category (Symbol, nil) (defaults to: nil)

    a key of CATEGORY_CODES; nil infers standard or zero-rated from the rate

  • exemption_reason (String, nil) (defaults to: nil)

    BT-120

  • exemption_reason_code (String, nil) (defaults to: nil)

    BT-121

Raises:

  • (ArgumentError)


67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/einvoicing/tax.rb', line 67

def initialize(rate:, taxable_amount:, tax_amount:, category: nil,
               exemption_reason: nil, exemption_reason_code: nil)
  raise ArgumentError, Einvoicing::I18n.t("tax.invalid_rate", rate: rate) if rate.to_f.negative?

  code = Tax.category_code_for(rate: rate, category: category)
  default_code, default_reason = DEFAULT_EXEMPTIONS.fetch(code, [ nil, nil ])

  super(
    rate: rate,
    taxable_amount: taxable_amount,
    tax_amount: tax_amount,
    category: category,
    exemption_reason: exemption_reason || default_reason,
    exemption_reason_code: exemption_reason_code || default_code
  )
end

Class Method Details

.category_code_for(rate:, category: nil) ⇒ Object

Shared lookup used by Tax, LineItem and AllowanceCharge.



43
44
45
46
47
48
49
# File 'lib/einvoicing/tax.rb', line 43

def self.category_code_for(rate:, category: nil)
  return rate.to_f.zero? ? "Z" : "S" if category.nil?

  CATEGORY_CODES.fetch(category) do
    raise ArgumentError, Einvoicing::I18n.t("tax.unknown_category", category: category.inspect)
  end
end

.effective_rate(rate:, category: nil) ⇒ Object

The rate actually billed once the category is taken into account: an exempt / reverse-charge / out-of-scope category charges 0 % whatever rate was passed in.



54
55
56
57
58
# File 'lib/einvoicing/tax.rb', line 54

def self.effective_rate(rate:, category: nil)
  return BigDecimal("0") if ZERO_VAT_CODES.include?(category_code_for(rate: rate, category: category))

  BigDecimal(rate.to_s)
end

Instance Method Details

#category_codeObject



84
85
86
# File 'lib/einvoicing/tax.rb', line 84

def category_code
  Tax.category_code_for(rate: rate, category: category)
end

#exemption_required?Boolean

True when EN 16931 requires BT-120 or BT-121 on this breakdown line.

Returns:

  • (Boolean)


89
90
91
# File 'lib/einvoicing/tax.rb', line 89

def exemption_required?
  EXEMPTION_REQUIRED_CODES.include?(category_code)
end

#rate_percentObject



93
94
95
# File 'lib/einvoicing/tax.rb', line 93

def rate_percent
  (Tax.effective_rate(rate: rate, category: category) * 100).round(2)
end