import io import os from base64 import b64encode import frappe from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.utils.data import add_to_date, get_time, getdate from pyqrcode import create as qr_create from erpnext import get_region def create_qr_code(doc, method=None): region = get_region(doc.company) if region not in ["Saudi Arabia"]: return # if QR Code field not present, create it. Invoices without QR are invalid as per law. if not hasattr(doc, "ksa_einv_qr"): create_custom_fields( { doc.doctype: [ dict( fieldname="ksa_einv_qr", label="KSA E-Invoicing QR", fieldtype="Attach Image", read_only=1, no_copy=1, hidden=1, ) ] } ) # Don't create QR Code if it already exists qr_code = doc.get("ksa_einv_qr") if qr_code and frappe.db.exists({"doctype": "File", "file_url": qr_code}): return meta = frappe.get_meta(doc.doctype) if "ksa_einv_qr" in [d.fieldname for d in meta.get_image_fields()]: """TLV conversion for 1. Seller's Name 2. VAT Number 3. Time Stamp 4. Invoice Amount 5. VAT Amount """ tlv_array = [] # Sellers Name seller_name = frappe.db.get_value("Company", doc.company, "company_name_in_arabic") if not seller_name: frappe.throw(_("Arabic name missing for {} in the company document").format(doc.company)) tag = bytes([1]).hex() length = bytes([len(seller_name.encode("utf-8"))]).hex() value = seller_name.encode("utf-8").hex() tlv_array.append("".join([tag, length, value])) # VAT Number tax_id = frappe.db.get_value("Company", doc.company, "tax_id") if not tax_id: frappe.throw(_("Tax ID missing for {} in the company document").format(doc.company)) tag = bytes([2]).hex() length = bytes([len(tax_id)]).hex() value = tax_id.encode("utf-8").hex() tlv_array.append("".join([tag, length, value])) # Time Stamp posting_date = getdate(doc.posting_date) time = get_time(doc.posting_time) seconds = time.hour * 60 * 60 + time.minute * 60 + time.second time_stamp = add_to_date(posting_date, seconds=seconds) time_stamp = time_stamp.strftime("%Y-%m-%dT%H:%M:%SZ") tag = bytes([3]).hex() length = bytes([len(time_stamp)]).hex() value = time_stamp.encode("utf-8").hex() tlv_array.append("".join([tag, length, value])) # Invoice Amount invoice_amount = str(doc.grand_total) tag = bytes([4]).hex() length = bytes([len(invoice_amount)]).hex() value = invoice_amount.encode("utf-8").hex() tlv_array.append("".join([tag, length, value])) # VAT Amount vat_amount = str(get_vat_amount(doc)) tag = bytes([5]).hex() length = bytes([len(vat_amount)]).hex() value = vat_amount.encode("utf-8").hex() tlv_array.append("".join([tag, length, value])) # Joining bytes into one tlv_buff = "".join(tlv_array) # base64 conversion for QR Code base64_string = b64encode(bytes.fromhex(tlv_buff)).decode() qr_image = io.BytesIO() url = qr_create(base64_string, error="L") url.png(qr_image, scale=2, quiet_zone=1) name = frappe.generate_hash(doc.name, 5) # making file filename = f"QRCode-{name}.png".replace(os.path.sep, "__") _file = frappe.get_doc( { "doctype": "File", "file_name": filename, "is_private": 0, "content": qr_image.getvalue(), "attached_to_doctype": doc.get("doctype"), "attached_to_name": doc.get("name"), "attached_to_field": "ksa_einv_qr", } ) _file.save() # assigning to document doc.db_set("ksa_einv_qr", _file.file_url) doc.notify_update() def get_vat_amount(doc): vat_settings = frappe.db.get_value("KSA VAT Setting", {"company": doc.company}) vat_accounts = [] vat_amount = 0 if vat_settings: vat_settings_doc = frappe.get_cached_doc("KSA VAT Setting", vat_settings) for row in vat_settings_doc.get("ksa_vat_sales_accounts"): vat_accounts.append(row.account) for tax in doc.get("taxes"): if tax.account_head in vat_accounts: vat_amount += tax.tax_amount return vat_amount def delete_qr_code_file(doc, method=None): region = get_region(doc.company) if region not in ["Saudi Arabia"]: return if hasattr(doc, "ksa_einv_qr"): if doc.get("ksa_einv_qr"): file_doc = frappe.get_list("File", {"file_url": doc.get("ksa_einv_qr")}) if len(file_doc): frappe.delete_doc("File", file_doc[0].name) def delete_vat_settings_for_company(doc, method=None): if doc.country != "Saudi Arabia": return if frappe.db.exists("KSA VAT Setting", doc.name): frappe.delete_doc("KSA VAT Setting", doc.name)