mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-07 04:24:13 +00:00
chore: fix conflicts
Added a method to prepare stock reconciliation voucher-wise count and updated logic for handling stock reconciliation entries.
This commit is contained in:
@@ -7,12 +7,8 @@ from typing import Any, TypedDict
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
<<<<<<< HEAD
|
||||
from frappe.query_builder import Order
|
||||
from frappe.query_builder.functions import Coalesce
|
||||
=======
|
||||
from frappe.query_builder.functions import Coalesce, Count
|
||||
>>>>>>> 0874cbc268 (fix: old stock reco entries causing issue in the stock balance report)
|
||||
from frappe.utils import add_days, cint, date_diff, flt, getdate
|
||||
from frappe.utils.nestedset import get_descendants_of
|
||||
|
||||
@@ -159,6 +155,8 @@ class StockBalanceReport:
|
||||
if self.filters.get("show_stock_ageing_data"):
|
||||
self.sle_entries = self.sle_query.run(as_dict=True)
|
||||
|
||||
self.prepare_stock_reco_voucher_wise_count()
|
||||
|
||||
# HACK: This is required to avoid causing db query in flt
|
||||
_system_settings = frappe.get_cached_doc("System Settings")
|
||||
with frappe.db.unbuffered_cursor():
|
||||
@@ -185,6 +183,30 @@ class StockBalanceReport:
|
||||
|
||||
return item_warehouse_map
|
||||
|
||||
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)
|
||||
& (item.has_serial_no == 1)
|
||||
)
|
||||
.groupby(doctype.voucher_detail_no)
|
||||
)
|
||||
|
||||
data = query.run(as_list=True)
|
||||
if data:
|
||||
self.stock_reco_voucher_wise_count = frappe._dict(data)
|
||||
|
||||
def get_sre_reserved_qty_details(self) -> dict:
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
get_sre_reserved_qty_for_items_and_warehouses as get_reserved_qty_details,
|
||||
@@ -203,9 +225,13 @@ class StockBalanceReport:
|
||||
qty_dict[field] = entry.get(field)
|
||||
|
||||
if entry.voucher_type == "Stock Reconciliation" and (
|
||||
not entry.batch_no and not entry.serial_no and not entry.serial_and_batch_bundle
|
||||
not entry.batch_no or entry.serial_no or entry.serial_and_batch_bundle
|
||||
):
|
||||
qty_diff = flt(entry.qty_after_transaction) - flt(qty_dict.bal_qty)
|
||||
if entry.serial_no and self.stock_reco_voucher_wise_count.get(entry.voucher_detail_no, 0) == 1:
|
||||
qty_dict.bal_qty = 0.0
|
||||
qty_diff = flt(entry.actual_qty)
|
||||
else:
|
||||
qty_diff = flt(entry.qty_after_transaction) - flt(qty_dict.bal_qty)
|
||||
else:
|
||||
qty_diff = flt(entry.actual_qty)
|
||||
|
||||
@@ -342,202 +368,6 @@ class StockBalanceReport:
|
||||
|
||||
self.sle_query = query
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
def prepare_item_warehouse_map_for_current_period(self):
|
||||
self.opening_vouchers = self.get_opening_vouchers()
|
||||
|
||||
if self.filters.get("show_stock_ageing_data"):
|
||||
self.sle_entries = self.sle_query.run(as_dict=True)
|
||||
|
||||
self.prepare_stock_reco_voucher_wise_count()
|
||||
|
||||
# HACK: This is required to avoid causing db query in flt
|
||||
_system_settings = frappe.get_cached_doc("System Settings")
|
||||
with frappe.db.unbuffered_cursor():
|
||||
if not self.filters.get("show_stock_ageing_data"):
|
||||
self.sle_entries = self.sle_query.run(as_dict=True, as_iterator=True)
|
||||
|
||||
for entry in self.sle_entries:
|
||||
group_by_key = self.get_group_by_key(entry)
|
||||
if group_by_key not in self.item_warehouse_map:
|
||||
self.initialize_data(group_by_key, entry)
|
||||
|
||||
self.prepare_item_warehouse_map(entry, group_by_key)
|
||||
|
||||
self.item_warehouse_map = filter_items_with_no_transactions(
|
||||
self.item_warehouse_map, self.float_precision, self.inventory_dimensions
|
||||
)
|
||||
|
||||
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)
|
||||
& (item.has_serial_no == 1)
|
||||
)
|
||||
.groupby(doctype.voucher_detail_no)
|
||||
)
|
||||
|
||||
data = query.run(as_list=True)
|
||||
if data:
|
||||
self.stock_reco_voucher_wise_count = frappe._dict(data)
|
||||
|
||||
def prepare_new_data(self):
|
||||
if self.filters.get("show_stock_ageing_data"):
|
||||
self.filters["show_warehouse_wise_stock"] = True
|
||||
item_wise_fifo_queue = FIFOSlots(self.filters).generate()
|
||||
|
||||
_func = itemgetter(1)
|
||||
|
||||
del self.sle_entries
|
||||
|
||||
sre_details = self.get_sre_reserved_qty_details()
|
||||
|
||||
variant_values = {}
|
||||
if self.filters.get("show_variant_attributes"):
|
||||
variant_values = self.get_variant_values_for()
|
||||
|
||||
for _key, report_data in self.item_warehouse_map.items():
|
||||
if variant_data := variant_values.get(report_data.item_code):
|
||||
report_data.update(variant_data)
|
||||
|
||||
if self.filters.get("show_stock_ageing_data"):
|
||||
opening_fifo_queue = self.get_opening_fifo_queue(report_data) or []
|
||||
|
||||
fifo_queue = []
|
||||
if fifo_queue := item_wise_fifo_queue.get((report_data.item_code, report_data.warehouse)):
|
||||
fifo_queue = fifo_queue.get("fifo_queue")
|
||||
|
||||
if fifo_queue:
|
||||
opening_fifo_queue.extend(fifo_queue)
|
||||
|
||||
stock_ageing_data = {"average_age": 0, "earliest_age": 0, "latest_age": 0}
|
||||
|
||||
if opening_fifo_queue:
|
||||
fifo_queue = sorted(filter(_func, opening_fifo_queue), key=_func)
|
||||
if not fifo_queue:
|
||||
continue
|
||||
|
||||
to_date = self.to_date
|
||||
stock_ageing_data["average_age"] = get_average_age(fifo_queue, to_date)
|
||||
stock_ageing_data["earliest_age"] = date_diff(to_date, fifo_queue[0][1])
|
||||
stock_ageing_data["latest_age"] = date_diff(to_date, fifo_queue[-1][1])
|
||||
stock_ageing_data["fifo_queue"] = fifo_queue
|
||||
|
||||
report_data.update(stock_ageing_data)
|
||||
|
||||
report_data.update(
|
||||
{"reserved_stock": sre_details.get((report_data.item_code, report_data.warehouse), 0.0)}
|
||||
)
|
||||
|
||||
if (
|
||||
not self.filters.get("include_zero_stock_items")
|
||||
and report_data
|
||||
and report_data.bal_qty == 0
|
||||
and report_data.bal_val == 0
|
||||
):
|
||||
continue
|
||||
|
||||
self.data.append(report_data)
|
||||
|
||||
def get_sre_reserved_qty_details(self) -> dict:
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
get_sre_reserved_qty_for_items_and_warehouses as get_reserved_qty_details,
|
||||
)
|
||||
|
||||
item_code_list, warehouse_list = [], []
|
||||
for d in self.item_warehouse_map:
|
||||
item_code_list.append(d[0])
|
||||
warehouse_list.append(d[1])
|
||||
|
||||
return get_reserved_qty_details(item_code_list, warehouse_list)
|
||||
|
||||
def prepare_item_warehouse_map(self, entry, group_by_key):
|
||||
qty_dict = self.item_warehouse_map[group_by_key]
|
||||
for field in self.inventory_dimensions:
|
||||
qty_dict[field] = entry.get(field)
|
||||
|
||||
if entry.voucher_type == "Stock Reconciliation" and (
|
||||
not entry.batch_no or entry.serial_no or entry.serial_and_batch_bundle
|
||||
):
|
||||
if entry.serial_no and self.stock_reco_voucher_wise_count.get(entry.voucher_detail_no, 0) == 1:
|
||||
qty_dict.bal_qty = 0.0
|
||||
qty_diff = flt(entry.actual_qty)
|
||||
else:
|
||||
qty_diff = flt(entry.qty_after_transaction) - flt(qty_dict.bal_qty)
|
||||
else:
|
||||
qty_diff = flt(entry.actual_qty)
|
||||
|
||||
value_diff = flt(entry.stock_value_difference)
|
||||
|
||||
if entry.posting_date < self.from_date or entry.voucher_no in self.opening_vouchers.get(
|
||||
entry.voucher_type, []
|
||||
):
|
||||
qty_dict.opening_qty += qty_diff
|
||||
qty_dict.opening_val += value_diff
|
||||
|
||||
elif entry.posting_date >= self.from_date and entry.posting_date <= self.to_date:
|
||||
if flt(qty_diff, self.float_precision) >= 0:
|
||||
qty_dict.in_qty += qty_diff
|
||||
else:
|
||||
qty_dict.out_qty += abs(qty_diff)
|
||||
|
||||
if flt(value_diff, self.float_precision) >= 0:
|
||||
qty_dict.in_val += value_diff
|
||||
else:
|
||||
qty_dict.out_val += abs(value_diff)
|
||||
|
||||
qty_dict.val_rate = entry.valuation_rate
|
||||
qty_dict.bal_qty += qty_diff
|
||||
qty_dict.bal_val += value_diff
|
||||
|
||||
def initialize_data(self, group_by_key, entry):
|
||||
self.item_warehouse_map[group_by_key] = frappe._dict(
|
||||
{
|
||||
"item_code": entry.item_code,
|
||||
"warehouse": entry.warehouse,
|
||||
"item_group": entry.item_group,
|
||||
"company": entry.company,
|
||||
"currency": self.company_currency,
|
||||
"stock_uom": entry.stock_uom,
|
||||
"item_name": entry.item_name,
|
||||
"opening_qty": 0.0,
|
||||
"opening_val": 0.0,
|
||||
"opening_fifo_queue": [],
|
||||
"in_qty": 0.0,
|
||||
"in_val": 0.0,
|
||||
"out_qty": 0.0,
|
||||
"out_val": 0.0,
|
||||
"bal_qty": 0.0,
|
||||
"bal_val": 0.0,
|
||||
"val_rate": 0.0,
|
||||
}
|
||||
)
|
||||
|
||||
def get_group_by_key(self, row) -> tuple:
|
||||
group_by_key = [row.item_code, row.warehouse]
|
||||
|
||||
for fieldname in self.inventory_dimensions:
|
||||
if not row.get(fieldname):
|
||||
continue
|
||||
|
||||
if self.filters.get(fieldname) or self.filters.get("show_dimension_wise_stock"):
|
||||
group_by_key.append(row.get(fieldname))
|
||||
|
||||
return tuple(group_by_key)
|
||||
|
||||
>>>>>>> 0874cbc268 (fix: old stock reco entries causing issue in the stock balance report)
|
||||
def apply_inventory_dimensions_filters(self, query, sle) -> str:
|
||||
inventory_dimension_fields = self.get_inventory_dimension_fields()
|
||||
if inventory_dimension_fields:
|
||||
|
||||
Reference in New Issue
Block a user