diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index 91e71e90dd80..faceaf35d599 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -185,6 +185,7 @@ function refresh_payments(d, frm) { } if (payment) { payment.expected_amount += flt(p.amount); + payment.closing_amount = payment.expected_amount; payment.difference = payment.closing_amount - payment.expected_amount; } else { frm.add_child("payment_reconciliation", { diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json index 9d15e6cf357a..a98a24c463a9 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json @@ -221,6 +221,7 @@ "read_only": 1 }, { + "default": "Now", "fieldname": "posting_time", "fieldtype": "Time", "label": "Posting Time", @@ -235,7 +236,7 @@ "link_fieldname": "pos_closing_entry" } ], - "modified": "2022-08-01 11:37:14.991228", + "modified": "2023-08-10 16:25:49.322697", "modified_by": "Administrator", "module": "Accounts", "name": "POS Closing Entry", diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index 1deb3c52ac6a..93ba90ad9f99 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -8,9 +8,11 @@ from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import ( make_closing_entry_from_opening, ) +from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile +from erpnext.selling.page.point_of_sale.point_of_sale import get_items from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry @@ -67,6 +69,36 @@ def test_pos_closing_without_item_code(self): self.assertTrue(pcv_doc.name) + def test_pos_qty_for_item(self): + """ + Test if quantity is calculated correctly for an item in POS Closing Entry + """ + test_user, pos_profile = init_user_and_profile() + opening_entry = create_opening_entry(pos_profile, test_user.name) + + test_item_qty = get_test_item_qty(pos_profile) + + pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) + pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) + pos_inv1.submit() + + pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) + pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}) + pos_inv2.submit() + + # make return entry of pos_inv2 + pos_return = make_sales_return(pos_inv2.name) + pos_return.paid_amount = pos_return.grand_total + pos_return.save() + pos_return.submit() + + pcv_doc = make_closing_entry_from_opening(opening_entry) + pcv_doc.submit() + + opening_entry = create_opening_entry(pos_profile, test_user.name) + test_item_qty_after_sales = get_test_item_qty(pos_profile) + self.assertEqual(test_item_qty_after_sales, test_item_qty - 1) + def test_cancelling_of_pos_closing_entry(self): test_user, pos_profile = init_user_and_profile() opening_entry = create_opening_entry(pos_profile, test_user.name) @@ -123,3 +155,19 @@ def init_user_and_profile(**args): pos_profile.save() return test_user, pos_profile + + +def get_test_item_qty(pos_profile): + test_item_pos = get_items( + start=0, + page_length=5, + price_list="Standard Selling", + pos_profile=pos_profile.name, + search_term="_Test Item", + item_group="All Item Groups", + ) + + test_item_qty = [item for item in test_item_pos["items"] if item["item_code"] == "_Test Item"][ + 0 + ].get("actual_qty") + return test_item_qty diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 4b2fcec75791..89a96118ec43 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -542,6 +542,7 @@ def get_stock_availability(item_code, warehouse): is_stock_item = True bin_qty = get_bin_qty(item_code, warehouse) pos_sales_qty = get_pos_reserved_qty(item_code, warehouse) + return bin_qty - pos_sales_qty, is_stock_item else: is_stock_item = True @@ -595,7 +596,6 @@ def get_pos_reserved_qty(item_code, warehouse): .where( (p_inv.name == p_item.parent) & (IfNull(p_inv.consolidated_invoice, "") == "") - & (p_inv.is_return == 0) & (p_item.docstatus == 1) & (p_item.item_code == item_code) & (p_item.warehouse == warehouse) 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 d8cbcc141bda..b587ce603f42 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 @@ -95,7 +95,6 @@ def on_submit(self): sales_invoice = self.process_merging_into_sales_invoice(sales) self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log - self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) def on_cancel(self): @@ -108,7 +107,6 @@ def on_cancel(self): def process_merging_into_sales_invoice(self, data): sales_invoice = self.get_new_sales_invoice() - sales_invoice = self.merge_pos_invoice_into(sales_invoice, data) sales_invoice.is_consolidated = 1 @@ -165,8 +163,7 @@ def merge_pos_invoice_into(self, invoice, data): for i in items: if ( i.item_code == item.item_code - and not i.serial_no - and not i.batch_no + and not i.serial_and_batch_bundle and i.uom == item.uom and i.net_rate == item.net_rate and i.warehouse == item.warehouse @@ -385,6 +382,7 @@ def split_invoices(invoices): 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 and not item.serial_and_batch_bundle: diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 720d142ca073..db6255a4bee4 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -225,6 +225,7 @@ erpnext.PointOfSale.Controller = class { voucher.pos_opening_entry = this.pos_opening; voucher.period_end_date = frappe.datetime.now_datetime(); voucher.posting_date = frappe.datetime.now_date(); + voucher.posting_time = frappe.datetime.now_time(); frappe.set_route('Form', 'POS Closing Entry', voucher.name); } diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 43bd7ac78cb5..1f90c5bf7a53 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -3,7 +3,7 @@ import collections import csv -from collections import defaultdict +from collections import Counter, defaultdict from typing import Dict, List import frappe @@ -1197,6 +1197,7 @@ def get_reserved_serial_nos_for_pos(kwargs): filters=[ ["POS Invoice", "consolidated_invoice", "is", "not set"], ["POS Invoice", "docstatus", "=", 1], + ["POS Invoice", "is_return", "=", 0], ["POS Invoice Item", "item_code", "=", kwargs.item_code], ["POS Invoice", "name", "!=", kwargs.ignore_voucher_no], ], @@ -1214,7 +1215,6 @@ def get_reserved_serial_nos_for_pos(kwargs): for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids): ignore_serial_nos.append(d.serial_no) - # Will be deprecated in v16 returned_serial_nos = [] for pos_invoice in pos_invoices: if pos_invoice.serial_no: @@ -1242,8 +1242,13 @@ def get_reserved_serial_nos_for_pos(kwargs): child_doc, parent_doc, ignore_voucher_detail_no=kwargs.get("ignore_voucher_detail_no") ) ) + # Counter is used to create a hashmap of serial nos, which contains count of each serial no + # so we subtract returned serial nos from ignore serial nos after creating a counter of each to get the items which we need to ignore(which are sold) - return list(set(ignore_serial_nos) - set(returned_serial_nos)) + ignore_serial_nos_counter = Counter(ignore_serial_nos) + returned_serial_nos_counter = Counter(returned_serial_nos) + + return list(ignore_serial_nos_counter - returned_serial_nos_counter) def get_reserved_batches_for_pos(kwargs):