diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 8df2e3587df..885e3882287 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -183,16 +183,7 @@ class POSInvoice(SalesInvoice): ) def validate_delivered_serial_nos(self, item): -<<<<<<< HEAD - serial_nos = get_serial_nos(item.serial_no) - delivered_serial_nos = frappe.db.get_list( - "Serial No", - {"item_code": item.item_code, "name": ["in", serial_nos], "sales_invoice": ["is", "set"]}, - pluck="name", - ) -======= delivered_serial_nos = get_delivered_serial_nos(item.serial_no) ->>>>>>> f2ae63cbfd (fix(pos): remove returned sr. nos. from pos reserved sr. nos. list) if delivered_serial_nos: bold_delivered_serial_nos = frappe.bold(", ".join(delivered_serial_nos)) @@ -220,24 +211,18 @@ class POSInvoice(SalesInvoice): frappe.throw(error_msg, title=_("Invalid Item"), as_list=True) def validate_stock_availablility(self): -<<<<<<< HEAD - if self.is_return or self.docstatus != 1: - return - allow_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock") - for d in self.get("items"): - is_service_item = not (frappe.db.get_value("Item", d.get("item_code"), "is_stock_item")) -======= if self.is_return: return - if self.docstatus.is_draft() and not frappe.db.get_value('POS Profile', self.pos_profile, 'validate_stock_on_save'): + if self.docstatus.is_draft() and not frappe.db.get_value( + "POS Profile", self.pos_profile, "validate_stock_on_save" + ): return - from erpnext.stock.stock_ledger import is_negative_stock_allowed + allow_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock") - for d in self.get('items'): - is_service_item = not (frappe.db.get_value('Item', d.get('item_code'), 'is_stock_item')) ->>>>>>> aff7408775 (fix(pos): allow validating stock on save) + for d in self.get("items"): + is_service_item = not (frappe.db.get_value("Item", d.get("item_code"), "is_stock_item")) if is_service_item: return if d.serial_no: 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 77f9755de16..d3a81fe61dc 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 @@ -5,7 +5,6 @@ import json import frappe -import six from frappe import _ from frappe.core.page.background_jobs.background_jobs import get_info from frappe.model.document import Document @@ -66,7 +65,9 @@ class POSInvoiceMergeLog(Document): frappe.throw(msg) def on_submit(self): - pos_invoice_docs = [frappe.get_cached_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 +84,9 @@ class POSInvoiceMergeLog(Document): self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) def on_cancel(self): - pos_invoice_docs = [frappe.get_cached_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,16 +282,18 @@ 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"], + fields=[ + "name as pos_invoice", + "posting_date", + "grand_total", + "customer", + "is_return", + "return_against", + ], ) -======= - 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 @@ -331,29 +336,14 @@ 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: - for customer, invoices in six.iteritems(invoice_by_customer): - 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() -======= 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}, @@ -368,17 +358,26 @@ def split_invoices(invoices): _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] + 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) + 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' + return_against_is_consolidated = ( + frappe.db.get_value("POS Invoice", pos_invoice.return_against, "status", cache=True) + == "Consolidated" + ) if return_against_is_consolidated: break @@ -391,19 +390,21 @@ def split_invoices(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 = 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.pos_closing_entry = closing_entry.get("name") if closing_entry else None - merge_log.set('pos_invoices', _invoices) + 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 71cb87f75cd..9e696f18b6a 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,11 +391,9 @@ 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 @@ -403,7 +401,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): 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 @@ -417,15 +415,13 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): 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 + 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100}) pos_inv.submit() pos_inv_cn = make_sales_return(pos_inv.name) @@ -433,15 +429,13 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): 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 + 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_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100}) pos_inv2.submit() consolidate_pos_invoices() @@ -455,4 +449,3 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): 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/patches.txt b/erpnext/patches.txt index 23e86332f74..98e07783c11 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -352,17 +352,9 @@ erpnext.patches.v13_0.update_reserved_qty_closed_wo erpnext.patches.v13_0.amazon_mws_deprecation_warning erpnext.patches.v13_0.set_work_order_qty_in_so_from_mr erpnext.patches.v13_0.update_accounts_in_loan_docs -<<<<<<< HEAD erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022 erpnext.patches.v13_0.rename_non_profit_fields erpnext.patches.v13_0.enable_ksa_vat_docs #1 erpnext.patches.v13_0.create_gst_custom_fields_in_quotation erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances -======= -erpnext.patches.v14_0.update_batch_valuation_flag -erpnext.patches.v14_0.delete_non_profit_doctypes -erpnext.patches.v14_0.update_employee_advance_status -erpnext.patches.v13_0.add_cost_center_in_loans -erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items erpnext.patches.v13_0.set_return_against_in_pos_invoice_references ->>>>>>> 16253a2f72 (fix: set is_return & return_against in POS Invoice Reference table) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 3b4c358e425..6d3969892f2 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -821,16 +821,17 @@ def auto_fetch_serial_number( def get_delivered_serial_nos(serial_nos): - ''' + """ Returns serial numbers that delivered from the list of serial numbers - ''' + """ from frappe.query_builder.functions import Coalesce SerialNo = frappe.qb.DocType("Serial No") serial_nos = get_serial_nos(serial_nos) - query = frappe.qb.select(SerialNo.name).from_(SerialNo).where( - (SerialNo.name.isin(serial_nos)) - & (Coalesce(SerialNo.delivery_document_type, "") != "") + query = ( + frappe.qb.select(SerialNo.name) + .from_(SerialNo) + .where((SerialNo.name.isin(serial_nos)) & (Coalesce(SerialNo.delivery_document_type, "") != "")) ) result = query.run() @@ -838,48 +839,30 @@ def get_delivered_serial_nos(serial_nos): delivered_serial_nos = [row[0] for row in result] return delivered_serial_nos + @frappe.whitelist() def get_pos_reserved_serial_nos(filters): if isinstance(filters, str): filters = json.loads(filters) -<<<<<<< HEAD - pos_transacted_sr_nos = frappe.db.sql( - """select item.serial_no as serial_no - from `tabPOS Invoice` p, `tabPOS Invoice Item` item - where p.name = item.parent - and p.consolidated_invoice is NULL - and p.docstatus = 1 - and item.docstatus = 1 - and item.item_code = %(item_code)s - and item.warehouse = %(warehouse)s - and item.serial_no is NOT NULL and item.serial_no != '' - """, - filters, - as_dict=1, - ) -======= POSInvoice = frappe.qb.DocType("POS Invoice") POSInvoiceItem = frappe.qb.DocType("POS Invoice Item") - query = frappe.qb.from_( - POSInvoice - ).from_( - POSInvoiceItem - ).select( - POSInvoice.is_return, - POSInvoiceItem.serial_no - ).where( - (POSInvoice.name == POSInvoiceItem.parent) - & (POSInvoice.docstatus == 1) - & (POSInvoiceItem.docstatus == 1) - & (POSInvoiceItem.item_code == filters.get('item_code')) - & (POSInvoiceItem.warehouse == filters.get('warehouse')) - & (POSInvoiceItem.serial_no.isnotnull()) - & (POSInvoiceItem.serial_no != '') + query = ( + frappe.qb.from_(POSInvoice) + .from_(POSInvoiceItem) + .select(POSInvoice.is_return, POSInvoiceItem.serial_no) + .where( + (POSInvoice.name == POSInvoiceItem.parent) + & (POSInvoice.docstatus == 1) + & (POSInvoiceItem.docstatus == 1) + & (POSInvoiceItem.item_code == filters.get("item_code")) + & (POSInvoiceItem.warehouse == filters.get("warehouse")) + & (POSInvoiceItem.serial_no.isnotnull()) + & (POSInvoiceItem.serial_no != "") + ) ) pos_transacted_sr_nos = query.run(as_dict=True) ->>>>>>> f2ae63cbfd (fix(pos): remove returned sr. nos. from pos reserved sr. nos. list) reserved_sr_nos = [] returned_sr_nos = []