feat: get_valuation_rate batch wise
This function is used to show valuation rate on frontend and also as fallback in case values aren't available. Add "batch_no" param to get batch specific valuation rates. Co-Authored-By: Alan Tom <2.alan.tom@gmail.com>
This commit is contained in:
parent
ce0514c8db
commit
342d09a671
@ -249,6 +249,7 @@ class BuyingController(StockController, Subcontracting):
|
||||
"posting_time": self.get('posting_time'),
|
||||
"qty": -1 * flt(d.get('stock_qty')),
|
||||
"serial_no": d.get('serial_no'),
|
||||
"batch_no": d.get("batch_no"),
|
||||
"company": self.company,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
|
@ -420,6 +420,7 @@ def get_rate_for_return(voucher_type, voucher_no, item_code, return_against=None
|
||||
"posting_time": sle.get('posting_time'),
|
||||
"qty": sle.actual_qty,
|
||||
"serial_no": sle.get('serial_no'),
|
||||
"batch_no": sle.get("batch_no"),
|
||||
"company": sle.company,
|
||||
"voucher_type": sle.voucher_type,
|
||||
"voucher_no": sle.voucher_no
|
||||
|
@ -394,6 +394,7 @@ class SellingController(StockController):
|
||||
"posting_time": self.get('posting_time') or nowtime(),
|
||||
"qty": qty if cint(self.get("is_return")) else (-1 * qty),
|
||||
"serial_no": d.get('serial_no'),
|
||||
"batch_no": d.get("batch_no"),
|
||||
"company": self.company,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
|
@ -719,6 +719,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
'posting_time': posting_time,
|
||||
'qty': item.qty * item.conversion_factor,
|
||||
'serial_no': item.serial_no,
|
||||
'batch_no': item.batch_no,
|
||||
'voucher_type': voucher_type,
|
||||
'company': company,
|
||||
'allow_zero_valuation_rate': item.allow_zero_valuation_rate
|
||||
|
@ -8,7 +8,11 @@ from frappe.utils import cint, flt
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||
create_stock_reconciliation,
|
||||
)
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
|
||||
@ -345,6 +349,41 @@ class TestBatch(ERPNextTestCase):
|
||||
|
||||
self.assertEqual(sle.stock_queue, []) # queues don't apply on batched items
|
||||
|
||||
def test_moving_batch_valuation_rates(self):
|
||||
item_code = "_TestBatchWiseVal"
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
self.make_batch_item(item_code)
|
||||
|
||||
def assertValuation(expected):
|
||||
actual = get_valuation_rate(item_code, warehouse, "voucher_type", "voucher_no", batch_no=batch_no)
|
||||
self.assertAlmostEqual(actual, expected)
|
||||
|
||||
se = make_stock_entry(item_code=item_code, qty=100, rate=10, target=warehouse)
|
||||
batch_no = se.items[0].batch_no
|
||||
assertValuation(10)
|
||||
|
||||
# consumption should never affect current valuation rate
|
||||
make_stock_entry(item_code=item_code, qty=20, source=warehouse)
|
||||
assertValuation(10)
|
||||
|
||||
make_stock_entry(item_code=item_code, qty=30, source=warehouse)
|
||||
assertValuation(10)
|
||||
|
||||
# 50 * 10 = 500 current value, add more item with higher valuation
|
||||
make_stock_entry(item_code=item_code, qty=50, rate=20, target=warehouse, batch_no=batch_no)
|
||||
assertValuation(15)
|
||||
|
||||
# consuming again shouldn't do anything
|
||||
make_stock_entry(item_code=item_code, qty=20, source=warehouse)
|
||||
assertValuation(15)
|
||||
|
||||
# reset rate with stock reconiliation
|
||||
create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=10, rate=25, batch_no=batch_no)
|
||||
assertValuation(25)
|
||||
|
||||
make_stock_entry(item_code=item_code, qty=20, rate=20, target=warehouse, batch_no=batch_no)
|
||||
assertValuation((20 * 20 + 10 * 25) / (10 + 20))
|
||||
|
||||
|
||||
def create_batch(item_code, rate, create_item_price_for_batch):
|
||||
pi = make_purchase_invoice(company="_Test Company",
|
||||
|
@ -425,6 +425,7 @@ frappe.ui.form.on('Stock Entry', {
|
||||
'posting_time' : frm.doc.posting_time,
|
||||
'warehouse' : cstr(item.s_warehouse) || cstr(item.t_warehouse),
|
||||
'serial_no' : item.serial_no,
|
||||
'batch_no' : item.batch_no,
|
||||
'company' : frm.doc.company,
|
||||
'qty' : item.s_warehouse ? -1*flt(item.transfer_qty) : flt(item.transfer_qty),
|
||||
'voucher_type' : frm.doc.doctype,
|
||||
@ -457,6 +458,7 @@ frappe.ui.form.on('Stock Entry', {
|
||||
'warehouse': cstr(child.s_warehouse) || cstr(child.t_warehouse),
|
||||
'transfer_qty': child.transfer_qty,
|
||||
'serial_no': child.serial_no,
|
||||
'batch_no': child.batch_no,
|
||||
'qty': child.s_warehouse ? -1* child.transfer_qty : child.transfer_qty,
|
||||
'posting_date': frm.doc.posting_date,
|
||||
'posting_time': frm.doc.posting_time,
|
||||
@ -680,6 +682,7 @@ frappe.ui.form.on('Stock Entry Detail', {
|
||||
'warehouse' : cstr(d.s_warehouse) || cstr(d.t_warehouse),
|
||||
'transfer_qty' : d.transfer_qty,
|
||||
'serial_no' : d.serial_no,
|
||||
'batch_no' : d.batch_no,
|
||||
'bom_no' : d.bom_no,
|
||||
'expense_account' : d.expense_account,
|
||||
'cost_center' : d.cost_center,
|
||||
|
@ -510,7 +510,7 @@ class StockEntry(StockController):
|
||||
d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse,
|
||||
self.doctype, self.name, d.allow_zero_valuation_rate,
|
||||
currency=erpnext.get_company_currency(self.company), company=self.company,
|
||||
raise_error_if_no_rate=raise_error_if_no_rate)
|
||||
raise_error_if_no_rate=raise_error_if_no_rate, batch_no=d.batch_no)
|
||||
|
||||
d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
|
||||
if d.is_process_loss:
|
||||
@ -541,6 +541,7 @@ class StockEntry(StockController):
|
||||
"posting_time": self.posting_time,
|
||||
"qty": item.s_warehouse and -1*flt(item.transfer_qty) or flt(item.transfer_qty),
|
||||
"serial_no": item.serial_no,
|
||||
"batch_no": item.batch_no,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"company": self.company,
|
||||
|
@ -634,7 +634,7 @@ class update_entries_after(object):
|
||||
if not allow_zero_rate:
|
||||
self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
|
||||
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
|
||||
currency=erpnext.get_company_currency(sle.company), company=sle.company)
|
||||
currency=erpnext.get_company_currency(sle.company), company=sle.company, batch_no=sle.batch_no)
|
||||
|
||||
def get_incoming_value_for_serial_nos(self, sle, serial_nos):
|
||||
# get rate from serial nos within same company
|
||||
@ -702,7 +702,7 @@ class update_entries_after(object):
|
||||
if not allow_zero_valuation_rate:
|
||||
self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
|
||||
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
|
||||
currency=erpnext.get_company_currency(sle.company), company=sle.company)
|
||||
currency=erpnext.get_company_currency(sle.company), company=sle.company, batch_no=sle.batch_no)
|
||||
|
||||
def update_queue_values(self, sle):
|
||||
incoming_rate = flt(sle.incoming_rate)
|
||||
@ -722,7 +722,7 @@ class update_entries_after(object):
|
||||
if not allow_zero_valuation_rate:
|
||||
return get_valuation_rate(sle.item_code, sle.warehouse,
|
||||
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
|
||||
currency=erpnext.get_company_currency(sle.company), company=sle.company)
|
||||
currency=erpnext.get_company_currency(sle.company), company=sle.company, batch_no=sle.batch_no)
|
||||
else:
|
||||
return 0.0
|
||||
|
||||
@ -950,21 +950,38 @@ def _get_batch_outgoing_rate(item_code, warehouse, batch_no, posting_date, posti
|
||||
|
||||
|
||||
def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
|
||||
allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True):
|
||||
allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True, batch_no=None):
|
||||
|
||||
if not company:
|
||||
company = frappe.get_cached_value("Warehouse", warehouse, "company")
|
||||
|
||||
last_valuation_rate = None
|
||||
|
||||
# Get moving average rate of a specific batch number
|
||||
if warehouse and batch_no and frappe.db.get_value("Batch", batch_no, "use_batchwise_valuation"):
|
||||
last_valuation_rate = frappe.db.sql("""
|
||||
select sum(stock_value_difference) / sum(actual_qty)
|
||||
from `tabStock Ledger Entry`
|
||||
where
|
||||
item_code = %s
|
||||
AND warehouse = %s
|
||||
AND batch_no = %s
|
||||
AND is_cancelled = 0
|
||||
AND NOT (voucher_no = %s AND voucher_type = %s)
|
||||
""",
|
||||
(item_code, warehouse, batch_no, voucher_no, voucher_type))
|
||||
|
||||
# Get valuation rate from last sle for the same item and warehouse
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry` force index (item_warehouse)
|
||||
where
|
||||
item_code = %s
|
||||
AND warehouse = %s
|
||||
AND valuation_rate >= 0
|
||||
AND is_cancelled = 0
|
||||
AND NOT (voucher_no = %s AND voucher_type = %s)
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse, voucher_no, voucher_type))
|
||||
if not last_valuation_rate or last_valuation_rate[0][0] is None:
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry` force index (item_warehouse)
|
||||
where
|
||||
item_code = %s
|
||||
AND warehouse = %s
|
||||
AND valuation_rate >= 0
|
||||
AND is_cancelled = 0
|
||||
AND NOT (voucher_no = %s AND voucher_type = %s)
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse, voucher_no, voucher_type))
|
||||
|
||||
if not last_valuation_rate:
|
||||
# Get valuation rate from last sle for the item against any warehouse
|
||||
|
@ -231,7 +231,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
|
||||
in_rate = get_valuation_rate(args.get('item_code'), args.get('warehouse'),
|
||||
args.get('voucher_type'), voucher_no, args.get('allow_zero_valuation'),
|
||||
currency=erpnext.get_company_currency(args.get('company')), company=args.get('company'),
|
||||
raise_error_if_no_rate=raise_error_if_no_rate)
|
||||
raise_error_if_no_rate=raise_error_if_no_rate, batch_no=args.get("batch_no"))
|
||||
|
||||
return flt(in_rate)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user