mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-15 11:09:17 +00:00
feat: enable item wise inventory account
This commit is contained in:
@@ -22,10 +22,13 @@ from erpnext.controllers.sales_and_purchase_return import (
|
||||
filter_serial_batches,
|
||||
make_serial_batch_bundle_for_return,
|
||||
)
|
||||
from erpnext.setup.doctype.brand.brand import get_brand_defaults
|
||||
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
||||
from erpnext.stock import get_warehouse_account_map
|
||||
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import (
|
||||
get_evaluated_inventory_dimension,
|
||||
)
|
||||
from erpnext.stock.doctype.item.item import get_item_defaults
|
||||
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||
combine_datetime,
|
||||
get_type_of_transaction,
|
||||
@@ -152,6 +155,62 @@ class StockController(AccountsController):
|
||||
)
|
||||
)
|
||||
|
||||
def get_item_wise_inventory_account_map(self, company):
|
||||
inventory_account_map = frappe._dict()
|
||||
for table in ["items", "packed_items", "supplied_items"]:
|
||||
if not self.get(table):
|
||||
continue
|
||||
|
||||
_map = get_item_wise_inventory_account_map(self.get(table), self.company)
|
||||
inventory_account_map.update(_map)
|
||||
|
||||
return inventory_account_map
|
||||
|
||||
@property
|
||||
def use_item_inventory_account(self):
|
||||
return frappe.get_cached_value("Company", self.company, "enable_item_wise_inventory_account")
|
||||
|
||||
def get_inventory_account_dict(self, row, inventory_account_map, warehouse_field=None):
|
||||
account_dict = frappe._dict()
|
||||
|
||||
if isinstance(row, dict):
|
||||
row = frappe._dict(row)
|
||||
|
||||
if self.use_item_inventory_account:
|
||||
item_code = (
|
||||
row.rm_item_code if hasattr(row, "rm_item_code") and row.rm_item_code else row.item_code
|
||||
)
|
||||
|
||||
account_dict = inventory_account_map.get(item_code)
|
||||
|
||||
if not account_dict:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Please set default inventory account for item {0}, or their item group or brand."
|
||||
).format(bold(item_code))
|
||||
)
|
||||
|
||||
if account_dict:
|
||||
return account_dict
|
||||
|
||||
if not warehouse_field:
|
||||
warehouse_field = "warehouse"
|
||||
|
||||
warehouse = row.get(warehouse_field)
|
||||
if not warehouse:
|
||||
warehouse = self.get(warehouse_field)
|
||||
|
||||
if warehouse and warehouse in inventory_account_map:
|
||||
account_dict = inventory_account_map[warehouse]
|
||||
|
||||
return account_dict
|
||||
|
||||
def get_inventory_account_map(self):
|
||||
if self.use_item_inventory_account:
|
||||
return self.get_item_wise_inventory_account_map(self.company)
|
||||
|
||||
return get_warehouse_account_map(self.company)
|
||||
|
||||
def make_gl_entries(self, gl_entries=None, from_repost=False, via_landed_cost_voucher=False):
|
||||
if self.docstatus == 2:
|
||||
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||
@@ -169,14 +228,14 @@ class StockController(AccountsController):
|
||||
or provisional_accounting_for_non_stock_items
|
||||
or is_asset_pr
|
||||
):
|
||||
warehouse_account = get_warehouse_account_map(self.company)
|
||||
inventory_account_map = self.get_inventory_account_map()
|
||||
|
||||
if self.docstatus == 1:
|
||||
if not gl_entries:
|
||||
gl_entries = (
|
||||
self.get_gl_entries(warehouse_account, via_landed_cost_voucher)
|
||||
self.get_gl_entries(inventory_account_map, via_landed_cost_voucher)
|
||||
if self.doctype == "Purchase Receipt"
|
||||
else self.get_gl_entries(warehouse_account)
|
||||
else self.get_gl_entries(inventory_account_map)
|
||||
)
|
||||
make_gl_entries(gl_entries, from_repost=from_repost)
|
||||
|
||||
@@ -578,9 +637,11 @@ class StockController(AccountsController):
|
||||
for row in self.items:
|
||||
row.use_serial_batch_fields = 1
|
||||
|
||||
def get_gl_entries(self, warehouse_account=None, default_expense_account=None, default_cost_center=None):
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account_map(self.company)
|
||||
def get_gl_entries(
|
||||
self, inventory_account_map=None, default_expense_account=None, default_cost_center=None
|
||||
):
|
||||
if not inventory_account_map:
|
||||
inventory_account_map = self.get_inventory_account_map()
|
||||
|
||||
sle_map = self.get_stock_ledger_details()
|
||||
voucher_details = self.get_voucher_details(default_expense_account, default_cost_center, sle_map)
|
||||
@@ -593,7 +654,9 @@ class StockController(AccountsController):
|
||||
sle_rounding_diff = 0.0
|
||||
if sle_list:
|
||||
for sle in sle_list:
|
||||
if warehouse_account.get(sle.warehouse):
|
||||
_inv_dict = self.get_inventory_account_dict(sle, inventory_account_map)
|
||||
|
||||
if _inv_dict.get("account"):
|
||||
# from warehouse account
|
||||
|
||||
sle_rounding_diff += flt(sle.stock_value_difference)
|
||||
@@ -602,15 +665,17 @@ class StockController(AccountsController):
|
||||
|
||||
# expense account/ target_warehouse / source_warehouse
|
||||
if item_row.get("target_warehouse"):
|
||||
warehouse = item_row.get("target_warehouse")
|
||||
expense_account = warehouse_account[warehouse]["account"]
|
||||
_target_wh_inv_dict = self.get_inventory_account_dict(
|
||||
item_row, inventory_account_map, warehouse_field="target_warehouse"
|
||||
)
|
||||
expense_account = _target_wh_inv_dict["account"]
|
||||
else:
|
||||
expense_account = item_row.expense_account
|
||||
|
||||
gl_list.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": warehouse_account[sle.warehouse]["account"],
|
||||
"account": _inv_dict["account"],
|
||||
"against": expense_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": sle.get("project") or item_row.project or self.get("project"),
|
||||
@@ -620,7 +685,7 @@ class StockController(AccountsController):
|
||||
or self.get("is_opening")
|
||||
or "No",
|
||||
},
|
||||
warehouse_account[sle.warehouse]["account_currency"],
|
||||
_inv_dict["account_currency"],
|
||||
item=item_row,
|
||||
)
|
||||
)
|
||||
@@ -629,7 +694,7 @@ class StockController(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": expense_account,
|
||||
"against": warehouse_account[sle.warehouse]["account"],
|
||||
"against": _inv_dict["account"],
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": -1 * flt(sle.stock_value_difference, precision),
|
||||
@@ -649,9 +714,15 @@ class StockController(AccountsController):
|
||||
if abs(sle_rounding_diff) > (1.0 / (10**precision)) and self.is_internal_transfer():
|
||||
warehouse_asset_account = ""
|
||||
if self.get("is_internal_customer"):
|
||||
warehouse_asset_account = warehouse_account[item_row.get("target_warehouse")]["account"]
|
||||
_inv_dict = self.get_inventory_account_dict(
|
||||
item_row, inventory_account_map, warehouse_field="target_warehouse"
|
||||
)
|
||||
|
||||
warehouse_asset_account = _inv_dict["account"]
|
||||
elif self.get("is_internal_supplier"):
|
||||
warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"]
|
||||
_inv_dict = self.get_inventory_account_dict(item_row, inventory_account_map)
|
||||
|
||||
warehouse_asset_account = _inv_dict["account"]
|
||||
|
||||
expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account")
|
||||
if not expense_account:
|
||||
@@ -672,7 +743,7 @@ class StockController(AccountsController):
|
||||
"debit": sle_rounding_diff,
|
||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
||||
},
|
||||
warehouse_account[sle.warehouse]["account_currency"],
|
||||
_inv_dict["account_currency"],
|
||||
item=item_row,
|
||||
)
|
||||
)
|
||||
@@ -2015,3 +2086,49 @@ def make_bundle_for_material_transfer(**kwargs):
|
||||
bundle_doc.submit()
|
||||
|
||||
return bundle_doc.name
|
||||
|
||||
|
||||
def get_item_wise_inventory_account_map(rows, company):
|
||||
# returns dict of item_code and its inventory account details
|
||||
# Example: {"ITEM-001": {"account": "Stock - ABC", "account_currency": "INR"}, ...}
|
||||
|
||||
inventory_map = frappe._dict()
|
||||
|
||||
for row in rows:
|
||||
item_code = row.rm_item_code if hasattr(row, "rm_item_code") else row.item_code
|
||||
if not item_code:
|
||||
continue
|
||||
|
||||
if inventory_map.get(item_code):
|
||||
continue
|
||||
|
||||
item_defaults = get_item_defaults(item_code, company)
|
||||
if item_defaults.default_inventory_account:
|
||||
inventory_map[item_code] = frappe._dict(
|
||||
{
|
||||
"account": item_defaults.default_inventory_account,
|
||||
"account_currency": item_defaults.inventory_account_currency,
|
||||
}
|
||||
)
|
||||
|
||||
if not inventory_map.get(item_code):
|
||||
item_group_defaults = get_item_group_defaults(item_code, company)
|
||||
if item_group_defaults.default_inventory_account:
|
||||
inventory_map[item_code] = frappe._dict(
|
||||
{
|
||||
"account": item_group_defaults.default_inventory_account,
|
||||
"account_currency": item_group_defaults.inventory_account_currency,
|
||||
}
|
||||
)
|
||||
|
||||
if not inventory_map.get(item_code):
|
||||
brand_defaults = get_brand_defaults(item_code, company)
|
||||
if brand_defaults.default_inventory_account:
|
||||
inventory_map[item_code] = frappe._dict(
|
||||
{
|
||||
"account": brand_defaults.default_inventory_account,
|
||||
"account_currency": brand_defaults.inventory_account_currency,
|
||||
}
|
||||
)
|
||||
|
||||
return inventory_map
|
||||
|
||||
Reference in New Issue
Block a user