mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-02 06:29:54 +00:00
fix(stock): handle legacy single sle recon entries
(cherry picked from commit 7e6bbcc3fb)
This commit is contained in:
committed by
Rohit Waghchaure
parent
d7237519a2
commit
dd0613a4a8
@@ -7,6 +7,7 @@ from operator import itemgetter
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder.functions import Count
|
||||
from frappe.utils import cint, date_diff, flt, get_datetime
|
||||
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
@@ -240,9 +241,9 @@ class FIFOSlots:
|
||||
Returns dict of the foll.g structure:
|
||||
Key = Item A / (Item A, Warehouse A)
|
||||
Key: {
|
||||
'details' -> Dict: ** item details **,
|
||||
'fifo_queue' -> List: ** list of lists containing entries/slots for existing stock,
|
||||
consumed/updated and maintained via FIFO. **
|
||||
'details' -> Dict: ** item details **,
|
||||
'fifo_queue' -> List: ** list of lists containing entries/slots for existing stock,
|
||||
consumed/updated and maintained via FIFO. **
|
||||
}
|
||||
"""
|
||||
from erpnext.stock.serial_batch_bundle import get_serial_nos_from_bundle
|
||||
@@ -253,16 +254,33 @@ class FIFOSlots:
|
||||
if stock_ledger_entries is None:
|
||||
bundle_wise_serial_nos = self.__get_bundle_wise_serial_nos()
|
||||
|
||||
# prepare single sle voucher detail lookup
|
||||
self.prepare_stock_reco_voucher_wise_count()
|
||||
|
||||
with frappe.db.unbuffered_cursor():
|
||||
if stock_ledger_entries is None:
|
||||
stock_ledger_entries = self.__get_stock_ledger_entries()
|
||||
|
||||
for d in stock_ledger_entries:
|
||||
key, fifo_queue, transferred_item_key = self.__init_key_stores(d)
|
||||
prev_balance_qty = self.item_details[key].get("qty_after_transaction", 0)
|
||||
|
||||
if d.voucher_type == "Stock Reconciliation":
|
||||
if d.voucher_type == "Stock Reconciliation" and (
|
||||
not d.batch_no or d.serial_no or d.serial_and_batch_bundle
|
||||
):
|
||||
if d.voucher_detail_no in self.stock_reco_voucher_wise_count:
|
||||
# for legacy recon with single sle has qty_after_transaction and stock_value_difference without outward entry
|
||||
# for exisitng handle emptying the existing queue and details.
|
||||
d.stock_value_difference = flt(d.qty_after_transaction * d.valuation_rate)
|
||||
d.actual_qty = d.qty_after_transaction
|
||||
self.item_details[key]["qty_after_transaction"] = 0
|
||||
self.item_details[key]["total_qty"] = 0
|
||||
fifo_queue.clear()
|
||||
else:
|
||||
d.actual_qty = flt(d.qty_after_transaction) - flt(prev_balance_qty)
|
||||
|
||||
elif d.voucher_type == "Stock Reconciliation":
|
||||
# get difference in qty shift as actual qty
|
||||
prev_balance_qty = self.item_details[key].get("qty_after_transaction", 0)
|
||||
d.actual_qty = flt(d.qty_after_transaction) - flt(prev_balance_qty)
|
||||
|
||||
serial_nos = get_serial_nos(d.serial_no) if d.serial_no else []
|
||||
@@ -280,6 +298,14 @@ class FIFOSlots:
|
||||
|
||||
self.__update_balances(d, key)
|
||||
|
||||
# handle serial nos misconsumption
|
||||
if d.has_serial_no:
|
||||
qty_after = cint(self.item_details[key]["qty_after_transaction"])
|
||||
if qty_after <= 0:
|
||||
fifo_queue.clear()
|
||||
elif len(fifo_queue) > qty_after:
|
||||
fifo_queue[:] = fifo_queue[:qty_after]
|
||||
|
||||
# Note that stock_ledger_entries is an iterator, you can not reuse it like a list
|
||||
del stock_ledger_entries
|
||||
|
||||
@@ -406,7 +432,6 @@ class FIFOSlots:
|
||||
|
||||
def __update_balances(self, row: dict, key: tuple | str):
|
||||
self.item_details[key]["qty_after_transaction"] = row.qty_after_transaction
|
||||
|
||||
if "total_qty" not in self.item_details[key]:
|
||||
self.item_details[key]["total_qty"] = row.actual_qty
|
||||
else:
|
||||
@@ -462,6 +487,7 @@ class FIFOSlots:
|
||||
sle.posting_date,
|
||||
sle.voucher_type,
|
||||
sle.voucher_no,
|
||||
sle.voucher_detail_no,
|
||||
sle.serial_no,
|
||||
sle.batch_no,
|
||||
sle.qty_after_transaction,
|
||||
@@ -558,3 +584,36 @@ class FIFOSlots:
|
||||
warehouse_results = [x[0] for x in warehouse_results]
|
||||
|
||||
return sle_query.where(sle.warehouse.isin(warehouse_results))
|
||||
|
||||
def prepare_stock_reco_voucher_wise_count(self):
|
||||
self.stock_reco_voucher_wise_count = frappe._dict()
|
||||
|
||||
doctype = frappe.qb.DocType("Stock Ledger Entry")
|
||||
item = frappe.qb.DocType("Item")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(doctype)
|
||||
.inner_join(item)
|
||||
.on(doctype.item_code == item.name)
|
||||
.select(doctype.voucher_detail_no, Count(doctype.name).as_("count"))
|
||||
.where(
|
||||
(doctype.voucher_type == "Stock Reconciliation")
|
||||
& (doctype.docstatus < 2)
|
||||
& (doctype.is_cancelled == 0)
|
||||
)
|
||||
.groupby(doctype.voucher_detail_no)
|
||||
)
|
||||
|
||||
data = query.run(as_dict=True)
|
||||
if not data:
|
||||
return
|
||||
|
||||
for row in data:
|
||||
if row.count != 1:
|
||||
continue
|
||||
|
||||
sr_item = frappe.db.get_value(
|
||||
"Stock Reconciliation Item", row.voucher_detail_no, ["current_qty", "qty"], as_dict=True
|
||||
)
|
||||
if sr_item.qty and sr_item.current_qty:
|
||||
self.stock_reco_voucher_wise_count[row.voucher_detail_no] = sr_item.current_qty
|
||||
|
||||
Reference in New Issue
Block a user