From a29ece7df9653e96d69182cbfcddb7df1cba2a0c Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Thu, 11 Jun 2026 14:45:29 +0500 Subject: [PATCH 1/6] feat: enhance invoice item description with row number for better clarity --- di/integrations/di_api.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/di/integrations/di_api.py b/di/integrations/di_api.py index 6f4315a..7b07e41 100755 --- a/di/integrations/di_api.py +++ b/di/integrations/di_api.py @@ -414,13 +414,18 @@ def _build_invoice_items(doc): sale_type = line.get("di_sale_type") or "" + product_description = _safe_str(line.get("item_name", "")) + row_no = line.get("idx") + if row_no: + product_description = f"{product_description} (Row {row_no})".strip() + invoice_item = InvoiceItem( discount=max(_round_currency(discount), 0.0), fedPayable=_round_currency(_as_decimal(line.get("di_fed_payable", 0)) * conversion_rate), furtherTax=further_tax_amt, hsCode=_safe_str(line.get("di_hs_code", "")), extraTax=extra_tax_amt, - productDescription=_safe_str(line.get("item_name", "")), + productDescription=product_description, quantity=round(qty, 4), rate=_format_rate(gst["percentage"], sale_type), salesTaxApplicable=sales_tax, From 1c72df37e570719e7e7cb0668cf7fbf96d7b3827 Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Thu, 11 Jun 2026 15:04:17 +0500 Subject: [PATCH 2/6] fix: update sales tax invoice format to include base currency and total quantity --- .../di_sales_tax_invoice/di_sales_tax_invoice.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json b/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json index 957d430..d3e0e1b 100755 --- a/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json +++ b/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json @@ -10,14 +10,14 @@ "docstatus": 0, "doctype": "Print Format", "font_size": 0, - "html": "{% set company = frappe.get_doc(\"Company\", doc.company) %}\r\n{% set di_settings = frappe.db.get_value(\"DI Settings\", {\"company\": doc.company}, [\"ntn_cnic\", \"strn_no\", \"province\", \"address\"], as_dict=1) %}\r\n{% set customer = frappe.get_doc(\"Customer\", doc.customer) if doc.customer else None %}\r\n\r\n
\r\n\t
\r\n\t\t

{{ company.company_name }}

\r\n\t\t

{{ di_settings.address or company.get(\"address\") or \"\" }}

\r\n\t\t

Sales Tax Invoice

\r\n\t
\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t{% if di_settings.strn_no %}{% endif %}\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t
Seller Details
Business Name:{{ company.company_name }}
NTN/CNIC:{{ di_settings.ntn_cnic or company.get(\"tax_id\") or \"\" }}
STRN:{{ di_settings.strn_no }}
Province:{{ di_settings.province or \"\" }}
Address:{{ di_settings.address or \"\" }}
\r\n\t\t\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t
Buyer Details
Business Name:{{ doc.customer_name or \"\" }}
NTN/CNIC:{{ customer.ntn_cnic if customer else \"\" }}
Registration:{{ customer.registration_type if customer else \"\" }}
Province:{{ customer.province if customer else \"\" }}
Address:{{ customer.di_address if customer else \"\" }}
\r\n\t\t\t
\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t{% if doc.di_integration_id %}\r\n\t\t\t\r\n\t\t\t{% endif %}\r\n\t\t\r\n\t
Invoice No:{{ doc.name }}Date:{{ doc.posting_date }}FBR Invoice No:{{ doc.di_integration_id }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\t{% for item in doc.items %}\r\n\t\t\t{% set st = namespace(rate=0, amount=0, found=false) %}\r\n\t\t\t{% set ft = namespace(amount=0) %}\r\n\t\t\t{% for tax in doc.taxes %}\r\n\t\t\t\t{% set item_tax = json.loads(tax.item_wise_tax_detail or \"{}\").get(item.item_code) %}\r\n\t\t\t\t{% if item_tax is iterable and item_tax is not string %}\r\n\t\t\t\t\t{% if tax.di_tax_type == \"Further Tax\" %}\r\n\t\t\t\t\t\t{% set ft.amount = item_tax[1] if item_tax|length > 1 else 0 %}\r\n\t\t\t\t\t{% elif tax.di_tax_type == \"Sales Tax\" %}\r\n\t\t\t\t\t\t{% set st.rate = item_tax[0] if item_tax|length > 0 else 0 %}\r\n\t\t\t\t\t\t{% set st.amount = item_tax[1] if item_tax|length > 1 else 0 %}\r\n\t\t\t\t\t\t{% set st.found = true %}\r\n\t\t\t\t\t{% endif %}\r\n\t\t\t\t{% endif %}\r\n\t\t\t{% endfor %}\r\n\t\t\t{% if not st.found and doc.taxes %}\r\n\t\t\t\t{% set fallback = json.loads(doc.taxes[0].item_wise_tax_detail or \"{}\").get(item.item_code) %}\r\n\t\t\t\t{% if fallback is iterable and fallback is not string %}\r\n\t\t\t\t\t{% set st.rate = fallback[0] if fallback|length > 0 else 0 %}\r\n\t\t\t\t\t{% set st.amount = fallback[1] if fallback|length > 1 else 0 %}\r\n\t\t\t\t{% endif %}\r\n\t\t\t{% endif %}\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\t{% endfor %}\r\n\t\t\r\n\t
#HS CodeDescriptionSale TypeQtyUOMValue (Excl. ST)ST RateSales TaxFurther TaxFEDTotal
{{ item.idx }}{{ item.di_hs_code or \"\" }}{{ item.item_name }}{{ item.di_sale_type or \"\" }}{{ item.qty }}{{ item.di_hs_uom or item.uom or \"\" }}{{ frappe.utils.fmt_money(item.net_amount, currency=doc.currency) }}{{ st.rate }}%{{ frappe.utils.fmt_money(st.amount, currency=doc.currency) }}{{ frappe.utils.fmt_money(ft.amount, currency=doc.currency) }}{{ frappe.utils.fmt_money(item.di_fed_payable or 0, currency=doc.currency) }}{{ frappe.utils.fmt_money(item.amount, currency=doc.currency) }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t{% for tax in doc.taxes %}\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t{% endfor %}\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
Net Total:{{ frappe.utils.fmt_money(doc.net_total, currency=doc.currency) }}
{{ tax.description }}:{{ frappe.utils.fmt_money(tax.tax_amount, currency=doc.currency) }}
Grand Total:{{ frappe.utils.fmt_money(doc.grand_total, currency=doc.currency) }}
\r\n\t\r\n\t{% if doc.is_di_posted and doc.di_integration_id %}\r\n\t{% set di_qr = generate_qr_code(doc.di_integration_id) %}\r\n\t
\r\n\t\t{% if di_qr %}\r\n\t\t
\r\n\t\t\t\"FBR\r\n\t\t
\r\n\t\t{% endif %}\r\n\t\t
\r\n\t\t\t\"FBR\r\n\t\t\t

FBR Invoice Number: {{ doc.di_integration_id }}

\r\n\t\t\t

Date & Time: {{ doc.di_posting_datetime }}

\r\n\t\t\t

\r\n\t\t\t\tThis invoice has been reported to FBR through Digital Invoicing System.\r\n\t\t\t\tVerify this invoice at https://e.fbr.gov.pk\r\n\t\t\t

\r\n\t\t
\r\n\t
\r\n\t{% endif %}\r\n
\r\n", + "html": "{% set company = frappe.get_doc(\"Company\", doc.company) %}\r\n{% set base_currency = company.default_currency %}\r\n{% set di_settings = frappe.db.get_value(\"DI Settings\", {\"company\": doc.company}, [\"ntn_cnic\", \"strn_no\", \"province\", \"address\"], as_dict=1) %}\r\n{% set customer = frappe.get_doc(\"Customer\", doc.customer) if doc.customer else None %}\r\n\r\n
\r\n\t
\r\n\t\t

{{ company.company_name }}

\r\n\t\t

{{ di_settings.address or company.get(\"address\") or \"\" }}

\r\n\t\t

Sales Tax Invoice

\r\n\t
\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t{% if di_settings.strn_no %}{% endif %}\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t
Seller Details
Business Name:{{ company.company_name }}
NTN/CNIC:{{ di_settings.ntn_cnic or company.get(\"tax_id\") or \"\" }}
STRN:{{ di_settings.strn_no }}
Province:{{ di_settings.province or \"\" }}
Address:{{ di_settings.address or \"\" }}
\r\n\t\t\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t
Buyer Details
Business Name:{{ doc.customer_name or \"\" }}
NTN/CNIC:{{ customer.ntn_cnic if customer else \"\" }}
Registration:{{ customer.registration_type if customer else \"\" }}
Province:{{ customer.province if customer else \"\" }}
Address:{{ customer.di_address if customer else \"\" }}
\r\n\t\t\t
\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t{% if doc.di_integration_id %}\r\n\t\t\t\r\n\t\t\t{% endif %}\r\n\t\t\r\n\t
Invoice No:{{ doc.name }}Date:{{ doc.posting_date }}FBR Invoice No:{{ doc.di_integration_id }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\t{% for item in doc.items %}\r\n\t\t\t{% set st = namespace(rate=0, amount=0, found=false) %}\r\n\t\t\t{% set ft = namespace(amount=0) %}\r\n\t\t\t{% for tax in doc.taxes %}\r\n\t\t\t\t{% set item_tax = json.loads(tax.item_wise_tax_detail or \"{}\").get(item.item_code) %}\r\n\t\t\t\t{% if item_tax is iterable and item_tax is not string %}\r\n\t\t\t\t\t{% if tax.di_tax_type == \"Further Tax\" %}\r\n\t\t\t\t\t\t{% set ft.amount = item_tax[1] if item_tax|length > 1 else 0 %}\r\n\t\t\t\t\t{% elif tax.di_tax_type == \"Sales Tax\" %}\r\n\t\t\t\t\t\t{% set st.rate = item_tax[0] if item_tax|length > 0 else 0 %}\r\n\t\t\t\t\t\t{% set st.amount = item_tax[1] if item_tax|length > 1 else 0 %}\r\n\t\t\t\t\t\t{% set st.found = true %}\r\n\t\t\t\t\t{% endif %}\r\n\t\t\t\t{% endif %}\r\n\t\t\t{% endfor %}\r\n\t\t\t{% if not st.found and doc.taxes %}\r\n\t\t\t\t{% set fallback = json.loads(doc.taxes[0].item_wise_tax_detail or \"{}\").get(item.item_code) %}\r\n\t\t\t\t{% if fallback is iterable and fallback is not string %}\r\n\t\t\t\t\t{% set st.rate = fallback[0] if fallback|length > 0 else 0 %}\r\n\t\t\t\t\t{% set st.amount = fallback[1] if fallback|length > 1 else 0 %}\r\n\t\t\t\t{% endif %}\r\n\t\t\t{% endif %}\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\t{% endfor %}\r\n\t\t\r\n\t
#HS CodeDescriptionSale TypeQtyUOMValue (Excl. ST)ST RateSales TaxFurther TaxFEDTotal
{{ item.idx }}{{ item.di_hs_code or \"\" }}{{ item.item_name }}{{ item.di_sale_type or \"\" }}{{ item.qty }}{{ item.di_hs_uom or item.uom or \"\" }}{{ frappe.utils.fmt_money(item.base_net_amount, currency=base_currency) }}{{ st.rate }}%{{ frappe.utils.fmt_money(st.amount, currency=base_currency) }}{{ frappe.utils.fmt_money(ft.amount, currency=base_currency) }}{{ frappe.utils.fmt_money((item.di_fed_payable or 0) * (doc.conversion_rate or 1), currency=base_currency) }}{{ frappe.utils.fmt_money(item.base_amount, currency=base_currency) }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t{% for tax in doc.taxes %}\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t{% endfor %}\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
Net Total:{{ frappe.utils.fmt_money(doc.base_net_total, currency=base_currency) }}
{{ tax.description }}:{{ frappe.utils.fmt_money(tax.base_tax_amount, currency=base_currency) }}
Grand Total:{{ frappe.utils.fmt_money(doc.base_grand_total, currency=base_currency) }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
Total Quantity:{{ doc.total_qty }}
\r\n\r\n\t\r\n\t{% if doc.is_di_posted and doc.di_integration_id %}\r\n\t{% set di_qr = generate_qr_code(doc.di_integration_id) %}\r\n\t
\r\n\t\t{% if di_qr %}\r\n\t\t
\r\n\t\t\t\"FBR\r\n\t\t
\r\n\t\t{% endif %}\r\n\t\t
\r\n\t\t\t{% set fbr_logo = get_fbr_logo_data_uri() %}\r\n\t\t\t{% if fbr_logo %}\r\n\t\t\t\"FBR\r\n\t\t\t{% endif %}\r\n\t\t\t

FBR Invoice Number: {{ doc.di_integration_id }}

\r\n\t\t\t

Date & Time: {{ doc.di_posting_datetime }}

\r\n\t\t\t

\r\n\t\t\t\tThis invoice has been reported to FBR through Digital Invoicing System.\r\n\t\t\t\tVerify this invoice at https://e.fbr.gov.pk\r\n\t\t\t

\r\n\t\t
\r\n\t
\r\n\t{% endif %}\r\n
\r\n", "idx": 0, "line_breaks": 0, "margin_bottom": 0.0, "margin_left": 0.0, "margin_right": 0.0, "margin_top": 0.0, - "modified": "2026-06-08 21:39:53.332233", + "modified": "2026-06-08 12:56:25.394656", "modified_by": "Administrator", "module": "Digital Invoicing", "name": "DI Sales Tax Invoice", From c8b1e838edb669d233a6a4df6f01ae03eb9a823e Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Thu, 11 Jun 2026 15:10:18 +0500 Subject: [PATCH 3/6] fix: update modified date for sales tax invoice print format --- .../print_format/di_sales_tax_invoice/di_sales_tax_invoice.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json b/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json index d3e0e1b..0d107d9 100755 --- a/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json +++ b/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json @@ -17,7 +17,7 @@ "margin_left": 0.0, "margin_right": 0.0, "margin_top": 0.0, - "modified": "2026-06-08 12:56:25.394656", + "modified": "2026-06-11 12:56:25.394656", "modified_by": "Administrator", "module": "Digital Invoicing", "name": "DI Sales Tax Invoice", From 226b86b95a5f12cbcc66eb2b5466105b48600656 Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Thu, 11 Jun 2026 15:20:22 +0500 Subject: [PATCH 4/6] feat: update sales tax invoice HTML format to include total quantity and FBR logo --- .../di_sales_tax_invoice/di_sales_tax_invoice.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json b/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json index 0d107d9..4eeebc9 100755 --- a/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json +++ b/di/digital_invoicing/print_format/di_sales_tax_invoice/di_sales_tax_invoice.json @@ -10,14 +10,14 @@ "docstatus": 0, "doctype": "Print Format", "font_size": 0, - "html": "{% set company = frappe.get_doc(\"Company\", doc.company) %}\r\n{% set base_currency = company.default_currency %}\r\n{% set di_settings = frappe.db.get_value(\"DI Settings\", {\"company\": doc.company}, [\"ntn_cnic\", \"strn_no\", \"province\", \"address\"], as_dict=1) %}\r\n{% set customer = frappe.get_doc(\"Customer\", doc.customer) if doc.customer else None %}\r\n\r\n
\r\n\t
\r\n\t\t

{{ company.company_name }}

\r\n\t\t

{{ di_settings.address or company.get(\"address\") or \"\" }}

\r\n\t\t

Sales Tax Invoice

\r\n\t
\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t{% if di_settings.strn_no %}{% endif %}\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t
Seller Details
Business Name:{{ company.company_name }}
NTN/CNIC:{{ di_settings.ntn_cnic or company.get(\"tax_id\") or \"\" }}
STRN:{{ di_settings.strn_no }}
Province:{{ di_settings.province or \"\" }}
Address:{{ di_settings.address or \"\" }}
\r\n\t\t\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t
Buyer Details
Business Name:{{ doc.customer_name or \"\" }}
NTN/CNIC:{{ customer.ntn_cnic if customer else \"\" }}
Registration:{{ customer.registration_type if customer else \"\" }}
Province:{{ customer.province if customer else \"\" }}
Address:{{ customer.di_address if customer else \"\" }}
\r\n\t\t\t
\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t{% if doc.di_integration_id %}\r\n\t\t\t\r\n\t\t\t{% endif %}\r\n\t\t\r\n\t
Invoice No:{{ doc.name }}Date:{{ doc.posting_date }}FBR Invoice No:{{ doc.di_integration_id }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\t{% for item in doc.items %}\r\n\t\t\t{% set st = namespace(rate=0, amount=0, found=false) %}\r\n\t\t\t{% set ft = namespace(amount=0) %}\r\n\t\t\t{% for tax in doc.taxes %}\r\n\t\t\t\t{% set item_tax = json.loads(tax.item_wise_tax_detail or \"{}\").get(item.item_code) %}\r\n\t\t\t\t{% if item_tax is iterable and item_tax is not string %}\r\n\t\t\t\t\t{% if tax.di_tax_type == \"Further Tax\" %}\r\n\t\t\t\t\t\t{% set ft.amount = item_tax[1] if item_tax|length > 1 else 0 %}\r\n\t\t\t\t\t{% elif tax.di_tax_type == \"Sales Tax\" %}\r\n\t\t\t\t\t\t{% set st.rate = item_tax[0] if item_tax|length > 0 else 0 %}\r\n\t\t\t\t\t\t{% set st.amount = item_tax[1] if item_tax|length > 1 else 0 %}\r\n\t\t\t\t\t\t{% set st.found = true %}\r\n\t\t\t\t\t{% endif %}\r\n\t\t\t\t{% endif %}\r\n\t\t\t{% endfor %}\r\n\t\t\t{% if not st.found and doc.taxes %}\r\n\t\t\t\t{% set fallback = json.loads(doc.taxes[0].item_wise_tax_detail or \"{}\").get(item.item_code) %}\r\n\t\t\t\t{% if fallback is iterable and fallback is not string %}\r\n\t\t\t\t\t{% set st.rate = fallback[0] if fallback|length > 0 else 0 %}\r\n\t\t\t\t\t{% set st.amount = fallback[1] if fallback|length > 1 else 0 %}\r\n\t\t\t\t{% endif %}\r\n\t\t\t{% endif %}\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\t{% endfor %}\r\n\t\t\r\n\t
#HS CodeDescriptionSale TypeQtyUOMValue (Excl. ST)ST RateSales TaxFurther TaxFEDTotal
{{ item.idx }}{{ item.di_hs_code or \"\" }}{{ item.item_name }}{{ item.di_sale_type or \"\" }}{{ item.qty }}{{ item.di_hs_uom or item.uom or \"\" }}{{ frappe.utils.fmt_money(item.base_net_amount, currency=base_currency) }}{{ st.rate }}%{{ frappe.utils.fmt_money(st.amount, currency=base_currency) }}{{ frappe.utils.fmt_money(ft.amount, currency=base_currency) }}{{ frappe.utils.fmt_money((item.di_fed_payable or 0) * (doc.conversion_rate or 1), currency=base_currency) }}{{ frappe.utils.fmt_money(item.base_amount, currency=base_currency) }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t{% for tax in doc.taxes %}\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t{% endfor %}\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
Net Total:{{ frappe.utils.fmt_money(doc.base_net_total, currency=base_currency) }}
{{ tax.description }}:{{ frappe.utils.fmt_money(tax.base_tax_amount, currency=base_currency) }}
Grand Total:{{ frappe.utils.fmt_money(doc.base_grand_total, currency=base_currency) }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
Total Quantity:{{ doc.total_qty }}
\r\n\r\n\t\r\n\t{% if doc.is_di_posted and doc.di_integration_id %}\r\n\t{% set di_qr = generate_qr_code(doc.di_integration_id) %}\r\n\t
\r\n\t\t{% if di_qr %}\r\n\t\t
\r\n\t\t\t\"FBR\r\n\t\t
\r\n\t\t{% endif %}\r\n\t\t
\r\n\t\t\t{% set fbr_logo = get_fbr_logo_data_uri() %}\r\n\t\t\t{% if fbr_logo %}\r\n\t\t\t\"FBR\r\n\t\t\t{% endif %}\r\n\t\t\t

FBR Invoice Number: {{ doc.di_integration_id }}

\r\n\t\t\t

Date & Time: {{ doc.di_posting_datetime }}

\r\n\t\t\t

\r\n\t\t\t\tThis invoice has been reported to FBR through Digital Invoicing System.\r\n\t\t\t\tVerify this invoice at https://e.fbr.gov.pk\r\n\t\t\t

\r\n\t\t
\r\n\t
\r\n\t{% endif %}\r\n
\r\n", + "html": "{% set company = frappe.get_doc(\"Company\", doc.company) %}\r\n{% set base_currency = company.default_currency %}\r\n{% set di_settings = frappe.db.get_value(\"DI Settings\", {\"company\": doc.company}, [\"ntn_cnic\", \"strn_no\", \"province\", \"address\"], as_dict=1) %}\r\n{% set customer = frappe.get_doc(\"Customer\", doc.customer) if doc.customer else None %}\r\n\r\n
\r\n\t
\r\n\t\t

{{ company.company_name }}

\r\n\t\t

{{ di_settings.address or company.get(\"address\") or \"\" }}

\r\n\t\t

Sales Tax Invoice

\r\n\t
\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t{% if di_settings.strn_no %}{% endif %}\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t
Seller Details
Business Name:{{ company.company_name }}
NTN/CNIC:{{ di_settings.ntn_cnic or company.get(\"tax_id\") or \"\" }}
STRN:{{ di_settings.strn_no }}
Province:{{ di_settings.province or \"\" }}
Address:{{ di_settings.address or \"\" }}
\r\n\t\t\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t
Buyer Details
Business Name:{{ doc.customer_name or \"\" }}
NTN/CNIC:{{ customer.ntn_cnic if customer else \"\" }}
Registration:{{ customer.registration_type if customer else \"\" }}
Province:{{ customer.province if customer else \"\" }}
Address:{{ customer.di_address if customer else \"\" }}
\r\n\t\t\t
\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t{% if doc.di_integration_id %}\r\n\t\t\t\r\n\t\t\t{% endif %}\r\n\t\t\r\n\t
Invoice No:{{ doc.name }}Date:{{ doc.posting_date }}FBR Invoice No:{{ doc.di_integration_id }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\t{% for item in doc.items %}\r\n\t\t\t{% set st = namespace(rate=0, amount=0, found=false) %}\r\n\t\t\t{% set ft = namespace(amount=0) %}\r\n\t\t\t{% for tax in doc.taxes %}\r\n\t\t\t\t{% set item_tax = json.loads(tax.item_wise_tax_detail or \"{}\").get(item.item_code) %}\r\n\t\t\t\t{% if item_tax is iterable and item_tax is not string %}\r\n\t\t\t\t\t{% if tax.di_tax_type == \"Further Tax\" %}\r\n\t\t\t\t\t\t{% set ft.amount = item_tax[1] if item_tax|length > 1 else 0 %}\r\n\t\t\t\t\t{% elif tax.di_tax_type == \"Sales Tax\" %}\r\n\t\t\t\t\t\t{% set st.rate = item_tax[0] if item_tax|length > 0 else 0 %}\r\n\t\t\t\t\t\t{% set st.amount = item_tax[1] if item_tax|length > 1 else 0 %}\r\n\t\t\t\t\t\t{% set st.found = true %}\r\n\t\t\t\t\t{% endif %}\r\n\t\t\t\t{% endif %}\r\n\t\t\t{% endfor %}\r\n\t\t\t{% if not st.found and doc.taxes %}\r\n\t\t\t\t{% set fallback = json.loads(doc.taxes[0].item_wise_tax_detail or \"{}\").get(item.item_code) %}\r\n\t\t\t\t{% if fallback is iterable and fallback is not string %}\r\n\t\t\t\t\t{% set st.rate = fallback[0] if fallback|length > 0 else 0 %}\r\n\t\t\t\t\t{% set st.amount = fallback[1] if fallback|length > 1 else 0 %}\r\n\t\t\t\t{% endif %}\r\n\t\t\t{% endif %}\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\t{% endfor %}\r\n\t\t\r\n\t
#HS CodeDescriptionSale TypeQtyUOMValue (Excl. ST)ST RateSales TaxFurther TaxFEDTotal
{{ item.idx }}{{ item.di_hs_code or \"\" }}{{ item.item_name }}{{ item.di_sale_type or \"\" }}{{ item.qty }}{{ item.di_hs_uom or item.uom or \"\" }}{{ frappe.utils.fmt_money(item.base_net_amount, currency=base_currency) }}{{ st.rate }}%{{ frappe.utils.fmt_money(st.amount, currency=base_currency) }}{{ frappe.utils.fmt_money(ft.amount, currency=base_currency) }}{{ frappe.utils.fmt_money((item.di_fed_payable or 0) * (doc.conversion_rate or 1), currency=base_currency) }}{{ frappe.utils.fmt_money(item.base_amount, currency=base_currency) }}
\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t{% for tax in doc.taxes %}\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t{% endfor %}\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t
Total Qty:{{ doc.total_qty }}
Net Total:{{ frappe.utils.fmt_money(doc.base_net_total, currency=base_currency) }}
{{ tax.description }}:{{ frappe.utils.fmt_money(tax.base_tax_amount, currency=base_currency) }}
Grand Total:{{ frappe.utils.fmt_money(doc.base_grand_total, currency=base_currency) }}
\r\n\r\n\t\r\n\t{% if doc.is_di_posted and doc.di_integration_id %}\r\n\t{% set di_qr = generate_qr_code(doc.di_integration_id) %}\r\n\t
\r\n\t\t{% if di_qr %}\r\n\t\t
\r\n\t\t\t\"FBR\r\n\t\t
\r\n\t\t{% endif %}\r\n\t\t
\r\n\t\t\t\"FBR\r\n\t\t\t

FBR Invoice Number: {{ doc.di_integration_id }}

\r\n\t\t\t

Date & Time: {{ doc.di_posting_datetime }}

\r\n\t\t\t

\r\n\t\t\t\tThis invoice has been reported to FBR through Digital Invoicing System.\r\n\t\t\t\tVerify this invoice at https://e.fbr.gov.pk\r\n\t\t\t

\r\n\t\t
\r\n\t
\r\n\t{% endif %}\r\n
\r\n", "idx": 0, "line_breaks": 0, "margin_bottom": 0.0, "margin_left": 0.0, "margin_right": 0.0, "margin_top": 0.0, - "modified": "2026-06-11 12:56:25.394656", + "modified": "2026-06-11 15:19:03.542899", "modified_by": "Administrator", "module": "Digital Invoicing", "name": "DI Sales Tax Invoice", From a872b1f80cc3851dedd7fd1c8fdada9626072773 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 22:24:06 +0000 Subject: [PATCH 5/6] ci: bump actions/setup-node from 6 to 7 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6 to 7. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/semantic-commits.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) mode change 100755 => 100644 .github/workflows/ci.yml mode change 100755 => 100644 .github/workflows/release.yml mode change 100755 => 100644 .github/workflows/semantic-commits.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100755 new mode 100644 index f3b766a..61dfdbe --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: python-version: "3.10" - name: Setup Node - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: 18 check-latest: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml old mode 100755 new mode 100644 index 7c87721..a3005df --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: token: ${{ env.RELEASE_AUTH_TOKEN }} - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: 20 diff --git a/.github/workflows/semantic-commits.yml b/.github/workflows/semantic-commits.yml old mode 100755 new mode 100644 index 4d9f4ce..95677bd --- a/.github/workflows/semantic-commits.yml +++ b/.github/workflows/semantic-commits.yml @@ -19,7 +19,7 @@ jobs: with: fetch-depth: 200 - - uses: actions/setup-node@v6 + - uses: actions/setup-node@v7 with: node-version: 20 check-latest: true From a7b606b19e200984460830e5d171667440adb805 Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Wed, 29 Jul 2026 11:30:59 +0500 Subject: [PATCH 6/6] fix: rename 'Advance Tax' to 'Extra Tax' in tax options and update related patches --- README.md | 2 +- di/custom_fields.py | 2 +- di/integrations/di_api.py | 2 +- di/patches.txt | 1 + di/patches/v1_0/__init__.py | 0 .../v1_0/rename_advance_tax_to_extra_tax.py | 19 +++++++++++++++++++ 6 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 di/patches/v1_0/__init__.py create mode 100644 di/patches/v1_0/rename_advance_tax_to_extra_tax.py diff --git a/README.md b/README.md index b563e5e..8d667cb 100755 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ On **Sales Taxes and Charges** templates, set the **Tax Type** field to one of: - `Sales Tax` - `Further Tax` -- `Advance Tax` +- `Extra Tax` ### 5. POS Fiscal Setup diff --git a/di/custom_fields.py b/di/custom_fields.py index 0c5ac23..a9bdd06 100755 --- a/di/custom_fields.py +++ b/di/custom_fields.py @@ -292,7 +292,7 @@ def setup_custom_fields(): fieldname="di_tax_type", fieldtype="Select", insert_after="account_head", - options="Sales Tax\nFurther Tax\nAdvance Tax", + options="Sales Tax\nFurther Tax\nExtra Tax", in_list_view=1, ), ], diff --git a/di/integrations/di_api.py b/di/integrations/di_api.py index 7b07e41..f8f5cc9 100755 --- a/di/integrations/di_api.py +++ b/di/integrations/di_api.py @@ -387,7 +387,7 @@ def _build_invoice_items(doc): tax_data = item_taxes.get(item_code, {}) gst = tax_data.get("Sales Tax", {"percentage": 0.0, "amount": 0.0}) further_tax = tax_data.get("Further Tax", {"percentage": 0.0, "amount": 0.0}) - extra_tax = tax_data.get("Advance Tax", {"percentage": 0.0, "amount": 0.0}) + extra_tax = tax_data.get("Extra Tax", {"percentage": 0.0, "amount": 0.0}) qty = flt(line.get("qty", 0)) value_excl_st = _round_currency(line.get("base_net_amount") or line.get("net_amount", 0)) diff --git a/di/patches.txt b/di/patches.txt index 73efa6f..1f732cb 100755 --- a/di/patches.txt +++ b/di/patches.txt @@ -4,3 +4,4 @@ [post_model_sync] # Patches added in this section will be executed after doctypes are migrated +di.patches.v1_0.rename_advance_tax_to_extra_tax \ No newline at end of file diff --git a/di/patches/v1_0/__init__.py b/di/patches/v1_0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/di/patches/v1_0/rename_advance_tax_to_extra_tax.py b/di/patches/v1_0/rename_advance_tax_to_extra_tax.py new file mode 100644 index 0000000..349a50c --- /dev/null +++ b/di/patches/v1_0/rename_advance_tax_to_extra_tax.py @@ -0,0 +1,19 @@ +import frappe + + +def execute(): + """Rename the `Advance Tax` DI tax type to `Extra Tax`. + + FBR DI maps this tax to the `extraTax` field, so the option was renamed to + match. Existing rows still hold the old value and would otherwise become + invalid select values and stop being picked up by the payload builder. + """ + if not frappe.db.has_column("Sales Taxes and Charges", "di_tax_type"): + return + + table = frappe.qb.DocType("Sales Taxes and Charges") + ( + frappe.qb.update(table) + .set(table.di_tax_type, "Extra Tax") + .where(table.di_tax_type == "Advance Tax") + ).run() \ No newline at end of file