perf: serial and batch bundle valuation (reposting) (#40255)
perf: serial and batch bundle valuation
This commit is contained in:
parent
031b99f373
commit
6379238893
@ -13,7 +13,9 @@ class DeprecatedSerialNoValuation:
|
|||||||
):
|
):
|
||||||
return
|
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)
|
actual_qty = flt(self.sle.actual_qty)
|
||||||
|
|
||||||
@ -25,8 +27,21 @@ class DeprecatedSerialNoValuation:
|
|||||||
|
|
||||||
self.stock_value_change += stock_value_change
|
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
|
@deprecated
|
||||||
def get_incoming_value_for_serial_nos(self, serial_nos):
|
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
|
# get rate from serial nos within same company
|
||||||
incoming_values = 0.0
|
incoming_values = 0.0
|
||||||
for serial_no in serial_nos:
|
for serial_no in serial_nos:
|
||||||
@ -42,18 +57,19 @@ class DeprecatedSerialNoValuation:
|
|||||||
| (table.serial_no.like("%\n" + serial_no + "\n%"))
|
| (table.serial_no.like("%\n" + serial_no + "\n%"))
|
||||||
)
|
)
|
||||||
& (table.company == self.sle.company)
|
& (table.company == self.sle.company)
|
||||||
|
& (table.warehouse == self.sle.warehouse)
|
||||||
& (table.serial_and_batch_bundle.isnull())
|
& (table.serial_and_batch_bundle.isnull())
|
||||||
|
& (table.actual_qty > 0)
|
||||||
& (table.is_cancelled == 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)
|
.orderby(table.posting_datetime, order=Order.desc)
|
||||||
|
.limit(1)
|
||||||
).run(as_dict=1)
|
).run(as_dict=1)
|
||||||
|
|
||||||
for sle in stock_ledgers:
|
for sle in stock_ledgers:
|
||||||
self.serial_no_incoming_rate[serial_no] += (
|
self.serial_no_incoming_rate[serial_no] += flt(sle.incoming_rate)
|
||||||
flt(sle.incoming_rate)
|
|
||||||
if sle.actual_qty > 0
|
|
||||||
else (sle.stock_value_difference / sle.actual_qty) * -1
|
|
||||||
)
|
|
||||||
incoming_values += self.serial_no_incoming_rate[serial_no]
|
incoming_values += self.serial_no_incoming_rate[serial_no]
|
||||||
|
|
||||||
return incoming_values
|
return incoming_values
|
||||||
|
@ -415,23 +415,6 @@ class TestLandedCostVoucher(FrappeTestCase):
|
|||||||
create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=charges)
|
create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=charges)
|
||||||
new_purchase_rate = serial_no_rate + 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_value_difference = frappe.db.get_value(
|
||||||
"Stock Ledger Entry",
|
"Stock Ledger Entry",
|
||||||
filters={
|
filters={
|
||||||
|
@ -332,13 +332,8 @@ class SerialandBatchBundle(Document):
|
|||||||
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
|
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
|
||||||
|
|
||||||
for d in self.entries:
|
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)
|
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)
|
d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate)
|
||||||
|
|
||||||
if save:
|
if save:
|
||||||
|
@ -4,8 +4,9 @@ from typing import List
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _, bold
|
from frappe import _, bold
|
||||||
from frappe.model.naming import make_autoname
|
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 frappe.utils import cint, cstr, flt, get_link_to_form, now, nowtime, today
|
||||||
|
from pypika import Order
|
||||||
|
|
||||||
from erpnext.stock.deprecated_serial_batch import (
|
from erpnext.stock.deprecated_serial_batch import (
|
||||||
DeprecatedBatchNoValuation,
|
DeprecatedBatchNoValuation,
|
||||||
@ -424,19 +425,21 @@ class SerialNoValuation(DeprecatedSerialNoValuation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
entries = self.get_serial_no_ledgers()
|
|
||||||
|
|
||||||
self.serial_no_incoming_rate = defaultdict(float)
|
self.serial_no_incoming_rate = defaultdict(float)
|
||||||
self.stock_value_change = 0.0
|
self.stock_value_change = 0.0
|
||||||
|
|
||||||
for ledger in entries:
|
serial_nos = self.get_serial_nos()
|
||||||
self.stock_value_change += ledger.incoming_rate
|
for serial_no in serial_nos:
|
||||||
self.serial_no_incoming_rate[ledger.serial_no] += ledger.incoming_rate
|
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()
|
self.calculate_stock_value_from_deprecarated_ledgers()
|
||||||
|
|
||||||
def get_serial_no_ledgers(self):
|
def get_incoming_rate_from_bundle(self, serial_no) -> float:
|
||||||
serial_nos = self.get_serial_nos()
|
|
||||||
bundle = frappe.qb.DocType("Serial and Batch Bundle")
|
bundle = frappe.qb.DocType("Serial and Batch Bundle")
|
||||||
bundle_child = frappe.qb.DocType("Serial and Batch Entry")
|
bundle_child = frappe.qb.DocType("Serial and Batch Entry")
|
||||||
|
|
||||||
@ -444,20 +447,18 @@ class SerialNoValuation(DeprecatedSerialNoValuation):
|
|||||||
frappe.qb.from_(bundle)
|
frappe.qb.from_(bundle)
|
||||||
.inner_join(bundle_child)
|
.inner_join(bundle_child)
|
||||||
.on(bundle.name == bundle_child.parent)
|
.on(bundle.name == bundle_child.parent)
|
||||||
.select(
|
.select((bundle_child.incoming_rate * bundle_child.qty).as_("incoming_rate"))
|
||||||
bundle.name,
|
|
||||||
bundle_child.serial_no,
|
|
||||||
(bundle_child.incoming_rate * bundle_child.qty).as_("incoming_rate"),
|
|
||||||
)
|
|
||||||
.where(
|
.where(
|
||||||
(bundle.is_cancelled == 0)
|
(bundle.is_cancelled == 0)
|
||||||
& (bundle.docstatus == 1)
|
& (bundle.docstatus == 1)
|
||||||
& (bundle_child.serial_no.isin(serial_nos))
|
& (bundle_child.serial_no == serial_no)
|
||||||
& (bundle.type_of_transaction.isin(["Inward", "Outward"]))
|
& (bundle.type_of_transaction == "Inward")
|
||||||
|
& (bundle_child.qty > 0)
|
||||||
& (bundle.item_code == self.sle.item_code)
|
& (bundle.item_code == self.sle.item_code)
|
||||||
& (bundle_child.warehouse == self.sle.warehouse)
|
& (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
|
# Important to exclude the current voucher to calculate correct the stock value difference
|
||||||
@ -474,7 +475,8 @@ class SerialNoValuation(DeprecatedSerialNoValuation):
|
|||||||
|
|
||||||
query = query.where(timestamp_condition)
|
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):
|
def get_serial_nos(self):
|
||||||
if self.sle.get("serial_nos"):
|
if self.sle.get("serial_nos"):
|
||||||
|
@ -952,7 +952,12 @@ class update_entries_after(object):
|
|||||||
get_rate_for_return, # don't move this import to top
|
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(
|
rate = get_incoming_rate(
|
||||||
{
|
{
|
||||||
"item_code": sle.item_code,
|
"item_code": sle.item_code,
|
||||||
@ -979,6 +984,18 @@ class update_entries_after(object):
|
|||||||
voucher_detail_no=sle.voucher_detail_no,
|
voucher_detail_no=sle.voucher_detail_no,
|
||||||
sle=sle,
|
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 (
|
elif (
|
||||||
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
|
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
|
||||||
and sle.voucher_detail_no
|
and sle.voucher_detail_no
|
||||||
|
Loading…
x
Reference in New Issue
Block a user