From 24dc1bf1a330aab5d60374f93037ab93d95f01c6 Mon Sep 17 00:00:00 2001 From: Diptanil Saha Date: Fri, 24 Jan 2025 22:51:22 +0530 Subject: [PATCH] fix: resolved pos return setting to default mode of payment instead of user selection (#45377) (#45436) * fix: resolved pos return setting to default mode of payment instead of user selection * refactor: removed console log statement * refactor: moved get_payment_data to sales_and_purchase_return.py --- .../controllers/sales_and_purchase_return.py | 71 +++++++++++++++---- .../public/js/controllers/taxes_and_totals.js | 41 ++++++++++- 2 files changed, 99 insertions(+), 13 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index b2a4a4e0f7e..839bd5e154f 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -75,7 +75,11 @@ def validate_returned_items(doc): if doc.doctype != "Purchase Invoice": select_fields += ",serial_no, batch_no" - if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]: + if doc.doctype in [ + "Purchase Invoice", + "Purchase Receipt", + "Subcontracting Receipt", + ]: select_fields += ",rejected_qty, received_qty" for d in frappe.db.sql( @@ -105,7 +109,12 @@ def validate_returned_items(doc): for d in doc.get("items"): key = d.item_code raise_exception = False - if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Sales Invoice", "POS Invoice"]: + if doc.doctype in [ + "Purchase Receipt", + "Purchase Invoice", + "Sales Invoice", + "POS Invoice", + ]: field = frappe.scrub(doc.doctype) + "_item" if d.get(field): key = (d.item_code, d.get(field)) @@ -175,7 +184,11 @@ def validate_returned_items(doc): def validate_quantity(doc, key, args, ref, valid_items, already_returned_items): fields = ["stock_qty"] - if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]: + if doc.doctype in [ + "Purchase Receipt", + "Purchase Invoice", + "Subcontracting Receipt", + ]: fields.extend(["received_qty", "rejected_qty"]) already_returned_data = already_returned_items.get(key) or {} @@ -203,7 +216,8 @@ def validate_quantity(doc, key, args, ref, valid_items, already_returned_items): frappe.throw(_("{0} must be negative in return document").format(label)) elif returned_qty >= reference_qty and args.get(column): frappe.throw( - _("Item {0} has already been returned").format(args.item_code), StockOverReturnError + _("Item {0} has already been returned").format(args.item_code), + StockOverReturnError, ) elif abs(flt(current_stock_qty, stock_qty_precision)) > max_returnable_qty: frappe.throw( @@ -242,7 +256,11 @@ def get_ref_item_dict(valid_items, ref_item_row): if ref_item_row.get("rate", 0) > item_dict["rate"]: item_dict["rate"] = ref_item_row.get("rate", 0) - if ref_item_row.parenttype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]: + if ref_item_row.parenttype in [ + "Purchase Invoice", + "Purchase Receipt", + "Subcontracting Receipt", + ]: item_dict["received_qty"] += ref_item_row.received_qty item_dict["rejected_qty"] += ref_item_row.rejected_qty @@ -257,7 +275,11 @@ def get_ref_item_dict(valid_items, ref_item_row): def get_already_returned_items(doc): column = "child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty" - if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]: + if doc.doctype in [ + "Purchase Invoice", + "Purchase Receipt", + "Subcontracting Receipt", + ]: column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty, sum(abs(child.received_qty) * child.conversion_factor) as received_qty""" @@ -384,7 +406,8 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None): paid_amount = 0.00 base_paid_amount = 0.00 data.base_amount = flt( - data.amount * source.conversion_rate, source.precision("base_paid_amount") + data.amount * source.conversion_rate, + source.precision("base_paid_amount"), ) paid_amount += data.amount base_paid_amount += data.base_amount @@ -544,10 +567,17 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None): }, doctype + " Item": { "doctype": doctype + " Item", - "field_map": {"serial_no": "serial_no", "batch_no": "batch_no", "bom": "bom"}, + "field_map": { + "serial_no": "serial_no", + "batch_no": "batch_no", + "bom": "bom", + }, "postprocess": update_item, }, - "Payment Schedule": {"doctype": "Payment Schedule", "postprocess": update_terms}, + "Payment Schedule": { + "doctype": "Payment Schedule", + "postprocess": update_terms, + }, }, target_doc, set_missing_values, @@ -580,13 +610,20 @@ def get_rate_for_return( item_row, ) - if voucher_type in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"): + if voucher_type in ( + "Purchase Receipt", + "Purchase Invoice", + "Subcontracting Receipt", + ): select_field = "incoming_rate" else: select_field = "abs(stock_value_difference / actual_qty)" rate = flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field)) - if not (rate and return_against) and voucher_type in ["Sales Invoice", "Delivery Note"]: + if not (rate and return_against) and voucher_type in [ + "Sales Invoice", + "Delivery Note", + ]: rate = frappe.db.get_value(f"{voucher_type} Item", voucher_detail_no, "incoming_rate") if not rate and sle: @@ -629,7 +666,11 @@ def get_filters( return_against_item_field, item_row, ): - filters = {"voucher_type": voucher_type, "voucher_no": return_against, "item_code": item_code} + filters = { + "voucher_type": voucher_type, + "voucher_no": return_against, + "item_code": item_code, + } if item_row: reference_voucher_detail_no = item_row.get(return_against_item_field) @@ -669,3 +710,9 @@ def get_returned_serial_nos(child_doc, parent_doc, serial_no_field="serial_no"): serial_nos.extend(get_serial_nos(row.get(serial_no_field))) return serial_nos + + +@frappe.whitelist() +def get_payment_data(invoice): + payment = frappe.db.get_all("Sales Invoice Payment", {"parent": invoice}, ["mode_of_payment", "amount"]) + return payment diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 8ca72d55c23..f715aa8836e 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -806,7 +806,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } } - set_total_amount_to_default_mop() { + async set_total_amount_to_default_mop() { let grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total; let base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total; @@ -828,6 +828,45 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { ); } + /* + During returns, if an user select mode of payment other than + default mode of payment, it should retain the user selection + instead resetting it to default mode of payment. + */ + + let payment_amount = 0; + this.frm.doc.payments.forEach(payment => { + payment_amount += payment.amount + }); + + if (payment_amount == total_amount_to_pay) { + return; + } + + /* + For partial return, if the payment was made using single mode of payment + it should set the return to that mode of payment only. + */ + + let return_against_mop = await frappe.call({ + method: 'erpnext.controllers.sales_and_purchase_return.get_payment_data', + args: { + invoice: this.frm.doc.return_against + } + }); + + if (return_against_mop.message.length === 1) { + this.frm.doc.payments.forEach(payment => { + if (payment.mode_of_payment == return_against_mop.message[0].mode_of_payment) { + payment.amount = total_amount_to_pay; + } else { + payment.amount = 0; + } + }); + this.frm.refresh_fields(); + return; + } + this.frm.doc.payments.find(payment => { if (payment.default) { payment.amount = total_amount_to_pay;