diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index 872e939344c..1a7bc328155 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -24,15 +24,22 @@ frappe.ui.form.on("Opening Invoice Creation Tool", { setTimeout( () => { frm.doc.import_in_progress = false; - frm.clear_table("invoices"); - frm.refresh_fields(); frm.page.clear_indicator(); frm.dashboard.hide_progress(); - if (frm.doc.invoice_type == "Sales") { - frappe.msgprint(__("Opening Sales Invoices have been created.")); + if (!data.errors) { + frm.clear_table("invoices"); + frm.refresh_fields(); + const message = + frm.doc.invoice_type == "Sales" + ? __("Opening Sales Invoice(s) have been created.") + : __("Opening Purchase Invoice(s) have been created."); + frappe.show_alert({ + message: message, + indicator: "green", + }); } else { - frappe.msgprint(__("Opening Purchase Invoices have been created.")); + frm.refresh_fields(); } }, 1500, diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index b5c46f3688d..b4bbe1b869c 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -281,12 +281,20 @@ class OpeningInvoiceCreationTool(Document): def start_import(invoices): errors = 0 names = [] + total = len(invoices) for idx, d in enumerate(invoices): + # Scope each invoice to a savepoint so a failure only undoes that invoice. + # A plain rollback() would discard the whole transaction — including invoices + # imported earlier in this batch and the error logs of earlier failures (the + # latter only survive on mariadb because the Error Log table is MyISAM; on + # postgres they would be lost). Rolling back to a savepoint keeps both. + savepoint = f"opening_invoice_{frappe.generate_hash(length=8)}" + frappe.db.savepoint(savepoint) + is_last = idx == total - 1 try: invoice_number = None if d.invoice_number: invoice_number = d.invoice_number - publish(idx, len(invoices), d.doctype) doc = frappe.get_doc(d) doc.flags.ignore_mandatory = True doc.insert(set_name=invoice_number) @@ -294,10 +302,12 @@ def start_import(invoices): if not frappe.in_test: frappe.db.commit() names.append(doc.name) + publish(idx, total, d.doctype, errors=errors if is_last else None) except Exception: errors += 1 frappe.db.rollback() doc.log_error("Opening invoice creation failed") + publish(idx, total, d.doctype, errors=errors if is_last else None) if errors: frappe.msgprint( _("You had {} errors while creating opening invoices. Check {} for more details").format( @@ -309,7 +319,7 @@ def start_import(invoices): return names -def publish(index, total, doctype): +def publish(index, total, doctype, errors=None): frappe.publish_realtime( "opening_invoice_creation_progress", dict( @@ -317,6 +327,7 @@ def publish(index, total, doctype): message=_("Creating {} out of {} {}").format(index + 1, total, doctype), count=index + 1, total=total, + errors=errors, ), user=frappe.session.user, )