2
0
account-financial-tools/account_asset_management/tests/test_account_asset_management.py
2022-12-24 00:42:21 +01:00

598 lines
24 KiB
Python

# Copyright (c) 2014 ACSONE SA/NV (acsone.eu).
# Copyright 2009-2018 Noviat
# Copyright 2021 Tecnativa - João Marques
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import calendar
import time
from datetime import date, datetime
from odoo.tests.common import Form
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
class TestAssetManagement(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
# ENVIRONEMENTS
cls.asset_model = cls.env["account.asset"]
cls.asset_profile_model = cls.env["account.asset.profile"]
cls.dl_model = cls.env["account.asset.line"]
cls.remove_model = cls.env["account.asset.remove"]
# INSTANCES
cls.partner = cls.env["res.partner"].create({"name": "Test Partner"})
cls.product = cls.env["product.product"].create(
{"name": "Test", "standard_price": 500.0}
)
move_form = Form(
cls.env["account.move"].with_context(
default_move_type="in_invoice", check_move_validity=False
)
)
move_form.partner_id = cls.partner
with move_form.invoice_line_ids.new() as line_form:
line_form.name = "test"
line_form.product_id = cls.product
line_form.price_unit = 2000.00
line_form.quantity = 1
cls.invoice = move_form.save()
move_form = Form(
cls.env["account.move"].with_context(
default_move_type="in_invoice", check_move_validity=False
)
)
move_form.partner_id = cls.partner
with move_form.invoice_line_ids.new() as line_form:
line_form.name = "test 2"
line_form.product_id = cls.product
line_form.price_unit = 10000.00
line_form.quantity = 1
with move_form.invoice_line_ids.new() as line_form:
line_form.name = "test 3"
line_form.product_id = cls.product
line_form.price_unit = 20000.00
line_form.quantity = 1
cls.invoice_2 = move_form.save()
# Asset Profile 1
cls.ict3Y = cls.asset_profile_model.create(
{
"account_expense_depreciation_id": cls.company_data[
"default_account_expense"
].id,
"account_asset_id": cls.company_data["default_account_assets"].id,
"account_depreciation_id": cls.company_data[
"default_account_assets"
].id,
"journal_id": cls.company_data["default_journal_purchase"].id,
"name": "Hardware - 3 Years",
"method_time": "year",
"method_number": 3,
"method_period": "year",
}
)
# Asset Profile 2
cls.car5y = cls.asset_profile_model.create(
{
"account_expense_depreciation_id": cls.company_data[
"default_account_expense"
].id,
"account_asset_id": cls.company_data["default_account_assets"].id,
"account_depreciation_id": cls.company_data[
"default_account_assets"
].id,
"journal_id": cls.company_data["default_journal_purchase"].id,
"name": "Cars - 5 Years",
"method_time": "year",
"method_number": 5,
"method_period": "year",
}
)
def test_01_nonprorata_basic(self):
"""Basic tests of depreciation board computations and postings."""
# First create demo assets and do some sanity checks
# Asset Model 1
ict0 = self.asset_model.create(
{
"state": "draft",
"method_time": "year",
"method_number": 3,
"method_period": "year",
"name": "Laptop",
"code": "PI00101",
"purchase_value": 1500.0,
"profile_id": self.ict3Y.id,
"date_start": time.strftime("%Y-01-01"),
}
)
# Sanity checks
self.assertEqual(ict0.state, "draft")
self.assertEqual(ict0.purchase_value, 1500)
self.assertEqual(ict0.salvage_value, 0)
self.assertEqual(ict0.depreciation_base, 1500)
self.assertEqual(len(ict0.depreciation_line_ids), 1)
# Asset Model 2
vehicle0 = self.asset_model.create(
{
"state": "draft",
"method_time": "year",
"method_number": 5,
"method_period": "year",
"name": "CEO's Car",
"purchase_value": 12000.0,
"salvage_value": 2000.0,
"profile_id": self.car5y.id,
"date_start": time.strftime("%Y-01-01"),
}
)
# Sanity checks
self.assertEqual(vehicle0.state, "draft")
self.assertEqual(vehicle0.purchase_value, 12000)
self.assertEqual(vehicle0.salvage_value, 2000)
self.assertEqual(vehicle0.depreciation_base, 10000)
self.assertEqual(len(vehicle0.depreciation_line_ids), 1)
# Compute the depreciation boards
ict0.compute_depreciation_board()
ict0.refresh()
self.assertEqual(len(ict0.depreciation_line_ids), 4)
self.assertEqual(ict0.depreciation_line_ids[1].amount, 500)
vehicle0.compute_depreciation_board()
vehicle0.refresh()
self.assertEqual(len(vehicle0.depreciation_line_ids), 6)
self.assertEqual(vehicle0.depreciation_line_ids[1].amount, 2000)
# Post the first depreciation line
ict0.validate()
ict0.depreciation_line_ids[1].create_move()
ict0.refresh()
self.assertEqual(ict0.state, "open")
self.assertEqual(ict0.value_depreciated, 500)
self.assertEqual(ict0.value_residual, 1000)
vehicle0.validate()
vehicle0.depreciation_line_ids[1].create_move()
vehicle0.refresh()
self.assertEqual(vehicle0.state, "open")
self.assertEqual(vehicle0.value_depreciated, 2000)
self.assertEqual(vehicle0.value_residual, 8000)
def test_02_prorata_basic(self):
"""Prorata temporis depreciation basic test."""
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 3333,
"salvage_value": 0,
"date_start": time.strftime("%Y-07-07"),
"method_time": "year",
"method_number": 5,
"method_period": "month",
"prorata": True,
}
)
asset.compute_depreciation_board()
asset.refresh()
if calendar.isleap(date.today().year):
self.assertAlmostEqual(
asset.depreciation_line_ids[1].amount, 46.44, places=2
)
else:
self.assertAlmostEqual(
asset.depreciation_line_ids[1].amount, 47.33, places=2
)
self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, 55.55, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 55.55, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, 55.55, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[5].amount, 55.55, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[6].amount, 55.55, places=2)
if calendar.isleap(date.today().year):
self.assertAlmostEqual(
asset.depreciation_line_ids[-1].amount, 9.11, places=2
)
else:
self.assertAlmostEqual(
asset.depreciation_line_ids[-1].amount, 8.22, places=2
)
def test_03_proprata_init_prev_year(self):
"""Prorata temporis depreciation with init value in prev year."""
# Create an asset in current year
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 3333,
"salvage_value": 0,
"date_start": "%d-07-07" % (datetime.now().year - 1,),
"method_time": "year",
"method_number": 5,
"method_period": "month",
"prorata": True,
}
)
# Create a initial depreciation line in previous year
self.dl_model.create(
{
"asset_id": asset.id,
"amount": 325.08,
"line_date": "%d-12-31" % (datetime.now().year - 1,),
"type": "depreciate",
"init_entry": True,
}
)
self.assertEqual(len(asset.depreciation_line_ids), 2)
asset.compute_depreciation_board()
asset.refresh()
# check the depreciated value is the initial value
self.assertAlmostEqual(asset.value_depreciated, 325.08, places=2)
# check computed values in the depreciation board
self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 55.55, places=2)
if calendar.isleap(date.today().year - 1):
# for leap years the first year depreciation amount of 325.08
# is too high and hence a correction is applied to the next
# entry of the table
self.assertAlmostEqual(
asset.depreciation_line_ids[2].amount, 54.66, places=2
)
self.assertAlmostEqual(
asset.depreciation_line_ids[3].amount, 55.55, places=2
)
self.assertAlmostEqual(
asset.depreciation_line_ids[-1].amount, 9.11, places=2
)
else:
self.assertAlmostEqual(
asset.depreciation_line_ids[2].amount, 55.55, places=2
)
self.assertAlmostEqual(
asset.depreciation_line_ids[-1].amount, 8.22, places=2
)
def test_04_prorata_init_cur_year(self):
"""Prorata temporis depreciation with init value in curent year."""
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 3333,
"salvage_value": 0,
"date_start": time.strftime("%Y-07-07"),
"method_time": "year",
"method_number": 5,
"method_period": "month",
"prorata": True,
}
)
self.dl_model.create(
{
"asset_id": asset.id,
"amount": 279.44,
"line_date": time.strftime("%Y-11-30"),
"type": "depreciate",
"init_entry": True,
}
)
self.assertEqual(len(asset.depreciation_line_ids), 2)
asset.compute_depreciation_board()
asset.refresh()
# check the depreciated value is the initial value
self.assertAlmostEqual(asset.value_depreciated, 279.44, places=2)
# check computed values in the depreciation board
if calendar.isleap(date.today().year):
self.assertAlmostEqual(
asset.depreciation_line_ids[2].amount, 44.75, places=2
)
else:
self.assertAlmostEqual(
asset.depreciation_line_ids[2].amount, 45.64, places=2
)
self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 55.55, places=2)
if calendar.isleap(date.today().year):
self.assertAlmostEqual(
asset.depreciation_line_ids[-1].amount, 9.11, places=2
)
else:
self.assertAlmostEqual(
asset.depreciation_line_ids[-1].amount, 8.22, places=2
)
def test_05_degressive_linear(self):
"""Degressive-Linear with annual and quarterly depreciation."""
# annual depreciation
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 1000,
"salvage_value": 0,
"date_start": time.strftime("%Y-07-07"),
"method_time": "year",
"method": "degr-linear",
"method_progress_factor": 0.40,
"method_number": 5,
"method_period": "year",
"prorata": False,
}
)
asset.compute_depreciation_board()
asset.refresh()
# check values in the depreciation board
self.assertEqual(len(asset.depreciation_line_ids), 5)
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 400.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, 240.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 200.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, 160.00, places=2)
# quarterly depreciation
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 1000,
"salvage_value": 0,
"date_start": time.strftime("%Y-07-07"),
"method_time": "year",
"method": "degr-linear",
"method_progress_factor": 0.40,
"method_number": 5,
"method_period": "quarter",
"prorata": False,
}
)
asset.compute_depreciation_board()
asset.refresh()
# check values in the depreciation board
self.assertEqual(len(asset.depreciation_line_ids), 15)
# lines prior to asset start period are grouped in the first entry
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 300.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 60.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[7].amount, 50.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[13].amount, 40.00, places=2)
def test_06_degressive_limit(self):
"""Degressive with annual depreciation."""
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 1000,
"salvage_value": 100,
"date_start": time.strftime("%Y-07-07"),
"method_time": "year",
"method": "degr-limit",
"method_progress_factor": 0.40,
"method_number": 5,
"method_period": "year",
"prorata": False,
}
)
asset.compute_depreciation_board()
asset.refresh()
# check values in the depreciation board
self.assertEqual(len(asset.depreciation_line_ids), 6)
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 400.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, 240.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 144.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, 86.40, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[5].amount, 29.60, places=2)
def test_07_linear_limit(self):
"""Degressive with annual depreciation."""
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 1000,
"salvage_value": 100,
"date_start": time.strftime("%Y-07-07"),
"method_time": "year",
"method": "linear-limit",
"method_number": 5,
"method_period": "year",
"prorata": False,
}
)
asset.compute_depreciation_board()
asset.refresh()
# check values in the depreciation board
self.assertEqual(len(asset.depreciation_line_ids), 6)
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 200.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, 100.00, places=2)
def test_08_asset_removal(self):
"""Asset removal"""
asset = self.asset_model.create(
{
"name": "test asset removal",
"profile_id": self.car5y.id,
"purchase_value": 5000,
"salvage_value": 0,
"date_start": "2019-01-01",
"method_time": "year",
"method_number": 5,
"method_period": "quarter",
"prorata": False,
}
)
asset.compute_depreciation_board()
asset.validate()
wiz_ctx = {"active_id": asset.id, "early_removal": True}
wiz = self.remove_model.with_context(wiz_ctx).create(
{
"date_remove": "2019-01-31",
"sale_value": 0.0,
"posting_regime": "gain_loss_on_sale",
"account_plus_value_id": self.company_data[
"default_account_revenue"
].id,
"account_min_value_id": self.company_data["default_account_expense"].id,
}
)
wiz.remove()
asset.refresh()
self.assertEqual(len(asset.depreciation_line_ids), 3)
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 81.46, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, 4918.54, places=2)
def test_09_asset_from_invoice(self):
all_asset = self.env["account.asset"].search([])
invoice = self.invoice
asset_profile = self.car5y
asset_profile.asset_product_item = False
self.assertTrue(len(invoice.invoice_line_ids) > 0)
line = invoice.invoice_line_ids[0]
self.assertTrue(line.price_unit > 0.0)
move_form = Form(invoice)
with move_form.invoice_line_ids.edit(0) as line_form:
line_form.quantity = 2
line_form.asset_profile_id = asset_profile
invoice = move_form.save()
invoice.action_post()
# get all asset after invoice validation
current_asset = self.env["account.asset"].search([])
# get the new asset
new_asset = current_asset - all_asset
# check that a new asset is created
self.assertEqual(len(new_asset), 1)
# check that the new asset has the correct purchase value
self.assertAlmostEqual(
new_asset.purchase_value, line.price_unit * line.quantity, places=2
)
def test_10_asset_from_invoice_product_item(self):
all_asset = self.env["account.asset"].search([])
invoice = self.invoice
asset_profile = self.car5y
asset_profile.asset_product_item = True
self.assertTrue(len(invoice.invoice_line_ids) > 0)
line = invoice.invoice_line_ids[0]
self.assertTrue(line.price_unit > 0.0)
line.quantity = 2
line.asset_profile_id = asset_profile
self.assertEqual(len(invoice.invoice_line_ids), 2)
invoice.action_post()
# get all asset after invoice validation
current_asset = self.env["account.asset"].search([])
# get the new asset
new_asset = current_asset - all_asset
# check that a new asset is created
self.assertEqual(len(new_asset), 2)
for asset in new_asset:
# check that the new asset has the correct purchase value
self.assertAlmostEqual(asset.purchase_value, line.price_unit, places=2)
def test_11_assets_from_invoice(self):
all_assets = self.env["account.asset"].search([])
ctx = dict(self.invoice_2._context)
del ctx["default_move_type"]
invoice = self.invoice_2.with_context(ctx)
asset_profile = self.car5y
asset_profile.asset_product_item = True
# Compute depreciation lines on invoice validation
asset_profile.open_asset = True
self.assertTrue(len(invoice.invoice_line_ids) == 2)
invoice.invoice_line_ids.write(
{"quantity": 1, "asset_profile_id": asset_profile.id}
)
invoice.action_post()
# Retrieve all assets after invoice validation
current_assets = self.env["account.asset"].search([])
# What are the new assets?
new_assets = current_assets - all_assets
self.assertEqual(len(new_assets), 2)
for asset in new_assets:
dlines = asset.depreciation_line_ids.filtered(
lambda l: l.type == "depreciate"
)
dlines = dlines.sorted(key=lambda l: l.line_date)
self.assertAlmostEqual(dlines[0].depreciated_value, 0.0)
self.assertAlmostEqual(dlines[-1].remaining_value, 0.0)
def test_12_prorata_days_calc(self):
"""Prorata temporis depreciation with days calc option."""
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 3333,
"salvage_value": 0,
"date_start": "2019-07-07",
"method_time": "year",
"method_number": 5,
"method_period": "month",
"prorata": True,
"days_calc": True,
"use_leap_years": False,
}
)
asset.compute_depreciation_board()
asset.refresh()
day_rate = 3333 / 1827 # 3333 / 1827 depreciation days
for i in range(1, 10):
self.assertAlmostEqual(
asset.depreciation_line_ids[i].amount,
asset.depreciation_line_ids[i].line_days * day_rate,
places=2,
)
# Last depreciation remaining
self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, 11.05, places=2)
def test_13_use_leap_year(self):
# When you use the depreciation with years method and using lap years,
# the depreciation amount is calculated as 10000 / 1826 days * 365 days
# = yearly depreciation amount of 1998.90.
# Then 1998.90 / 12 = 166.58
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 10000,
"salvage_value": 0,
"date_start": time.strftime("2019-01-01"),
"method_time": "year",
"method_number": 5,
"method_period": "month",
"prorata": False,
"days_calc": False,
"use_leap_years": True,
}
)
asset.compute_depreciation_board()
asset.refresh()
for i in range(2, 11):
self.assertAlmostEqual(
asset.depreciation_line_ids[i].amount, 166.58, places=2
)
self.assertAlmostEqual(
asset.depreciation_line_ids[13].depreciated_value, 1998.90, places=2
)
def test_14_not_use_leap_year(self):
# When you run a depreciation with method = 'year' and no not use
# lap years you divide 1000 / 5 years = 2000, then divided by 12 months
# to get 166.67 per month, equal for all periods.
asset = self.asset_model.create(
{
"name": "test asset",
"profile_id": self.car5y.id,
"purchase_value": 10000,
"salvage_value": 0,
"date_start": time.strftime("2019-01-01"),
"method_time": "year",
"method_number": 5,
"method_period": "month",
"prorata": False,
"days_calc": False,
"use_leap_years": False,
}
)
asset.compute_depreciation_board()
asset.refresh()
for _i in range(1, 11):
self.assertAlmostEqual(
asset.depreciation_line_ids[1].amount, 166.67, places=2
)
# In the last month of the fiscal year we compensate for the small
# deviations if that is necessary.
self.assertAlmostEqual(asset.depreciation_line_ids[12].amount, 166.63, places=2)