fix: reserve the pos invoice batches

This commit is contained in:
Rohit Waghchaure 2023-07-03 17:28:55 +05:30
parent c51e6dba8d
commit 1e8f6c0840
4 changed files with 135 additions and 36 deletions

View File

@ -20,7 +20,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex
onload(doc) {
super.onload();
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log', 'POS Closing Entry'];
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log', 'POS Closing Entry', 'Serial and Batch Bundle'];
if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
this.frm.script_manager.trigger("is_pos");

View File

@ -93,7 +93,7 @@ class POSInvoice(SalesInvoice):
)
def on_cancel(self):
self.ignore_linked_doctypes = "Payment Ledger Entry"
self.ignore_linked_doctypes = ["Payment Ledger Entry", "Serial and Batch Bundle"]
# run on cancel method of selling controller
super(SalesInvoice, self).on_cancel()
if not self.is_return and self.loyalty_program:

View File

@ -767,6 +767,39 @@ class TestPOSInvoice(unittest.TestCase):
)
self.assertEqual(rounded_total, 400)
def test_pos_batch_reservation(self):
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
get_auto_batch_nos,
)
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_batch_item_with_batch,
)
create_batch_item_with_batch("_BATCH ITEM Test For Reserve", "TestBatch-RS 02")
make_stock_entry(
target="_Test Warehouse - _TC",
item_code="_BATCH ITEM Test For Reserve",
qty=20,
basic_rate=100,
batch_no="TestBatch-RS 02",
)
pos_inv1 = create_pos_invoice(
item="_BATCH ITEM Test For Reserve", rate=300, qty=15, batch_no="TestBatch-RS 02"
)
pos_inv1.save()
pos_inv1.submit()
batches = get_auto_batch_nos(
frappe._dict(
{"item_code": "_BATCH ITEM Test For Reserve", "warehouse": "_Test Warehouse - _TC"}
)
)
for batch in batches:
if batch.batch_no == "TestBatch-RS 02" and batch.warehouse == "_Test Warehouse - _TC":
self.assertEqual(batch.qty, 5)
def test_pos_batch_item_qty_validation(self):
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
BatchNegativeStockError,

View File

@ -1241,59 +1241,125 @@ def get_reserved_serial_nos_for_pos(kwargs):
return list(set(ignore_serial_nos) - set(returned_serial_nos))
def get_reserved_batches_for_pos(kwargs):
pos_batches = frappe._dict()
pos_invoices = frappe.get_all(
"POS Invoice",
fields=[
"`tabPOS Invoice Item`.batch_no",
"`tabPOS Invoice`.is_return",
"`tabPOS Invoice Item`.warehouse",
"`tabPOS Invoice Item`.name as child_docname",
"`tabPOS Invoice`.name as parent_docname",
"`tabPOS Invoice Item`.serial_and_batch_bundle",
],
filters=[
["POS Invoice", "consolidated_invoice", "is", "not set"],
["POS Invoice", "docstatus", "=", 1],
["POS Invoice Item", "item_code", "=", kwargs.item_code],
["POS Invoice", "name", "!=", kwargs.ignore_voucher_no],
],
)
ids = [
pos_invoice.serial_and_batch_bundle
for pos_invoice in pos_invoices
if pos_invoice.serial_and_batch_bundle
]
if not ids:
return []
if ids:
for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids):
if d.batch_no not in pos_batches:
pos_batches[d.batch_no] = frappe._dict(
{
"qty": d.qty,
"warehouse": d.warehouse,
}
)
else:
pos_batches[d.batch_no].qty += d.qty
for row in pos_invoices:
if not row.batch_no:
continue
if row.batch_no in pos_batches:
pos_batches[row.batch_no] -= row.qty * -1 if row.is_return else row.qty
else:
pos_batches[row.batch_no] = frappe._dict(
{
"qty": (row.qty * -1 if row.is_return else row.qty),
"warehouse": row.warehouse,
}
)
return pos_batches
def get_auto_batch_nos(kwargs):
available_batches = get_available_batches(kwargs)
qty = flt(kwargs.qty)
pos_invoice_batches = get_reserved_batches_for_pos(kwargs)
stock_ledgers_batches = get_stock_ledgers_batches(kwargs)
if stock_ledgers_batches:
update_available_batches(available_batches, stock_ledgers_batches)
if stock_ledgers_batches or pos_invoice_batches:
update_available_batches(available_batches, stock_ledgers_batches, pos_invoice_batches)
available_batches = list(filter(lambda x: x.qty > 0, available_batches))
if not qty:
return available_batches
return get_qty_based_available_batches(available_batches, qty)
def get_qty_based_available_batches(available_batches, qty):
batches = []
for batch in available_batches:
if qty > 0:
batch_qty = flt(batch.qty)
if qty > batch_qty:
batches.append(
frappe._dict(
{
"batch_no": batch.batch_no,
"qty": batch_qty,
"warehouse": batch.warehouse,
}
)
if qty <= 0:
break
batch_qty = flt(batch.qty)
if qty > batch_qty:
batches.append(
frappe._dict(
{
"batch_no": batch.batch_no,
"qty": batch_qty,
"warehouse": batch.warehouse,
}
)
qty -= batch_qty
else:
batches.append(
frappe._dict(
{
"batch_no": batch.batch_no,
"qty": qty,
"warehouse": batch.warehouse,
}
)
)
qty -= batch_qty
else:
batches.append(
frappe._dict(
{
"batch_no": batch.batch_no,
"qty": qty,
"warehouse": batch.warehouse,
}
)
qty = 0
)
qty = 0
return batches
def update_available_batches(available_batches, reserved_batches):
for batch_no, data in reserved_batches.items():
batch_not_exists = True
for batch in available_batches:
if batch.batch_no == batch_no:
batch.qty += data.qty
batch_not_exists = False
def update_available_batches(available_batches, reserved_batches=None, pos_invoice_batches=None):
for batches in [reserved_batches, pos_invoice_batches]:
if batches:
for batch_no, data in batches.items():
batch_not_exists = True
for batch in available_batches:
if batch.batch_no == batch_no and batch.warehouse == data.warehouse:
batch.qty += data.qty
batch_not_exists = False
if batch_not_exists:
available_batches.append(data)
if batch_not_exists:
available_batches.append(data)
def get_available_batches(kwargs):