Skip to content

Commit

Permalink
perf: serial and batch bundle valuation (reposting) (backport #40255) (
Browse files Browse the repository at this point in the history
…#40262)

* perf: serial and batch bundle valuation (reposting) (#40255)

perf: serial and batch bundle valuation
(cherry picked from commit 6379238)

* chore: fix styles

* chore: fix linters

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
  • Loading branch information
mergify[bot] and rohitwaghchaure authored Mar 4, 2024
1 parent 0b1417e commit 93f3af7
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 47 deletions.
29 changes: 23 additions & 6 deletions erpnext/stock/deprecated_serial_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ def calculate_stock_value_from_deprecarated_ledgers(self):
):
return

serial_nos = self.get_serial_nos()
serial_nos = self.get_filterd_serial_nos()
if not serial_nos:
return

actual_qty = flt(self.sle.actual_qty)

Expand All @@ -25,8 +27,21 @@ def calculate_stock_value_from_deprecarated_ledgers(self):

self.stock_value_change += stock_value_change

def get_filterd_serial_nos(self):
serial_nos = []
non_filtered_serial_nos = self.get_serial_nos()

# If the serial no inwarded using the Serial and Batch Bundle, then the serial no should not be considered
for serial_no in non_filtered_serial_nos:
if serial_no and serial_no not in self.serial_no_incoming_rate:
serial_nos.append(serial_no)

return serial_nos

@deprecated
def get_incoming_value_for_serial_nos(self, serial_nos):
from erpnext.stock.utils import get_combine_datetime

# get rate from serial nos within same company
incoming_values = 0.0
for serial_no in serial_nos:
Expand All @@ -42,18 +57,20 @@ def get_incoming_value_for_serial_nos(self, serial_nos):
| (table.serial_no.like("%\n" + serial_no + "\n%"))
)
& (table.company == self.sle.company)
& (table.warehouse == self.sle.warehouse)
& (table.serial_and_batch_bundle.isnull())
& (table.actual_qty > 0)
& (table.is_cancelled == 0)
& (
table.posting_datetime <= get_combine_datetime(self.sle.posting_date, self.sle.posting_time)
)
)
.orderby(table.posting_datetime, order=Order.desc)
.limit(1)
).run(as_dict=1)

for sle in stock_ledgers:
self.serial_no_incoming_rate[serial_no] += (
flt(sle.incoming_rate)
if sle.actual_qty > 0
else (sle.stock_value_difference / sle.actual_qty) * -1
)
self.serial_no_incoming_rate[serial_no] += flt(sle.incoming_rate)
incoming_values += self.serial_no_incoming_rate[serial_no]

return incoming_values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,23 +415,6 @@ def test_serialized_lcv_delivered(self):
create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=charges)
new_purchase_rate = serial_no_rate + charges

sn_obj = SerialNoValuation(
sle=frappe._dict(
{
"posting_date": today(),
"posting_time": nowtime(),
"item_code": "_Test Serialized Item",
"warehouse": "Stores - TCP1",
"serial_nos": [serial_no],
}
)
)

new_serial_no_rate = sn_obj.get_incoming_rate_of_serial_no(serial_no)

# Since the serial no is already delivered the rate must be zero
self.assertFalse(new_serial_no_rate)

stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
filters={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,8 @@ def set_incoming_rate_for_inward_transaction(self, row=None, save=False):
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)

for d in self.entries:
if not rate or (
flt(rate, precision) == flt(d.incoming_rate, precision) and d.stock_value_difference
):
continue

d.incoming_rate = flt(rate, precision)
if self.has_batch_no:
if d.qty:
d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate)

if save:
Expand Down
36 changes: 19 additions & 17 deletions erpnext/stock/serial_batch_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import frappe
from frappe import _, bold
from frappe.model.naming import make_autoname
from frappe.query_builder.functions import CombineDatetime, Sum
from frappe.query_builder.functions import CombineDatetime, Sum, Timestamp
from frappe.utils import cint, cstr, flt, get_link_to_form, now, nowtime, today
from pypika import Order

from erpnext.stock.deprecated_serial_batch import (
DeprecatedBatchNoValuation,
Expand Down Expand Up @@ -424,40 +425,40 @@ def calculate_stock_value_change(self):
)

else:
entries = self.get_serial_no_ledgers()

self.serial_no_incoming_rate = defaultdict(float)
self.stock_value_change = 0.0

for ledger in entries:
self.stock_value_change += ledger.incoming_rate
self.serial_no_incoming_rate[ledger.serial_no] += ledger.incoming_rate
serial_nos = self.get_serial_nos()
for serial_no in serial_nos:
incoming_rate = self.get_incoming_rate_from_bundle(serial_no)
if not incoming_rate:
continue

self.stock_value_change += incoming_rate
self.serial_no_incoming_rate[serial_no] += incoming_rate

self.calculate_stock_value_from_deprecarated_ledgers()

def get_serial_no_ledgers(self):
serial_nos = self.get_serial_nos()
def get_incoming_rate_from_bundle(self, serial_no) -> float:
bundle = frappe.qb.DocType("Serial and Batch Bundle")
bundle_child = frappe.qb.DocType("Serial and Batch Entry")

query = (
frappe.qb.from_(bundle)
.inner_join(bundle_child)
.on(bundle.name == bundle_child.parent)
.select(
bundle.name,
bundle_child.serial_no,
(bundle_child.incoming_rate * bundle_child.qty).as_("incoming_rate"),
)
.select((bundle_child.incoming_rate * bundle_child.qty).as_("incoming_rate"))
.where(
(bundle.is_cancelled == 0)
& (bundle.docstatus == 1)
& (bundle_child.serial_no.isin(serial_nos))
& (bundle.type_of_transaction.isin(["Inward", "Outward"]))
& (bundle_child.serial_no == serial_no)
& (bundle.type_of_transaction == "Inward")
& (bundle_child.qty > 0)
& (bundle.item_code == self.sle.item_code)
& (bundle_child.warehouse == self.sle.warehouse)
)
.orderby(bundle.posting_date, bundle.posting_time, bundle.creation)
.orderby(Timestamp(bundle.posting_date, bundle.posting_time), order=Order.desc)
.limit(1)
)

# Important to exclude the current voucher to calculate correct the stock value difference
Expand All @@ -474,7 +475,8 @@ def get_serial_no_ledgers(self):

query = query.where(timestamp_condition)

return query.run(as_dict=True)
incoming_rate = query.run()
return flt(incoming_rate[0][0]) if incoming_rate else 0.0

def get_serial_nos(self):
if self.sle.get("serial_nos"):
Expand Down
19 changes: 18 additions & 1 deletion erpnext/stock/stock_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,12 @@ def get_incoming_outgoing_rate_from_transaction(self, sle):
get_rate_for_return, # don't move this import to top
)

if self.valuation_method == "Moving Average":
if (
self.valuation_method == "Moving Average"
and not sle.get("serial_no")
and not sle.get("batch_no")
and not sle.get("serial_and_batch_bundle")
):
rate = get_incoming_rate(
{
"item_code": sle.item_code,
Expand All @@ -979,6 +984,18 @@ def get_incoming_outgoing_rate_from_transaction(self, sle):
voucher_detail_no=sle.voucher_detail_no,
sle=sle,
)

if (
sle.get("serial_and_batch_bundle")
and rate > 0
and sle.voucher_type in ["Delivery Note", "Sales Invoice"]
):
frappe.db.set_value(
sle.voucher_type + " Item",
sle.voucher_detail_no,
"incoming_rate",
rate,
)
elif (
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
and sle.voucher_detail_no
Expand Down

0 comments on commit 93f3af7

Please sign in to comment.