From b6165844ed1314fbe40370fd3a2ae8cf9ab74986 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 29 Jun 2026 20:39:23 +0530 Subject: [PATCH 1/4] fix(buying): savepoint Subcontracting Order submit in make_subcontracting_order (Postgres) target_doc.submit() is wrapped in except Exception whose handler calls add_comment (a Comment insert). On Postgres a failed submit poisons the transaction so the add_comment insert raises InFailedSqlTransaction; MariaDB logs the comment. Savepoint + rollback before add_comment. No-op on MariaDB. --- erpnext/buying/doctype/purchase_order/mapper.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/mapper.py b/erpnext/buying/doctype/purchase_order/mapper.py index 468ab3e2e5d..1aa3d2c6eac 100644 --- a/erpnext/buying/doctype/purchase_order/mapper.py +++ b/erpnext/buying/doctype/purchase_order/mapper.py @@ -232,9 +232,11 @@ def make_subcontracting_order( target_doc.save() if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): + frappe.db.savepoint("submit_subcontracting_order") try: target_doc.submit() except Exception as e: + frappe.db.rollback(save_point="submit_subcontracting_order") target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) if notify: From 864fe50b243fefbc5e023b017119d136957e44d2 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 29 Jun 2026 20:39:23 +0530 Subject: [PATCH 2/4] fix(subcontracting): savepoint Purchase Receipt submit in make_purchase_receipt (Postgres) Same submit()/add_comment-in-except shape as the PO->SCO mapper: on Postgres a failed submit aborts the transaction so the follow-on Comment insert raises InFailedSqlTransaction; MariaDB continues. Savepoint + rollback before add_comment. No-op on MariaDB. --- erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py b/erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py index 4927d6723c0..bf5fbd5775a 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py @@ -125,9 +125,11 @@ def make_purchase_receipt( target_doc.save() if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): + frappe.db.savepoint("submit_subcontracting_receipt") try: target_doc.submit() except Exception as e: + frappe.db.rollback(save_point="submit_subcontracting_receipt") target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) if notify: From 2b966b69cef99c5c0ecafc1f796ba967f50dbc66 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 29 Jun 2026 20:39:24 +0530 Subject: [PATCH 3/4] fix(stock): savepoint per-voucher accounting repost submit (Postgres) make_reposting_for_accounting_ledgers submits a new Repost Item Valuation per voucher in a loop under except Exception. On Postgres a failed submit aborts the transaction so the next iteration's DB work dies with InFailedSqlTransaction; MariaDB continues. Savepoint per iteration, roll back on failure. No-op on MariaDB. --- .../doctype/repost_item_valuation/repost_item_valuation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 4be57de747a..b9bb3d931da 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -779,6 +779,7 @@ def make_reposting_for_accounting_ledgers(transactions, company, repost_doc): if reposting_map.get((voucher_type, voucher_no)): continue + frappe.db.savepoint("repost_accounting_ledger") try: new_repost_doc = frappe.new_doc("Repost Item Valuation") new_repost_doc.company = company @@ -789,7 +790,7 @@ def make_reposting_for_accounting_ledgers(transactions, company, repost_doc): new_repost_doc.flags.ignore_permissions = True new_repost_doc.submit() except Exception: - pass + frappe.db.rollback(save_point="repost_accounting_ledger") def get_existing_reposting_only_gl_entries(reposting_reference): From 0978d0304fbf4e0f54012d34594c608924ab574c Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 29 Jun 2026 20:39:25 +0530 Subject: [PATCH 4/4] test(manufacturing): savepoint duplicate Routing insert in create_routing (Postgres) create_routing inserts a Routing and, on DuplicateEntryError, re-fetches and updates. On Postgres the failed insert aborts the transaction so the get_doc/save in the except raises InFailedSqlTransaction; MariaDB recovers. Savepoint + rollback before the fallback path. --- erpnext/manufacturing/doctype/routing/test_routing.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/manufacturing/doctype/routing/test_routing.py b/erpnext/manufacturing/doctype/routing/test_routing.py index 4575c1d1d57..1cec3b657b4 100644 --- a/erpnext/manufacturing/doctype/routing/test_routing.py +++ b/erpnext/manufacturing/doctype/routing/test_routing.py @@ -102,9 +102,11 @@ def create_routing(**args): doc.update(args) if not args.do_not_save: + frappe.db.savepoint("create_routing") try: doc.insert() except frappe.DuplicateEntryError: + frappe.db.rollback(save_point="create_routing") doc = frappe.get_doc("Routing", args.routing_name) doc.delete_key("operations") for operation in args.operations: