Skip to content

Commit

Permalink
perf: SABB
Browse files Browse the repository at this point in the history
  • Loading branch information
rohitwaghchaure committed Dec 18, 2024
1 parent 5f53961 commit 9c75336
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 30 deletions.
5 changes: 4 additions & 1 deletion erpnext/controllers/stock_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,9 @@ def make_bundle_for_material_transfer(**kwargs):
bundle_doc.calculate_qty_and_amount()
bundle_doc.flags.ignore_permissions = True
bundle_doc.flags.ignore_validate = True
bundle_doc.save(ignore_permissions=True)
if kwargs.do_not_submit:
bundle_doc.save(ignore_permissions=True)
else:
bundle_doc.submit()

return bundle_doc.name
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,10 @@ def validate(self):
self.validate_duplicate_serial_and_batch_no()
self.validate_voucher_no()

if self.docstatus == 0:
self.allow_existing_serial_nos()

if self.type_of_transaction == "Maintenance":
return

self.allow_existing_serial_nos()
if not self.flags.ignore_validate_serial_batch or frappe.flags.in_test:
self.validate_serial_nos_duplicate()
self.check_future_entries_exists()
Expand Down Expand Up @@ -2486,3 +2484,25 @@ def make_batch_no(batch_no, item_code):
@frappe.whitelist()
def is_duplicate_serial_no(bundle_id, serial_no):
return frappe.db.exists("Serial and Batch Entry", {"parent": bundle_id, "serial_no": serial_no})


@frappe.request_cache
def get_bundle_details(bundle_id):
fields = [
"`tabSerial and Batch Entry`.`name`",
"`tabSerial and Batch Entry`.`parent`",
"`tabSerial and Batch Entry`.`serial_no`",
"`tabSerial and Batch Entry`.`batch_no`",
"`tabSerial and Batch Entry`.`qty`",
"`tabSerial and Batch Bundle`.`item_code`",
"`tabSerial and Batch Bundle`.`warehouse`",
"`tabSerial and Batch Bundle`.`is_rejected`",
]

filters = [
["Serial and Batch Bundle", "name", "=", bundle_id],
["Serial and Batch Entry", "docstatus", "=", 1],
["Serial and Batch Bundle", "is_cancelled", "=", 0],
]

return frappe.get_all("Serial and Batch Bundle", fields=fields, filters=filters)
101 changes: 86 additions & 15 deletions erpnext/stock/serial_batch_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,21 @@ def is_rejected(voucher_type, voucher_detail_no, warehouse):

class BatchNoValuation(DeprecatedBatchNoValuation):
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)

self.set(kwargs)
self.batch_nos = self.get_batch_nos()
self.prepare_batches()
self.calculate_avg_rate()
self.calculate_valuation_rate()

def set(self, kwargs):
self.__dict__ = {}
for key, value in kwargs.items():
setattr(self, key, value)
self.__dict__[key] = value

def get(self, key):
return self.__dict__.get(key)

def calculate_avg_rate(self):
if flt(self.sle.actual_qty) > 0:
self.stock_value_change = frappe.get_cached_value(
Expand Down Expand Up @@ -725,15 +732,6 @@ def set_stock_value_difference(self):
stock_value_change = self.batch_avg_rate[batch_no] * ledger.qty
self.stock_value_change += stock_value_change

frappe.db.set_value(
"Serial and Batch Entry",
ledger.name,
{
"stock_value_difference": stock_value_change,
"incoming_rate": self.batch_avg_rate[batch_no],
},
)

def calculate_valuation_rate(self):
if not hasattr(self, "wh_data"):
return
Expand Down Expand Up @@ -947,12 +945,13 @@ def make_serial_and_batch_bundle(self):
if self.get("make_bundle_from_sle") and self.type_of_transaction == "Inward":
doc.flags.ignore_validate_serial_batch = True

doc.save()
self.validate_qty(doc)

if not hasattr(self, "do_not_submit") or not self.do_not_submit:
doc.flags.ignore_voucher_validation = True
doc.submit()
else:
doc.save()

self.validate_qty(doc)

return doc

Expand Down Expand Up @@ -1238,3 +1237,75 @@ def get_serial_nos_batch(serial_nos):
as_list=1,
)
)


def get_valuation_rate_for_inward_entry(voucher_type, voucher_no, voucher_detail_no):
valuation_field = "valuation_rate"
if voucher_type in ["Sales Invoice", "Delivery Note", "Quotation"]:
valuation_field = "incoming_rate"

if voucher_type == "POS Invoice":
valuation_field = "rate"

rate = 0.0
child_table = get_child_table(voucher_type)

if voucher_type == "Subcontracting Receipt":
if not voucher_detail_no:
return 0.0
elif frappe.db.exists("Subcontracting Receipt Supplied Item", voucher_detail_no):
valuation_field = "rate"
child_table = "Subcontracting Receipt Supplied Item"
else:
valuation_field = "rate"
child_table = "Subcontracting Receipt Item"

if not rate and voucher_detail_no and voucher_no:
rate = frappe.db.get_value(child_table, voucher_detail_no, valuation_field)

return rate


def get_child_table(voucher_type):
if voucher_type == "Job Card":
return

parent_child_map = {
"Asset Capitalization": "Asset Capitalization Stock Item",
"Asset Repair": "Asset Repair Consumed Item",
"Quotation": "Packed Item",
"Stock Entry": "Stock Entry Detail",
}

return parent_child_map[voucher_type] if voucher_type in parent_child_map else f"{voucher_type} Item"


def get_valuation_rate_for_outward_entry(sle):
kwargs = get_args_for_sabb(sle)

if sle.has_serial_no:
cls_obj = SerialNoValuation(sle=kwargs, warehouse=sle.warehouse, item_code=sle.item_code)
return cls_obj.serial_no_incoming_rate
else:
cls_obj = BatchNoValuation(sle=kwargs, warehouse=sle.warehouse, item_code=sle.item_code)
return cls_obj.batch_avg_rate

return defaultdict(float)


def get_args_for_sabb(sle):
return frappe._dict(
{
"item_code": sle.item_code,
"warehouse": sle.warehouse,
"posting_date": sle.posting_date,
"posting_time": sle.posting_time,
"qty": sle.actual_qty,
"serial_and_batch_bundle": sle.serial_and_batch_bundle,
"company": sle.company,
"voucher_type": sle.voucher_type,
"voucher_no": sle.voucher_no,
"voucher_detail_no": sle.voucher_detail_no,
"sle": sle.name,
}
)
61 changes: 50 additions & 11 deletions erpnext/stock/stock_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
get_available_batches,
get_bundle_details,
)
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
get_sre_reserved_batch_nos_details,
Expand Down Expand Up @@ -986,21 +987,59 @@ def update_serial_no_status(self, sle):
query.run()

def calculate_valuation_for_serial_batch_bundle(self, sle):
if not frappe.db.exists("Serial and Batch Bundle", sle.serial_and_batch_bundle):
return
from erpnext.stock.serial_batch_bundle import (
get_valuation_rate_for_inward_entry,
get_valuation_rate_for_outward_entry,
)

doc = frappe.get_cached_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle)
if sle.actual_qty > 0:
valuation_rate = get_valuation_rate_for_inward_entry(
sle.voucher_type, sle.voucher_no, sle.voucher_detail_no
)
self.set_valuation_for_sabb(sle, valuation_rate)
else:
valuation_dict = get_valuation_rate_for_outward_entry(sle)
self.set_valuation_for_sabb(sle, valuation_dict=valuation_dict)

doc.set_incoming_rate(save=True, allow_negative_stock=self.allow_negative_stock)
doc.calculate_qty_and_amount(save=True)
def set_valuation_for_sabb(self, sle, valuation_rate=None, valuation_dict=None):
entries = get_bundle_details(sle.serial_and_batch_bundle)
total_qty = 0.0
total_amount = 0.0

for row in entries:
if row.is_rejected:
valuation_rate = 0

elif valuation_rate is None and valuation_dict:
valuation_rate = valuation_dict[row.serial_no] or valuation_dict[row.batch_no]

self.wh_data.stock_value = round_off_if_near_zero(self.wh_data.stock_value + doc.total_amount)
total_qty += row.qty
stock_value_difference = row.qty * flt(valuation_rate)
total_amount += stock_value_difference

precision = doc.precision("total_qty")
self.wh_data.qty_after_transaction += flt(doc.total_qty, precision)
if flt(self.wh_data.qty_after_transaction, precision):
self.wh_data.valuation_rate = flt(self.wh_data.stock_value, precision) / flt(
self.wh_data.qty_after_transaction, precision
frappe.db.set_value(
"Serial and Batch Entry",
row.name,
{
"incoming_rate": valuation_rate,
"stock_value_difference": stock_value_difference,
},
)

frappe.db.set_value(
"Serial and Batch Bundle",
sle.serial_and_batch_bundle,
{
"total_qty": total_qty,
"total_amount": total_amount,
"avg_rate": flt(total_amount) / flt(total_qty),
},
)
self.wh_data.stock_value = round_off_if_near_zero(self.wh_data.stock_value + total_amount)
self.wh_data.qty_after_transaction += total_qty
if self.wh_data.qty_after_transaction:
self.wh_data.valuation_rate = flt(self.wh_data.stock_value) / flt(
self.wh_data.qty_after_transaction
)

def update_valuation_rate_in_serial_and_batch_bundle(self, sle, valuation_rate):
Expand Down

0 comments on commit 9c75336

Please sign in to comment.