Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { type Money, money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { applyRate } from "./money_apply_rate.ts"; ← from money.apply-rate ^1.0.0 · built alongside by fune
import { OSS_RULES, OSS_RULES_HISTORY } from "./retail_oss_vat_data.ts"; ← this capability’s own data, compiled from data/oss-rules.json into the same file by fune build
import { type OssVat } from "./retail_oss_vat_types.ts";
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
const COUNTRY = /^[A-Z]{2}$/;
// The EU writes Greece as EL in VAT matters; ISO 3166 says GR. Accept both.
function country(code: string, name: string): string {
if (!COUNTRY.test(code)) throw new RangeError(`${name} must be an ISO 3166-1 alpha-2 code, received "${code}"`);
return code === "EL" ? "GR" : code;
}
function isMember(code: string): boolean {
return OSS_RULES.some((r) => r.rule === "standard-rate" && r.country === code);
}
function lookup(rule: string, where: string, onDate: string): number {
let best: { validFrom: string; value: number } | null = null;
for (const r of OSS_RULES) {
if (r.rule !== rule || r.country !== where) continue;
if (onDate < r.validFrom || (r.validTo !== null && onDate > r.validTo)) continue;
if (best === null || r.validFrom > best.validFrom) best = { validFrom: r.validFrom, value: r.value };
}
if (best !== null) return best.value;
// A history=current build carries only the rules still in force; answering
// an older date with today's rate is the failure the dates exist to prevent.
if (OSS_RULES_HISTORY !== "full") {
let earliest: string | null = null;
for (const r of OSS_RULES) {
if (r.rule === rule && r.country === where && (earliest === null || r.validFrom < earliest)) earliest = r.validFrom;
}
if (earliest !== null && onDate < earliest) {
throw new RangeError(
`no OSS rule "${rule}" for ${where} on ${onDate}: this build was installed with history=${OSS_RULES_HISTORY}, ` +
`so it only carries rules from ${earliest}. Reinstall with history=full for older supplies.`,
);
}
}
throw new RangeError(`no OSS rule "${rule}" for ${where} on ${onDate}`);
}
/**
* VAT on a business-to-consumer sale to another EU member state: the seller's
* own rate while its cross-border sales stay within EUR 10,000 (and it has not
* opted in), the customer's state's rate through the One Stop Shop once they
* exceed it, and always the customer's for sellers outside the EU.
*/
export function ossVat(
net: Money,
sellerCountry: string,
customerCountry: string,
euSalesPreviousYear: Money,
euSalesThisYear: Money,
optedIn: boolean,
onDate: string,
): OssVat {
if (!ISO_DATE.test(onDate)) throw new RangeError(`onDate must be an ISO date (YYYY-MM-DD), received "${onDate}"`);
const seller = country(sellerCountry, "sellerCountry");
const customer = country(customerCountry, "customerCountry");
if (!isMember(customer)) throw new RangeError(`customer country "${customerCountry}" is not an EU member state`);
for (const [name, amount] of [
["euSalesPreviousYear", euSalesPreviousYear],
["euSalesThisYear", euSalesThisYear],
] as const) {
if (amount.currency !== "EUR") throw new RangeError(`${name} must be in EUR, received ${amount.currency}`);
if (amount.minor < 0) throw new RangeError(`${name} must not be negative, received ${amount.minor}`);
}
const threshold = lookup("distance-selling-threshold", "EU", onDate);
const thresholdExceeded = euSalesPreviousYear.minor > threshold || euSalesThisYear.minor > threshold;
let scheme: string;
let taxCountry: string;
if (seller === customer) {
scheme = "domestic";
taxCountry = customer;
} else if (isMember(seller) && !thresholdExceeded && !optedIn) {
scheme = "origin";
taxCountry = seller;
} else {
scheme = "oss";
taxCountry = customer;
}
const basisPoints = lookup("standard-rate", taxCountry, onDate);
const vat = applyRate(net, basisPoints, "half-up");
return {
taxCountry,
scheme,
basisPoints,
thresholdExceeded,
net,
vat,
gross: money(net.minor + vat.minor, net.currency),
};
}