feat: Asset Capitalization
- manual selection of entry type - GLE cleanup with smaller functions - GLE considering periodical inventory - test cases
This commit is contained in:
parent
fefe95052d
commit
58d430fe3e
@ -1425,6 +1425,16 @@ def create_asset_category():
|
||||
"depreciation_expense_account": "_Test Depreciations - _TC",
|
||||
},
|
||||
)
|
||||
asset_category.append(
|
||||
"accounts",
|
||||
{
|
||||
"company_name": "_Test Company with perpetual inventory",
|
||||
"fixed_asset_account": "_Test Fixed Asset - TCP1",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - TCP1",
|
||||
"depreciation_expense_account": "_Test Depreciations - TCP1",
|
||||
},
|
||||
)
|
||||
|
||||
asset_category.insert()
|
||||
|
||||
|
||||
|
@ -27,7 +27,11 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
||||
me.setup_warehouse_query();
|
||||
|
||||
me.frm.set_query("target_item_code", function() {
|
||||
return erpnext.queries.item();
|
||||
if (me.frm.doc.entry_type == "Capitalization") {
|
||||
return erpnext.queries.item({"is_stock_item": 0, "is_fixed_asset": 1});
|
||||
} else {
|
||||
return erpnext.queries.item({"is_stock_item": 1, "is_fixed_asset": 0});
|
||||
}
|
||||
});
|
||||
|
||||
me.frm.set_query("target_asset", function() {
|
||||
@ -410,5 +414,4 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
||||
}
|
||||
};
|
||||
|
||||
//$.extend(cur_frm.cscript, new erpnext.assets.AssetCapitalization({frm: cur_frm}));
|
||||
cur_frm.cscript = new erpnext.assets.AssetCapitalization({frm: cur_frm});
|
||||
|
@ -8,29 +8,28 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"naming_series",
|
||||
"entry_type",
|
||||
"target_item_code",
|
||||
"target_item_name",
|
||||
"target_is_fixed_asset",
|
||||
"target_has_batch_no",
|
||||
"target_has_serial_no",
|
||||
"entry_type",
|
||||
"finance_book",
|
||||
"naming_series",
|
||||
"column_break_9",
|
||||
"target_asset",
|
||||
"target_asset_name",
|
||||
"target_warehouse",
|
||||
"target_qty",
|
||||
"target_stock_uom",
|
||||
"target_batch_no",
|
||||
"target_serial_no",
|
||||
"column_break_5",
|
||||
"company",
|
||||
"finance_book",
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
"set_posting_time",
|
||||
"amended_from",
|
||||
"target_item_details_section",
|
||||
"target_asset",
|
||||
"target_asset_name",
|
||||
"target_warehouse",
|
||||
"target_batch_no",
|
||||
"target_serial_no",
|
||||
"column_break_5",
|
||||
"target_qty",
|
||||
"target_stock_uom",
|
||||
"section_break_16",
|
||||
"stock_items",
|
||||
"stock_items_total",
|
||||
@ -86,16 +85,17 @@
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.target_item_code || doc.target_is_fixed_asset",
|
||||
"depends_on": "eval:doc.entry_type=='Capitalization'",
|
||||
"fieldname": "target_asset",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Target Asset",
|
||||
"mandatory_depends_on": "eval:doc.entry_type=='Capitalization'",
|
||||
"no_copy": 1,
|
||||
"options": "Asset"
|
||||
},
|
||||
{
|
||||
"depends_on": "target_asset",
|
||||
"depends_on": "eval:doc.entry_type=='Capitalization'",
|
||||
"fetch_from": "target_asset.asset_name",
|
||||
"fieldname": "target_asset_name",
|
||||
"fieldtype": "Data",
|
||||
@ -170,15 +170,11 @@
|
||||
"options": "Asset Capitalization Stock Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "target_item_details_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Target Item Details"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.target_is_fixed_asset",
|
||||
"depends_on": "eval:doc.entry_type=='Decapitalization'",
|
||||
"fieldname": "target_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Target Warehouse",
|
||||
"mandatory_depends_on": "eval:doc.entry_type=='Decapitalization'",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
@ -240,13 +236,14 @@
|
||||
"options": "Asset Capitalization Asset Item"
|
||||
},
|
||||
{
|
||||
"default": "Capitalization",
|
||||
"fieldname": "entry_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Entry Type",
|
||||
"options": "\nCapitalization\nDecapitalization",
|
||||
"read_only": 1
|
||||
"options": "Capitalization\nDecapitalization",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_items_total",
|
||||
@ -337,7 +334,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-09-15 15:41:27.917458",
|
||||
"modified": "2022-09-12 15:09:40.771332",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Capitalization",
|
||||
@ -377,6 +374,7 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "title",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
|
@ -10,9 +10,9 @@ from frappe import _
|
||||
from frappe.utils import cint, flt
|
||||
from six import string_types
|
||||
|
||||
import erpnext
|
||||
from erpnext.assets.doctype.asset.depreciation import (
|
||||
get_gl_entries_on_asset_disposal,
|
||||
get_gl_entries_on_asset_regain,
|
||||
get_value_after_depreciation_on_disposal_date,
|
||||
)
|
||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||
@ -32,16 +32,26 @@ from erpnext.stock.get_item_details import (
|
||||
from erpnext.stock.stock_ledger import get_previous_sle
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
|
||||
force_fields = ['target_item_name', 'target_asset_name', 'item_name', 'asset_name',
|
||||
'target_is_fixed_asset', 'target_has_serial_no', 'target_has_batch_no',
|
||||
'target_stock_uom', 'stock_uom', 'target_fixed_asset_account', 'fixed_asset_account']
|
||||
force_fields = [
|
||||
"target_item_name",
|
||||
"target_asset_name",
|
||||
"item_name",
|
||||
"asset_name",
|
||||
"target_is_fixed_asset",
|
||||
"target_has_serial_no",
|
||||
"target_has_batch_no",
|
||||
"target_stock_uom",
|
||||
"stock_uom",
|
||||
"target_fixed_asset_account",
|
||||
"fixed_asset_account",
|
||||
"valuation_rate",
|
||||
]
|
||||
|
||||
|
||||
class AssetCapitalization(StockController):
|
||||
def validate(self):
|
||||
self.validate_posting_time()
|
||||
self.set_missing_values(for_validate=True)
|
||||
self.set_entry_type()
|
||||
self.validate_target_item()
|
||||
self.validate_target_asset()
|
||||
self.validate_consumed_stock_item()
|
||||
@ -58,14 +68,13 @@ class AssetCapitalization(StockController):
|
||||
def on_submit(self):
|
||||
self.update_stock_ledger()
|
||||
self.make_gl_entries()
|
||||
self.update_target_asset()
|
||||
|
||||
def on_cancel(self):
|
||||
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
|
||||
self.update_stock_ledger()
|
||||
self.make_gl_entries()
|
||||
|
||||
def set_entry_type(self):
|
||||
self.entry_type = "Capitalization" if self.target_is_fixed_asset else "Decapitalization"
|
||||
self.update_target_asset()
|
||||
|
||||
def set_title(self):
|
||||
self.title = self.target_asset_name or self.target_item_name or self.target_item_code
|
||||
@ -90,7 +99,7 @@ class AssetCapitalization(StockController):
|
||||
args.update(d.as_dict())
|
||||
args.doctype = self.doctype
|
||||
args.name = self.name
|
||||
consumed_stock_item_details = get_consumed_stock_item_details(args, get_valuation_rate=False)
|
||||
consumed_stock_item_details = get_consumed_stock_item_details(args)
|
||||
for k, v in consumed_stock_item_details.items():
|
||||
if d.meta.has_field(k) and (not d.get(k) or k in force_fields):
|
||||
d.set(k, v)
|
||||
@ -100,8 +109,8 @@ class AssetCapitalization(StockController):
|
||||
args.update(d.as_dict())
|
||||
args.doctype = self.doctype
|
||||
args.name = self.name
|
||||
args.finance_book = d.get('finance_book') or self.get('finance_book')
|
||||
consumed_asset_details = get_consumed_asset_details(args, get_asset_value=False)
|
||||
args.finance_book = d.get("finance_book") or self.get("finance_book")
|
||||
consumed_asset_details = get_consumed_asset_details(args)
|
||||
for k, v in consumed_asset_details.items():
|
||||
if d.meta.has_field(k) and (not d.get(k) or k in force_fields):
|
||||
d.set(k, v)
|
||||
@ -120,8 +129,14 @@ class AssetCapitalization(StockController):
|
||||
target_item = frappe.get_cached_doc("Item", self.target_item_code)
|
||||
|
||||
if not target_item.is_fixed_asset and not target_item.is_stock_item:
|
||||
frappe.throw(_("Target Item {0} is neither a Fixed Asset nor a Stock Item")
|
||||
.format(target_item.name))
|
||||
frappe.throw(
|
||||
_("Target Item {0} is neither a Fixed Asset nor a Stock Item").format(target_item.name)
|
||||
)
|
||||
|
||||
if self.entry_type == "Capitalization" and not target_item.is_fixed_asset:
|
||||
frappe.throw(_("Target Item {0} must be a Fixed Asset item").format(target_item.name))
|
||||
elif self.entry_type == "Decapitalization" and not target_item.is_stock_item:
|
||||
frappe.throw(_("Target Item {0} must be a Stock Item").format(target_item.name))
|
||||
|
||||
if target_item.is_fixed_asset:
|
||||
self.target_qty = 1
|
||||
@ -144,14 +159,13 @@ class AssetCapitalization(StockController):
|
||||
self.validate_item(target_item)
|
||||
|
||||
def validate_target_asset(self):
|
||||
if self.target_is_fixed_asset and not self.target_asset:
|
||||
frappe.throw(_("Target Asset is mandatory for Capitalization"))
|
||||
|
||||
if self.target_asset:
|
||||
target_asset = self.get_asset_for_validation(self.target_asset)
|
||||
|
||||
if target_asset.item_code != self.target_item_code:
|
||||
frappe.throw(_("Asset {0} does not belong to Item {1}").format(self.target_asset, self.target_item_code))
|
||||
frappe.throw(
|
||||
_("Asset {0} does not belong to Item {1}").format(self.target_asset, self.target_item_code)
|
||||
)
|
||||
|
||||
self.validate_asset(target_asset)
|
||||
|
||||
@ -172,8 +186,11 @@ class AssetCapitalization(StockController):
|
||||
for d in self.asset_items:
|
||||
if d.asset:
|
||||
if d.asset == self.target_asset:
|
||||
frappe.throw(_("Row #{0}: Consumed Asset {1} cannot be the same as the Target Asset")
|
||||
.format(d.idx, d.asset))
|
||||
frappe.throw(
|
||||
_("Row #{0}: Consumed Asset {1} cannot be the same as the Target Asset").format(
|
||||
d.idx, d.asset
|
||||
)
|
||||
)
|
||||
|
||||
asset = self.get_asset_for_validation(d.asset)
|
||||
self.validate_asset(asset)
|
||||
@ -198,18 +215,21 @@ class AssetCapitalization(StockController):
|
||||
d.cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
|
||||
|
||||
def validate_source_mandatory(self):
|
||||
if not self.target_is_fixed_asset and not self.get('asset_items'):
|
||||
if not self.target_is_fixed_asset and not self.get("asset_items"):
|
||||
frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization"))
|
||||
|
||||
if not self.get('stock_items') and not self.get('asset_items'):
|
||||
if not self.get("stock_items") and not self.get("asset_items"):
|
||||
frappe.throw(_("Consumed Stock Items or Consumed Asset Items is mandatory for Capitalization"))
|
||||
|
||||
def validate_item(self, item):
|
||||
from erpnext.stock.doctype.item.item import validate_end_of_life
|
||||
|
||||
validate_end_of_life(item.name, item.end_of_life, item.disabled)
|
||||
|
||||
def get_asset_for_validation(self, asset):
|
||||
return frappe.db.get_value("Asset", asset, ["name", "item_code", "company", "status", "docstatus"], as_dict=1)
|
||||
return frappe.db.get_value(
|
||||
"Asset", asset, ["name", "item_code", "company", "status", "docstatus"], as_dict=1
|
||||
)
|
||||
|
||||
def validate_asset(self, asset):
|
||||
if asset.status in ("Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"):
|
||||
@ -225,7 +245,7 @@ class AssetCapitalization(StockController):
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_warehouse_details(self):
|
||||
for d in self.get('stock_items'):
|
||||
for d in self.get("stock_items"):
|
||||
if d.item_code and d.warehouse:
|
||||
args = self.get_args_for_incoming_rate(d)
|
||||
warehouse_details = get_warehouse_details(args)
|
||||
@ -233,27 +253,30 @@ class AssetCapitalization(StockController):
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_asset_values(self):
|
||||
for d in self.get('asset_items'):
|
||||
for d in self.get("asset_items"):
|
||||
if d.asset:
|
||||
finance_book = d.get('finance_book') or self.get('finance_book')
|
||||
finance_book = d.get("finance_book") or self.get("finance_book")
|
||||
d.current_asset_value = flt(get_current_asset_value(d.asset, finance_book=finance_book))
|
||||
d.asset_value = get_value_after_depreciation_on_disposal_date(d.asset, self.posting_date,
|
||||
finance_book=finance_book)
|
||||
d.asset_value = get_value_after_depreciation_on_disposal_date(
|
||||
d.asset, self.posting_date, finance_book=finance_book
|
||||
)
|
||||
|
||||
def get_args_for_incoming_rate(self, item):
|
||||
return frappe._dict({
|
||||
"item_code": item.item_code,
|
||||
"warehouse": item.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1 * flt(item.stock_qty),
|
||||
"serial_no": item.serial_no,
|
||||
"batch_no": item.batch_no,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"company": self.company,
|
||||
"allow_zero_valuation": cint(item.get('allow_zero_valuation_rate')),
|
||||
})
|
||||
return frappe._dict(
|
||||
{
|
||||
"item_code": item.item_code,
|
||||
"warehouse": item.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1 * flt(item.stock_qty),
|
||||
"serial_no": item.serial_no,
|
||||
"batch_no": item.batch_no,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"company": self.company,
|
||||
"allow_zero_valuation": cint(item.get("allow_zero_valuation_rate")),
|
||||
}
|
||||
)
|
||||
|
||||
def calculate_totals(self):
|
||||
self.stock_items_total = 0
|
||||
@ -261,45 +284,51 @@ class AssetCapitalization(StockController):
|
||||
self.service_items_total = 0
|
||||
|
||||
for d in self.stock_items:
|
||||
d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), d.precision('amount'))
|
||||
d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), d.precision("amount"))
|
||||
self.stock_items_total += d.amount
|
||||
|
||||
for d in self.asset_items:
|
||||
d.asset_value = flt(flt(d.asset_value), d.precision('asset_value'))
|
||||
d.asset_value = flt(flt(d.asset_value), d.precision("asset_value"))
|
||||
self.asset_items_total += d.asset_value
|
||||
|
||||
for d in self.service_items:
|
||||
d.amount = flt(flt(d.qty) * flt(d.rate), d.precision('amount'))
|
||||
d.amount = flt(flt(d.qty) * flt(d.rate), d.precision("amount"))
|
||||
self.service_items_total += d.amount
|
||||
|
||||
self.stock_items_total = flt(self.stock_items_total, self.precision('stock_items_total'))
|
||||
self.asset_items_total = flt(self.asset_items_total, self.precision('asset_items_total'))
|
||||
self.service_items_total = flt(self.service_items_total, self.precision('service_items_total'))
|
||||
self.stock_items_total = flt(self.stock_items_total, self.precision("stock_items_total"))
|
||||
self.asset_items_total = flt(self.asset_items_total, self.precision("asset_items_total"))
|
||||
self.service_items_total = flt(self.service_items_total, self.precision("service_items_total"))
|
||||
|
||||
self.total_value = self.stock_items_total + self.asset_items_total + self.service_items_total
|
||||
self.total_value = flt(self.total_value, self.precision('total_value'))
|
||||
self.total_value = flt(self.total_value, self.precision("total_value"))
|
||||
|
||||
self.target_qty = flt(self.target_qty, self.precision('target_qty'))
|
||||
self.target_qty = flt(self.target_qty, self.precision("target_qty"))
|
||||
self.target_incoming_rate = self.total_value / self.target_qty
|
||||
|
||||
def update_stock_ledger(self):
|
||||
sl_entries = []
|
||||
|
||||
for d in self.stock_items:
|
||||
sle = self.get_sl_entries(d, {
|
||||
"actual_qty": -flt(d.stock_qty),
|
||||
})
|
||||
sle = self.get_sl_entries(
|
||||
d,
|
||||
{
|
||||
"actual_qty": -flt(d.stock_qty),
|
||||
},
|
||||
)
|
||||
sl_entries.append(sle)
|
||||
|
||||
if not frappe.db.get_value("Item", self.target_item_code, "is_fixed_asset", cache=1):
|
||||
sle = self.get_sl_entries(self, {
|
||||
"item_code": self.target_item_code,
|
||||
"warehouse": self.target_warehouse,
|
||||
"batch_no": self.target_batch_no,
|
||||
"serial_no": self.target_serial_no,
|
||||
"actual_qty": flt(self.target_qty),
|
||||
"incoming_rate": flt(self.target_incoming_rate)
|
||||
})
|
||||
if self.entry_type == "Decapitalization" and not self.target_is_fixed_asset:
|
||||
sle = self.get_sl_entries(
|
||||
self,
|
||||
{
|
||||
"item_code": self.target_item_code,
|
||||
"warehouse": self.target_warehouse,
|
||||
"batch_no": self.target_batch_no,
|
||||
"serial_no": self.target_serial_no,
|
||||
"actual_qty": flt(self.target_qty),
|
||||
"incoming_rate": flt(self.target_incoming_rate),
|
||||
},
|
||||
)
|
||||
sl_entries.append(sle)
|
||||
|
||||
# reverse sl entries if cancel
|
||||
@ -312,139 +341,183 @@ class AssetCapitalization(StockController):
|
||||
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
|
||||
|
||||
if not gl_entries:
|
||||
gl_entries = self.get_gl_entries()
|
||||
|
||||
if self.docstatus == 1:
|
||||
if not gl_entries:
|
||||
gl_entries = self.get_gl_entries()
|
||||
|
||||
if gl_entries:
|
||||
make_gl_entries(gl_entries, from_repost=from_repost)
|
||||
elif self.docstatus == 2:
|
||||
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||
|
||||
def get_gl_entries(self, warehouse_account=None, default_expense_account=None, default_cost_center=None):
|
||||
def get_gl_entries(
|
||||
self, warehouse_account=None, default_expense_account=None, default_cost_center=None
|
||||
):
|
||||
# Stock GL Entries
|
||||
gl_entries = []
|
||||
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account_map(self.company)
|
||||
self.warehouse_account = warehouse_account
|
||||
if not self.warehouse_account:
|
||||
self.warehouse_account = get_warehouse_account_map(self.company)
|
||||
|
||||
precision = self.get_debit_field_precision()
|
||||
sle_map = self.get_stock_ledger_details()
|
||||
|
||||
if self.target_is_fixed_asset:
|
||||
target_account = self.target_fixed_asset_account
|
||||
else:
|
||||
target_account = warehouse_account[self.target_warehouse]["account"]
|
||||
self.sle_map = self.get_stock_ledger_details()
|
||||
|
||||
target_account = self.get_target_account()
|
||||
target_against = set()
|
||||
|
||||
self.get_gl_entries_for_consumed_stock_items(
|
||||
gl_entries, target_account, target_against, precision
|
||||
)
|
||||
self.get_gl_entries_for_consumed_asset_items(
|
||||
gl_entries, target_account, target_against, precision
|
||||
)
|
||||
self.get_gl_entries_for_consumed_service_items(
|
||||
gl_entries, target_account, target_against, precision
|
||||
)
|
||||
|
||||
self.get_gl_entries_for_target_item(gl_entries, target_against, precision)
|
||||
return gl_entries
|
||||
|
||||
def get_target_account(self):
|
||||
if self.target_is_fixed_asset:
|
||||
return self.target_fixed_asset_account
|
||||
else:
|
||||
return self.warehouse_account[self.target_warehouse]["account"]
|
||||
|
||||
def get_gl_entries_for_consumed_stock_items(
|
||||
self, gl_entries, target_account, target_against, precision
|
||||
):
|
||||
# Consumed Stock Items
|
||||
total_consumed_stock_value = 0
|
||||
for item_row in self.stock_items:
|
||||
sle_list = sle_map.get(item_row.name)
|
||||
sle_list = self.sle_map.get(item_row.name)
|
||||
if sle_list:
|
||||
for sle in sle_list:
|
||||
stock_value_difference = flt(sle.stock_value_difference, precision)
|
||||
total_consumed_stock_value += -1 * sle.stock_value_difference
|
||||
|
||||
account = warehouse_account[sle.warehouse]["account"]
|
||||
if erpnext.is_perpetual_inventory_enabled(self.company):
|
||||
account = self.warehouse_account[sle.warehouse]["account"]
|
||||
else:
|
||||
account = self.get_company_default("default_expense_account")
|
||||
|
||||
target_against.add(account)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": account,
|
||||
"against": target_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.get("project") or self.get("project"),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": -1 * stock_value_difference,
|
||||
},
|
||||
self.warehouse_account[sle.warehouse]["account_currency"],
|
||||
item=item_row,
|
||||
)
|
||||
)
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": account,
|
||||
"against": target_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.get('project') or self.get('project'),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": -1 * stock_value_difference,
|
||||
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
|
||||
|
||||
def get_gl_entries_for_consumed_asset_items(
|
||||
self, gl_entries, target_account, target_against, precision
|
||||
):
|
||||
# Consumed Assets
|
||||
for item in self.asset_items:
|
||||
asset = self.get_asset(item)
|
||||
|
||||
if self.docstatus == 2:
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset,
|
||||
item.asset_value, item.get('finance_book') or self.get('finance_book'))
|
||||
asset.db_set("disposal_date", None)
|
||||
if asset.calculate_depreciation:
|
||||
self.depreciate_asset(asset)
|
||||
asset.reload()
|
||||
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
|
||||
asset, item.asset_value, item.get("finance_book") or self.get("finance_book")
|
||||
)
|
||||
|
||||
asset.db_set("disposal_date", self.posting_date)
|
||||
|
||||
self.set_consumed_asset_status(asset)
|
||||
|
||||
for gle in fixed_asset_gl_entries:
|
||||
gle["against"] = target_account
|
||||
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||
target_against.add(gle["account"])
|
||||
|
||||
def get_gl_entries_for_consumed_service_items(
|
||||
self, gl_entries, target_account, target_against, precision
|
||||
):
|
||||
# Service Expenses
|
||||
for item_row in self.service_items:
|
||||
expense_amount = flt(item_row.amount, precision)
|
||||
target_against.add(item_row.expense_account)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": item_row.expense_account,
|
||||
"against": target_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.get("project") or self.get("project"),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": expense_amount,
|
||||
},
|
||||
item=item_row,
|
||||
)
|
||||
)
|
||||
|
||||
def get_gl_entries_for_target_item(self, gl_entries, target_against, precision):
|
||||
if self.target_is_fixed_asset:
|
||||
# Capitalization
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.target_fixed_asset_account,
|
||||
"against": ", ".join(target_against),
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"debit": flt(self.total_value, precision),
|
||||
"cost_center": self.get("cost_center"),
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Target Stock Item
|
||||
sle_list = self.sle_map.get(self.name)
|
||||
for sle in sle_list:
|
||||
stock_value_difference = flt(sle.stock_value_difference, precision)
|
||||
account = self.warehouse_account[sle.warehouse]["account"]
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": account,
|
||||
"against": ", ".join(target_against),
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.get("project"),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"debit": stock_value_difference,
|
||||
},
|
||||
self.warehouse_account[sle.warehouse]["account_currency"],
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
def update_target_asset(self):
|
||||
total_target_asset_value = flt(self.total_value, self.precision("total_value"))
|
||||
if self.docstatus == 1 and self.entry_type == "Capitalization":
|
||||
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
||||
asset_doc.purchase_date = self.posting_date
|
||||
asset_doc.gross_purchase_amount = total_target_asset_value
|
||||
asset_doc.purchase_receipt_amount = total_target_asset_value
|
||||
asset_doc.prepare_depreciation_data()
|
||||
asset_doc.flags.ignore_validate_update_after_submit = True
|
||||
asset_doc.save()
|
||||
elif self.docstatus == 2:
|
||||
for item in self.asset_items:
|
||||
asset = self.get_asset(item)
|
||||
asset.db_set("disposal_date", None)
|
||||
self.set_consumed_asset_status(asset)
|
||||
|
||||
if asset.calculate_depreciation:
|
||||
self.reverse_depreciation_entry_made_after_disposal(asset)
|
||||
self.reset_depreciation_schedule(asset)
|
||||
else:
|
||||
if asset.calculate_depreciation:
|
||||
self.depreciate_asset(asset)
|
||||
asset.reload()
|
||||
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
|
||||
item.asset_value, item.get('finance_book') or self.get('finance_book'))
|
||||
asset.db_set("disposal_date", self.posting_date)
|
||||
|
||||
self.set_consumed_asset_status(asset)
|
||||
|
||||
for gle in fixed_asset_gl_entries:
|
||||
gle["against"] = target_account
|
||||
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||
|
||||
# Service Expenses
|
||||
total_service_expenses = 0
|
||||
for item_row in self.service_items:
|
||||
expense_amount = flt(item_row.amount, precision)
|
||||
total_service_expenses += expense_amount
|
||||
target_against.add(item_row.expense_account)
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": item_row.expense_account,
|
||||
"against": target_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.get('project') or self.get('project'),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": expense_amount,
|
||||
}, item=item_row))
|
||||
|
||||
target_against = ", ".join(target_against)
|
||||
total_target_stock_value = 0
|
||||
total_target_asset_value = 0
|
||||
|
||||
if self.target_is_fixed_asset:
|
||||
# Target Asset Item
|
||||
total_target_asset_value = flt(self.total_value, precision)
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": self.target_fixed_asset_account,
|
||||
"against": target_against,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"debit": total_target_asset_value,
|
||||
"cost_center": self.get('cost_center')
|
||||
}, item=self))
|
||||
|
||||
if self.docstatus == 1:
|
||||
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
||||
asset_doc.purchase_date = self.posting_date
|
||||
asset_doc.gross_purchase_amount = total_target_asset_value
|
||||
asset_doc.purchase_receipt_amount = total_target_asset_value
|
||||
asset_doc.prepare_depreciation_data()
|
||||
asset_doc.flags.ignore_validate_update_after_submit = True
|
||||
asset_doc.save()
|
||||
else:
|
||||
# Target Stock Item
|
||||
sle_list = sle_map.get(self.name)
|
||||
for sle in sle_list:
|
||||
stock_value_difference = flt(sle.stock_value_difference, precision)
|
||||
total_target_stock_value += sle.stock_value_difference
|
||||
account = warehouse_account[sle.warehouse]["account"]
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": account,
|
||||
"against": target_against,
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.get('project'),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"debit": stock_value_difference,
|
||||
}, warehouse_account[sle.warehouse]["account_currency"], item=self))
|
||||
|
||||
return gl_entries
|
||||
|
||||
def get_asset(self, item):
|
||||
asset = frappe.get_doc("Asset", item.asset)
|
||||
@ -489,16 +562,12 @@ def get_target_item_details(item_code=None, company=None):
|
||||
item_defaults = get_item_defaults(item.name, company)
|
||||
item_group_defaults = get_item_group_defaults(item.name, company)
|
||||
brand_defaults = get_brand_defaults(item.name, company)
|
||||
out.cost_center = get_default_cost_center(frappe._dict({'item_code': item.name, 'company': company}),
|
||||
item_defaults, item_group_defaults, brand_defaults)
|
||||
|
||||
# Set Entry Type
|
||||
if not item_code:
|
||||
out.entry_type = ""
|
||||
elif out.target_is_fixed_asset:
|
||||
out.entry_type = "Capitalization"
|
||||
else:
|
||||
out.entry_type = "Decapitalization"
|
||||
out.cost_center = get_default_cost_center(
|
||||
frappe._dict({"item_code": item.name, "company": company}),
|
||||
item_defaults,
|
||||
item_group_defaults,
|
||||
brand_defaults,
|
||||
)
|
||||
|
||||
return out
|
||||
|
||||
@ -510,7 +579,7 @@ def get_target_asset_details(asset=None, company=None):
|
||||
# Get Asset Details
|
||||
asset_details = frappe._dict()
|
||||
if asset:
|
||||
asset_details = frappe.db.get_value("Asset", asset, ['asset_name', 'item_code'], as_dict=1)
|
||||
asset_details = frappe.db.get_value("Asset", asset, ["asset_name", "item_code"], as_dict=1)
|
||||
if not asset_details:
|
||||
frappe.throw(_("Asset {0} does not exist").format(asset))
|
||||
|
||||
@ -521,8 +590,9 @@ def get_target_asset_details(asset=None, company=None):
|
||||
out.asset_name = asset_details.asset_name
|
||||
|
||||
if asset_details.item_code:
|
||||
out.target_fixed_asset_account = get_asset_category_account('fixed_asset_account', item=asset_details.item_code,
|
||||
company=company)
|
||||
out.target_fixed_asset_account = get_asset_category_account(
|
||||
"fixed_asset_account", item=asset_details.item_code, company=company
|
||||
)
|
||||
else:
|
||||
out.target_fixed_asset_account = None
|
||||
|
||||
@ -530,7 +600,7 @@ def get_target_asset_details(asset=None, company=None):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_consumed_stock_item_details(args, get_valuation_rate=True):
|
||||
def get_consumed_stock_item_details(args):
|
||||
if isinstance(args, string_types):
|
||||
args = json.loads(args)
|
||||
|
||||
@ -554,24 +624,29 @@ def get_consumed_stock_item_details(args, get_valuation_rate=True):
|
||||
item_defaults = get_item_defaults(item.name, args.company)
|
||||
item_group_defaults = get_item_group_defaults(item.name, args.company)
|
||||
brand_defaults = get_brand_defaults(item.name, args.company)
|
||||
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
|
||||
out.cost_center = get_default_cost_center(
|
||||
args, item_defaults, item_group_defaults, brand_defaults
|
||||
)
|
||||
|
||||
if get_valuation_rate:
|
||||
if args.item_code and out.warehouse:
|
||||
incoming_rate_args = frappe._dict({
|
||||
'item_code': args.item_code,
|
||||
'warehouse': out.warehouse,
|
||||
'posting_date': args.posting_date,
|
||||
'posting_time': args.posting_time,
|
||||
'qty': -1 * flt(out.stock_qty),
|
||||
if args.item_code and out.warehouse:
|
||||
incoming_rate_args = frappe._dict(
|
||||
{
|
||||
"item_code": args.item_code,
|
||||
"warehouse": out.warehouse,
|
||||
"posting_date": args.posting_date,
|
||||
"posting_time": args.posting_time,
|
||||
"qty": -1 * flt(out.stock_qty),
|
||||
"voucher_type": args.doctype,
|
||||
"voucher_no": args.name,
|
||||
"company": args.company,
|
||||
})
|
||||
out.update(get_warehouse_details(incoming_rate_args))
|
||||
else:
|
||||
out.valuation_rate = 0
|
||||
out.actual_qty = 0
|
||||
"serial_no": args.serial_no,
|
||||
"batch_no": args.batch_no,
|
||||
}
|
||||
)
|
||||
out.update(get_warehouse_details(incoming_rate_args))
|
||||
else:
|
||||
out.valuation_rate = 0
|
||||
out.actual_qty = 0
|
||||
|
||||
return out
|
||||
|
||||
@ -587,13 +662,13 @@ def get_warehouse_details(args):
|
||||
if args.warehouse and args.item_code:
|
||||
out = {
|
||||
"actual_qty": get_previous_sle(args).get("qty_after_transaction") or 0,
|
||||
"valuation_rate": get_incoming_rate(args, raise_error_if_no_rate=False)
|
||||
"valuation_rate": get_incoming_rate(args, raise_error_if_no_rate=False),
|
||||
}
|
||||
return out
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_consumed_asset_details(args, get_asset_value=True):
|
||||
def get_consumed_asset_details(args):
|
||||
if isinstance(args, string_types):
|
||||
args = json.loads(args)
|
||||
|
||||
@ -602,7 +677,9 @@ def get_consumed_asset_details(args, get_asset_value=True):
|
||||
|
||||
asset_details = frappe._dict()
|
||||
if args.asset:
|
||||
asset_details = frappe.db.get_value("Asset", args.asset, ['asset_name', 'item_code', 'item_name'], as_dict=1)
|
||||
asset_details = frappe.db.get_value(
|
||||
"Asset", args.asset, ["asset_name", "item_code", "item_name"], as_dict=1
|
||||
)
|
||||
if not asset_details:
|
||||
frappe.throw(_("Asset {0} does not exist").format(args.asset))
|
||||
|
||||
@ -610,19 +687,22 @@ def get_consumed_asset_details(args, get_asset_value=True):
|
||||
out.asset_name = asset_details.asset_name
|
||||
out.item_name = asset_details.item_name
|
||||
|
||||
if get_asset_value:
|
||||
if args.asset:
|
||||
out.current_asset_value = flt(get_current_asset_value(args.asset, finance_book=args.finance_book))
|
||||
out.asset_value = get_value_after_depreciation_on_disposal_date(args.asset, args.posting_date,
|
||||
finance_book=args.finance_book)
|
||||
else:
|
||||
out.current_asset_value = 0
|
||||
out.asset_value = 0
|
||||
if args.asset:
|
||||
out.current_asset_value = flt(
|
||||
get_current_asset_value(args.asset, finance_book=args.finance_book)
|
||||
)
|
||||
out.asset_value = get_value_after_depreciation_on_disposal_date(
|
||||
args.asset, args.posting_date, finance_book=args.finance_book
|
||||
)
|
||||
else:
|
||||
out.current_asset_value = 0
|
||||
out.asset_value = 0
|
||||
|
||||
# Account
|
||||
if asset_details.item_code:
|
||||
out.fixed_asset_account = get_asset_category_account('fixed_asset_account', item=asset_details.item_code,
|
||||
company=args.company)
|
||||
out.fixed_asset_account = get_asset_category_account(
|
||||
"fixed_asset_account", item=asset_details.item_code, company=args.company
|
||||
)
|
||||
else:
|
||||
out.fixed_asset_account = None
|
||||
|
||||
@ -632,7 +712,9 @@ def get_consumed_asset_details(args, get_asset_value=True):
|
||||
item_defaults = get_item_defaults(item.name, args.company)
|
||||
item_group_defaults = get_item_group_defaults(item.name, args.company)
|
||||
brand_defaults = get_brand_defaults(item.name, args.company)
|
||||
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
|
||||
out.cost_center = get_default_cost_center(
|
||||
args, item_defaults, item_group_defaults, brand_defaults
|
||||
)
|
||||
|
||||
return out
|
||||
|
||||
@ -657,7 +739,11 @@ def get_service_item_details(args):
|
||||
item_group_defaults = get_item_group_defaults(item.name, args.company)
|
||||
brand_defaults = get_brand_defaults(item.name, args.company)
|
||||
|
||||
out.expense_account = get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults)
|
||||
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
|
||||
out.expense_account = get_default_expense_account(
|
||||
args, item_defaults, item_group_defaults, brand_defaults
|
||||
)
|
||||
out.cost_center = get_default_cost_center(
|
||||
args, item_defaults, item_group_defaults, brand_defaults
|
||||
)
|
||||
|
||||
return out
|
||||
|
@ -22,9 +22,12 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
create_asset_capitalization_data()
|
||||
frappe.db.sql("delete from `tabTax Rule`")
|
||||
|
||||
def test_capitalization(self):
|
||||
def test_capitalization_with_perpetual_inventory(self):
|
||||
company = "_Test Company with perpetual inventory"
|
||||
set_depreciation_settings_in_company(company=company)
|
||||
|
||||
# Variables
|
||||
consumed_asset_value = 100_000
|
||||
consumed_asset_value = 100000
|
||||
|
||||
stock_rate = 1000
|
||||
stock_qty = 2
|
||||
@ -34,23 +37,39 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
service_qty = 2
|
||||
service_amount = 1000
|
||||
|
||||
total_amount = 103_000
|
||||
total_amount = 103000
|
||||
|
||||
# Create assets
|
||||
target_asset = create_asset(asset_name='Asset Capitalization Target Asset', submit=1)
|
||||
consumed_asset = create_asset(asset_name='Asset Capitalization Consumable Asset', asset_value=consumed_asset_value,
|
||||
submit=1)
|
||||
target_asset = create_asset(
|
||||
asset_name="Asset Capitalization Target Asset",
|
||||
submit=1,
|
||||
warehouse="Stores - TCP1",
|
||||
company=company,
|
||||
)
|
||||
consumed_asset = create_asset(
|
||||
asset_name="Asset Capitalization Consumable Asset",
|
||||
asset_value=consumed_asset_value,
|
||||
submit=1,
|
||||
warehouse="Stores - TCP1",
|
||||
company=company,
|
||||
)
|
||||
|
||||
# Create and submit Asset Captitalization
|
||||
asset_capitalization = create_asset_capitalization(target_asset=target_asset.name,
|
||||
stock_qty=stock_qty, stock_rate=stock_rate,
|
||||
asset_capitalization = create_asset_capitalization(
|
||||
entry_type="Capitalization",
|
||||
target_asset=target_asset.name,
|
||||
stock_qty=stock_qty,
|
||||
stock_rate=stock_rate,
|
||||
consumed_asset=consumed_asset.name,
|
||||
service_qty=service_qty, service_rate=service_rate,
|
||||
service_expense_account='Expenses Included In Asset Valuation - _TC',
|
||||
submit=1)
|
||||
service_qty=service_qty,
|
||||
service_rate=service_rate,
|
||||
service_expense_account="Expenses Included In Asset Valuation - TCP1",
|
||||
company=company,
|
||||
submit=1,
|
||||
)
|
||||
|
||||
# Test Asset Capitalization values
|
||||
self.assertEqual(asset_capitalization.entry_type, 'Capitalization')
|
||||
self.assertEqual(asset_capitalization.entry_type, "Capitalization")
|
||||
self.assertEqual(asset_capitalization.target_qty, 1)
|
||||
|
||||
self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate)
|
||||
@ -72,13 +91,13 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
self.assertEqual(target_asset.purchase_receipt_amount, total_amount)
|
||||
|
||||
# Test Consumed Asset values
|
||||
self.assertEqual(consumed_asset.db_get('status'), 'Capitalized')
|
||||
self.assertEqual(consumed_asset.db_get("status"), "Capitalized")
|
||||
|
||||
# Test General Ledger Entries
|
||||
expected_gle = {
|
||||
'_Test Fixed Asset - _TC': 3000,
|
||||
'Expenses Included In Asset Valuation - _TC': -1000,
|
||||
'Stock In Hand - _TC' : -2000
|
||||
"_Test Fixed Asset - TCP1": 3000,
|
||||
"Expenses Included In Asset Valuation - TCP1": -1000,
|
||||
"_Test Warehouse - TCP1": -2000,
|
||||
}
|
||||
actual_gle = get_actual_gle_dict(asset_capitalization.name)
|
||||
|
||||
@ -86,25 +105,121 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
|
||||
# Test Stock Ledger Entries
|
||||
expected_sle = {
|
||||
('Capitalization Source Stock Item', '_Test Warehouse - _TC'): {
|
||||
'actual_qty': -stock_qty, 'stock_value_difference': -stock_amount
|
||||
("Capitalization Source Stock Item", "_Test Warehouse - TCP1"): {
|
||||
"actual_qty": -stock_qty,
|
||||
"stock_value_difference": -stock_amount,
|
||||
}
|
||||
}
|
||||
actual_sle = get_actual_sle_dict(asset_capitalization.name)
|
||||
|
||||
self.assertEqual(actual_sle, expected_sle)
|
||||
|
||||
# Cancel Asset Capitalization and make test entries and status are reversed
|
||||
asset_capitalization.cancel()
|
||||
self.assertEqual(consumed_asset.db_get('status'), 'Submitted')
|
||||
self.assertEqual(consumed_asset.db_get("status"), "Submitted")
|
||||
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
|
||||
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
|
||||
|
||||
def test_capitalization_with_periodical_inventory(self):
|
||||
company = "_Test Company"
|
||||
# Variables
|
||||
consumed_asset_value = 100000
|
||||
|
||||
stock_rate = 1000
|
||||
stock_qty = 2
|
||||
stock_amount = 2000
|
||||
|
||||
service_rate = 500
|
||||
service_qty = 2
|
||||
service_amount = 1000
|
||||
|
||||
total_amount = 103000
|
||||
|
||||
# Create assets
|
||||
target_asset = create_asset(
|
||||
asset_name="Asset Capitalization Target Asset",
|
||||
submit=1,
|
||||
warehouse="Stores - _TC",
|
||||
company=company,
|
||||
)
|
||||
consumed_asset = create_asset(
|
||||
asset_name="Asset Capitalization Consumable Asset",
|
||||
asset_value=consumed_asset_value,
|
||||
submit=1,
|
||||
warehouse="Stores - _TC",
|
||||
company=company,
|
||||
)
|
||||
|
||||
# Create and submit Asset Captitalization
|
||||
asset_capitalization = create_asset_capitalization(
|
||||
entry_type="Capitalization",
|
||||
target_asset=target_asset.name,
|
||||
stock_qty=stock_qty,
|
||||
stock_rate=stock_rate,
|
||||
consumed_asset=consumed_asset.name,
|
||||
service_qty=service_qty,
|
||||
service_rate=service_rate,
|
||||
service_expense_account="Expenses Included In Asset Valuation - _TC",
|
||||
company=company,
|
||||
submit=1,
|
||||
)
|
||||
|
||||
# Test Asset Capitalization values
|
||||
self.assertEqual(asset_capitalization.entry_type, "Capitalization")
|
||||
self.assertEqual(asset_capitalization.target_qty, 1)
|
||||
|
||||
self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate)
|
||||
self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount)
|
||||
self.assertEqual(asset_capitalization.stock_items_total, stock_amount)
|
||||
|
||||
self.assertEqual(asset_capitalization.asset_items[0].asset_value, consumed_asset_value)
|
||||
self.assertEqual(asset_capitalization.asset_items_total, consumed_asset_value)
|
||||
|
||||
self.assertEqual(asset_capitalization.service_items[0].amount, service_amount)
|
||||
self.assertEqual(asset_capitalization.service_items_total, service_amount)
|
||||
|
||||
self.assertEqual(asset_capitalization.total_value, total_amount)
|
||||
self.assertEqual(asset_capitalization.target_incoming_rate, total_amount)
|
||||
|
||||
# Test Target Asset values
|
||||
target_asset.reload()
|
||||
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
|
||||
self.assertEqual(target_asset.purchase_receipt_amount, total_amount)
|
||||
|
||||
# Test Consumed Asset values
|
||||
self.assertEqual(consumed_asset.db_get("status"), "Capitalized")
|
||||
|
||||
# Test General Ledger Entries
|
||||
default_expense_account = frappe.db.get_value("Company", company, "default_expense_account")
|
||||
expected_gle = {
|
||||
"_Test Fixed Asset - _TC": 3000,
|
||||
"Expenses Included In Asset Valuation - _TC": -1000,
|
||||
default_expense_account: -2000,
|
||||
}
|
||||
actual_gle = get_actual_gle_dict(asset_capitalization.name)
|
||||
|
||||
self.assertEqual(actual_gle, expected_gle)
|
||||
|
||||
# Test Stock Ledger Entries
|
||||
expected_sle = {
|
||||
("Capitalization Source Stock Item", "_Test Warehouse - _TC"): {
|
||||
"actual_qty": -stock_qty,
|
||||
"stock_value_difference": -stock_amount,
|
||||
}
|
||||
}
|
||||
actual_sle = get_actual_sle_dict(asset_capitalization.name)
|
||||
self.assertEqual(actual_sle, expected_sle)
|
||||
|
||||
# Cancel Asset Capitalization and make test entries and status are reversed
|
||||
asset_capitalization.cancel()
|
||||
self.assertEqual(consumed_asset.db_get("status"), "Submitted")
|
||||
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
|
||||
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
|
||||
|
||||
def test_decapitalization_with_depreciation(self):
|
||||
# Variables
|
||||
purchase_date = '2020-01-01'
|
||||
depreciation_start_date = '2020-12-31'
|
||||
capitalization_date = '2021-06-30'
|
||||
purchase_date = "2020-01-01"
|
||||
depreciation_start_date = "2020-12-31"
|
||||
capitalization_date = "2021-06-30"
|
||||
|
||||
total_number_of_depreciations = 3
|
||||
expected_value_after_useful_life = 10_000
|
||||
@ -126,29 +241,38 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
|
||||
# Create assets
|
||||
consumed_asset = create_depreciation_asset(
|
||||
asset_name='Asset Capitalization Consumable Asset',
|
||||
asset_name="Asset Capitalization Consumable Asset",
|
||||
asset_value=consumed_asset_purchase_value,
|
||||
purchase_date=purchase_date,
|
||||
depreciation_start_date=depreciation_start_date,
|
||||
depreciation_method='Straight Line',
|
||||
depreciation_method="Straight Line",
|
||||
total_number_of_depreciations=total_number_of_depreciations,
|
||||
frequency_of_depreciation=12,
|
||||
expected_value_after_useful_life=expected_value_after_useful_life,
|
||||
submit=1)
|
||||
company="_Test Company with perpetual inventory",
|
||||
submit=1,
|
||||
)
|
||||
|
||||
# Create and submit Asset Captitalization
|
||||
asset_capitalization = create_asset_capitalization(
|
||||
entry_type="Decapitalization",
|
||||
posting_date=capitalization_date, # half a year
|
||||
target_item_code="Capitalization Target Stock Item",
|
||||
target_qty=target_qty,
|
||||
consumed_asset=consumed_asset.name,
|
||||
submit=1)
|
||||
company="_Test Company with perpetual inventory",
|
||||
submit=1,
|
||||
)
|
||||
|
||||
# Test Asset Capitalization values
|
||||
self.assertEqual(asset_capitalization.entry_type, 'Decapitalization')
|
||||
self.assertEqual(asset_capitalization.entry_type, "Decapitalization")
|
||||
|
||||
self.assertEqual(asset_capitalization.asset_items[0].current_asset_value, consumed_asset_current_value)
|
||||
self.assertEqual(asset_capitalization.asset_items[0].asset_value, consumed_asset_value_before_disposal)
|
||||
self.assertEqual(
|
||||
asset_capitalization.asset_items[0].current_asset_value, consumed_asset_current_value
|
||||
)
|
||||
self.assertEqual(
|
||||
asset_capitalization.asset_items[0].asset_value, consumed_asset_value_before_disposal
|
||||
)
|
||||
self.assertEqual(asset_capitalization.asset_items_total, consumed_asset_value_before_disposal)
|
||||
|
||||
self.assertEqual(asset_capitalization.total_value, consumed_asset_value_before_disposal)
|
||||
@ -156,38 +280,45 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
|
||||
# Test Consumed Asset values
|
||||
consumed_asset.reload()
|
||||
self.assertEqual(consumed_asset.status, 'Decapitalized')
|
||||
self.assertEqual(consumed_asset.status, "Decapitalized")
|
||||
|
||||
consumed_depreciation_schedule = [d for d in consumed_asset.schedules
|
||||
if getdate(d.schedule_date) == getdate(capitalization_date)]
|
||||
self.assertTrue(consumed_depreciation_schedule and consumed_depreciation_schedule[0].journal_entry)
|
||||
self.assertEqual(consumed_depreciation_schedule[0].depreciation_amount, depreciation_before_disposal_amount)
|
||||
consumed_depreciation_schedule = [
|
||||
d for d in consumed_asset.schedules if getdate(d.schedule_date) == getdate(capitalization_date)
|
||||
]
|
||||
self.assertTrue(
|
||||
consumed_depreciation_schedule and consumed_depreciation_schedule[0].journal_entry
|
||||
)
|
||||
self.assertEqual(
|
||||
consumed_depreciation_schedule[0].depreciation_amount, depreciation_before_disposal_amount
|
||||
)
|
||||
|
||||
# Test General Ledger Entries
|
||||
expected_gle = {
|
||||
'Stock In Hand - _TC': consumed_asset_value_before_disposal,
|
||||
'_Test Accumulated Depreciations - _TC': accumulated_depreciation,
|
||||
'_Test Fixed Asset - _TC': -consumed_asset_purchase_value,
|
||||
"_Test Warehouse - TCP1": consumed_asset_value_before_disposal,
|
||||
"_Test Accumulated Depreciations - TCP1": accumulated_depreciation,
|
||||
"_Test Fixed Asset - TCP1": -consumed_asset_purchase_value,
|
||||
}
|
||||
actual_gle = get_actual_gle_dict(asset_capitalization.name)
|
||||
|
||||
self.assertEqual(actual_gle, expected_gle)
|
||||
|
||||
# Cancel Asset Capitalization and make test entries and status are reversed
|
||||
asset_capitalization.reload()
|
||||
asset_capitalization.cancel()
|
||||
self.assertEqual(consumed_asset.db_get('status'), 'Partially Depreciated')
|
||||
self.assertEqual(consumed_asset.db_get("status"), "Partially Depreciated")
|
||||
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
|
||||
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
|
||||
|
||||
|
||||
def create_asset_capitalization_data():
|
||||
create_item("Capitalization Target Stock Item",
|
||||
is_stock_item=1, is_fixed_asset=0, is_purchase_item=0)
|
||||
create_item("Capitalization Source Stock Item",
|
||||
is_stock_item=1, is_fixed_asset=0, is_purchase_item=0)
|
||||
create_item("Capitalization Source Service Item",
|
||||
is_stock_item=0, is_fixed_asset=0, is_purchase_item=0)
|
||||
create_item(
|
||||
"Capitalization Target Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0
|
||||
)
|
||||
create_item(
|
||||
"Capitalization Source Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0
|
||||
)
|
||||
create_item(
|
||||
"Capitalization Source Service Item", is_stock_item=0, is_fixed_asset=0, is_purchase_item=0
|
||||
)
|
||||
|
||||
|
||||
def create_asset_capitalization(**args):
|
||||
@ -204,43 +335,55 @@ def create_asset_capitalization(**args):
|
||||
source_warehouse = args.source_warehouse or warehouse
|
||||
|
||||
asset_capitalization = frappe.new_doc("Asset Capitalization")
|
||||
asset_capitalization.update({
|
||||
"company": company,
|
||||
"posting_date": args.posting_date or now.strftime('%Y-%m-%d'),
|
||||
"posting_time": args.posting_time or now.strftime('%H:%M:%S.%f'),
|
||||
"target_item_code": target_item_code,
|
||||
"target_asset": target_asset.name,
|
||||
"target_warehouse": target_warehouse,
|
||||
"target_qty": flt(args.target_qty) or 1,
|
||||
"target_batch_no": args.target_batch_no,
|
||||
"target_serial_no": args.target_serial_no,
|
||||
"finance_book": args.finance_book
|
||||
})
|
||||
asset_capitalization.update(
|
||||
{
|
||||
"entry_type": args.entry_type or "Capitalization",
|
||||
"company": company,
|
||||
"posting_date": args.posting_date or now.strftime("%Y-%m-%d"),
|
||||
"posting_time": args.posting_time or now.strftime("%H:%M:%S.%f"),
|
||||
"target_item_code": target_item_code,
|
||||
"target_asset": target_asset.name,
|
||||
"target_warehouse": target_warehouse,
|
||||
"target_qty": flt(args.target_qty) or 1,
|
||||
"target_batch_no": args.target_batch_no,
|
||||
"target_serial_no": args.target_serial_no,
|
||||
"finance_book": args.finance_book,
|
||||
}
|
||||
)
|
||||
|
||||
if args.posting_date or args.posting_time:
|
||||
asset_capitalization.set_posting_time = 1
|
||||
|
||||
if flt(args.stock_rate):
|
||||
asset_capitalization.append("stock_items", {
|
||||
"item_code": args.stock_item or "Capitalization Source Stock Item",
|
||||
"warehouse": source_warehouse,
|
||||
"stock_qty": flt(args.stock_qty) or 1,
|
||||
"batch_no": args.stock_batch_no,
|
||||
"serial_no": args.stock_serial_no,
|
||||
})
|
||||
asset_capitalization.append(
|
||||
"stock_items",
|
||||
{
|
||||
"item_code": args.stock_item or "Capitalization Source Stock Item",
|
||||
"warehouse": source_warehouse,
|
||||
"stock_qty": flt(args.stock_qty) or 1,
|
||||
"batch_no": args.stock_batch_no,
|
||||
"serial_no": args.stock_serial_no,
|
||||
},
|
||||
)
|
||||
|
||||
if args.consumed_asset:
|
||||
asset_capitalization.append("asset_items", {
|
||||
"asset": args.consumed_asset,
|
||||
})
|
||||
asset_capitalization.append(
|
||||
"asset_items",
|
||||
{
|
||||
"asset": args.consumed_asset,
|
||||
},
|
||||
)
|
||||
|
||||
if flt(args.service_rate):
|
||||
asset_capitalization.append("service_items", {
|
||||
"item_code": args.service_item or "Capitalization Source Service Item",
|
||||
"expense_account": args.service_expense_account,
|
||||
"qty": flt(args.service_qty) or 1,
|
||||
"rate": flt(args.service_rate)
|
||||
})
|
||||
asset_capitalization.append(
|
||||
"service_items",
|
||||
{
|
||||
"item_code": args.service_item or "Capitalization Source Service Item",
|
||||
"expense_account": args.service_expense_account,
|
||||
"qty": flt(args.service_qty) or 1,
|
||||
"rate": flt(args.service_rate),
|
||||
},
|
||||
)
|
||||
|
||||
if args.submit:
|
||||
create_stock_reconciliation(asset_capitalization, stock_rate=args.stock_rate)
|
||||
@ -255,17 +398,23 @@ def create_asset_capitalization(**args):
|
||||
|
||||
def create_stock_reconciliation(asset_capitalization, stock_rate=0):
|
||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||
EmptyStockReconciliationItemsError,
|
||||
create_stock_reconciliation,
|
||||
)
|
||||
if not asset_capitalization.get('stock_items'):
|
||||
|
||||
if not asset_capitalization.get("stock_items"):
|
||||
return
|
||||
|
||||
return create_stock_reconciliation(
|
||||
item_code=asset_capitalization.stock_items[0].item_code,
|
||||
warehouse=asset_capitalization.stock_items[0].warehouse,
|
||||
qty=flt(asset_capitalization.stock_items[0].stock_qty),
|
||||
rate=flt(stock_rate),
|
||||
company=asset_capitalization.company)
|
||||
try:
|
||||
create_stock_reconciliation(
|
||||
item_code=asset_capitalization.stock_items[0].item_code,
|
||||
warehouse=asset_capitalization.stock_items[0].warehouse,
|
||||
qty=flt(asset_capitalization.stock_items[0].stock_qty),
|
||||
rate=flt(stock_rate),
|
||||
company=asset_capitalization.company,
|
||||
)
|
||||
except EmptyStockReconciliationItemsError:
|
||||
pass
|
||||
|
||||
|
||||
def create_depreciation_asset(**args):
|
||||
@ -281,15 +430,15 @@ def create_depreciation_asset(**args):
|
||||
asset.asset_name = args.asset_name or asset.item_code
|
||||
asset.location = args.location or "Test Location"
|
||||
|
||||
asset.purchase_date = args.purchase_date or '2020-01-01'
|
||||
asset.purchase_date = args.purchase_date or "2020-01-01"
|
||||
asset.available_for_use_date = args.available_for_use_date or asset.purchase_date
|
||||
|
||||
asset.gross_purchase_amount = args.asset_value or 100000
|
||||
asset.purchase_receipt_amount = asset.gross_purchase_amount
|
||||
|
||||
finance_book = asset.append('finance_books')
|
||||
finance_book.depreciation_start_date = args.depreciation_start_date or '2020-12-31'
|
||||
finance_book.depreciation_method = args.depreciation_method or 'Straight Line'
|
||||
finance_book = asset.append("finance_books")
|
||||
finance_book.depreciation_start_date = args.depreciation_start_date or "2020-12-31"
|
||||
finance_book.depreciation_method = args.depreciation_method or "Straight Line"
|
||||
finance_book.total_number_of_depreciations = cint(args.total_number_of_depreciations) or 3
|
||||
finance_book.frequency_of_depreciation = cint(args.frequency_of_depreciation) or 12
|
||||
finance_book.expected_value_after_useful_life = flt(args.expected_value_after_useful_life)
|
||||
@ -305,17 +454,23 @@ def create_depreciation_asset(**args):
|
||||
|
||||
|
||||
def get_actual_gle_dict(name):
|
||||
return dict(frappe.db.sql("""
|
||||
return dict(
|
||||
frappe.db.sql(
|
||||
"""
|
||||
select account, sum(debit-credit) as diff
|
||||
from `tabGL Entry`
|
||||
where voucher_type = 'Asset Capitalization' and voucher_no = %s
|
||||
group by account
|
||||
having diff != 0
|
||||
""", name))
|
||||
""",
|
||||
name,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_actual_sle_dict(name):
|
||||
sles = frappe.db.sql("""
|
||||
sles = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
item_code, warehouse,
|
||||
sum(actual_qty) as actual_qty,
|
||||
@ -324,12 +479,16 @@ def get_actual_sle_dict(name):
|
||||
where voucher_type = 'Asset Capitalization' and voucher_no = %s
|
||||
group by item_code, warehouse
|
||||
having actual_qty != 0
|
||||
""", name, as_dict=1)
|
||||
""",
|
||||
name,
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
sle_dict = {}
|
||||
for d in sles:
|
||||
sle_dict[(d.item_code, d.warehouse)] = {
|
||||
'actual_qty': d.actual_qty, 'stock_value_difference': d.stock_value_difference
|
||||
"actual_qty": d.actual_qty,
|
||||
"stock_value_difference": d.stock_value_difference,
|
||||
}
|
||||
|
||||
return sle_dict
|
||||
|
@ -129,18 +129,6 @@ class TestAssetRepair(unittest.TestCase):
|
||||
def test_gl_entries_with_perpetual_inventory(self):
|
||||
set_depreciation_settings_in_company(company="_Test Company with perpetual inventory")
|
||||
|
||||
asset_category = frappe.get_doc("Asset Category", "Computers")
|
||||
asset_category.append(
|
||||
"accounts",
|
||||
{
|
||||
"company_name": "_Test Company with perpetual inventory",
|
||||
"fixed_asset_account": "_Test Fixed Asset - TCP1",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - TCP1",
|
||||
"depreciation_expense_account": "_Test Depreciations - TCP1",
|
||||
},
|
||||
)
|
||||
asset_category.save()
|
||||
|
||||
asset_repair = create_asset_repair(
|
||||
capitalize_repair_cost=1,
|
||||
stock_consumption=1,
|
||||
|
@ -727,7 +727,12 @@ def create_stock_reconciliation(**args):
|
||||
sr.set_posting_time = 1
|
||||
sr.company = args.company or "_Test Company"
|
||||
sr.expense_account = args.expense_account or (
|
||||
"Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC"
|
||||
(
|
||||
frappe.get_cached_value("Company", sr.company, "stock_adjustment_account")
|
||||
or "Stock Adjustment - _TC"
|
||||
)
|
||||
if frappe.get_all("Stock Ledger Entry", {"company": sr.company})
|
||||
else "Temporary Opening - _TC"
|
||||
)
|
||||
sr.cost_center = (
|
||||
args.cost_center
|
||||
|
Loading…
Reference in New Issue
Block a user