mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-14 04:15:10 +00:00
Merge pull request #50506 from mihir-kandoi/company-wise-valuation-method
feat: company wise valuation method
This commit is contained in:
@@ -12,7 +12,6 @@ from frappe.utils import cint, flt, format_datetime, get_datetime
|
||||
|
||||
import erpnext
|
||||
from erpnext.stock.serial_batch_bundle import get_batches_from_bundle
|
||||
from erpnext.stock.serial_batch_bundle import get_serial_nos as get_serial_nos_from_bundle
|
||||
from erpnext.stock.utils import get_combine_datetime, get_incoming_rate, get_valuation_method
|
||||
|
||||
|
||||
@@ -145,7 +144,7 @@ def validate_returned_items(doc):
|
||||
ref.rate
|
||||
and flt(d.rate) > ref.rate
|
||||
and doc.doctype in ("Delivery Note", "Sales Invoice")
|
||||
and get_valuation_method(ref.item_code) != "Moving Average"
|
||||
and get_valuation_method(ref.item_code, doc.company) != "Moving Average"
|
||||
):
|
||||
frappe.throw(
|
||||
_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}").format(
|
||||
|
||||
@@ -524,7 +524,7 @@ class SellingController(StockController):
|
||||
)
|
||||
|
||||
if not self.get("return_against") or (
|
||||
get_valuation_method(d.item_code) == "Moving Average"
|
||||
get_valuation_method(d.item_code, self.company) == "Moving Average"
|
||||
and self.get("is_return")
|
||||
and not item_details.has_serial_no
|
||||
and not item_details.has_batch_no
|
||||
@@ -535,7 +535,10 @@ class SellingController(StockController):
|
||||
if (
|
||||
not d.incoming_rate
|
||||
or self.is_internal_transfer()
|
||||
or (get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return"))
|
||||
or (
|
||||
get_valuation_method(d.item_code, self.company) == "Moving Average"
|
||||
and self.get("is_return")
|
||||
)
|
||||
):
|
||||
d.incoming_rate = get_incoming_rate(
|
||||
{
|
||||
@@ -560,7 +563,7 @@ class SellingController(StockController):
|
||||
not d.incoming_rate
|
||||
and self.get("return_against")
|
||||
and self.get("is_return")
|
||||
and get_valuation_method(d.item_code) == "Moving Average"
|
||||
and get_valuation_method(d.item_code, self.company) == "Moving Average"
|
||||
):
|
||||
d.incoming_rate = get_rate_for_return(
|
||||
self.doctype, self.name, d.item_code, self.return_against, item_row=d
|
||||
|
||||
@@ -446,4 +446,5 @@ erpnext.patches.v16_0.add_new_stock_entry_types
|
||||
erpnext.patches.v15_0.set_asset_status_if_not_already_set
|
||||
erpnext.patches.v15_0.toggle_legacy_controller_for_period_closing
|
||||
erpnext.patches.v16_0.update_serial_batch_entries
|
||||
erpnext.patches.v16_0.set_company_wise_warehouses
|
||||
erpnext.patches.v16_0.set_company_wise_warehouses
|
||||
erpnext.patches.v16_0.set_valuation_method_on_companies
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
valuation_method = frappe.get_single_value("Stock Settings", "valuation_method")
|
||||
for company in frappe.get_all("Company", pluck="name"):
|
||||
frappe.db.set_value("Company", company, "valuation_method", valuation_method)
|
||||
@@ -117,6 +117,7 @@
|
||||
"enable_item_wise_inventory_account",
|
||||
"enable_provisional_accounting_for_non_stock_items",
|
||||
"default_inventory_account",
|
||||
"valuation_method",
|
||||
"column_break_32",
|
||||
"stock_adjustment_account",
|
||||
"stock_received_but_not_billed",
|
||||
@@ -890,6 +891,14 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Item-wise Inventory Account"
|
||||
},
|
||||
{
|
||||
"default": "FIFO",
|
||||
"fieldname": "valuation_method",
|
||||
"fieldtype": "Select",
|
||||
"label": "Default Stock Valuation Method",
|
||||
"options": "FIFO\nMoving Average\nLIFO",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "default_wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
|
||||
@@ -116,6 +116,7 @@ class Company(NestedSet):
|
||||
transactions_annual_history: DF.Code | None
|
||||
unrealized_exchange_gain_loss_account: DF.Link | None
|
||||
unrealized_profit_loss_account: DF.Link | None
|
||||
valuation_method: DF.Literal["FIFO", "Moving Average", "LIFO"]
|
||||
website: DF.Data | None
|
||||
write_off_account: DF.Link | None
|
||||
# end: auto-generated types
|
||||
@@ -166,6 +167,32 @@ class Company(NestedSet):
|
||||
self.validate_parent_company()
|
||||
self.set_reporting_currency()
|
||||
self.validate_inventory_account_settings()
|
||||
self.cant_change_valuation_method()
|
||||
|
||||
def cant_change_valuation_method(self):
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
if not doc_before_save:
|
||||
return
|
||||
|
||||
previous_valuation_method = doc_before_save.get("valuation_method")
|
||||
|
||||
if previous_valuation_method and previous_valuation_method != self.valuation_method:
|
||||
# check if there are any stock ledger entries against items
|
||||
# which does not have it's own valuation method
|
||||
sle = frappe.db.sql(
|
||||
"""select name from `tabStock Ledger Entry` sle
|
||||
where exists(select name from tabItem
|
||||
where name=sle.item_code and (valuation_method is null or valuation_method='')) and sle.company=%s limit 1
|
||||
""",
|
||||
self.name,
|
||||
)
|
||||
|
||||
if sle:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Can't change the valuation method, as there are transactions against some items which do not have its own valuation method"
|
||||
)
|
||||
)
|
||||
|
||||
def validate_inventory_account_settings(self):
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
|
||||
@@ -514,7 +514,7 @@ class SerialandBatchBundle(Document):
|
||||
if hasattr(sn_obj, "stock_queue") and sn_obj.stock_queue:
|
||||
stock_queue = parse_json(sn_obj.stock_queue)
|
||||
|
||||
val_method = get_valuation_method(self.item_code)
|
||||
val_method = get_valuation_method(self.item_code, self.company)
|
||||
|
||||
for d in self.entries:
|
||||
available_qty = 0
|
||||
@@ -642,7 +642,7 @@ class SerialandBatchBundle(Document):
|
||||
def set_incoming_rate_for_inward_transaction(self, row=None, save=False, prev_sle=None):
|
||||
from erpnext.stock.utils import get_valuation_method
|
||||
|
||||
valuation_method = get_valuation_method(self.item_code)
|
||||
valuation_method = get_valuation_method(self.item_code, self.company)
|
||||
|
||||
valuation_field = "valuation_rate"
|
||||
if self.voucher_type in ["Sales Invoice", "Delivery Note", "Quotation"]:
|
||||
|
||||
@@ -52,7 +52,7 @@ def format_report_data(filters: Filters, item_details: dict, to_date: str) -> li
|
||||
range_values = get_range_age(filters, fifo_queue, to_date, item_dict)
|
||||
|
||||
check_and_replace_valuations_if_moving_average(
|
||||
range_values, details.valuation_method, details.valuation_rate
|
||||
range_values, details.valuation_method, details.valuation_rate, filters.get("company")
|
||||
)
|
||||
|
||||
row = [details.name, details.item_name, details.description, details.item_group, details.brand]
|
||||
@@ -76,10 +76,12 @@ def format_report_data(filters: Filters, item_details: dict, to_date: str) -> li
|
||||
return data
|
||||
|
||||
|
||||
def check_and_replace_valuations_if_moving_average(range_values, item_valuation_method, valuation_rate):
|
||||
def check_and_replace_valuations_if_moving_average(
|
||||
range_values, item_valuation_method, valuation_rate, company
|
||||
):
|
||||
if item_valuation_method == "Moving Average" or (
|
||||
not item_valuation_method
|
||||
and frappe.db.get_single_value("Stock Settings", "valuation_method") == "Moving Average"
|
||||
and frappe.get_cached_value("Company", company, "valuation_method") == "Moving Average"
|
||||
):
|
||||
for i in range(0, len(range_values), 2):
|
||||
range_values[i + 1] = range_values[i] * valuation_rate
|
||||
|
||||
@@ -201,7 +201,7 @@ def get_columns():
|
||||
def get_data(filters=None):
|
||||
filters = frappe._dict(filters or {})
|
||||
item_warehouse_map = get_item_warehouse_combinations(filters)
|
||||
valuation_method = frappe.db.get_single_value("Stock Settings", "valuation_method")
|
||||
valuation_method = frappe.get_cached_value("Company", filters.get("company"), "valuation_method")
|
||||
|
||||
data = []
|
||||
if item_warehouse_map:
|
||||
|
||||
@@ -861,9 +861,9 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
||||
self.batchwise_valuation_batches = []
|
||||
self.non_batchwise_valuation_batches = []
|
||||
|
||||
if get_valuation_method(self.sle.item_code) == "Moving Average" and frappe.get_single_value(
|
||||
"Stock Settings", "do_not_use_batchwise_valuation"
|
||||
):
|
||||
if get_valuation_method(
|
||||
self.sle.item_code, self.sle.company
|
||||
) == "Moving Average" and frappe.get_single_value("Stock Settings", "do_not_use_batchwise_valuation"):
|
||||
self.non_batchwise_valuation_batches = self.batches
|
||||
return
|
||||
|
||||
|
||||
@@ -563,7 +563,7 @@ class update_entries_after:
|
||||
|
||||
self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
|
||||
self.set_precision()
|
||||
self.valuation_method = get_valuation_method(self.item_code)
|
||||
self.valuation_method = get_valuation_method(self.item_code, self.company)
|
||||
|
||||
self.new_items_found = False
|
||||
self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict())
|
||||
@@ -1087,7 +1087,7 @@ class update_entries_after:
|
||||
avg_rate = 0.0
|
||||
|
||||
for d in sabb_data:
|
||||
incoming_rate = get_incoming_rate_for_serial_and_batch(self.item_code, d, sn_obj)
|
||||
incoming_rate = get_incoming_rate_for_serial_and_batch(self.item_code, d, sn_obj, self.company)
|
||||
amount = incoming_rate * flt(d.qty)
|
||||
tot_amt += flt(amount)
|
||||
total_qty += flt(d.qty)
|
||||
@@ -2398,7 +2398,7 @@ def get_serial_from_sabb(serial_and_batch_bundle):
|
||||
)
|
||||
|
||||
|
||||
def get_incoming_rate_for_serial_and_batch(item_code, row, sn_obj):
|
||||
def get_incoming_rate_for_serial_and_batch(item_code, row, sn_obj, company):
|
||||
if row.serial_no:
|
||||
return abs(sn_obj.serial_no_incoming_rate.get(row.serial_no, 0.0))
|
||||
else:
|
||||
@@ -2406,7 +2406,7 @@ def get_incoming_rate_for_serial_and_batch(item_code, row, sn_obj):
|
||||
if hasattr(sn_obj, "stock_queue") and sn_obj.stock_queue:
|
||||
stock_queue = parse_json(sn_obj.stock_queue)
|
||||
|
||||
val_method = get_valuation_method(item_code)
|
||||
val_method = get_valuation_method(item_code, company)
|
||||
|
||||
actual_qty = row.qty
|
||||
if stock_queue and val_method == "FIFO" and row.batch_no in sn_obj.non_batchwise_valuation_batches:
|
||||
|
||||
@@ -302,7 +302,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
|
||||
|
||||
return batch_obj.get_incoming_rate()
|
||||
else:
|
||||
valuation_method = get_valuation_method(args.get("item_code"))
|
||||
valuation_method = get_valuation_method(args.get("item_code"), args.get("company"))
|
||||
previous_sle = get_previous_sle(args)
|
||||
if valuation_method in ("FIFO", "LIFO"):
|
||||
if previous_sle:
|
||||
@@ -374,11 +374,15 @@ def get_avg_purchase_rate(serial_nos):
|
||||
|
||||
|
||||
@frappe.request_cache
|
||||
def get_valuation_method(item_code):
|
||||
def get_valuation_method(item_code, company=None):
|
||||
"""get valuation method from item or default"""
|
||||
val_method = frappe.get_cached_value("Item", item_code, "valuation_method")
|
||||
if not val_method:
|
||||
val_method = frappe.get_cached_doc("Stock Settings").valuation_method or "FIFO"
|
||||
val_method = (
|
||||
frappe.get_cached_value("Company", company, "valuation_method")
|
||||
if company
|
||||
else frappe.get_single_value("Stock Settings", "valuation_method") or "FIFO"
|
||||
)
|
||||
return val_method
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user