use crate::bt::{Group, Path};
use crate::{
Attachment, Date, DocumentReference, Identifier, InvoiceAmount, Percentage, Quantity,
UnitPriceAmount,
};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Code(String);
impl Code {
pub fn new(code: impl Into<String>) -> Self {
Self(code.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn is_in(&self, list: &[&str]) -> bool {
crate::codes::contains(list, &self.0)
}
#[must_use]
pub fn is_blank(&self) -> bool {
self.0.trim().is_empty()
}
}
impl core::fmt::Display for Code {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.pad(&self.0)
}
}
impl From<&str> for Code {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for Code {
fn from(s: String) -> Self {
Self::new(s)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Period {
pub start: Option<Date>,
pub end: Option<Date>,
}
impl Period {
#[must_use]
pub fn is_ordered(&self) -> Option<bool> {
match (self.start, self.end) {
(Some(s), Some(e)) => Some(e >= s),
_ => None,
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PostalAddress {
pub line1: Option<String>,
pub line2: Option<String>,
pub line3: Option<String>,
pub city: Option<String>,
pub post_code: Option<String>,
pub subdivision: Option<String>,
pub country: Option<Code>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Contact {
pub name: Option<String>,
pub phone: Option<String>,
pub email: Option<String>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Party {
pub name: Option<String>,
pub trading_name: Option<String>,
pub identifiers: Vec<Identifier>,
pub legal_registration: Option<Identifier>,
pub vat_identifier: Option<String>,
pub tax_registration: Option<String>,
pub additional_legal_information: Option<String>,
pub electronic_address: Option<Identifier>,
pub address: PostalAddress,
pub contact: Contact,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum DocumentKind {
#[default]
Invoice,
CreditNote,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct InvoiceNote {
pub subject_code: Option<Code>,
pub note: Option<String>,
}
impl InvoiceNote {
#[must_use]
pub fn new(note: impl Into<String>) -> Self {
Self {
subject_code: None,
note: Some(note.into()),
}
}
#[must_use]
pub fn with_subject(mut self, code: impl Into<String>) -> Self {
self.subject_code = Some(Code::new(code));
self
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Payee {
pub name: Option<String>,
pub identifier: Option<Identifier>,
pub legal_registration: Option<Identifier>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TaxRepresentative {
pub name: Option<String>,
pub vat_identifier: Option<String>,
pub address: PostalAddress,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PrecedingInvoice {
pub reference: DocumentReference,
pub issue_date: Option<Date>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SupportingDocument {
pub reference: DocumentReference,
pub description: Option<String>,
pub uri: Option<String>,
pub attachment: Option<Attachment>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct CreditTransfer {
pub account_identifier: Option<String>,
pub account_name: Option<String>,
pub provider_identifier: Option<String>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PaymentCard {
pub primary_account_number: Option<String>,
pub holder_name: Option<String>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct DirectDebit {
pub mandate_reference: Option<String>,
pub creditor_identifier: Option<String>,
pub debited_account: Option<String>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PaymentMeans {
CreditTransfer(Vec<CreditTransfer>),
Card(PaymentCard),
DirectDebit(DirectDebit),
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PaymentInstructions {
pub means_code: Option<Code>,
pub means_text: Option<String>,
pub remittance_information: Option<String>,
pub means: Option<PaymentMeans>,
}
impl PaymentInstructions {
#[must_use]
pub fn account_identifier(&self) -> Option<&str> {
match &self.means {
Some(PaymentMeans::CreditTransfer(ts)) => {
ts.first().and_then(|t| t.account_identifier.as_deref())
}
_ => None,
}
}
#[must_use]
pub fn mandate_reference(&self) -> Option<&str> {
match &self.means {
Some(PaymentMeans::DirectDebit(d)) => d.mandate_reference.as_deref(),
_ => None,
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Delivery {
pub party_name: Option<String>,
pub location: Option<Identifier>,
pub date: Option<Date>,
pub address: Option<PostalAddress>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LineVat {
pub category: Code,
pub rate: Option<Percentage>,
}
impl LineVat {
#[must_use]
pub fn semantics(&self) -> Option<crate::VatCategory> {
crate::VatCategory::from_code(self.category.as_str())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VatBreakdown {
pub taxable_amount: InvoiceAmount,
pub tax_amount: InvoiceAmount,
pub category: Code,
pub rate: Option<Percentage>,
pub exemption_reason: Option<String>,
pub exemption_reason_code: Option<Code>,
}
impl VatBreakdown {
#[must_use]
pub fn semantics(&self) -> Option<crate::VatCategory> {
crate::VatCategory::from_code(self.category.as_str())
}
#[must_use]
pub fn has_exemption_reason(&self) -> bool {
self.exemption_reason.is_some() || self.exemption_reason_code.is_some()
}
#[must_use]
pub fn group_key(&self) -> (Code, Option<Percentage>) {
(self.category.clone(), self.rate)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DocumentAllowanceCharge {
pub amount: InvoiceAmount,
pub base_amount: Option<InvoiceAmount>,
pub percentage: Option<Percentage>,
pub vat: LineVat,
pub reason: Option<String>,
pub reason_code: Option<Code>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineAllowanceCharge {
pub amount: InvoiceAmount,
pub base_amount: Option<InvoiceAmount>,
pub percentage: Option<Percentage>,
pub reason: Option<String>,
pub reason_code: Option<Code>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PriceDetails {
pub net_price: UnitPriceAmount,
pub price_discount: Option<UnitPriceAmount>,
pub gross_price: Option<UnitPriceAmount>,
pub base_quantity: Option<Quantity>,
pub base_quantity_code: Option<Code>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ItemAttribute {
pub name: Option<String>,
pub value: Option<String>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Item {
pub name: Option<String>,
pub description: Option<String>,
pub seller_identifier: Option<String>,
pub buyer_identifier: Option<String>,
pub standard_identifier: Option<Identifier>,
pub classification_identifiers: Vec<Identifier>,
pub origin_country: Option<Code>,
pub attributes: Vec<ItemAttribute>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvoiceLine {
pub id: String,
pub note: Option<String>,
pub order_line_reference: Option<DocumentReference>,
pub accounting_reference: Option<String>,
pub object_identifier: Option<Identifier>,
pub quantity: Quantity,
pub unit_code: Code,
pub net_amount: InvoiceAmount,
pub period: Option<Period>,
pub allowances: Vec<LineAllowanceCharge>,
pub charges: Vec<LineAllowanceCharge>,
pub price: PriceDetails,
pub vat: LineVat,
pub item: Item,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct DocumentTotals {
pub line_total: InvoiceAmount,
pub allowance_total: Option<InvoiceAmount>,
pub charge_total: Option<InvoiceAmount>,
pub taxable_total: InvoiceAmount,
pub vat_total: Option<InvoiceAmount>,
pub vat_total_accounting: Option<InvoiceAmount>,
pub gross_total: InvoiceAmount,
pub paid: Option<InvoiceAmount>,
pub rounding: Option<InvoiceAmount>,
pub due: InvoiceAmount,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[non_exhaustive]
pub struct Invoice {
pub kind: DocumentKind,
pub specification_id: Option<String>,
pub business_process: Option<String>,
pub number: Option<String>,
pub issue_date: Option<Date>,
pub type_code: Option<Code>,
pub currency: Option<Code>,
pub vat_accounting_currency: Option<Code>,
pub vat_point_date: Option<Date>,
pub vat_point_date_code: Option<Code>,
pub due_date: Option<Date>,
pub buyer_reference: Option<String>,
pub project_reference: Option<DocumentReference>,
pub contract_reference: Option<DocumentReference>,
pub purchase_order_reference: Option<DocumentReference>,
pub sales_order_reference: Option<DocumentReference>,
pub receiving_advice_reference: Option<DocumentReference>,
pub despatch_advice_reference: Option<DocumentReference>,
pub tender_reference: Option<DocumentReference>,
pub object_identifier: Option<Identifier>,
pub accounting_reference: Option<String>,
pub payment_terms: Option<String>,
pub notes: Vec<InvoiceNote>,
pub preceding_invoices: Vec<PrecedingInvoice>,
pub seller: Party,
pub buyer: Party,
pub payee: Option<Payee>,
pub tax_representative: Option<TaxRepresentative>,
pub delivery: Option<Delivery>,
pub invoicing_period: Option<Period>,
pub payment: Option<PaymentInstructions>,
pub allowances: Vec<DocumentAllowanceCharge>,
pub charges: Vec<DocumentAllowanceCharge>,
pub vat_breakdown: Vec<VatBreakdown>,
pub attachments: Vec<SupportingDocument>,
pub lines: Vec<InvoiceLine>,
pub totals: DocumentTotals,
pub extensions: crate::extensions::Extensions,
}
impl Invoice {
#[must_use]
pub fn builder(
specification_id: impl Into<String>,
number: impl Into<String>,
issue_date: Date,
type_code: impl Into<Code>,
currency: impl Into<Code>,
) -> InvoiceBuilder {
InvoiceBuilder {
inv: Invoice {
specification_id: Some(specification_id.into()),
number: Some(number.into()),
issue_date: Some(issue_date),
type_code: Some(type_code.into()),
currency: Some(currency.into()),
..Default::default()
},
}
}
#[must_use]
pub fn occupied_groups(&self) -> Vec<Path> {
let mut v = vec![Path::group(Group::Totals)];
v.extend((0..self.lines.len()).map(|i| Path::at(Group::Line, i)));
v.extend((0..self.vat_breakdown.len()).map(|i| Path::at(Group::VatBreakdown, i)));
v.extend((0..self.allowances.len()).map(|i| Path::at(Group::DocumentAllowance, i)));
v.extend((0..self.charges.len()).map(|i| Path::at(Group::DocumentCharge, i)));
v
}
#[must_use]
pub fn categories_used(&self) -> Vec<crate::VatCategory> {
let mut v: Vec<_> = self
.lines
.iter()
.filter_map(|l| l.vat.semantics())
.chain(self.allowances.iter().filter_map(|a| a.vat.semantics()))
.chain(self.charges.iter().filter_map(|c| c.vat.semantics()))
.chain(
self.vat_breakdown
.iter()
.filter_map(VatBreakdown::semantics),
)
.collect();
v.sort_unstable();
v.dedup();
v
}
}
pub mod terms {
use crate::bt::BtId;
macro_rules! bt {
($($name:ident = $n:literal, $doc:literal;)*) => {
$(#[doc = $doc] pub const $name: BtId = BtId($n);)*
};
}
bt! {
NUMBER = 1, "Invoice number";
ISSUE_DATE = 2, "Invoice issue date";
TYPE_CODE = 3, "Invoice type code";
CURRENCY = 5, "Invoice currency code";
VAT_ACCOUNTING_CURRENCY = 6, "VAT accounting currency code";
VAT_POINT_DATE = 7, "Value added tax point date";
VAT_POINT_DATE_CODE = 8, "Value added tax point date code";
DUE_DATE = 9, "Payment due date";
BUSINESS_PROCESS = 23, "Business process type";
BUYER_REFERENCE = 10, "Buyer reference";
PROJECT_REFERENCE = 11, "Project reference";
CONTRACT_REFERENCE = 12, "Contract reference";
PURCHASE_ORDER_REFERENCE = 13, "Purchase order reference";
SALES_ORDER_REFERENCE = 14, "Sales order reference";
RECEIVING_ADVICE_REFERENCE = 15, "Receiving advice reference";
DESPATCH_ADVICE_REFERENCE = 16, "Despatch advice reference";
TENDER_REFERENCE = 17, "Tender or lot reference";
OBJECT_IDENTIFIER = 18, "Invoiced object identifier";
ACCOUNTING_REFERENCE = 19, "Buyer accounting reference";
PAYMENT_TERMS = 20, "Payment terms";
SPECIFICATION_ID = 24, "Specification identifier";
PRECEDING_INVOICE = 25, "Preceding Invoice reference";
SELLER_NAME = 27, "Seller name";
SELLER_VAT_ID = 31, "Seller VAT identifier";
SELLER_TAX_ID = 32, "Seller tax registration identifier";
SELLER_LEGAL_INFO = 33, "Seller additional legal information";
SELLER_ELECTRONIC_ADDRESS = 34, "Seller electronic address";
SELLER_COUNTRY = 40, "Seller country code";
BUYER_NAME = 44, "Buyer name";
BUYER_VAT_ID = 48, "Buyer VAT identifier";
BUYER_ELECTRONIC_ADDRESS = 49, "Buyer electronic address";
BUYER_COUNTRY = 55, "Buyer country code";
DELIVERY_DATE = 72, "Actual delivery date";
PERIOD_START = 73, "Invoicing period start date";
PERIOD_END = 74, "Invoicing period end date";
DELIVER_TO_COUNTRY = 80, "Deliver to country code";
PAYMENT_MEANS_CODE = 81, "Payment means type code";
PAYMENT_ACCOUNT = 84, "Payment account identifier";
ALLOWANCE_AMOUNT = 92, "Document level allowance amount";
ALLOWANCE_BASE = 93, "Document level allowance base amount";
ALLOWANCE_PERCENTAGE = 94, "Document level allowance percentage";
ALLOWANCE_VAT_CATEGORY = 95, "Document level allowance VAT category code";
ALLOWANCE_VAT_RATE = 96, "Document level allowance VAT rate";
ALLOWANCE_REASON = 97, "Document level allowance reason";
ALLOWANCE_REASON_CODE = 98, "Document level allowance reason code";
CHARGE_AMOUNT = 99, "Document level charge amount";
CHARGE_BASE = 100, "Document level charge base amount";
CHARGE_PERCENTAGE = 101, "Document level charge percentage";
CHARGE_VAT_CATEGORY = 102, "Document level charge VAT category code";
CHARGE_VAT_RATE = 103, "Document level charge VAT rate";
CHARGE_REASON = 104, "Document level charge reason";
CHARGE_REASON_CODE = 105, "Document level charge reason code";
LINE_TOTAL = 106, "Sum of Invoice line net amount";
ALLOWANCE_TOTAL = 107, "Sum of allowances on document level";
CHARGE_TOTAL = 108, "Sum of charges on document level";
TAXABLE_TOTAL = 109, "Invoice total amount without VAT";
VAT_TOTAL = 110, "Invoice total VAT amount";
VAT_TOTAL_ACCOUNTING = 111, "Invoice total VAT amount in accounting currency";
GROSS_TOTAL = 112, "Invoice total amount with VAT";
PAID = 113, "Paid amount";
ROUNDING = 114, "Rounding amount";
DUE = 115, "Amount due for payment";
VAT_TAXABLE_AMOUNT = 116, "VAT category taxable amount";
VAT_TAX_AMOUNT = 117, "VAT category tax amount";
VAT_CATEGORY = 118, "VAT category code";
VAT_RATE = 119, "VAT category rate";
EXEMPTION_REASON = 120, "VAT exemption reason text";
EXEMPTION_REASON_CODE = 121, "VAT exemption reason code";
SUPPORTING_DOCUMENT = 122, "Supporting document reference";
LINE_ID = 126, "Invoice line identifier";
LINE_QUANTITY = 129, "Invoiced quantity";
LINE_UNIT_CODE = 130, "Invoiced quantity unit of measure code";
LINE_NET_AMOUNT = 131, "Invoice line net amount";
LINE_PERIOD_START = 134, "Invoice line period start date";
LINE_PERIOD_END = 135, "Invoice line period end date";
LINE_ALLOWANCE_AMOUNT = 136, "Invoice line allowance amount";
LINE_ALLOWANCE_REASON = 139, "Invoice line allowance reason";
LINE_ALLOWANCE_REASON_CODE = 140, "Invoice line allowance reason code";
LINE_CHARGE_AMOUNT = 141, "Invoice line charge amount";
LINE_CHARGE_REASON = 144, "Invoice line charge reason";
LINE_CHARGE_REASON_CODE = 145, "Invoice line charge reason code";
ITEM_NET_PRICE = 146, "Item net price";
ITEM_PRICE_DISCOUNT = 147, "Item price discount";
ITEM_GROSS_PRICE = 148, "Item gross price";
PRICE_BASE_QUANTITY = 149, "Item price base quantity";
PRICE_BASE_QUANTITY_CODE = 150, "Item price base quantity unit of measure";
LINE_VAT_CATEGORY = 151, "Invoiced item VAT category code";
LINE_VAT_RATE = 152, "Invoiced item VAT rate";
ITEM_NAME = 153, "Item name";
ITEM_STANDARD_ID = 157, "Item standard identifier";
ITEM_CLASSIFICATION_ID = 158, "Item classification identifier";
}
}
#[allow(unused_imports)]
use terms as _terms_are_public;
#[cfg(test)]
mod tests {
use super::*;
use crate::codes::generated::UNIT_CODES;
#[test]
fn code_is_verbatim_and_case_sensitive() {
assert!(Code::new("KWH").is_in(UNIT_CODES));
assert!(!Code::new("kwh").is_in(UNIT_CODES));
assert!(!Code::new(" KWH").is_in(UNIT_CODES), "no trimming");
assert!(Code::new(" ").is_blank());
}
#[test]
fn an_invalid_code_is_representable() {
let bad = Code::new("999");
assert_eq!(bad.as_str(), "999");
assert!(!bad.is_in(crate::codes::generated::INVOICE_TYPE_CODES));
}
#[test]
fn line_vat_semantics_are_optional() {
let ok = LineVat {
category: Code::new("S"),
rate: Some(Percentage::new(rust_decimal::dec!(19))),
};
assert_eq!(ok.semantics(), Some(crate::VatCategory::Standard));
let bad = LineVat {
category: Code::new("Q"),
rate: None,
};
assert_eq!(bad.semantics(), None, "so BR-CL-18 fires and others skip");
}
#[test]
fn period_ordering_matches_br_29() {
let p = Period {
start: Some(Date::parse("2026-06-01").unwrap()),
end: Some(Date::parse("2026-06-30").unwrap()),
};
assert_eq!(p.is_ordered(), Some(true));
let reversed = Period {
start: p.end,
end: p.start,
};
assert_eq!(reversed.is_ordered(), Some(false));
let open = Period {
start: p.start,
end: None,
};
assert_eq!(open.is_ordered(), None);
}
}
#[derive(Debug, Clone)]
pub struct InvoiceBuilder {
inv: Invoice,
}
macro_rules! setter {
($name:ident, $field:ident, $ty:ty, $doc:literal) => {
#[doc = $doc]
#[must_use]
pub fn $name(mut self, value: $ty) -> Self {
self.inv.$field = Some(value);
self
}
};
}
impl InvoiceBuilder {
setter!(due_date, due_date, Date, "BT-9 — payment due date.");
setter!(
payment_terms,
payment_terms,
String,
"BT-20 — payment terms."
);
setter!(
delivery,
delivery,
Delivery,
"BG-13 — delivery information."
);
setter!(
invoicing_period,
invoicing_period,
Period,
"BG-14 — invoicing period."
);
setter!(
payment,
payment,
PaymentInstructions,
"BG-16 — payment instructions."
);
setter!(
vat_accounting_currency,
vat_accounting_currency,
Code,
"BT-6 — VAT accounting currency code."
);
#[must_use]
pub fn buyer_reference(mut self, value: impl Into<String>) -> Self {
self.inv.buyer_reference = Some(value.into());
self
}
#[must_use]
pub fn business_process(mut self, value: impl Into<String>) -> Self {
self.inv.business_process = Some(value.into());
self
}
#[must_use]
pub fn seller(mut self, seller: Party) -> Self {
self.inv.seller = seller;
self
}
#[must_use]
pub fn buyer(mut self, buyer: Party) -> Self {
self.inv.buyer = buyer;
self
}
setter!(payee, payee, Payee, "BG-10 — the payee.");
setter!(
tax_representative,
tax_representative,
TaxRepresentative,
"BG-11 — the seller's tax representative."
);
#[must_use]
pub fn line(mut self, line: InvoiceLine) -> Self {
self.inv.lines.push(line);
self
}
#[must_use]
pub fn vat_breakdown(mut self, entry: VatBreakdown) -> Self {
self.inv.vat_breakdown.push(entry);
self
}
#[must_use]
pub fn allowance(mut self, allowance: DocumentAllowanceCharge) -> Self {
self.inv.allowances.push(allowance);
self
}
#[must_use]
pub fn charge(mut self, charge: DocumentAllowanceCharge) -> Self {
self.inv.charges.push(charge);
self
}
#[must_use]
pub fn credit_note(mut self) -> Self {
self.inv.kind = DocumentKind::CreditNote;
self
}
#[must_use]
pub fn note(mut self, note: impl Into<String>) -> Self {
self.inv.notes.push(InvoiceNote::new(note));
self
}
#[must_use]
pub fn coded_note(mut self, note: InvoiceNote) -> Self {
self.inv.notes.push(note);
self
}
#[must_use]
pub fn totals(mut self, totals: DocumentTotals) -> Self {
self.inv.totals = totals;
self
}
#[must_use]
pub fn build(self) -> Invoice {
self.inv
}
}