From 9d27a25e5f2335386402f96f83a10729695b20c8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 19 Jun 2023 11:04:50 +0530 Subject: [PATCH 1/4] fix: Allocated amount validation for other party types (#35741) * fix: Allocated amount validation for other party types * chore: Validation for return allocations * chore: minor typo --------- Co-authored-by: anandbaburajan --- .../doctype/payment_entry/payment_entry.py | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index b6d3e5a30e..b9be5ec724 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -151,6 +151,19 @@ class PaymentEntry(AccountsController): if self.payment_type == "Internal Transfer": return + if self.party_type in ("Customer", "Supplier"): + self.validate_allocated_amount_with_latest_data() + else: + fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") + for d in self.get("references"): + if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(d.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) + + # Check for negative outstanding invoices as well + if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(d.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) + + def validate_allocated_amount_with_latest_data(self): latest_references = get_outstanding_reference_documents( { "posting_date": self.posting_date, @@ -168,7 +181,7 @@ class PaymentEntry(AccountsController): d = frappe._dict(d) latest_lookup.update({(d.voucher_type, d.voucher_no): d}) - for d in self.get("references").copy(): + for d in self.get("references"): latest = latest_lookup.get((d.reference_doctype, d.reference_name)) # The reference has already been fully paid @@ -187,18 +200,14 @@ class PaymentEntry(AccountsController): ).format(d.reference_doctype, d.reference_name) ) - d.outstanding_amount = latest.outstanding_amount - fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") - if (flt(d.allocated_amount)) > 0: - if flt(d.allocated_amount) > flt(d.outstanding_amount): - frappe.throw(fail_message.format(d.idx)) + if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) # Check for negative outstanding invoices as well - if flt(d.allocated_amount) < 0: - if flt(d.allocated_amount) < flt(d.outstanding_amount): - frappe.throw(fail_message.format(d.idx)) + if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) def delink_advance_entry_references(self): for reference in self.references: From 4f941ac5c07a60aa52d1a1ddfc37e55ec3bea886 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 19 Jun 2023 18:26:14 +0530 Subject: [PATCH 2/4] perf: index `purpose` in `Stock Entry` --- erpnext/stock/doctype/stock_entry/stock_entry.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index fe42b1f135..564c380017 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -125,7 +125,8 @@ "oldfieldname": "purpose", "oldfieldtype": "Select", "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor", - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "company", @@ -678,7 +679,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-16 14:59:10.917235", + "modified": "2023-06-19 18:23:40.748114", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", From c1da3ddbbf5e57a0f96733ef05682056051f9ebd Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Mon, 19 Jun 2023 19:53:05 +0530 Subject: [PATCH 3/4] fix: fix get outstanding invoices btn and add get outstanding orders btn (#35776) * fix: fix get outstanding invoices btn and add get outstanding orders btn * chore: remove unnecessary arg --- .../doctype/payment_entry/payment_entry.js | 32 +++++- .../doctype/payment_entry/payment_entry.json | 23 ++-- .../doctype/payment_entry/payment_entry.py | 103 ++++++++++-------- 3 files changed, 102 insertions(+), 56 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2843824934..9f55ba1167 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -612,7 +612,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); }, - get_outstanding_invoice: function(frm) { + get_outstanding_invoices_or_orders: function(frm, get_outstanding_invoices, get_orders_to_be_billed) { const today = frappe.datetime.get_today(); const fields = [ {fieldtype:"Section Break", label: __("Posting Date")}, @@ -642,12 +642,29 @@ frappe.ui.form.on('Payment Entry', { {fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1}, ]; + let btn_text = ""; + + if (get_outstanding_invoices) { + btn_text = "Get Outstanding Invoices"; + } + else if (get_orders_to_be_billed) { + btn_text = "Get Outstanding Orders"; + } + frappe.prompt(fields, function(filters){ frappe.flags.allocate_payment_amount = true; frm.events.validate_filters_data(frm, filters); frm.doc.cost_center = filters.cost_center; - frm.events.get_outstanding_documents(frm, filters); - }, __("Filters"), __("Get Outstanding Documents")); + frm.events.get_outstanding_documents(frm, filters, get_outstanding_invoices, get_orders_to_be_billed); + }, __("Filters"), __(btn_text)); + }, + + get_outstanding_invoices: function(frm) { + frm.events.get_outstanding_invoices_or_orders(frm, true, false); + }, + + get_outstanding_orders: function(frm) { + frm.events.get_outstanding_invoices_or_orders(frm, false, true); }, validate_filters_data: function(frm, filters) { @@ -673,7 +690,7 @@ frappe.ui.form.on('Payment Entry', { } }, - get_outstanding_documents: function(frm, filters) { + get_outstanding_documents: function(frm, filters, get_outstanding_invoices, get_orders_to_be_billed) { frm.clear_table("references"); if(!frm.doc.party) { @@ -697,6 +714,13 @@ frappe.ui.form.on('Payment Entry', { args[key] = filters[key]; } + if (get_outstanding_invoices) { + args["get_outstanding_invoices"] = true; + } + else if (get_orders_to_be_billed) { + args["get_orders_to_be_billed"] = true; + } + frappe.flags.allocate_payment_amount = filters['allocate_payment_amount']; return frappe.call({ diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 3927ecae43..6224d4038d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -48,7 +48,8 @@ "base_received_amount", "base_received_amount_after_tax", "section_break_14", - "get_outstanding_invoice", + "get_outstanding_invoices", + "get_outstanding_orders", "references", "section_break_34", "total_allocated_amount", @@ -355,12 +356,6 @@ "fieldtype": "Section Break", "label": "Reference" }, - { - "depends_on": "eval:doc.docstatus==0", - "fieldname": "get_outstanding_invoice", - "fieldtype": "Button", - "label": "Get Outstanding Invoice" - }, { "fieldname": "references", "fieldtype": "Table", @@ -728,12 +723,24 @@ "fieldname": "section_break_60", "fieldtype": "Section Break", "hide_border": 1 + }, + { + "depends_on": "eval:doc.docstatus==0", + "fieldname": "get_outstanding_invoices", + "fieldtype": "Button", + "label": "Get Outstanding Invoices" + }, + { + "depends_on": "eval:doc.docstatus==0", + "fieldname": "get_outstanding_orders", + "fieldtype": "Button", + "label": "Get Outstanding Orders" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-02-14 04:52:30.478523", + "modified": "2023-06-19 11:38:04.387219", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index b9be5ec724..1f23fe1d54 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -172,6 +172,8 @@ class PaymentEntry(AccountsController): "payment_type": self.payment_type, "party": self.party, "party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to, + "get_outstanding_invoices": True, + "get_orders_to_be_billed": True, } ) @@ -196,7 +198,7 @@ class PaymentEntry(AccountsController): ): frappe.throw( _( - "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' button to get the latest outstanding amount." + "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts." ).format(d.reference_doctype, d.reference_name) ) @@ -1356,62 +1358,75 @@ def get_outstanding_reference_documents(args): condition += " and company = {0}".format(frappe.db.escape(args.get("company"))) common_filter.append(ple.company == args.get("company")) - outstanding_invoices = get_outstanding_invoices( - args.get("party_type"), - args.get("party"), - args.get("party_account"), - common_filter=common_filter, - posting_date=posting_and_due_date, - min_outstanding=args.get("outstanding_amt_greater_than"), - max_outstanding=args.get("outstanding_amt_less_than"), - accounting_dimensions=accounting_dimensions_filter, - ) - - outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices) - - for d in outstanding_invoices: - d["exchange_rate"] = 1 - if party_account_currency != company_currency: - if d.voucher_type in frappe.get_hooks("invoice_doctypes"): - d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate") - elif d.voucher_type == "Journal Entry": - d["exchange_rate"] = get_exchange_rate( - party_account_currency, company_currency, d.posting_date - ) - if d.voucher_type in ("Purchase Invoice"): - d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no") - - # Get all SO / PO which are not fully billed or against which full advance not paid - orders_to_be_billed = [] - orders_to_be_billed = get_orders_to_be_billed( - args.get("posting_date"), - args.get("party_type"), - args.get("party"), - args.get("company"), - party_account_currency, - company_currency, - filters=args, - ) - - # Get negative outstanding sales /purchase invoices + outstanding_invoices = [] negative_outstanding_invoices = [] - if args.get("party_type") != "Employee" and not args.get("voucher_no"): - negative_outstanding_invoices = get_negative_outstanding_invoices( + + if args.get("get_outstanding_invoices"): + outstanding_invoices = get_outstanding_invoices( args.get("party_type"), args.get("party"), args.get("party_account"), + common_filter=common_filter, + posting_date=posting_and_due_date, + min_outstanding=args.get("outstanding_amt_greater_than"), + max_outstanding=args.get("outstanding_amt_less_than"), + accounting_dimensions=accounting_dimensions_filter, + ) + + outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices) + + for d in outstanding_invoices: + d["exchange_rate"] = 1 + if party_account_currency != company_currency: + if d.voucher_type in frappe.get_hooks("invoice_doctypes"): + d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate") + elif d.voucher_type == "Journal Entry": + d["exchange_rate"] = get_exchange_rate( + party_account_currency, company_currency, d.posting_date + ) + if d.voucher_type in ("Purchase Invoice"): + d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no") + + # Get negative outstanding sales /purchase invoices + if args.get("party_type") != "Employee" and not args.get("voucher_no"): + negative_outstanding_invoices = get_negative_outstanding_invoices( + args.get("party_type"), + args.get("party"), + args.get("party_account"), + party_account_currency, + company_currency, + condition=condition, + ) + + # Get all SO / PO which are not fully billed or against which full advance not paid + orders_to_be_billed = [] + if args.get("get_orders_to_be_billed"): + orders_to_be_billed = get_orders_to_be_billed( + args.get("posting_date"), + args.get("party_type"), + args.get("party"), + args.get("company"), party_account_currency, company_currency, - condition=condition, + filters=args, ) data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed if not data: + if args.get("get_outstanding_invoices") and args.get("get_orders_to_be_billed"): + ref_document_type = "invoices or orders" + elif args.get("get_outstanding_invoices"): + ref_document_type = "invoices" + elif args.get("get_orders_to_be_billed"): + ref_document_type = "orders" + frappe.msgprint( _( - "No outstanding invoices found for the {0} {1} which qualify the filters you have specified." - ).format(_(args.get("party_type")).lower(), frappe.bold(args.get("party"))) + "No outstanding {0} found for the {1} {2} which qualify the filters you have specified." + ).format( + ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party")) + ) ) return data From 4fbff2095447f12e6941d39b156b50fa4f1c7b80 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 19 Jun 2023 21:14:42 +0530 Subject: [PATCH 4/4] fix: make credit note and debit note exclusive (#35781) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 7b68dd41d9..ab4aab3da2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -320,6 +320,7 @@ }, { "default": "0", + "depends_on": "eval: !doc.is_debit_note", "fieldname": "is_return", "fieldtype": "Check", "hide_days": 1, @@ -1960,6 +1961,7 @@ }, { "default": "0", + "depends_on": "eval: !doc.is_return", "description": "Issue a debit note with 0 qty against an existing Sales Invoice", "fieldname": "is_debit_note", "fieldtype": "Check", @@ -2155,7 +2157,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2023-06-03 16:22:16.219333", + "modified": "2023-06-19 16:02:05.309332", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice",