From 63aaa1e357280b24c537a502a479f7bb7a6654e4 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Wed, 12 Jan 2022 16:10:57 +0530 Subject: [PATCH 1/5] fix: consider returned_qty while updating billed_amt --- .../doctype/delivery_note/delivery_note.py | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 70d48a42d72a..1573512fc659 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -335,15 +335,25 @@ def make_return_invoice(self): def update_billed_amount_based_on_so(so_detail, update_modified=True): # Billed against Sales Order directly - billed_against_so = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item` - where so_detail=%s and (dn_detail is null or dn_detail = '') and docstatus=1""", so_detail) + billed_against_so = frappe.db.sql("""select sum(si_item.amount) + from `tabSales Invoice Item` si_item, `tabSales Invoice` si + where + si_item.parent = si.name + and si_item.so_detail=%s + and (si_item.dn_detail is null or si_item.dn_detail = '') + and si_item.docstatus=1 + and si.update_stock = 0 + """, so_detail) billed_against_so = billed_against_so and billed_against_so[0][0] or 0 # Get all Delivery Note Item rows against the Sales Order Item row - dn_details = frappe.db.sql("""select dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent + dn_details = frappe.db.sql("""select dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent, dn_item.stock_qty, dn_item.returned_qty from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn - where dn.name=dn_item.parent and dn_item.so_detail=%s - and dn.docstatus=1 and dn.is_return = 0 + where + dn.name = dn_item.parent + and dn_item.so_detail=%s + and dn.docstatus=1 + and dn.is_return = 0 order by dn.posting_date asc, dn.posting_time asc, dn.name asc""", so_detail, as_dict=1) updated_dn = [] @@ -362,7 +372,11 @@ def update_billed_amount_based_on_so(so_detail, update_modified=True): # Distribute billed amount directly against SO between DNs based on FIFO if billed_against_so and billed_amt_agianst_dn < dnd.amount: - pending_to_bill = flt(dnd.amount) - billed_amt_agianst_dn + if dnd.returned_qty: + pending_to_bill = flt(dnd.amount) * (dnd.stock_qty - dnd.returned_qty) / dnd.stock_qty + else: + pending_to_bill = flt(dnd.amount) + pending_to_bill -= billed_amt_agianst_dn if pending_to_bill <= billed_against_so: billed_amt_agianst_dn += pending_to_bill billed_against_so -= pending_to_bill From 5de6b8dc4df407fd953efe69640e22bd4ea90b6e Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Wed, 12 Jan 2022 16:13:06 +0530 Subject: [PATCH 2/5] fix: check so_detail before dn_detail --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 772e8c4e8723..5b6d39151197 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1256,14 +1256,14 @@ def make_gle_for_rounding_adjustment(self, gl_entries): def update_billing_status_in_dn(self, update_modified=True): updated_delivery_notes = [] for d in self.get("items"): - if d.dn_detail: + if d.so_detail: + updated_delivery_notes += update_billed_amount_based_on_so(d.so_detail, update_modified) + elif d.dn_detail: billed_amt = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item` where dn_detail=%s and docstatus=1""", d.dn_detail) billed_amt = billed_amt and billed_amt[0][0] or 0 frappe.db.set_value("Delivery Note Item", d.dn_detail, "billed_amt", billed_amt, update_modified=update_modified) updated_delivery_notes.append(d.delivery_note) - elif d.so_detail: - updated_delivery_notes += update_billed_amount_based_on_so(d.so_detail, update_modified) for dn in set(updated_delivery_notes): frappe.get_doc("Delivery Note", dn).update_billing_percentage(update_modified=update_modified) From fc65a3d9895c8ba9de957da820ed6b59c6c1bcbd Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 14 Jan 2022 16:15:26 +0530 Subject: [PATCH 3/5] feat: add patch --- erpnext/patches.txt | 1 + .../v13_0/set_billed_amount_in_returned_dn.py | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 erpnext/patches/v13_0/set_billed_amount_in_returned_dn.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 18319f70c47a..e8619a00bb94 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -341,3 +341,4 @@ erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 erpnext.patches.v13_0.update_tax_category_for_rcm erpnext.patches.v13_0.convert_to_website_item_in_item_card_group_template erpnext.patches.v13_0.agriculture_deprecation_warning +erpnext.patches.v13_0.set_billed_amount_in_returned_dn \ No newline at end of file diff --git a/erpnext/patches/v13_0/set_billed_amount_in_returned_dn.py b/erpnext/patches/v13_0/set_billed_amount_in_returned_dn.py new file mode 100644 index 000000000000..1f86c76d14f7 --- /dev/null +++ b/erpnext/patches/v13_0/set_billed_amount_in_returned_dn.py @@ -0,0 +1,22 @@ +# Copyright (c) 2022, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + +from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so + + +def execute(): + dn_item = frappe.qb.DocType('Delivery Note Item') + + so_detail_list = (frappe.qb.from_(dn_item) + .select(dn_item.so_detail) + .where( + (dn_item.so_detail.notnull()) & + (dn_item.so_detail != '') & + (dn_item.docstatus == 1) & + (dn_item.returned_qty > 0) + )).run() + + for so_detail in so_detail_list: + update_billed_amount_based_on_so(so_detail[0], False) \ No newline at end of file From 0a9ec9f591f8b4d0e630a3c902b69c9996f080dd Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 14 Jan 2022 19:21:52 +0530 Subject: [PATCH 4/5] refactor: use frappe.qb instead of sql --- .../doctype/delivery_note/delivery_note.py | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 1573512fc659..b9e1a420c1ab 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -334,27 +334,35 @@ def make_return_invoice(self): frappe.throw(_("Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again")) def update_billed_amount_based_on_so(so_detail, update_modified=True): + from frappe.query_builder.functions import Sum + # Billed against Sales Order directly - billed_against_so = frappe.db.sql("""select sum(si_item.amount) - from `tabSales Invoice Item` si_item, `tabSales Invoice` si - where - si_item.parent = si.name - and si_item.so_detail=%s - and (si_item.dn_detail is null or si_item.dn_detail = '') - and si_item.docstatus=1 - and si.update_stock = 0 - """, so_detail) + si = frappe.qb.DocType("Sales Invoice").as_("si") + si_item = frappe.qb.DocType("Sales Invoice Item").as_("si_item") + sum_amount = Sum(si_item.amount).as_("amount") + + billed_against_so = frappe.qb.from_(si).from_(si_item).select(sum_amount).where( + (si_item.parent == si.name) & + (si_item.so_detail == so_detail) & + ((si_item.dn_detail.isnull()) | (si_item.dn_detail == '')) & + (si_item.docstatus == 1) & + (si.update_stock == 0) + ).run() billed_against_so = billed_against_so and billed_against_so[0][0] or 0 # Get all Delivery Note Item rows against the Sales Order Item row - dn_details = frappe.db.sql("""select dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent, dn_item.stock_qty, dn_item.returned_qty - from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn - where - dn.name = dn_item.parent - and dn_item.so_detail=%s - and dn.docstatus=1 - and dn.is_return = 0 - order by dn.posting_date asc, dn.posting_time asc, dn.name asc""", so_detail, as_dict=1) + + dn = frappe.qb.DocType("Delivery Note").as_("dn") + dn_item = frappe.qb.DocType("Delivery Note Item").as_("dn_item") + + dn_details = frappe.qb.from_(dn).from_(dn_item).select(dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent, dn_item.stock_qty, dn_item.returned_qty).where( + (dn.name == dn_item.parent) & + (dn_item.so_detail == so_detail) & + (dn.docstatus == 1) & + (dn.is_return == 0) + ).orderby( + dn.posting_date, dn.posting_time, dn.name + ).run(as_dict=True) updated_dn = [] for dnd in dn_details: From fedeb2a70f80a39d31cb928d9876fbc94f27561c Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Mon, 31 Jan 2022 19:07:07 +0530 Subject: [PATCH 5/5] chore: remove patch --- erpnext/patches.txt | 1 - .../v13_0/set_billed_amount_in_returned_dn.py | 22 ------------------- 2 files changed, 23 deletions(-) delete mode 100644 erpnext/patches/v13_0/set_billed_amount_in_returned_dn.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4d3b5f913ca8..029b603debb4 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -342,7 +342,6 @@ erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 erpnext.patches.v13_0.update_tax_category_for_rcm erpnext.patches.v13_0.convert_to_website_item_in_item_card_group_template erpnext.patches.v13_0.agriculture_deprecation_warning -erpnext.patches.v13_0.set_billed_amount_in_returned_dn erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit erpnext.patches.v13_0.hospitality_deprecation_warning erpnext.patches.v13_0.delete_bank_reconciliation_detail diff --git a/erpnext/patches/v13_0/set_billed_amount_in_returned_dn.py b/erpnext/patches/v13_0/set_billed_amount_in_returned_dn.py deleted file mode 100644 index 1f86c76d14f7..000000000000 --- a/erpnext/patches/v13_0/set_billed_amount_in_returned_dn.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2022, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so - - -def execute(): - dn_item = frappe.qb.DocType('Delivery Note Item') - - so_detail_list = (frappe.qb.from_(dn_item) - .select(dn_item.so_detail) - .where( - (dn_item.so_detail.notnull()) & - (dn_item.so_detail != '') & - (dn_item.docstatus == 1) & - (dn_item.returned_qty > 0) - )).run() - - for so_detail in so_detail_list: - update_billed_amount_based_on_so(so_detail[0], False) \ No newline at end of file