mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-17 18:45:20 +00:00
feat(selling): keep cancelled proformas visible with their PDF
Cancelling a proforma should void it, not erase history. - Persist the Cancelled status on cancel (db_set) and keep the PDF attached - Proforma tab now lists cancelled proformas with a red status badge, so the voided document and its PDF stay reachable for audit
This commit is contained in:
@@ -189,7 +189,8 @@ Object.assign(erpnext.proforma, {
|
|||||||
const list = new frappe.ui.EmbeddedList({
|
const list = new frappe.ui.EmbeddedList({
|
||||||
wrapper,
|
wrapper,
|
||||||
doctype: "Proforma Invoice",
|
doctype: "Proforma Invoice",
|
||||||
filters: { sales_order: frm.doc.name, docstatus: 1 },
|
// Include cancelled (docstatus 2) so voided proformas stay visible for audit.
|
||||||
|
filters: { sales_order: frm.doc.name, docstatus: ["in", [1, 2]] },
|
||||||
fields: ["name", "proforma_date", "grand_total", "status", "proforma_pdf", "sent_on", "currency"],
|
fields: ["name", "proforma_date", "grand_total", "status", "proforma_pdf", "sent_on", "currency"],
|
||||||
order_by: "creation desc",
|
order_by: "creation desc",
|
||||||
empty_message: __("No proforma invoices yet."),
|
empty_message: __("No proforma invoices yet."),
|
||||||
@@ -218,7 +219,7 @@ Object.assign(erpnext.proforma, {
|
|||||||
label: __("Status"),
|
label: __("Status"),
|
||||||
type: "badge",
|
type: "badge",
|
||||||
fieldname: "status",
|
fieldname: "status",
|
||||||
color: (row) => (row.status === "Issued" ? "green" : "gray"),
|
color: (row) => (row.status === "Cancelled" ? "red" : "green"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "actions",
|
type: "actions",
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class ProformaInvoice(Document):
|
|||||||
self.generate_and_attach_pdf()
|
self.generate_and_attach_pdf()
|
||||||
|
|
||||||
def on_cancel(self) -> None:
|
def on_cancel(self) -> None:
|
||||||
self.status = "Cancelled"
|
self.db_set("status", "Cancelled")
|
||||||
|
|
||||||
def set_total_qty(self) -> None:
|
def set_total_qty(self) -> None:
|
||||||
self.total_qty = sum(flt(item.qty) for item in self.items)
|
self.total_qty = sum(flt(item.qty) for item in self.items)
|
||||||
|
|||||||
@@ -80,6 +80,18 @@ class TestProformaInvoice(ERPNextTestSuite):
|
|||||||
self.assertEqual(flt(item.amount), 250)
|
self.assertEqual(flt(item.amount), 250)
|
||||||
self.assertEqual(flt(proforma.grand_total), 250)
|
self.assertEqual(flt(proforma.grand_total), 250)
|
||||||
|
|
||||||
|
def test_cancelled_proforma_keeps_pdf(self):
|
||||||
|
"""Cancelling voids the proforma but keeps its PDF and status for the audit trail."""
|
||||||
|
sales_order = make_sales_order(qty=10)
|
||||||
|
proforma = self.create_proforma(sales_order, [(sales_order.items[0].name, 4)])
|
||||||
|
pdf = proforma.proforma_pdf
|
||||||
|
self.assertTrue(pdf)
|
||||||
|
|
||||||
|
proforma.cancel()
|
||||||
|
proforma.reload()
|
||||||
|
self.assertEqual(proforma.status, "Cancelled")
|
||||||
|
self.assertEqual(proforma.proforma_pdf, pdf)
|
||||||
|
|
||||||
def test_feature_toggle_is_enforced(self):
|
def test_feature_toggle_is_enforced(self):
|
||||||
sales_order = make_sales_order(qty=10)
|
sales_order = make_sales_order(qty=10)
|
||||||
frappe.db.set_single_value("Selling Settings", "enable_proforma_invoice", 0)
|
frappe.db.set_single_value("Selling Settings", "enable_proforma_invoice", 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user