diff --git a/ram_custom/patches.txt b/ram_custom/patches.txt
index 0c3c3b5..47df9c5 100644
--- a/ram_custom/patches.txt
+++ b/ram_custom/patches.txt
@@ -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
diff --git a/ram_custom/patches/v0_0_4/__init__.py b/ram_custom/patches/v0_0_4/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ram_custom/patches/v0_0_4/fix_po_print_vat_rate.py b/ram_custom/patches/v0_0_4/fix_po_print_vat_rate.py
new file mode 100644
index 0000000..cc483da
--- /dev/null
+++ b/ram_custom/patches/v0_0_4/fix_po_print_vat_rate.py
@@ -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 = '
15% | '
+NEW_RATE = '{{ "%g"|format(vat.rate) }}% | '
+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)
diff --git a/ram_custom/tests/test_po_print_vat.py b/ram_custom/tests/test_po_print_vat.py
new file mode 100644
index 0000000..8e60bf8
--- /dev/null
+++ b/ram_custom/tests/test_po_print_vat.py
@@ -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('15% | ', 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))