from .finance_tax_vat_rate import vat_rate from .math_round_div import RoundingMode from .money_add import add_money from .money_amount import Money from .money_apply_rate import apply_rate from .logistics_duty_estimate_types import DutyEstimate def _non_negative(name: str, amount: Money) -> None: if amount.minor < 0: raise ValueError("%s must not be negative, received %d" % (name, amount.minor)) def duty_estimate( goods: Money, freight: Money, insurance: Money, duty_basis_points: int, vat_additions: Money, jurisdiction: str, vat_category: str, import_date: str, rounding: RoundingMode, ) -> DutyEstimate: """Import duty on the CIF customs value, then import VAT on the customs value plus duty plus the VAT-only additions, at the rate in force on import_date.""" _non_negative("goods", goods) _non_negative("freight", freight) _non_negative("insurance", insurance) _non_negative("vatAdditions", vat_additions) if isinstance(duty_basis_points, bool) or not isinstance(duty_basis_points, int): raise TypeError("dutyBasisPoints must be an integer, received %r" % (duty_basis_points,)) if duty_basis_points < 0: raise ValueError("dutyBasisPoints must not be negative, received %d" % duty_basis_points) # CIF: duty is charged on what it cost to get the goods to the border. customs_value = add_money(add_money(goods, freight), insurance) duty = apply_rate(customs_value, duty_basis_points, rounding) # VAT is charged on the duty too, and on costs after the border. vat_value = add_money(add_money(customs_value, duty), vat_additions) rate = vat_rate(jurisdiction, vat_category, import_date) import_vat = apply_rate(vat_value, rate.basis_points, rounding) return DutyEstimate( customs_value=customs_value, duty_basis_points=duty_basis_points, duty=duty, vat_value=vat_value, vat_basis_points=rate.basis_points, import_vat=import_vat, total_payable=add_money(duty, import_vat), )