Skip to content

Commit bba0a6d

Browse files
rohitwaghchauremergify[bot]
authored andcommitted
fix: old stock reco entries causing issue in the stock balance report
(cherry picked from commit 0874cbc) # Conflicts: # erpnext/stock/report/stock_balance/stock_balance.py (cherry picked from commit b712467)
1 parent 7ad770a commit bba0a6d

2 files changed

Lines changed: 202 additions & 1 deletion

File tree

erpnext/stock/report/stock_balance/stock_balance.py

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77

88
import frappe
99
from frappe import _
10+
<<<<<<< HEAD
1011
from frappe.query_builder import Order
1112
from frappe.query_builder.functions import Coalesce
13+
=======
14+
from frappe.query_builder.functions import Coalesce, Count
15+
>>>>>>> 0874cbc268 (fix: old stock reco entries causing issue in the stock balance report)
1216
from frappe.utils import add_days, cint, date_diff, flt, getdate
1317
from frappe.utils.nestedset import get_descendants_of
1418

@@ -318,6 +322,7 @@ def prepare_stock_ledger_entries(self):
318322
sle.serial_no,
319323
sle.serial_and_batch_bundle,
320324
sle.has_serial_no,
325+
sle.voucher_detail_no,
321326
item_table.item_group,
322327
item_table.stock_uom,
323328
item_table.item_name,
@@ -337,6 +342,202 @@ def prepare_stock_ledger_entries(self):
337342

338343
self.sle_query = query
339344

345+
<<<<<<< HEAD
346+
=======
347+
def prepare_item_warehouse_map_for_current_period(self):
348+
self.opening_vouchers = self.get_opening_vouchers()
349+
350+
if self.filters.get("show_stock_ageing_data"):
351+
self.sle_entries = self.sle_query.run(as_dict=True)
352+
353+
self.prepare_stock_reco_voucher_wise_count()
354+
355+
# HACK: This is required to avoid causing db query in flt
356+
_system_settings = frappe.get_cached_doc("System Settings")
357+
with frappe.db.unbuffered_cursor():
358+
if not self.filters.get("show_stock_ageing_data"):
359+
self.sle_entries = self.sle_query.run(as_dict=True, as_iterator=True)
360+
361+
for entry in self.sle_entries:
362+
group_by_key = self.get_group_by_key(entry)
363+
if group_by_key not in self.item_warehouse_map:
364+
self.initialize_data(group_by_key, entry)
365+
366+
self.prepare_item_warehouse_map(entry, group_by_key)
367+
368+
self.item_warehouse_map = filter_items_with_no_transactions(
369+
self.item_warehouse_map, self.float_precision, self.inventory_dimensions
370+
)
371+
372+
def prepare_stock_reco_voucher_wise_count(self):
373+
self.stock_reco_voucher_wise_count = frappe._dict()
374+
375+
doctype = frappe.qb.DocType("Stock Ledger Entry")
376+
item = frappe.qb.DocType("Item")
377+
378+
query = (
379+
frappe.qb.from_(doctype)
380+
.inner_join(item)
381+
.on(doctype.item_code == item.name)
382+
.select(doctype.voucher_detail_no, Count(doctype.name).as_("count"))
383+
.where(
384+
(doctype.voucher_type == "Stock Reconciliation")
385+
& (doctype.docstatus < 2)
386+
& (doctype.is_cancelled == 0)
387+
& (item.has_serial_no == 1)
388+
)
389+
.groupby(doctype.voucher_detail_no)
390+
)
391+
392+
data = query.run(as_list=True)
393+
if data:
394+
self.stock_reco_voucher_wise_count = frappe._dict(data)
395+
396+
def prepare_new_data(self):
397+
if self.filters.get("show_stock_ageing_data"):
398+
self.filters["show_warehouse_wise_stock"] = True
399+
item_wise_fifo_queue = FIFOSlots(self.filters).generate()
400+
401+
_func = itemgetter(1)
402+
403+
del self.sle_entries
404+
405+
sre_details = self.get_sre_reserved_qty_details()
406+
407+
variant_values = {}
408+
if self.filters.get("show_variant_attributes"):
409+
variant_values = self.get_variant_values_for()
410+
411+
for _key, report_data in self.item_warehouse_map.items():
412+
if variant_data := variant_values.get(report_data.item_code):
413+
report_data.update(variant_data)
414+
415+
if self.filters.get("show_stock_ageing_data"):
416+
opening_fifo_queue = self.get_opening_fifo_queue(report_data) or []
417+
418+
fifo_queue = []
419+
if fifo_queue := item_wise_fifo_queue.get((report_data.item_code, report_data.warehouse)):
420+
fifo_queue = fifo_queue.get("fifo_queue")
421+
422+
if fifo_queue:
423+
opening_fifo_queue.extend(fifo_queue)
424+
425+
stock_ageing_data = {"average_age": 0, "earliest_age": 0, "latest_age": 0}
426+
427+
if opening_fifo_queue:
428+
fifo_queue = sorted(filter(_func, opening_fifo_queue), key=_func)
429+
if not fifo_queue:
430+
continue
431+
432+
to_date = self.to_date
433+
stock_ageing_data["average_age"] = get_average_age(fifo_queue, to_date)
434+
stock_ageing_data["earliest_age"] = date_diff(to_date, fifo_queue[0][1])
435+
stock_ageing_data["latest_age"] = date_diff(to_date, fifo_queue[-1][1])
436+
stock_ageing_data["fifo_queue"] = fifo_queue
437+
438+
report_data.update(stock_ageing_data)
439+
440+
report_data.update(
441+
{"reserved_stock": sre_details.get((report_data.item_code, report_data.warehouse), 0.0)}
442+
)
443+
444+
if (
445+
not self.filters.get("include_zero_stock_items")
446+
and report_data
447+
and report_data.bal_qty == 0
448+
and report_data.bal_val == 0
449+
):
450+
continue
451+
452+
self.data.append(report_data)
453+
454+
def get_sre_reserved_qty_details(self) -> dict:
455+
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
456+
get_sre_reserved_qty_for_items_and_warehouses as get_reserved_qty_details,
457+
)
458+
459+
item_code_list, warehouse_list = [], []
460+
for d in self.item_warehouse_map:
461+
item_code_list.append(d[0])
462+
warehouse_list.append(d[1])
463+
464+
return get_reserved_qty_details(item_code_list, warehouse_list)
465+
466+
def prepare_item_warehouse_map(self, entry, group_by_key):
467+
qty_dict = self.item_warehouse_map[group_by_key]
468+
for field in self.inventory_dimensions:
469+
qty_dict[field] = entry.get(field)
470+
471+
if entry.voucher_type == "Stock Reconciliation" and (
472+
not entry.batch_no or entry.serial_no or entry.serial_and_batch_bundle
473+
):
474+
if entry.serial_no and self.stock_reco_voucher_wise_count.get(entry.voucher_detail_no, 0) == 1:
475+
qty_dict.bal_qty = 0.0
476+
qty_diff = flt(entry.actual_qty)
477+
else:
478+
qty_diff = flt(entry.qty_after_transaction) - flt(qty_dict.bal_qty)
479+
else:
480+
qty_diff = flt(entry.actual_qty)
481+
482+
value_diff = flt(entry.stock_value_difference)
483+
484+
if entry.posting_date < self.from_date or entry.voucher_no in self.opening_vouchers.get(
485+
entry.voucher_type, []
486+
):
487+
qty_dict.opening_qty += qty_diff
488+
qty_dict.opening_val += value_diff
489+
490+
elif entry.posting_date >= self.from_date and entry.posting_date <= self.to_date:
491+
if flt(qty_diff, self.float_precision) >= 0:
492+
qty_dict.in_qty += qty_diff
493+
else:
494+
qty_dict.out_qty += abs(qty_diff)
495+
496+
if flt(value_diff, self.float_precision) >= 0:
497+
qty_dict.in_val += value_diff
498+
else:
499+
qty_dict.out_val += abs(value_diff)
500+
501+
qty_dict.val_rate = entry.valuation_rate
502+
qty_dict.bal_qty += qty_diff
503+
qty_dict.bal_val += value_diff
504+
505+
def initialize_data(self, group_by_key, entry):
506+
self.item_warehouse_map[group_by_key] = frappe._dict(
507+
{
508+
"item_code": entry.item_code,
509+
"warehouse": entry.warehouse,
510+
"item_group": entry.item_group,
511+
"company": entry.company,
512+
"currency": self.company_currency,
513+
"stock_uom": entry.stock_uom,
514+
"item_name": entry.item_name,
515+
"opening_qty": 0.0,
516+
"opening_val": 0.0,
517+
"opening_fifo_queue": [],
518+
"in_qty": 0.0,
519+
"in_val": 0.0,
520+
"out_qty": 0.0,
521+
"out_val": 0.0,
522+
"bal_qty": 0.0,
523+
"bal_val": 0.0,
524+
"val_rate": 0.0,
525+
}
526+
)
527+
528+
def get_group_by_key(self, row) -> tuple:
529+
group_by_key = [row.item_code, row.warehouse]
530+
531+
for fieldname in self.inventory_dimensions:
532+
if not row.get(fieldname):
533+
continue
534+
535+
if self.filters.get(fieldname) or self.filters.get("show_dimension_wise_stock"):
536+
group_by_key.append(row.get(fieldname))
537+
538+
return tuple(group_by_key)
539+
540+
>>>>>>> 0874cbc268 (fix: old stock reco entries causing issue in the stock balance report)
340541
def apply_inventory_dimensions_filters(self, query, sle) -> str:
341542
inventory_dimension_fields = self.get_inventory_dimension_fields()
342543
if inventory_dimension_fields:

erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ frappe.query_reports["Stock Ledger Invariant Check"] = {
2121
options: "Item",
2222
get_query: function () {
2323
return {
24-
filters: { is_stock_item: 1, has_serial_no: 0 },
24+
filters: { is_stock_item: 1 },
2525
};
2626
},
2727
},

0 commit comments

Comments
 (0)