Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/semantic-commits.yml
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion di/custom_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<div class=\"di-print\">\r\n\t<div class=\"di-header\">\r\n\t\t<h2>{{ company.company_name }}</h2>\r\n\t\t<p>{{ di_settings.address or company.get(\"address\") or \"\" }}</p>\r\n\t\t<p><strong>Sales Tax Invoice</strong></p>\r\n\t</div>\r\n\r\n\t<table style=\"margin-bottom:10px;\">\r\n\t\t<tr>\r\n\t\t\t<td style=\"width:50%;border:none;vertical-align:top;padding:0 10px 0 0;\">\r\n\t\t\t\t<table class=\"di-info-table\">\r\n\t\t\t\t\t<tr><td colspan=\"2\" style=\"font-weight:bold;font-size:12px;\">Seller Details</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Business Name:</td><td>{{ company.company_name }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">NTN/CNIC:</td><td>{{ di_settings.ntn_cnic or company.get(\"tax_id\") or \"\" }}</td></tr>\r\n\t\t\t\t\t{% if di_settings.strn_no %}<tr><td class=\"label-col\">STRN:</td><td>{{ di_settings.strn_no }}</td></tr>{% endif %}\r\n\t\t\t\t\t<tr><td class=\"label-col\">Province:</td><td>{{ di_settings.province or \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Address:</td><td>{{ di_settings.address or \"\" }}</td></tr>\r\n\t\t\t\t</table>\r\n\t\t\t</td>\r\n\t\t\t<td style=\"width:50%;border:none;vertical-align:top;padding:0 0 0 10px;\">\r\n\t\t\t\t<table class=\"di-info-table\">\r\n\t\t\t\t\t<tr><td colspan=\"2\" style=\"font-weight:bold;font-size:12px;\">Buyer Details</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Business Name:</td><td>{{ doc.customer_name or \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">NTN/CNIC:</td><td>{{ customer.ntn_cnic if customer else \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Registration:</td><td>{{ customer.registration_type if customer else \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Province:</td><td>{{ customer.province if customer else \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Address:</td><td>{{ customer.di_address if customer else \"\" }}</td></tr>\r\n\t\t\t\t</table>\r\n\t\t\t</td>\r\n\t\t</tr>\r\n\t</table>\r\n\r\n\t<table class=\"di-info-table\" style=\"margin-bottom:10px;\">\r\n\t\t<tr>\r\n\t\t\t<td class=\"label-col\">Invoice No:</td><td>{{ doc.name }}</td>\r\n\t\t\t<td class=\"label-col\">Date:</td><td>{{ doc.posting_date }}</td>\r\n\t\t\t{% if doc.di_integration_id %}\r\n\t\t\t<td class=\"label-col\">FBR Invoice No:</td><td>{{ doc.di_integration_id }}</td>\r\n\t\t\t{% endif %}\r\n\t\t</tr>\r\n\t</table>\r\n\r\n\t<!-- Items Table -->\r\n\t<table>\r\n\t\t<thead>\r\n\t\t\t<tr>\r\n\t\t\t\t<th style=\"width:30px;\">#</th>\r\n\t\t\t\t<th>HS Code</th>\r\n\t\t\t\t<th>Description</th>\r\n\t\t\t\t<th>Sale Type</th>\r\n\t\t\t\t<th class=\"di-right\">Qty</th>\r\n\t\t\t\t<th>UOM</th>\r\n\t\t\t\t<th class=\"di-right\">Value (Excl. ST)</th>\r\n\t\t\t\t<th class=\"di-right\">ST Rate</th>\r\n\t\t\t\t<th class=\"di-right\">Sales Tax</th>\r\n\t\t\t\t<th class=\"di-right\">Further Tax</th>\r\n\t\t\t\t<th class=\"di-right\">FED</th>\r\n\t\t\t\t<th class=\"di-right\">Total</th>\r\n\t\t\t</tr>\r\n\t\t</thead>\r\n\t\t<tbody>\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<tr>\r\n\t\t\t\t<td>{{ item.idx }}</td>\r\n\t\t\t\t<td>{{ item.di_hs_code or \"\" }}</td>\r\n\t\t\t\t<td>{{ item.item_name }}</td>\r\n\t\t\t\t<td>{{ item.di_sale_type or \"\" }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ item.qty }}</td>\r\n\t\t\t\t<td>{{ item.di_hs_uom or item.uom or \"\" }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(item.net_amount, currency=doc.currency) }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ st.rate }}%</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(st.amount, currency=doc.currency) }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(ft.amount, currency=doc.currency) }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(item.di_fed_payable or 0, currency=doc.currency) }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(item.amount, currency=doc.currency) }}</td>\r\n\t\t\t</tr>\r\n\t\t\t{% endfor %}\r\n\t\t</tbody>\r\n\t</table>\r\n\r\n\t<!-- Totals -->\r\n\t<table style=\"width:50%;margin-left:auto;margin-top:10px;\">\r\n\t\t<tr class=\"di-totals\">\r\n\t\t\t<td style=\"border:none;\">Net Total:</td>\r\n\t\t\t<td style=\"border:none;\">{{ frappe.utils.fmt_money(doc.net_total, currency=doc.currency) }}</td>\r\n\t\t</tr>\r\n\t\t{% for tax in doc.taxes %}\r\n\t\t<tr class=\"di-totals\">\r\n\t\t\t<td style=\"border:none;\">{{ tax.description }}:</td>\r\n\t\t\t<td style=\"border:none;\">{{ frappe.utils.fmt_money(tax.tax_amount, currency=doc.currency) }}</td>\r\n\t\t</tr>\r\n\t\t{% endfor %}\r\n\t\t<tr class=\"di-totals\" style=\"font-size:13px;\">\r\n\t\t\t<td style=\"border-top:2px solid #333;border-bottom:none;border-left:none;border-right:none;\">Grand Total:</td>\r\n\t\t\t<td style=\"border-top:2px solid #333;border-bottom:none;border-left:none;border-right:none;\">{{ frappe.utils.fmt_money(doc.grand_total, currency=doc.currency) }}</td>\r\n\t\t</tr>\r\n\t</table>\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<div class=\"di-fbr-section\">\r\n\t\t{% if di_qr %}\r\n\t\t<div>\r\n\t\t\t<img src=\"{{ di_qr }}\" class=\"di-qr-code\" alt=\"FBR QR Code\" />\r\n\t\t</div>\r\n\t\t{% endif %}\r\n\t\t<div class=\"di-fbr-info\">\r\n\t\t\t<img src=\"/assets/di/images/fbrlogo.png\" style=\"height:30px;margin-bottom:5px;\" alt=\"FBR Logo\" />\r\n\t\t\t<p><strong>FBR Invoice Number:</strong> {{ doc.di_integration_id }}</p>\r\n\t\t\t<p><strong>Date & Time:</strong> {{ doc.di_posting_datetime }}</p>\r\n\t\t\t<p style=\"font-size:9px;margin-top:5px;\">\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 <strong>https://e.fbr.gov.pk</strong>\r\n\t\t\t</p>\r\n\t\t</div>\r\n\t</div>\r\n\t{% endif %}\r\n</div>\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<div class=\"di-print\">\r\n\t<div class=\"di-header\">\r\n\t\t<h2>{{ company.company_name }}</h2>\r\n\t\t<p>{{ di_settings.address or company.get(\"address\") or \"\" }}</p>\r\n\t\t<p><strong>Sales Tax Invoice</strong></p>\r\n\t</div>\r\n\r\n\t<table style=\"margin-bottom:10px;\">\r\n\t\t<tr>\r\n\t\t\t<td style=\"width:50%;border:none;vertical-align:top;padding:0 10px 0 0;\">\r\n\t\t\t\t<table class=\"di-info-table\">\r\n\t\t\t\t\t<tr><td colspan=\"2\" style=\"font-weight:bold;font-size:12px;\">Seller Details</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Business Name:</td><td>{{ company.company_name }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">NTN/CNIC:</td><td>{{ di_settings.ntn_cnic or company.get(\"tax_id\") or \"\" }}</td></tr>\r\n\t\t\t\t\t{% if di_settings.strn_no %}<tr><td class=\"label-col\">STRN:</td><td>{{ di_settings.strn_no }}</td></tr>{% endif %}\r\n\t\t\t\t\t<tr><td class=\"label-col\">Province:</td><td>{{ di_settings.province or \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Address:</td><td>{{ di_settings.address or \"\" }}</td></tr>\r\n\t\t\t\t</table>\r\n\t\t\t</td>\r\n\t\t\t<td style=\"width:50%;border:none;vertical-align:top;padding:0 0 0 10px;\">\r\n\t\t\t\t<table class=\"di-info-table\">\r\n\t\t\t\t\t<tr><td colspan=\"2\" style=\"font-weight:bold;font-size:12px;\">Buyer Details</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Business Name:</td><td>{{ doc.customer_name or \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">NTN/CNIC:</td><td>{{ customer.ntn_cnic if customer else \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Registration:</td><td>{{ customer.registration_type if customer else \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Province:</td><td>{{ customer.province if customer else \"\" }}</td></tr>\r\n\t\t\t\t\t<tr><td class=\"label-col\">Address:</td><td>{{ customer.di_address if customer else \"\" }}</td></tr>\r\n\t\t\t\t</table>\r\n\t\t\t</td>\r\n\t\t</tr>\r\n\t</table>\r\n\r\n\t<table class=\"di-info-table\" style=\"margin-bottom:10px;\">\r\n\t\t<tr>\r\n\t\t\t<td class=\"label-col\">Invoice No:</td><td>{{ doc.name }}</td>\r\n\t\t\t<td class=\"label-col\">Date:</td><td>{{ doc.posting_date }}</td>\r\n\t\t\t{% if doc.di_integration_id %}\r\n\t\t\t<td class=\"label-col\">FBR Invoice No:</td><td>{{ doc.di_integration_id }}</td>\r\n\t\t\t{% endif %}\r\n\t\t</tr>\r\n\t</table>\r\n\r\n\t<!-- Items Table -->\r\n\t<table>\r\n\t\t<thead>\r\n\t\t\t<tr>\r\n\t\t\t\t<th style=\"width:30px;\">#</th>\r\n\t\t\t\t<th>HS Code</th>\r\n\t\t\t\t<th>Description</th>\r\n\t\t\t\t<th>Sale Type</th>\r\n\t\t\t\t<th class=\"di-right\">Qty</th>\r\n\t\t\t\t<th>UOM</th>\r\n\t\t\t\t<th class=\"di-right\">Value (Excl. ST)</th>\r\n\t\t\t\t<th class=\"di-right\">ST Rate</th>\r\n\t\t\t\t<th class=\"di-right\">Sales Tax</th>\r\n\t\t\t\t<th class=\"di-right\">Further Tax</th>\r\n\t\t\t\t<th class=\"di-right\">FED</th>\r\n\t\t\t\t<th class=\"di-right\">Total</th>\r\n\t\t\t</tr>\r\n\t\t</thead>\r\n\t\t<tbody>\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<tr>\r\n\t\t\t\t<td>{{ item.idx }}</td>\r\n\t\t\t\t<td>{{ item.di_hs_code or \"\" }}</td>\r\n\t\t\t\t<td>{{ item.item_name }}</td>\r\n\t\t\t\t<td>{{ item.di_sale_type or \"\" }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ item.qty }}</td>\r\n\t\t\t\t<td>{{ item.di_hs_uom or item.uom or \"\" }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(item.base_net_amount, currency=base_currency) }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ st.rate }}%</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(st.amount, currency=base_currency) }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(ft.amount, currency=base_currency) }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money((item.di_fed_payable or 0) * (doc.conversion_rate or 1), currency=base_currency) }}</td>\r\n\t\t\t\t<td class=\"di-right\">{{ frappe.utils.fmt_money(item.base_amount, currency=base_currency) }}</td>\r\n\t\t\t</tr>\r\n\t\t\t{% endfor %}\r\n\t\t</tbody>\r\n\t</table>\r\n\r\n\t<!-- Totals -->\r\n\t<table style=\"width:50%;margin-left:auto;margin-top:10px;\">\r\n\t\t<tr class=\"di-totals\">\r\n\t\t\t<td style=\"border:none;\">Total Qty:</td>\r\n\t\t\t<td style=\"border:none;\">{{ doc.total_qty }}</td>\r\n\t\t</tr>\r\n\t\t<tr class=\"di-totals\">\r\n\t\t\t<td style=\"border:none;\">Net Total:</td>\r\n\t\t\t<td style=\"border:none;\">{{ frappe.utils.fmt_money(doc.base_net_total, currency=base_currency) }}</td>\r\n\t\t</tr>\r\n\t\t{% for tax in doc.taxes %}\r\n\t\t<tr class=\"di-totals\">\r\n\t\t\t<td style=\"border:none;\">{{ tax.description }}:</td>\r\n\t\t\t<td style=\"border:none;\">{{ frappe.utils.fmt_money(tax.base_tax_amount, currency=base_currency) }}</td>\r\n\t\t</tr>\r\n\t\t{% endfor %}\r\n\t\t<tr class=\"di-totals\" style=\"font-size:13px;\">\r\n\t\t\t<td style=\"border-top:2px solid #333;border-bottom:none;border-left:none;border-right:none;\">Grand Total:</td>\r\n\t\t\t<td style=\"border-top:2px solid #333;border-bottom:none;border-left:none;border-right:none;\">{{ frappe.utils.fmt_money(doc.base_grand_total, currency=base_currency) }}</td>\r\n\t\t</tr>\r\n\t</table>\r\n\r\n\t<!-- FBR Section (only if posted) -->\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<div class=\"di-fbr-section\">\r\n\t\t{% if di_qr %}\r\n\t\t<div>\r\n\t\t\t<img src=\"{{ di_qr }}\" class=\"di-qr-code\" alt=\"FBR QR Code\" />\r\n\t\t</div>\r\n\t\t{% endif %}\r\n\t\t<div class=\"di-fbr-info\">\r\n\t\t\t<img src=\"/assets/di/images/fbrlogo.png\" style=\"height:30px;margin-bottom:5px;\" alt=\"FBR Logo\" />\r\n\t\t\t<p><strong>FBR Invoice Number:</strong> {{ doc.di_integration_id }}</p>\r\n\t\t\t<p><strong>Date & Time:</strong> {{ doc.di_posting_datetime }}</p>\r\n\t\t\t<p style=\"font-size:9px;margin-top:5px;\">\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 <strong>https://e.fbr.gov.pk</strong>\r\n\t\t\t</p>\r\n\t\t</div>\r\n\t</div>\r\n\t{% endif %}\r\n</div>\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-11 15:19:03.542899",
"modified_by": "Administrator",
"module": "Digital Invoicing",
"name": "DI Sales Tax Invoice",
Expand Down
9 changes: 7 additions & 2 deletions di/integrations/di_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions di/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Empty file added di/patches/v1_0/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions di/patches/v1_0/rename_advance_tax_to_extra_tax.py
Original file line number Diff line number Diff line change
@@ -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()
Loading