mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-23 23:19:20 +00:00
fix: reserve the pos invoice batches
This commit is contained in:
@@ -20,7 +20,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex
|
|||||||
|
|
||||||
onload(doc) {
|
onload(doc) {
|
||||||
super.onload();
|
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') {
|
if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
|
||||||
this.frm.script_manager.trigger("is_pos");
|
this.frm.script_manager.trigger("is_pos");
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def on_cancel(self):
|
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
|
# run on cancel method of selling controller
|
||||||
super(SalesInvoice, self).on_cancel()
|
super(SalesInvoice, self).on_cancel()
|
||||||
if not self.is_return and self.loyalty_program:
|
if not self.is_return and self.loyalty_program:
|
||||||
|
|||||||
@@ -767,6 +767,39 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(rounded_total, 400)
|
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):
|
def test_pos_batch_item_qty_validation(self):
|
||||||
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||||
BatchNegativeStockError,
|
BatchNegativeStockError,
|
||||||
|
|||||||
@@ -1241,59 +1241,125 @@ def get_reserved_serial_nos_for_pos(kwargs):
|
|||||||
return list(set(ignore_serial_nos) - set(returned_serial_nos))
|
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):
|
def get_auto_batch_nos(kwargs):
|
||||||
available_batches = get_available_batches(kwargs)
|
available_batches = get_available_batches(kwargs)
|
||||||
qty = flt(kwargs.qty)
|
qty = flt(kwargs.qty)
|
||||||
|
|
||||||
|
pos_invoice_batches = get_reserved_batches_for_pos(kwargs)
|
||||||
stock_ledgers_batches = get_stock_ledgers_batches(kwargs)
|
stock_ledgers_batches = get_stock_ledgers_batches(kwargs)
|
||||||
if stock_ledgers_batches:
|
if stock_ledgers_batches or pos_invoice_batches:
|
||||||
update_available_batches(available_batches, stock_ledgers_batches)
|
update_available_batches(available_batches, stock_ledgers_batches, pos_invoice_batches)
|
||||||
|
|
||||||
available_batches = list(filter(lambda x: x.qty > 0, available_batches))
|
available_batches = list(filter(lambda x: x.qty > 0, available_batches))
|
||||||
|
|
||||||
if not qty:
|
if not qty:
|
||||||
return available_batches
|
return available_batches
|
||||||
|
|
||||||
|
return get_qty_based_available_batches(available_batches, qty)
|
||||||
|
|
||||||
|
|
||||||
|
def get_qty_based_available_batches(available_batches, qty):
|
||||||
batches = []
|
batches = []
|
||||||
for batch in available_batches:
|
for batch in available_batches:
|
||||||
if qty > 0:
|
if qty <= 0:
|
||||||
batch_qty = flt(batch.qty)
|
break
|
||||||
if qty > batch_qty:
|
|
||||||
batches.append(
|
batch_qty = flt(batch.qty)
|
||||||
frappe._dict(
|
if qty > batch_qty:
|
||||||
{
|
batches.append(
|
||||||
"batch_no": batch.batch_no,
|
frappe._dict(
|
||||||
"qty": batch_qty,
|
{
|
||||||
"warehouse": batch.warehouse,
|
"batch_no": batch.batch_no,
|
||||||
}
|
"qty": batch_qty,
|
||||||
)
|
"warehouse": batch.warehouse,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
qty -= batch_qty
|
)
|
||||||
else:
|
qty -= batch_qty
|
||||||
batches.append(
|
else:
|
||||||
frappe._dict(
|
batches.append(
|
||||||
{
|
frappe._dict(
|
||||||
"batch_no": batch.batch_no,
|
{
|
||||||
"qty": qty,
|
"batch_no": batch.batch_no,
|
||||||
"warehouse": batch.warehouse,
|
"qty": qty,
|
||||||
}
|
"warehouse": batch.warehouse,
|
||||||
)
|
}
|
||||||
)
|
)
|
||||||
qty = 0
|
)
|
||||||
|
qty = 0
|
||||||
|
|
||||||
return batches
|
return batches
|
||||||
|
|
||||||
|
|
||||||
def update_available_batches(available_batches, reserved_batches):
|
def update_available_batches(available_batches, reserved_batches=None, pos_invoice_batches=None):
|
||||||
for batch_no, data in reserved_batches.items():
|
for batches in [reserved_batches, pos_invoice_batches]:
|
||||||
batch_not_exists = True
|
if batches:
|
||||||
for batch in available_batches:
|
for batch_no, data in batches.items():
|
||||||
if batch.batch_no == batch_no:
|
batch_not_exists = True
|
||||||
batch.qty += data.qty
|
for batch in available_batches:
|
||||||
batch_not_exists = False
|
if batch.batch_no == batch_no and batch.warehouse == data.warehouse:
|
||||||
|
batch.qty += data.qty
|
||||||
|
batch_not_exists = False
|
||||||
|
|
||||||
if batch_not_exists:
|
if batch_not_exists:
|
||||||
available_batches.append(data)
|
available_batches.append(data)
|
||||||
|
|
||||||
|
|
||||||
def get_available_batches(kwargs):
|
def get_available_batches(kwargs):
|
||||||
|
|||||||
Reference in New Issue
Block a user