Skip to content

Create an invoice

Build a simplified invoice with two notebooks priced at SAR 50 each, excluding VAT. fatoority calculates SAR 15 VAT and a SAR 115 payable total, then writes invoice.xml. No certificate or API credentials are needed.

This example uses demonstration seller details, a fixed timestamp, and a sample previous-invoice hash. Replace them with your own data for an integration. Your application owns invoice numbering, UUIDs, counters, and predecessor history. Running the example replaces invoice.xml in the working folder.

For one runnable program covering creation through reporting, follow the sandbox walkthrough.

Run the example

Create a project and add the library:

cargo new fatoority-create
cd fatoority-create
cargo add fatoora-core

Save this code as src/main.rs and run cargo run. Download creation.rs.

use fatoora_core::invoice::{
    Address, CountryCode, InvoiceBuilder, InvoiceSubType, InvoiceType, LineItem, OtherId, Party,
    SellerRole, VatCategory,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let seller = Party::<SellerRole>::new(
        "Example Store".into(),
        Address {
            country_code: CountryCode::parse("SA")?,
            city: "Riyadh".into(),
            street: "King Fahd Road".into(),
            building_number: "1234".into(),
            postal_code: "12222".into(),
            district: Some("Olaya".into()),
            additional_number: Some("5678".into()),
            additional_street: None,
            province: None,
        },
        "399999999900003",
        Some(OtherId::with_scheme("7003339333", "CRN")),
    )?;
    let item = LineItem::new(
        "Notebook",
        "2".parse()?,
        "PCE",
        "50".parse()?,
        "15".parse()?,
        VatCategory::Standard,
    )?;
    let invoice = InvoiceBuilder::new(InvoiceType::Tax(InvoiceSubType::Simplified))
        .id("INV-1001")
        .uuid("8e6000cf-1a98-4174-b3e7-b5d5954bc10d")
        .issue_datetime("2026-10-26T09:00:00Z")
        .currency("SAR")
        // Demonstration predecessor; use your own invoice history in production.
        .previous_invoice_hash("NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==")
        .invoice_counter(1)
        .seller(seller)
        .payment_means_code("10")
        .vat_category(VatCategory::Standard)
        .line_item(item)
        .build()?;

    std::fs::write("invoice.xml", invoice.to_xml()?)?;
    println!("VAT: {} SAR", invoice.totals().tax_amount());
    println!("Payable: {} SAR", invoice.totals().payable_amount());
    println!("Saved invoice.xml");
    Ok(())
}

Install the package:

pip install fatoority

Save this code as creation.py and run python creation.py. Download creation.py.

from pathlib import Path
from fatoora import InvoiceBuilder, InvoiceSubType, InvoiceTypeKind, VatCategory

builder = InvoiceBuilder.new(InvoiceTypeKind.TAX, InvoiceSubType.SIMPLIFIED)
builder.set_id("INV-1001")
builder.set_uuid("8e6000cf-1a98-4174-b3e7-b5d5954bc10d")
builder.set_issue_datetime("2026-10-26T09:00:00Z")
builder.set_currency("SAR")
# Demonstration predecessor; use your own invoice history in production.
builder.set_previous_invoice_hash(
    "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ=="
)
builder.set_invoice_counter(1)
builder.set_payment_means_code("10")
builder.set_vat_category(VatCategory.STANDARD)
builder.set_seller(
    name="Example Store", country_code="SA", city="Riyadh",
    street="King Fahd Road", building_number="1234", postal_code="12222",
    district="Olaya", additional_number="5678", vat_id="399999999900003",
    other_id="7003339333", other_id_scheme="CRN",
)
builder.add_line_item(
    description="Notebook", quantity="2", unit_code="PCE", unit_price="50",
    vat_rate="15", vat_category=VatCategory.STANDARD,
)
invoice = builder.build()
Path("invoice.xml").write_text(invoice.to_xml(), encoding="utf-8")
totals = invoice.totals()
print(f"VAT: {totals.tax_amount} SAR")
print(f"Payable: {totals.payable_amount} SAR")
print("Saved invoice.xml")

Expected output:

VAT: 15 SAR
Payable: 115 SAR
Saved invoice.xml

Open the XML to inspect the seller and line item, followed by the VAT breakdown and monetary totals. Decimal inputs keep their precision through calculation; XML monetary fields are written with two decimal places. See rounding for calculated and supplied amounts.

Adapt it to your application

Use the pricing and invoice recipes for discounts, charges, mixed VAT, credit/debit notes, and prepayment adjustments. The invoice model reference covers additional fields and the language bindings. JavaScript/TypeScript, C#, Kotlin/Java, and C/C++ expose builders through the same core.

If your application already has XML, use XML import to inspect the supported fields or pass the document to the signing guide. Unsigned model serialization may normalize formatting and omit unsupported extensions. Signed import retains the original XML and checks its local integrity.

Sign and validate

Building checks the model's fields and arithmetic. Run the validation guide for schema and local ZATCA rules. An unsigned document has no signature to verify; after signing with your certificate and matching key, validate that signed document before submission. The validation guide uses an unsigned fixture named invoice.xml; to check the document you just signed, point the CLI at signed.xml:

fatoora validate --invoice signed.xml --profile zatca --format json \
  --previous-invoice-hash "$PREVIOUS_INVOICE_HASH"

Set PREVIOUS_INVOICE_HASH to the expected predecessor from your invoice history. For this demonstration, use the sample predecessor supplied to the builder. Keep signed.xml intact when validating or submitting it.

The example creates UBL XML. PDF rendering is on the project roadmap.