GithubHelp home page GithubHelp logo

mrsool / zatca Goto Github PK

View Code? Open in Web Editor NEW
36.0 18.0 8.0 50.38 MB

An unofficial Ruby library for generating ZATCA e-invoices, QR Codes, and submitting e-invoices to ZATCA's servers.

License: MIT License

Ruby 99.89% Shell 0.11%
zatca taxes saudi-arabia qrcode e-invoicing ruby fatoora zatca-second-phase

zatca's Introduction

zatca

A Ruby library for generating QR Codes and e-invoices according to the standard created by ZATCA in Saudi Arabia.

This library supports both Phase 1 and Phase 2. Phase 2 support is still new so there may be bugs. Please report any issues you find.

Installation

Rubygems

gem install zatca

Bundler

bundle add zatca

Usage

Phase 1

require "zatca"

tags = {
  seller_name: "Mrsool",
  vat_registration_number: "310228833400003",
  timestamp: "2021-10-20T19:29:32+03:00",
  vat_total: "15",
  invoice_total: "115",
}

ZATCA.render_qr_code(tags: tags)
# => data:image/png;base64,...
# Hint (Try pasting the above into your web browser's address bar)

If you'd like to customize the size of the QR Code you can manually use the generator like so:

require "zatca"

tags = ZATCA::Tags.new({
  seller_name: "Mrsool",
  vat_registration_number: "310228833400003",
  timestamp: "2021-10-20T19:29:32+03:00",
  vat_total: "15",
  invoice_total: "115",
})

generator = ZATCA::QRCodeGenerator.new(tags: tags)
generator.render(size: 512)

Phase 2

Documentation lives in the wiki

Notice of Non-Affiliation and Disclaimer

This library is not affiliated, associated, authorized, endorsed by, or in any way officially connected with ZATCA (Zakat, Tax and Customs Authority), or any of its subsidiaries or its affiliates. The official ZATCA website can be found at https://zatca.gov.sa.

zatca's People

Contributors

obahareth avatar wes4m avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zatca's Issues

Httpx dependency needs an update

There's a known problem for windows users that is only solved in later versions of httpx.
For reference: HoneyryderChuck/httpx#19

I'm running into the same issue when running the application. I confirm the issue is solved after I forked the repo and updated the dependency myself.

Guidance to check the compliance of invoices - Phase 2

We have the documentation here https://github.com/mrsool/zatca/wiki/Checking-the-Compliance-of-an-Invoice

Please guide me on the optimal solution to pass compliance for invoices. I understand the system needs to send total 6 including standard & simplified. I have managed to generate simulation mode but need help on how to proceed. Any template invoices or samples where i can just change VAT numbers or test information to have this processed.

How to add new field in hash

How to add new field in hash?

I have tried adding new fieds in tag schema by overriding the tag schema behavior by going in the lib folder and overriding the tag.rb file by freezing the added field but it gives Argument error undefined

My hash:

tags = ZATCA::Tags.new({
  seller_name: "Mrsool",
  vat_registration_number: "310228833400003",
  timestamp: "2021-10-20T19:29:32+03:00",
  vat_total: "15",
  invoice_total: "115",
  invoice_number: '212'
})

error:
Image Pasted at 2021-11-19 12-12

Can you help me?

Enhance How Gem is Required

It would be great to have a structure similar to ActiveSupport, something like so:

  • Rename gem to zatca (it already currently needs to be required as zatca zatca anyways).
  • Allow developers to either require "zatca" (all of zatca) or specific modules like require "zatca/qr_code"

Always fails with invalid OTP

I have set valid OTP with the code below and correct VAT numbers etc

require "zatca"

vat_id = "131231231231312312"

# Four digits, each digit acting as a bool. The order is as follows: Standard Invoice, Simplified, future use, future use
invoice_type = "1100"

options = {
  common_name: "1231231123123123",
  organization_identifier: vat_id,
  organization_name: "testing",
  organization_unit: "IT",
  country: "SA",
  invoice_type: invoice_type,
  address: "Riyadh, Al Olaya D, Al Olaya, 111111",
  business_category: "IT",

  # The solution provider name
  egs_solution_name: "zxvasfsfsfadf",

  # The model of the unit the stamp is being generated for
  egs_model: "10001",

  # If you have multiple devices each should have a unique serial number
  egs_serial_number: "100000211"
}

# ENSURE THAT YOU USE YOUR OWN PRIVATE KEY by passing private_key_path and private_key_password.
# Otherwise the SDK will generate a passwordless one and delete it (this is only for testing purposes)
# Modes:
# :production (Via Fatoora Portal)
# :sandbox (Via Developer Portal)
# :simulation (Via Fatoora Portal - Simulation)
# For the sandbox set production_mode to false, otherwise set it to true. It is true by default.
#generator = ZATCA::Signing::CSR.new(csr_options: options, mode: :production)
generator = ZATCA::Signing::CSR.new(csr_options: options, mode: :simulation, private_key_path: '/home/ubuntu/myzats/ec-secp256k1-priv-key.pem')

# This is the CSR as PEM
csr = generator.generate

# Output the CSR
puts "CSR: #{csr}"

# ZATCA's API expects us to encode the PEM to Base64
csr_base64 = Base64.strict_encode64(csr)

# Output the CSR base64
puts "CSR: #{csr_base64}"

# Construct an unauthenticated API client, this is the only endpoint that is unauthenticated
client = ZATCA::Client.new(username: "", password: "")

# Get this OTP from Fatoora portal
otp = "253253"
response = client.issue_csid(csr: csr_base64, otp: otp)

# Output the response
puts "Response: #{response}"

always shows
Response: Invalid OTP

Issue with Zatca with Phase 2 - Generating a CSR

I followed https://github.com/mrsool/zatca/wiki/Generating-a-CSR

  1. Installed gem
    gem install zatca

  2. Created a file called csr.rb as below

require "zatca"
vat_id = "XXXXXXXX"

# Four digits, each digit acting as a bool. The order is as follows: Standard Invoice, Simplified, future use, future use
invoice_type = "1100"

options = {
  common_name: "XXXXXX",
  organization_identifier: vat_id,
  organization_name: "XXXXXX",
  organization_unit: "IT",
  country: "SA",
  invoice_type: invoice_type, 
  address: "Riyadh, Al Olaya D, Al Olaya, 12211",
  business_category: "IT",

  # The solution provider name
  egs_solution_name: "XXXXXXXXX",

  # The model of the unit the stamp is being generated for
  egs_model: "1000",

  # If you have multiple devices each should have a unique serial number
  egs_serial_number: "10000021"
}

# ENSURE THAT YOU USE YOUR OWN PRIVATE KEY by passing private_key_path and private_key_password.
# Otherwise the SDK will generate a passwordless one and delete it (this is only for testing purposes)
# Modes:
# :production (Via Fatoora Portal)
# :sandbox (Via Developer Portal)
# :simulation (Via Fatoora Portal - Simulation)
# For the sandbox set production_mode to false, otherwise set it to true. It is true by default.
#generator = ZATCA::Signing::CSR.new(csr_options: options, mode: :production)
generator = ZATCA::Signing::CSR.new(csr_options: options, mode: :sandbox)

# This is the CSR as PEM
csr = generator.generate

# ZATCA's API expects us to encode the PEM to Base64
csr_base64 = Base64.strict_encode64(csr)

# Construct an unauthenticated API client, this is the only endpoint that is unauthenticated
client = ZATCA::Client.new(username: "", password: "")

# Get this OTP from Fatoora portal
otp = "111111" 
response = client.issue_csid(csr: csr_base64, otp: otp)

# Output the response
puts "Response: #{response}"
  1. Error when running rb file
    ruby csr.rb
/home/ubuntu/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/zatca-1.0.1/lib/zatca/signing/csr.rb:118:in `generate_key!': pkeys are immutable on OpenSSL 3.0 (OpenSSL::PKey::PKeyError)
	from /home/ubuntu/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/zatca-1.0.1/lib/zatca/signing/csr.rb:118:in `generate_key'
	from /home/ubuntu/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/zatca-1.0.1/lib/zatca/signing/csr.rb:103:in `set_key'
	from /home/ubuntu/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/zatca-1.0.1/lib/zatca/signing/csr.rb:23:in `generate'
	from csr.rb:39:in `<main>'

Trouble with Phase 2 - Generating a CSR

I followed https://github.com/mrsool/zatca/wiki/Generating-a-CSR

  1. Installed gem
    gem install zatca

  2. Created a file called csr.rb as below

vat_id = "XXXXXXXX"

# Four digits, each digit acting as a bool. The order is as follows: Standard Invoice, Simplified, future use, future use
invoice_type = "1100"

options = {
  common_name: "XXXXXX",
  organization_identifier: vat_id,
  organization_name: "XXXXXX",
  organization_unit: "IT",
  country: "SA",
  invoice_type: invoice_type, 
  address: "Riyadh, Al Olaya D, Al Olaya, 12211",
  business_category: "IT",

  # The solution provider name
  egs_solution_name: "XXXXXXXXX",

  # The model of the unit the stamp is being generated for
  egs_model: "1000",

  # If you have multiple devices each should have a unique serial number
  egs_serial_number: "10000021"
}

# ENSURE THAT YOU USE YOUR OWN PRIVATE KEY by passing private_key_path and private_key_password.
# Otherwise the SDK will generate a passwordless one and delete it (this is only for testing purposes)
# Modes:
# :production (Via Fatoora Portal)
# :sandbox (Via Developer Portal)
# :simulation (Via Fatoora Portal - Simulation)
# For the sandbox set production_mode to false, otherwise set it to true. It is true by default.
#generator = ZATCA::Signing::CSR.new(csr_options: options, mode: :production)
generator = ZATCA::Signing::CSR.new(csr_options: options, mode: :sandbox)

# This is the CSR as PEM
csr = generator.generate

# ZATCA's API expects us to encode the PEM to Base64
csr_base64 = Base64.strict_encode64(csr)

# Construct an unauthenticated API client, this is the only endpoint that is unauthenticated
client = ZATCA::Client.new(username: "", password: "")

# Get this OTP from Fatoora portal
otp = "111111" 
response = client.issue_csid(csr: csr_base64, otp: otp)

# Output the response
puts "Response: #{response}"
  1. Error when running rb file
    ruby csr.rb
csr.rb:34:in `<main>': uninitialized constant ZATCA (NameError)

generator = ZATCA::Signing::CSR.new(csr_options: options, mode: :sandbox)
                          ^^^^^
  1. gem list (this shows Zatca)
*** LOCAL GEMS ***

abbrev (default: 0.1.1)
actioncable (7.0.4)
actionmailbox (7.0.4)
actionmailer (7.0.4)
actionpack (7.0.4)
actiontext (7.0.4)
actionview (7.0.4)
activejob (7.0.4)
activemodel (7.0.4)
activerecord (7.0.4)
activestorage (7.0.4)
activesupport (7.0.4)
base64 (default: 0.1.1)
benchmark (default: 0.2.1)
bigdecimal (default: 3.1.3)
builder (3.2.4)
bundler (2.4.22, default: 2.4.10)
cgi (default: 0.3.6)
chunky_png (1.4.0)
concurrent-ruby (1.2.2)
crass (1.0.6)
csv (default: 3.2.6)
date (default: 3.3.3)
debug (1.7.1)
delegate (default: 0.3.0)
did_you_mean (default: 1.6.3)
digest (default: 3.1.1)
drb (default: 2.1.1)
dry-configurable (1.1.0)
dry-core (1.0.1)
dry-inflector (1.0.0)
dry-initializer (3.1.1)
dry-logic (1.5.0)
dry-schema (1.13.3)
dry-types (1.7.1)
english (default: 0.7.2)
erb (default: 4.0.2)
error_highlight (default: 0.5.1)
erubi (1.12.0)
etc (default: 1.4.2)
fcntl (default: 1.0.2)
fiddle (default: 1.1.1)
fileutils (default: 1.7.0)
find (default: 0.1.1)
forwardable (default: 1.3.3)
getoptlong (default: 0.2.0)
globalid (1.2.1)
http-2-next (1.0.1)
httpx (0.21.1)
i18n (1.14.1)
io-console (default: 0.6.0)
io-nonblock (default: 0.2.0)
io-wait (default: 0.3.0)
ipaddr (default: 1.2.5)
irb (default: 1.6.2)
json (default: 2.6.3)
logger (default: 1.5.3)
loofah (2.22.0)
mail (2.8.1)
marcel (1.0.2)
matrix (0.4.2)
method_source (1.0.0)
mini_mime (1.1.5)
minitest (5.20.0, 5.16.3)
mutex_m (default: 0.1.2)
net-ftp (0.2.0)
net-http (default: 0.3.2)
net-imap (0.3.4)
net-pop (0.1.2)
net-protocol (default: 0.2.1)
net-smtp (0.3.3)
nio4r (2.7.0)
nkf (default: 0.1.2)
nokogiri (1.15.5 x86_64-linux)
observer (default: 0.1.1)
open-uri (default: 0.3.0)
open3 (default: 0.1.2)
openssl (default: 3.1.0)
optparse (default: 0.3.1)
ostruct (default: 0.5.5)
pathname (default: 0.2.1)
power_assert (2.0.3)
pp (default: 0.4.0)
prettyprint (default: 0.1.1)
prime (0.1.2)
pstore (default: 0.1.2)
psych (default: 5.0.1)
racc (default: 1.6.2)
rack (2.2.8)
rack-test (2.1.0)
rails (7.0.4)
rails-dom-testing (2.2.0)
rails-html-sanitizer (1.6.0)
railties (7.0.4)
rake (13.0.6)
rbs (2.8.2)
rdoc (default: 6.5.0)
readline (default: 0.0.3)
readline-ext (default: 0.1.5)
reline (default: 0.3.2)
resolv (default: 0.2.2)
resolv-replace (default: 0.1.1)
rexml (3.2.5)
rinda (default: 0.1.1)
rqrcode (2.1.2)
rqrcode_core (1.2.0)
rss (0.2.9)
ruby2_keywords (default: 0.0.5)
securerandom (default: 0.2.2)
set (default: 1.0.3)
shellwords (default: 0.1.0)
singleton (default: 0.1.1)
starkbank-ecdsa (2.0.0)
stringio (default: 3.0.4)
strscan (default: 3.0.5)
syntax_suggest (default: 1.0.2)
syslog (default: 0.1.1)
tempfile (default: 0.1.3)
test-unit (3.5.7)
thor (1.3.0)
time (default: 0.2.2)
timeout (default: 0.3.1)
tmpdir (default: 0.1.3)
tsort (default: 0.1.1)
typeprof (0.21.3)
tzinfo (2.0.6)
un (default: 0.2.1)
uri (default: 0.12.1)
weakref (default: 0.1.2)
websocket-driver (0.7.6)
websocket-extensions (0.1.5)
yaml (default: 0.2.1)
zatca (1.0.1)
zeitwerk (2.6.12)
zlib (default: 3.0.0)

out of char range when generating tags for qrcode

I'm facing this error here at tag 8 and 9 respectively... any guidance is appreciated

C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tag.rb:40:in chr': 550 out of char range (RangeError) from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tag.rb:40:in to_tlv'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tags.rb:47:in map' from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tags.rb:47:in to_tlv'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tags.rb:33:in `to_base64'

C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tag.rb:40:in chr': 512 out of char range (RangeError) from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tag.rb:40:in to_tlv'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tags.rb:47:in map' from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tags.rb:47:in to_tlv'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/zatca-1.1.0/lib/zatca/tags.rb:33:in `to_base64'


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

API integration

Wrap ZATCA's API and add samples of requests and valid responses.

gsub : invalid byte sequence in US-ASCII

When I try to follow example of implementation, I got an issue on the hashing part. Here is the error

Traceback (most recent call last):
        7: from test.rb:289:in `<main>'
        6: from /var/lib/gems/2.7.0/gems/zatca-1.1.0/lib/zatca/ubl/invoice.rb:188:in `to_base64'
        5: from /var/lib/gems/2.7.0/gems/zatca-1.1.0/lib/zatca/ubl/invoice.rb:214:in `generate_xml'
        4: from /var/lib/gems/2.7.0/gems/zatca-1.1.0/lib/zatca/ubl/base_component.rb:135:in `generate_xml'
        3: from /var/lib/gems/2.7.0/gems/zatca-1.1.0/lib/zatca/ubl/builder.rb:24:in `build'
        2: from /var/lib/gems/2.7.0/gems/zatca-1.1.0/lib/zatca/ubl/builder.rb:37:in `apply_hacks_to_invoice'
        1: from /var/lib/gems/2.7.0/gems/zatca-1.1.0/lib/zatca/ubl/builder.rb:45:in `apply_qualifying_properties_hacks'
/var/lib/gems/2.7.0/gems/zatca-1.1.0/lib/zatca/ubl/builder.rb:45:in `gsub': invalid byte sequence in US-ASCII (ArgumentError)

Here is my code

#encoding: utf-8

require "zatca"

vat_id = "310000000000003"

# Four digits, each digit acting as a bool. The order is as follows: Standard Invoice, Simplified, future use, future use
invoice_type = "1100"

options = {
  common_name: "The common name to be used in the certificate",
  organization_identifier: vat_id,
  organization_name: "The name of your organization",
  organization_unit: "A subunit in your organization",
  country: "SA",
  invoice_type: invoice_type,
  address: "Riyadh 1234 Street",
  business_category: "Your business category",

  # The solution provider name
  egs_solution_name: "mrsoolsdk",

  # The model of the unit the stamp is being generated for
  egs_model: "1",

  # If you have multiple devices each should have a unique serial number
  egs_serial_number: "1-Zatca|2-Hububl|3-fa4e99b8-c9c5-4c37-957a-3926352c34e6"
}

generator = ZATCA::Signing::CSR.new(csr_options: options, mode: :sandbox)


csr = generator.generate

# ZATCA's API expects us to encode the PEM to Base64
csr_base64 = Base64.strict_encode64(csr)

client = ZATCA::Client.new(username: "", password: "", environment: :sandbox)

otp = "123345"
response = client.issue_csid(csr: csr_base64, otp: otp)

request_id = response["requestID"]
binary_security_token = response["binarySecurityToken"]
secret = response["secret"]
#puts response ;

#binarySecurityToken as user
#secret as passwort

client = ZATCA::Client.new(username: binary_security_token, password: secret, environment: :sandbox)



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
)

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)

# openssl ecparam -name secp256k1 -genkey -noout -out ec-secp256k1-priv-key.pem
invoice.sign(
  private_key_path: "/var/www/html/public/ec-secp256k1-priv-key.pem",
  certificate_path: "/var/www/html/public/.private/app.pem",
  signing_time: signing_time,
  decode_private_key_from_base64: false # Optional
)

# 2. Send the invoice to ZATCA
# Assuming you have already constructed a ZATCA::UBL::Invoice
response = client.compliance_check(
  invoice: invoice.to_base64,
  invoice_hash: invoice.generate_hash,
  uuid: invoice.uuid
)

puts response

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.