| 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) |