From 3bb0716dffa3d72732d3e0ce70a870635e440502 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 24 Mar 2022 16:20:37 +0530 Subject: [PATCH] fix(pos): cannot close the pos if sr. no. is sold & returned (cherry picked from commit cf51a0a1b8ec45bf653c9478bd57cee676b384d9) # Conflicts: # erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py # erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py --- .../pos_invoice_merge_log.py | 65 ++++++++++++++++++- .../test_pos_invoice_merge_log.py | 65 +++++++++++++++++++ .../pos_invoice_reference.json | 23 ++++++- 3 files changed, 149 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 89af6ad4e57..d97ce1cffc0 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -66,7 +66,7 @@ class POSInvoiceMergeLog(Document): frappe.throw(msg) def on_submit(self): - pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices] + pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices] returns = [d for d in pos_invoice_docs if d.get("is_return") == 1] sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] @@ -83,7 +83,7 @@ class POSInvoiceMergeLog(Document): self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) def on_cancel(self): - pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices] + pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices] self.update_pos_invoices(pos_invoice_docs) self.cancel_linked_invoices() @@ -279,11 +279,16 @@ def get_all_unconsolidated_invoices(): "status": ["not in", ["Consolidated"]], "docstatus": 1, } +<<<<<<< HEAD pos_invoices = frappe.db.get_all( "POS Invoice", filters=filters, fields=["name as pos_invoice", "posting_date", "grand_total", "customer"], ) +======= + pos_invoices = frappe.db.get_all('POS Invoice', filters=filters, + fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer', 'is_return', 'return_against']) +>>>>>>> cf51a0a1b8 (fix(pos): cannot close the pos if sr. no. is sold & returned) return pos_invoices @@ -326,6 +331,7 @@ def unconsolidate_pos_invoices(closing_entry): else: cancel_merge_logs(merge_logs, closing_entry) +<<<<<<< HEAD def create_merge_logs(invoice_by_customer, closing_entry=None): try: @@ -340,6 +346,61 @@ def create_merge_logs(invoice_by_customer, closing_entry=None): merge_log.set("pos_invoices", invoices) merge_log.save(ignore_permissions=True) merge_log.submit() +======= +def split_invoices(invoices): + ''' + Splits invoices into multiple groups + Use-case: + If a serial no is sold and later it is returned + then split the invoices such that the selling entry is merged first and then the return entry + ''' + # Input + # invoices = [ + # {'pos_invoice': 'Invoice with SR#1 & SR#2', 'is_return': 0}, + # {'pos_invoice': 'Invoice with SR#1', 'is_return': 1}, + # {'pos_invoice': 'Invoice with SR#2', 'is_return': 0} + # ] + # Output + # _invoices = [ + # [{'pos_invoice': 'Invoice with SR#1 & SR#2', 'is_return': 0}], + # [{'pos_invoice': 'Invoice with SR#1', 'is_return': 1}, {'pos_invoice': 'Invoice with SR#2', 'is_return': 0}], + # ] + + _invoices = [] + special_invoices = [] + pos_return_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in invoices if d.is_return and d.return_against] + for pos_invoice in pos_return_docs: + for item in pos_invoice.items: + if not item.serial_no: continue + + return_against_is_added = any(d for d in _invoices if d.pos_invoice == pos_invoice.return_against) + if return_against_is_added: break + + return_against_is_consolidated = frappe.db.get_value('POS Invoice', pos_invoice.return_against, 'status', cache=True) == 'Consolidated' + if return_against_is_consolidated: break + + pos_invoice_row = [d for d in invoices if d.pos_invoice == pos_invoice.return_against] + _invoices.append(pos_invoice_row) + special_invoices.append(pos_invoice.return_against) + break + + _invoices.append([d for d in invoices if d.pos_invoice not in special_invoices]) + + return _invoices + +def create_merge_logs(invoice_by_customer, closing_entry=None): + try: + for customer, invoices in invoice_by_customer.items(): + for _invoices in split_invoices(invoices): + merge_log = frappe.new_doc('POS Invoice Merge Log') + merge_log.posting_date = getdate(closing_entry.get('posting_date')) if closing_entry else nowdate() + merge_log.customer = customer + merge_log.pos_closing_entry = closing_entry.get('name') if closing_entry else None + + merge_log.set('pos_invoices', _invoices) + merge_log.save(ignore_permissions=True) + merge_log.submit() +>>>>>>> cf51a0a1b8 (fix(pos): cannot close the pos if sr. no. is sold & returned) if closing_entry: closing_entry.set_status(update=True, status="Submitted") diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py index c6d8179c40c..e3cd2863f70 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py @@ -391,3 +391,68 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): frappe.set_user("Administrator") frappe.db.sql("delete from `tabPOS Profile`") frappe.db.sql("delete from `tabPOS Invoice`") +<<<<<<< HEAD +======= + + def test_serial_no_case_1(self): + ''' + Create a POS Invoice with serial no + Create a Return Invoice with serial no + Create a POS Invoice with serial no again + Consolidate the invoices + + The first POS Invoice should be consolidated with a separate single Merge Log + The second and third POS Invoice should be consolidated with a single Merge Log + ''' + + from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + + frappe.db.sql("delete from `tabPOS Invoice`") + + try: + se = make_serialized_item() + serial_no = get_serial_nos(se.get("items")[0].serial_no)[0] + + init_user_and_profile() + + pos_inv = create_pos_invoice( + item_code="_Test Serialized Item With Series", + serial_no=serial_no, + qty=1, + rate=100, + do_not_submit=1 + ) + pos_inv.append('payments', { + 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 100 + }) + pos_inv.submit() + + pos_inv_cn = make_sales_return(pos_inv.name) + pos_inv_cn.paid_amount = -100 + pos_inv_cn.submit() + + pos_inv2 = create_pos_invoice( + item_code="_Test Serialized Item With Series", + serial_no=serial_no, + qty=1, + rate=100, + do_not_submit=1 + ) + pos_inv2.append('payments', { + 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 100 + }) + pos_inv.submit() + + consolidate_pos_invoices() + + pos_inv.load_from_db() + pos_inv2.load_from_db() + + self.assertNotEqual(pos_inv.consolidated_invoice, pos_inv2.consolidated_invoice) + + finally: + frappe.set_user("Administrator") + frappe.db.sql("delete from `tabPOS Profile`") + frappe.db.sql("delete from `tabPOS Invoice`") +>>>>>>> cf51a0a1b8 (fix(pos): cannot close the pos if sr. no. is sold & returned) diff --git a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json index 205c4ede901..387c4b0f360 100644 --- a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json +++ b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json @@ -9,7 +9,9 @@ "posting_date", "column_break_3", "customer", - "grand_total" + "grand_total", + "is_return", + "return_against" ], "fields": [ { @@ -48,11 +50,27 @@ "in_list_view": 1, "label": "Amount", "reqd": 1 + }, + { + "default": "0", + "fetch_from": "pos_invoice.is_return", + "fieldname": "is_return", + "fieldtype": "Check", + "label": "Is Return", + "read_only": 1 + }, + { + "fetch_from": "pos_invoice.return_against", + "fieldname": "return_against", + "fieldtype": "Link", + "label": "Return Against", + "options": "POS Invoice", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2020-05-29 15:08:42.194979", + "modified": "2022-03-24 13:32:02.366257", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice Reference", @@ -61,5 +79,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file