-
Notifications
You must be signed in to change notification settings - Fork 16
Constructing a Simplified Invoice
Omar Bahareth edited this page Nov 16, 2023
·
17 revisions
For simplified invoices(B2C), you need to sign and create the QR-code yourself. For standard invoices (B2B), you use ZATCA's clearance API which signs and adds the QR Code for you.
#===============================================================================
# 1. Setup the Invoice Values
#===============================================================================
invoice_id = "SME00010"
invoice_uuid = "8e6000cf-1a98-4174-b3e7-b5d5954bc10d"
note = "ABC"
note_language_id = "ar"
issue_date = "2022-08-17"
issue_time = "17:41:08"
invoice_subtype = ZATCA::UBL::InvoiceSubtypeBuilder.build(
simplified: true,
third_party: false,
nominal: false,
exports: false,
summary: false,
self_billed: false
) # => "0200000"
payment_means_code = ZATCA::UBL::Invoice::PAYMENT_MEANS[:bank_card] # => "48"
invoice_type = ZATCA::UBL::Invoice::TYPES[:invoice] # => "388"
invoice_counter_value = "10"
previous_invoice_hash = "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ=="
currency_code = "SAR"
vat_registration_number = "311111111101113"
#===============================================================================
# 2. Setup the Invoice's Nested Properties
#===============================================================================
# Create the supplier party (the party issuing the invoice, e.g. the seller)
accounting_supplier_party = ZATCA::UBL::CommonAggregateComponents::Party.new(
party_identification: ZATCA::UBL::CommonAggregateComponents::PartyIdentification.new(
id: "324223432432432"
),
postal_address: ZATCA::UBL::CommonAggregateComponents::PostalAddress.new(
street_name: "الامير سلطان",
additional_street_name: nil,
building_number: "3242",
plot_identification: "4323",
city_subdivision_name: "32423423",
city_name: "الرياض | Riyadh",
postal_zone: "32432",
country_subentity: nil,
country_identification_code: "SA"
),
party_tax_scheme: ZATCA::UBL::CommonAggregateComponents::PartyTaxScheme.new(
company_id: vat_registration_number
),
party_legal_entity: ZATCA::UBL::CommonAggregateComponents::PartyLegalEntity.new(
registration_name: "Acme Widgets LTD"
)
)
# Create the customer party (the party receiving the invoice, e.g. the buyer)
accounting_customer_party = ZATCA::UBL::CommonAggregateComponents::Party.new(
# party_identification: ZATCA::UBL::CommonAggregateComponents::PartyIdentification.new(
# id: "2345",
# scheme_id: "NAT"
# ),
party_identification: nil,
postal_address: ZATCA::UBL::CommonAggregateComponents::PostalAddress.new(
street_name: nil,
additional_street_name: nil,
building_number: nil,
plot_identification: nil,
city_subdivision_name: "32423423",
city_name: nil,
postal_zone: nil,
country_subentity: nil,
country_identification_code: "SA"
),
party_tax_scheme: ZATCA::UBL::CommonAggregateComponents::PartyTaxScheme.new,
party_legal_entity: nil
)
# Create the (optional) delivery object (detailing the delivery date and time)
# delivery = ZATCA::UBL::CommonAggregateComponents::Delivery.new(
# actual_delivery_date: "2022-03-13",
# latest_delivery_date: "2022-03-15"
# )
delivery = nil
# Create the allowance charges (e.g. discounts) for the invoice
allowance_charges = [
ZATCA::UBL::CommonAggregateComponents::AllowanceCharge.new(
charge_indicator: false,
amount: "0.00",
allowance_charge_reason: "discount",
currency_id: "SAR",
tax_categories: [
# Yes, ZATCA's official valid sample duplicates these, not sure why
ZATCA::UBL::CommonAggregateComponents::TaxCategory.new(
tax_percent: "15"
),
ZATCA::UBL::CommonAggregateComponents::TaxCategory.new(
tax_percent: "15"
)
]
)
]
# Create the tax totals for the invoice
# ZATCA's official valid sample has two of these, not sure why
tax_totals = [
ZATCA::UBL::CommonAggregateComponents::TaxTotal.new(
tax_amount: "30.15"
),
ZATCA::UBL::CommonAggregateComponents::TaxTotal.new(
tax_amount: "30.15",
tax_subtotal_amount: "30.15",
taxable_amount: "201.00",
tax_category: ZATCA::UBL::CommonAggregateComponents::TaxCategory.new(
tax_percent: "15.00"
)
)
]
# Create the legal monetary total for the invoice
legal_monetary_total = ZATCA::UBL::CommonAggregateComponents::LegalMonetaryTotal.new(
line_extension_amount: "201.00",
tax_exclusive_amount: "201.00",
tax_inclusive_amount: "231.15",
allowance_total_amount: "0.00",
prepaid_amount: "0.00",
payable_amount: "231.15"
)
# Create the invoice lines for the invoice (the list of items that were sold)
invoice_lines = [
# Book
ZATCA::UBL::CommonAggregateComponents::InvoiceLine.new(
invoiced_quantity: "33.000000",
invoiced_quantity_unit_code: "PCE",
line_extension_amount: "99.00",
tax_total: ZATCA::UBL::CommonAggregateComponents::TaxTotal.new(
tax_amount: "14.85",
rounding_amount: "113.85"
),
item: ZATCA::UBL::CommonAggregateComponents::Item.new(
name: "كتاب"
),
price: ZATCA::UBL::CommonAggregateComponents::Price.new(
price_amount: "3.00",
allowance_charge: ZATCA::UBL::CommonAggregateComponents::AllowanceCharge.new(
charge_indicator: false,
allowance_charge_reason: "discount",
amount: "0.00",
add_tax_category: false,
# ZATCA's samples can sometimes have a nested tax scheme with an ID
# and sometimes they omit it. Setting this boolean controls whether it is
# present or not
add_id: false
)
)
),
# Pen
ZATCA::UBL::CommonAggregateComponents::InvoiceLine.new(
invoiced_quantity: "3.000000",
invoiced_quantity_unit_code: "PCE",
line_extension_amount: "102.00",
tax_total: ZATCA::UBL::CommonAggregateComponents::TaxTotal.new(
tax_amount: "15.30",
rounding_amount: "117.30"
),
item: ZATCA::UBL::CommonAggregateComponents::Item.new(
name: "قلم"
),
price: ZATCA::UBL::CommonAggregateComponents::Price.new(
price_amount: "34.00",
allowance_charge: ZATCA::UBL::CommonAggregateComponents::AllowanceCharge.new(
charge_indicator: false,
allowance_charge_reason: "discount",
amount: "0.00",
add_tax_category: false,
add_id: false
)
)
)
]
#===============================================================================
# 3. Construct the Invoice
#===============================================================================
# Construct the invoice using all of the above
invoice = ZATCA::UBL::Invoice.new(
add_ids_to_allowance_charges: false,
id: invoice_id,
uuid: invoice_uuid,
issue_date: issue_date,
issue_time: issue_time,
subtype: invoice_subtype,
type: invoice_type,
invoice_counter_value: invoice_counter_value,
previous_invoice_hash: previous_invoice_hash,
accounting_supplier_party: accounting_supplier_party,
accounting_customer_party: accounting_customer_party,
delivery: delivery,
payment_means_code: payment_means_code,
allowance_charges: allowance_charges,
tax_totals: tax_totals,
legal_monetary_total: legal_monetary_total,
invoice_lines: invoice_lines,
currency_code: currency_code,
note: note,
note_language_id: note_language_id
)
# NOTE: For credit or debit notes you must also fill out
# invoice.instruction_note with an explanation on why
# this note was issued.
#===============================================================================
# 4. Sign the Invoice (ONLY FOR SIMPLIFIED INVOICES)
#===============================================================================
# Must not be encoded in Base64 and must have header blocks
private_key_path = "path/to_your/private.key"
# Must be in pem format with header blocks
certificate_path = "path/to_your/certificate.pem"
# You can omit this if you don't need to customize it,
# the default value is the same as you see here.
signing_time = Time.now.utc.strftime("#{invoice.issue_date}T#{invoice.issue_time}")
# Sign the invoice (this adds a couple of new elements to the invoice)
invoice.sign(
private_key_path: private_key_path,
certificate_path: certificate_path,
signing_time: signing_time,
decode_private_key_from_base64: false # Optional
)
# See the section under this code block if you need more
# control over the signing process.
#===============================================================================
# 5. Create the QR Code (ONLY FOR SIMPLIFIED INVOICES)
#===============================================================================
# Setup values used in tags
invoice_hash = invoice.generate_hash
invoice_timestamp = "#{invoice.issue_date}T#{invoice.issue_time}"
# Create the tags
tags = ZATCA::Tags.new({
seller_name: "Acme Widgets LTD",
vat_registration_number: "311111111101113",
timestamp: invoice_timestamp,
vat_total: "30.15",
invoice_total: "231.15",
xml_invoice_hash: invoice_hash,
# These 3 properties on the invoice assume you have signed it before.
# They are nil unless signed.
ecdsa_signature: invoice.signed_hash,
ecdsa_public_key: invoice.public_key_bytes,
ecdsa_stamp_signature: invoice.certificate_signature
})
# Turn the tags to TLV and then encode that TLV to base64
invoice.qr_code = tags.to_base64
#===============================================================================
# Done! You now have an invoice constructed and signed
#===============================================================================
TODO