fix: reserve the pos invoice batches
This commit is contained in:
parent
c51e6dba8d
commit
1e8f6c0840
@ -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");
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user