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
1 change: 1 addition & 0 deletions ram_custom/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ ram_custom.patches.v0_0_2.seed_ict_reconciled_cost_value
ram_custom.patches.v0_0_2.seed_ict_uom_split_rates
ram_custom.patches.v0_0_3.fix_delivery_note_return_status
ram_custom.patches.v0_0_3.merge_duplicate_address_arabic_fields
ram_custom.patches.v0_0_4.fix_po_print_vat_rate
Empty file.
37 changes: 37 additions & 0 deletions ram_custom/patches/v0_0_4/fix_po_print_vat_rate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Purchase Order prints hard-coded "15%" and amount * 0.15 on every line, so a
# zero-rated purchase (e.g. an import taxed with "VAT 0%") still printed 15% and a
# VAT amount per line while the document total correctly said 0.00.
#
# The two custom PO print formats now take the rate from the document's own
# "On Net Total" tax rows, so 15% stays 15% and a zero-rated order prints 0%.
# Only those two lines and one added variable are touched; idempotent, and
# `modified` is left alone.

import frappe

FORMATS = ("Purchase order new format", "Purchase Order Print Format")

RATE_SET = (
'{% set vat = namespace(rate=0) %}'
'{% for t in doc.taxes %}{% if t.charge_type == "On Net Total" %}'
'{% set vat.rate = vat.rate + (t.rate or 0) %}{% endif %}{% endfor %}\n'
" {% for item in doc.items %}"
)
OLD_LOOP = " {% for item in doc.items %}"
OLD_RATE = '<td class="text-center">15%</td>'
NEW_RATE = '<td class="text-center">{{ "%g"|format(vat.rate) }}%</td>'
OLD_AMOUNT = '"%.2f"|format((item.amount or 0) * 0.15)'
NEW_AMOUNT = '"%.2f"|format((item.amount or 0) * vat.rate / 100)'


def execute():
for name in FORMATS:
html = frappe.db.get_value("Print Format", name, "html")
if not html or OLD_RATE not in html or OLD_AMOUNT not in html or "namespace(rate=0)" in html:
continue
if html.count(OLD_LOOP) != 1:
frappe.log_error(f"{name}: unexpected item loop count, left unchanged", "PO print VAT patch")
continue
html = html.replace(OLD_LOOP, " " + RATE_SET.lstrip(), 1)
html = html.replace(OLD_RATE, NEW_RATE).replace(OLD_AMOUNT, NEW_AMOUNT)
frappe.db.set_value("Print Format", name, "html", html, update_modified=False)
48 changes: 48 additions & 0 deletions ram_custom/tests/test_po_print_vat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright (c) 2026, Enfono Technologies and contributors
# For license information, please see license.txt

import frappe
from frappe.tests.utils import FrappeTestCase


class TestPurchaseOrderPrintVat(FrappeTestCase):
"""The PO print follows the document's own tax rate, not a hard-coded 15%."""

FORMAT = "Purchase order new format"

def _po(self, zero_rated):
rate_filter = ["=", 0] if zero_rated else [">", 0]
rows = frappe.get_all(
"Purchase Taxes and Charges",
filters={"parenttype": "Purchase Order", "charge_type": "On Net Total", "rate": rate_filter, "docstatus": ["<", 2]},
fields=["parent"],
limit=1,
)
return rows[0].parent if rows else None

def _html(self, po):
if not frappe.db.exists("Print Format", self.FORMAT):
self.skipTest(f"{self.FORMAT} is not installed on this site")
return frappe.get_print("Purchase Order", po, print_format=self.FORMAT)

def test_no_hardcoded_rate_left(self):
html = frappe.db.get_value("Print Format", self.FORMAT, "html") or ""
self.assertNotIn("* 0.15", html)
self.assertNotIn('<td class="text-center">15%</td>', html)

def test_zero_rated_order_prints_zero(self):
po = self._po(zero_rated=True)
if not po:
self.skipTest("no zero-rated Purchase Order")
html = self._html(po)
self.assertIn("0%", html)
self.assertNotIn("15%", html)

def test_standard_rated_order_still_prints_fifteen(self):
po = self._po(zero_rated=False)
if not po:
self.skipTest("no standard-rated Purchase Order")
rate = frappe.db.get_value(
"Purchase Taxes and Charges", {"parent": po, "charge_type": "On Net Total"}, "rate"
)
self.assertIn(f"{rate:g}%", self._html(po))
Loading