You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1890 lines
82 KiB
Python

# -*- coding: utf-8 -*-
import time
from dateutil.relativedelta import relativedelta
from freezegun import freeze_time
from odoo import fields, Command
from odoo.exceptions import UserError, MissingError
from odoo.tests.common import Form, tagged
from odoo.addons.account_reports.tests.common import TestAccountReportsCommon
@freeze_time('2021-07-01')
@tagged('post_install', '-at_install')
class TestAccountAsset(TestAccountReportsCommon):
@classmethod
def setUpClass(cls):
super(TestAccountAsset, cls).setUpClass()
today = fields.Date.today()
cls.truck = cls.env['account.asset'].create({
'account_asset_id': cls.company_data['default_account_assets'].id,
'account_depreciation_id': cls.company_data['default_account_assets'].copy().id,
'account_depreciation_expense_id': cls.company_data['default_account_expense'].id,
'journal_id': cls.company_data['default_journal_misc'].id,
'asset_type': 'purchase',
'name': 'truck',
'acquisition_date': today + relativedelta(years=-6, months=-6),
'original_value': 10000,
'salvage_value': 2500,
'method_number': 10,
'method_period': '12',
'method': 'linear',
})
cls.truck.validate()
cls.env['account.move']._autopost_draft_entries()
cls.account_asset_model_fixedassets = cls.env['account.asset'].create({
'account_depreciation_id': cls.company_data['default_account_assets'].copy().id,
'account_depreciation_expense_id': cls.company_data['default_account_expense'].id,
'account_asset_id': cls.company_data['default_account_assets'].id,
'journal_id': cls.company_data['default_journal_purchase'].id,
'name': 'Hardware - 3 Years',
'method_number': 3,
'method_period': '12',
'state': 'model',
})
cls.closing_invoice = cls.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_line_ids': [(0, 0, {'price_unit': 100})]
})
cls.env.company.loss_account_id = cls.company_data['default_account_expense'].copy()
cls.env.company.gain_account_id = cls.company_data['default_account_revenue'].copy()
cls.assert_counterpart_account_id = cls.company_data['default_account_expense'].copy().id
def update_form_values(self, asset_form):
for i in range(len(asset_form.depreciation_move_ids)):
with asset_form.depreciation_move_ids.edit(i) as line_edit:
line_edit.asset_remaining_value
def test_00_account_asset(self):
"""Test the lifecycle of an asset"""
CEO_car = self.env['account.asset'].with_context(asset_type='purchase').create({
'salvage_value': 2000.0,
'state': 'open',
'method_period': '12',
'method_number': 5,
'name': "CEO's Car",
'original_value': 12000.0,
'model_id': self.account_asset_model_fixedassets.id,
})
CEO_car._onchange_model_id()
CEO_car.prorata_computation_type = 'constant_periods'
CEO_car.method_number = 5
# In order to test the process of Account Asset, I perform a action to confirm Account Asset.
CEO_car.validate()
# TOFIX: the method validate() makes the field account.asset.asset_type
# dirty, but this field has to be flushed in CEO_car's environment.
# This is because the field 'asset_type' is stored, computed and
# context-dependent, which explains why its value must be retrieved
# from the right environment.
CEO_car.flush_recordset()
# I check Asset is now in Open state.
self.assertEqual(CEO_car.state, 'open',
'Asset should be in Open state')
# I compute depreciation lines for asset of CEOs Car.
self.assertEqual(CEO_car.method_number + 1, len(CEO_car.depreciation_move_ids),
'Depreciation lines not created correctly')
# Check that auto_post is set on the entries, in the future, and we cannot post them.
self.assertTrue(all(CEO_car.depreciation_move_ids.mapped(lambda m: m.auto_post != 'no')))
with self.assertRaises(UserError):
CEO_car.depreciation_move_ids.action_post()
# I Check that After creating all the moves of depreciation lines the state "Running".
CEO_car.depreciation_move_ids.write({'auto_post': 'no'})
CEO_car.depreciation_move_ids.action_post()
self.assertEqual(CEO_car.state, 'open',
'State of asset should be runing')
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 2000,
'value_residual': 0,
'salvage_value': 2000,
}])
self.assertRecordValues(CEO_car.depreciation_move_ids.sorted(lambda l: l.date), [{
'amount_total': 1000,
'asset_remaining_value': 9000,
}, {
'amount_total': 2000,
'asset_remaining_value': 7000,
}, {
'amount_total': 2000,
'asset_remaining_value': 5000,
}, {
'amount_total': 2000,
'asset_remaining_value': 3000,
}, {
'amount_total': 2000,
'asset_remaining_value': 1000,
}, {
'amount_total': 1000,
'asset_remaining_value': 0,
}])
# Revert posted entries in order to be able to close
CEO_car.depreciation_move_ids._reverse_moves(cancel=True)
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 12000,
'value_residual': 10000,
'salvage_value': 2000,
}])
reversed_moves_values = [{
'amount_total': 1000,
'asset_remaining_value': 11000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 13000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 15000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 17000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 19000,
'state': 'posted',
}, {
'amount_total': 1000,
'asset_remaining_value': 20000,
'state': 'posted',
}, {
'amount_total': 1000,
'asset_remaining_value': 19000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 17000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 15000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 13000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 11000,
'state': 'posted',
}, {
'amount_total': 1000,
'asset_remaining_value': 10000,
'state': 'posted',
}, {
'amount_total': 10000,
'asset_remaining_value': 0,
'state': 'draft',
}]
self.assertRecordValues(CEO_car.depreciation_move_ids.sorted(lambda l: l.date), reversed_moves_values)
self.assertRecordValues(CEO_car.depreciation_move_ids.filtered(lambda l: l.state == 'draft').line_ids, [{
'debit': 0,
'credit': 10000,
'account_id': CEO_car.account_depreciation_id.id,
}, {
'debit': 10000,
'credit': 0,
'account_id': CEO_car.account_depreciation_expense_id.id,
}])
# Close
CEO_car.set_to_close(self.closing_invoice.invoice_line_ids, date=fields.Date.today() + relativedelta(days=-1))
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 12000,
'value_residual': 10000,
'salvage_value': 2000,
}])
self.assertRecordValues(CEO_car.depreciation_move_ids.sorted(lambda l: (l.date, l.id)), [{
'amount_total': 12000,
'asset_remaining_value': 0,
'state': 'draft',
}, {
'amount_total': 1000,
'asset_remaining_value': 1000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 3000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 5000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 7000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 9000,
'state': 'posted',
}, {
'amount_total': 1000,
'asset_remaining_value': 10000,
'state': 'posted',
}, {
'amount_total': 1000,
'asset_remaining_value': 9000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 7000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 5000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 3000,
'state': 'posted',
}, {
'amount_total': 2000,
'asset_remaining_value': 1000,
'state': 'posted',
}, {
'amount_total': 1000,
'asset_remaining_value': 0,
'state': 'posted',
}])
closing_move = CEO_car.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 12000,
'account_id': CEO_car.account_asset_id.id,
}, {
'debit': 0,
'credit': 0,
'account_id': CEO_car.account_depreciation_id.id,
}, {
'debit': 100,
'credit': 0,
'account_id': self.closing_invoice.invoice_line_ids.account_id.id,
}, {
'debit': 11900,
'credit': 0,
'account_id': self.env.company.loss_account_id.id,
}])
closing_move.action_post()
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 2000,
'value_residual': 0,
'salvage_value': 2000,
}])
def test_00_account_asset_new(self):
"""Test the lifecycle of an asset"""
CEO_car = self.env['account.asset'].with_context(asset_type='purchase').create({
'salvage_value': 2000.0,
'state': 'open',
'method_period': '12',
'method_number': 5,
'name': "CEO's Car",
'original_value': 12000.0,
'model_id': self.account_asset_model_fixedassets.id,
})
CEO_car._onchange_model_id()
CEO_car.prorata_computation_type = 'constant_periods'
CEO_car.method_number = 5
# In order to test the process of Account Asset, I perform a action to confirm Account Asset.
CEO_car.validate()
# I Check that After creating all the moves of depreciation lines the state of the asset is "Running".
CEO_car.depreciation_move_ids.write({'auto_post': 'no'})
CEO_car.depreciation_move_ids.action_post()
self.assertEqual(CEO_car.state, 'open',
'State of the asset should be running')
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 2000,
'value_residual': 0,
'salvage_value': 2000,
}])
self.assertRecordValues(CEO_car.depreciation_move_ids.sorted(lambda l: l.date), [{
'amount_total': 1000,
'asset_remaining_value': 9000,
}, {
'amount_total': 2000,
'asset_remaining_value': 7000,
}, {
'amount_total': 2000,
'asset_remaining_value': 5000,
}, {
'amount_total': 2000,
'asset_remaining_value': 3000,
}, {
'amount_total': 2000,
'asset_remaining_value': 1000,
}, {
'amount_total': 1000,
'asset_remaining_value': 0,
}])
# Close
CEO_car.set_to_close(self.closing_invoice.invoice_line_ids, date=fields.Date.today() + relativedelta(days=30))
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 12000,
'value_residual': 10000,
'salvage_value': 2000,
}])
self.assertRecordValues(CEO_car.depreciation_move_ids.sorted(lambda l: (l.date, l.id)), [{
'amount_total': 166.67,
'asset_remaining_value': 9833.33,
'state': 'draft',
}, {
'amount_total': 12000,
'asset_remaining_value': 0,
'state': 'draft',
}])
closing_move = max(CEO_car.depreciation_move_ids, key=lambda m: (m.date, m.id))
self.assertRecordValues(closing_move, [{
'date': fields.Date.today() + relativedelta(days=30),
}])
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 12000,
'account_id': CEO_car.account_asset_id.id,
}, {
'debit': 166.67,
'credit': 0,
'account_id': CEO_car.account_depreciation_id.id,
}, {
'debit': 100,
'credit': 0,
'account_id': self.closing_invoice.invoice_line_ids.account_id.id,
}, {
'debit': 11733.33,
'credit': 0,
'account_id': self.env.company.loss_account_id.id,
}])
CEO_car.depreciation_move_ids.auto_post = 'no'
CEO_car.depreciation_move_ids.action_post()
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 2000,
'value_residual': 0,
'salvage_value': 2000,
'state': 'close',
}])
def test_01_account_asset(self):
""" Test if an an asset is created when an invoice is validated with an
item on an account for generating entries.
"""
account_asset_model_sale_test0 = self.env['account.asset'].with_context(asset_type='purchase').create({
'account_depreciation_id': self.company_data['default_account_assets'].id,
'account_depreciation_expense_id': self.company_data['default_account_revenue'].id,
'journal_id': self.company_data['default_journal_sale'].id,
'name': 'Maintenance Contract - 3 Years',
'method_number': 3,
'method_period': '12',
'prorata_computation_type': 'daily_computation',
'asset_type': 'sale',
'state': 'model',
})
# The account needs a default model for the invoice to validate the revenue
self.company_data['default_account_assets'].create_asset = 'validate'
self.company_data['default_account_assets'].asset_model = account_asset_model_sale_test0
invoice = self.env['account.move'].with_context(asset_type='purchase').create({
'move_type': 'in_invoice',
'partner_id': self.env['res.partner'].create({'name': 'Res Partner 12'}).id,
'invoice_date': '2020-12-31',
'invoice_line_ids': [(0, 0, {
'name': 'Insurance claim',
'account_id': self.company_data['default_account_assets'].id,
'price_unit': 450,
'quantity': 1,
})],
})
invoice.action_post()
recognition = invoice.asset_ids
self.assertEqual(len(recognition), 1, 'One and only one recognition should have been created from invoice.')
self.assertTrue(recognition.state == 'open',
'Recognition should be in Open state')
first_invoice_line = invoice.invoice_line_ids[0]
self.assertEqual(recognition.original_value, first_invoice_line.price_subtotal,
'Recognition value is not same as invoice line.')
# I check data in move line and installment line.
first_installment_line = recognition.depreciation_move_ids.sorted(lambda r: r.id)[0]
self.assertAlmostEqual(first_installment_line.asset_remaining_value, recognition.original_value - first_installment_line.amount_total,
msg='Remaining value is incorrect.')
self.assertAlmostEqual(first_installment_line.asset_depreciated_value, first_installment_line.amount_total,
msg='Depreciated value is incorrect.')
# I check next installment date.
last_installment_date = first_installment_line.date
installment_date = last_installment_date + relativedelta(months=+int(recognition.method_period))
self.assertEqual(recognition.depreciation_move_ids.sorted(lambda r: r.id)[1].date, installment_date,
'Installment date is incorrect.')
def test_02_account_asset(self):
"""Test the lifecycle of an asset"""
CEO_car = self.env['account.asset'].with_context(asset_type='purchase').create({
'salvage_value': 2000.0,
'state': 'open',
'method_period': '12',
'method_number': 5,
'name': "CEO's Car",
'original_value': 12000.0,
'model_id': self.account_asset_model_fixedassets.id,
'acquisition_date': '2010-01-31',
'already_depreciated_amount_import': 10000.0,
})
CEO_car._onchange_model_id()
CEO_car.validate()
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 2000,
'value_residual': 0,
'salvage_value': 2000,
}])
self.assertFalse(CEO_car.depreciation_move_ids)
CEO_car.set_to_close(self.closing_invoice.invoice_line_ids)
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 2000,
'value_residual': 0,
'salvage_value': 2000,
}])
closing_move = CEO_car.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 12000,
'account_id': CEO_car.account_asset_id.id,
}, {
'debit': 10000,
'credit': 0,
'account_id': CEO_car.account_depreciation_id.id,
}, {
'debit': 100,
'credit': 0,
'account_id': self.closing_invoice.invoice_line_ids.account_id.id,
}, {
'debit': 1900,
'credit': 0,
'account_id': CEO_car.company_id.loss_account_id.id,
}])
closing_move.action_post()
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 2000,
'value_residual': 0,
'salvage_value': 2000,
}])
def test_03_account_asset(self):
"""Test the salvage of an asset with gain"""
CEO_car = self.env['account.asset'].with_context(asset_type='purchase').create({
'salvage_value': 0,
'state': 'open',
'method_period': '12',
'method_number': 5,
'name': "CEO's Car",
'original_value': 12000.0,
'model_id': self.account_asset_model_fixedassets.id,
'acquisition_date': '2010-01-31',
'already_depreciated_amount_import': 12000.0,
})
CEO_car._onchange_model_id()
CEO_car.validate()
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 0,
'value_residual': 0,
'salvage_value': 0,
}])
self.assertFalse(CEO_car.depreciation_move_ids)
CEO_car.set_to_close(self.closing_invoice.invoice_line_ids)
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 0,
'value_residual': 0,
'salvage_value': 0,
}])
closing_move = CEO_car.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 12000,
'account_id': CEO_car.account_asset_id.id,
}, {
'debit': 12000,
'credit': 0,
'account_id': CEO_car.account_depreciation_id.id,
}, {
'debit': 100,
'credit': 0,
'account_id': self.closing_invoice.invoice_line_ids.account_id.id,
}, {
'debit': 0,
'credit': 100,
'account_id': CEO_car.company_id.gain_account_id.id,
}])
closing_move.action_post()
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 0,
'value_residual': 0,
'salvage_value': 0,
}])
def test_04_account_asset(self):
"""Test the salvage of an asset with gain"""
CEO_car = self.env['account.asset'].with_context(asset_type='purchase').create({
'salvage_value': 0,
'state': 'open',
'method_period': '12',
'method_number': 5,
'name': "CEO's Car",
'original_value': 800.0,
'model_id': self.account_asset_model_fixedassets.id,
'acquisition_date': '2021-01-01',
'already_depreciated_amount_import': 300.0,
})
CEO_car._onchange_model_id()
CEO_car.method_number = 5
CEO_car.validate()
self.assertRecordValues(CEO_car, [{
'original_value': 800,
'book_value': 500,
'value_residual': 500,
'salvage_value': 0,
}])
self.assertEqual(len(CEO_car.depreciation_move_ids), 4)
CEO_car.set_to_close(self.closing_invoice.invoice_line_ids, date=fields.Date.today() + relativedelta(months=-6, days=-1))
self.assertRecordValues(CEO_car, [{
'original_value': 800,
'book_value': 500,
'value_residual': 500,
'salvage_value': 0,
}])
closing_move = CEO_car.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 800,
'account_id': CEO_car.account_asset_id.id,
}, {
'debit': 300,
'credit': 0,
'account_id': CEO_car.account_depreciation_id.id,
}, {
'debit': 100,
'credit': 0,
'account_id': self.closing_invoice.invoice_line_ids.account_id.id,
}, {
'debit': 400,
'credit': 0,
'account_id': CEO_car.company_id.loss_account_id.id,
}])
closing_move.action_post()
self.assertRecordValues(CEO_car, [{
'original_value': 800,
'book_value': 0,
'value_residual': 0,
'salvage_value': 0,
}])
def test_05_account_asset(self):
"""Test the salvage of an asset with gain"""
CEO_car = self.env['account.asset'].with_context(asset_type='purchase').create({
'salvage_value': 0,
'state': 'open',
'method_period': '12',
'method_number': 5,
'name': "CEO's Car",
'original_value': 1000.0,
'model_id': self.account_asset_model_fixedassets.id,
'acquisition_date': '2020-01-01',
})
CEO_car._onchange_model_id()
CEO_car.method_number = 5
CEO_car.account_depreciation_id = CEO_car.account_asset_id
CEO_car.validate()
self.assertRecordValues(CEO_car, [{
'original_value': 1000,
'book_value': 800,
'value_residual': 800,
'salvage_value': 0,
}])
self.assertEqual(len(CEO_car.depreciation_move_ids), 5)
CEO_car.set_to_close(self.env['account.move.line'], date=fields.Date.today() + relativedelta(days=-1))
self.assertRecordValues(CEO_car, [{
'original_value': 1000,
'book_value': 700,
'value_residual': 700,
'salvage_value': 0,
}])
closing_move = CEO_car.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 1000,
'account_id': CEO_car.account_asset_id.id,
}, {
'debit': 300,
'credit': 0,
'account_id': CEO_car.account_depreciation_id.id,
}, {
'debit': 700,
'credit': 0,
'account_id': CEO_car.company_id.loss_account_id.id,
}])
closing_move.action_post()
self.assertRecordValues(CEO_car, [{
'original_value': 1000,
'book_value': 0,
'value_residual': 0,
'salvage_value': 0,
}])
def test_06_account_asset(self):
"""Test the correct computation of asset amounts"""
revenue_account = self.env['account.account'].create({
"name": "test_06_account_asset",
"code": "test.06.account.asset",
"account_type": 'liability_current',
"create_asset": "no",
"asset_type": "sale",
"multiple_assets_per_line": True,
})
CEO_car = self.env['account.asset'].with_context(asset_type='purchase').create({
'salvage_value': 0,
'state': 'draft',
'method_period': '12',
'method_number': 4,
'name': "CEO's Car",
'original_value': 1000.0,
'asset_type': 'sale',
'acquisition_date': fields.Date.today() - relativedelta(years=3),
'account_asset_id': revenue_account.id,
'account_depreciation_id': self.company_data['default_account_assets'].copy().id,
'account_depreciation_expense_id': revenue_account.id,
'journal_id': self.company_data['default_journal_misc'].id,
})
CEO_car.validate()
posted_entries = len(CEO_car.depreciation_move_ids.filtered(lambda x: x.state == 'posted'))
self.assertEqual(posted_entries, 3)
self.assertRecordValues(CEO_car, [{
'original_value': 1000,
'book_value': 250,
'value_residual': 250,
'salvage_value': 0,
}])
def test_account_asset_cancel(self):
"""Test the cancellation of an asset"""
today = fields.Date.today()
CEO_car = self.env['account.asset'].with_context(asset_type='purchase').create({
'salvage_value': 2000.0,
'state': 'open',
'method_period': '12',
'method_number': 5,
'name': "CEO's Car",
'original_value': 12000.0,
'model_id': self.account_asset_model_fixedassets.id,
'acquisition_date': today + relativedelta(years=-3, month=1, day=1),
})
CEO_car._onchange_model_id()
CEO_car.method_number = 5
CEO_car.validate()
self.assertRecordValues(CEO_car, [{
'original_value': 12000,
'book_value': 6000,
'value_residual': 4000,
'salvage_value': 2000,
}])
CEO_car.set_to_cancelled()
self.assertEqual(CEO_car.state, 'cancelled')
self.assertFalse(CEO_car.depreciation_move_ids)
# Hashed journals should reverse entries instead of deleting
Hashed_car = CEO_car.copy()
Hashed_car.write({
'original_value': 12000.0,
'method_number': 5,
'name': "Hashed Car",
'journal_id': CEO_car.journal_id.copy().id,
})
Hashed_car.journal_id.restrict_mode_hash_table = True
Hashed_car.validate()
for i in range(0, 4):
self.assertFalse(Hashed_car.depreciation_move_ids[i].reversal_move_id)
Hashed_car.set_to_cancelled()
self.assertEqual(Hashed_car.state, 'cancelled')
for i in range(0, 2):
self.assertTrue(Hashed_car.depreciation_move_ids[i].reversal_move_id.id > 0 or Hashed_car.depreciation_move_ids[i].reversed_entry_id.id > 0)
# The depreciation schedule report should not contain cancelled assets
report = self.env.ref('account_asset.assets_report')
options = self._generate_options(report, today + relativedelta(years=-6, month=1, day=1), today + relativedelta(years=+4, month=12, day=31))
lines = report._get_lines({**options, **{'unfold_all': False, 'all_entries': True}})
assets_in_report = [x['name'] for x in lines[:-1]]
self.assertNotIn(CEO_car.name, assets_in_report)
self.assertNotIn(Hashed_car.name, assets_in_report)
# When a lock date is applied, only the moves before the date are reversed, others are deleted
Locked_car = CEO_car.copy()
Locked_car.write({
'original_value': 12000.0,
'method_number': 10,
'name': "Locked Car",
})
Locked_car.validate()
Locked_car.company_id.fiscalyear_lock_date = today + relativedelta(years=-1)
self.assertEqual(len(Locked_car.depreciation_move_ids), 10)
Locked_car.set_to_cancelled()
self.assertRecordValues(Locked_car, [{
'state': 'cancelled',
'book_value': 12000.0,
'value_residual': 10000,
'salvage_value': 2000,
}])
self.assertEqual(len(Locked_car.depreciation_move_ids), 4)
for depreciation in Locked_car.depreciation_move_ids:
self.assertTrue(depreciation.reversal_move_id or depreciation.reversed_entry_id)
def test_asset_form(self):
"""Test the form view of assets"""
asset_form = Form(self.env['account.asset'].with_context(asset_type='purchase'))
asset_form.name = "Test Asset"
asset_form.original_value = 10000
asset_form.account_depreciation_id = self.company_data['default_account_assets']
asset_form.account_depreciation_expense_id = self.company_data['default_account_expense']
asset_form.journal_id = self.company_data['default_journal_misc']
asset = asset_form.save()
asset.validate()
# Test that the depreciations are created upon validation of the asset according to the default values
self.assertEqual(len(asset.depreciation_move_ids), 5)
for move in asset.depreciation_move_ids:
self.assertEqual(move.amount_total, 2000)
# Test that we cannot validate an asset with non zero remaining value of the last depreciation line
asset_form = Form(asset)
with self.assertRaises(UserError):
with self.cr.savepoint():
with asset_form.depreciation_move_ids.edit(4) as line_edit:
line_edit.depreciation_value = 1000.0
asset_form.save()
# ... but we can with a zero remaining value on the last line.
asset_form = Form(asset)
with asset_form.depreciation_move_ids.edit(4) as line_edit:
line_edit.depreciation_value = 1000.0
with asset_form.depreciation_move_ids.edit(3) as line_edit:
line_edit.depreciation_value = 3000.0
self.update_form_values(asset_form)
asset_form.save()
def test_asset_from_move_line_form(self):
"""Test that the asset is correcly created from a move line"""
move_ids = self.env['account.move'].create([{
'ref': 'line1',
'line_ids': [
(0, 0, {
'account_id': self.company_data['default_account_expense'].id,
'debit': 300,
'name': 'Furniture',
}),
(0, 0, {
'account_id': self.company_data['default_account_assets'].id,
'credit': 300,
}),
]
}, {
'ref': 'line2',
'line_ids': [
(0, 0, {
'account_id': self.company_data['default_account_expense'].id,
'debit': 600,
'name': 'Furniture too',
}),
(0, 0, {
'account_id': self.company_data['default_account_assets'].id,
'credit': 600,
}),
]
},
])
move_ids.action_post()
move_line_ids = move_ids.mapped('line_ids').filtered(lambda x: x.debit)
asset_form = Form(self.env['account.asset'].with_context(default_original_move_line_ids=move_line_ids.ids, asset_type='purchase'))
asset_form._values['original_move_line_ids'] = [(6, 0, move_line_ids.ids)]
asset_form._perform_onchange(['original_move_line_ids'])
asset_form.account_depreciation_expense_id = self.company_data['default_account_expense']
asset = asset_form.save()
self.assertEqual(asset.value_residual, 900.0)
self.assertIn(asset.name, ['Furniture', 'Furniture too'])
self.assertEqual(asset.journal_id.type, 'general')
self.assertEqual(asset.asset_type, 'purchase')
self.assertEqual(asset.account_asset_id, self.company_data['default_account_expense'])
self.assertEqual(asset.account_depreciation_id, self.company_data['default_account_expense'])
self.assertEqual(asset.account_depreciation_expense_id, self.company_data['default_account_expense'])
def test_asset_modify_depreciation(self):
"""Test the modification of depreciation parameters"""
values = {
'original_value': 10000,
'book_value': 5500,
'value_residual': 3000,
'salvage_value': 2500,
}
self.assertRecordValues(self.truck, [values])
self.assertEqual(10, len(self.truck.depreciation_move_ids))
self.env['asset.modify'].create({
'asset_id': self.truck.id,
'name': 'Test reason',
'method_number': 20.0,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
# I check the proper depreciation lines created.
self.assertEqual(14, len(self.truck.depreciation_move_ids)) # a part of the amount has been depreciated too early, there is a hole in the dates
# Check if the future deprecation moves are set to be auto posted
self.assertTrue(all([move.auto_post != 'no' for move in self.truck.depreciation_move_ids.filtered(lambda x: x.state == 'draft')]))
# The values are unchanged
self.assertRecordValues(self.truck, [values])
def test_asset_modify_value_00(self):
"""Test the values of the asset and value increase 'assets' after a
modification of residual and/or salvage values.
Increase the residual value, increase the salvage value"""
self.assertEqual(self.truck.value_residual, 3000)
self.assertEqual(self.truck.salvage_value, 2500)
self.env['asset.modify'].create({
'name': 'New beautiful sticker :D',
'asset_id': self.truck.id,
'value_residual': 4000,
'salvage_value': 3000,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
self.assertEqual(self.truck.value_residual, 3000)
self.assertEqual(self.truck.salvage_value, 2500)
self.assertEqual(self.truck.children_ids.value_residual, 400)
self.assertEqual(self.truck.children_ids.salvage_value, 500)
def test_asset_modify_value_01(self):
"Decrease the residual value, decrease the salvage value"
self.env['asset.modify'].create({
'name': "Accident :'(",
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'asset_id': self.truck.id,
'value_residual': 1000,
'salvage_value': 2000,
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
self.assertEqual(self.truck.value_residual, 1000)
self.assertEqual(self.truck.salvage_value, 2000)
self.assertEqual(self.truck.children_ids.value_residual, 0)
self.assertEqual(self.truck.children_ids.salvage_value, 0)
self.assertEqual(max(self.truck.depreciation_move_ids.filtered(lambda m: m.state == 'posted'), key=lambda m: (m.date, m.id)).amount_total, 2500)
def test_asset_modify_value_02(self):
"Decrease the residual value, increase the salvage value; same book value"
self.env['asset.modify'].create({
'name': "Don't wanna depreciate all of it",
'asset_id': self.truck.id,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'value_residual': 1000,
'salvage_value': 4500,
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
self.assertEqual(self.truck.value_residual, 1000)
self.assertEqual(self.truck.salvage_value, 4500)
self.assertEqual(self.truck.children_ids.value_residual, 0)
self.assertEqual(self.truck.children_ids.salvage_value, 0)
def test_asset_modify_value_03(self):
"Decrease the residual value, increase the salvage value; increase of book value"
self.env['asset.modify'].create({
'name': "Some aliens did something to my truck",
'asset_id': self.truck.id,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'value_residual': 1000,
'salvage_value': 6000,
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
self.assertEqual(self.truck.value_residual, 1000)
self.assertEqual(self.truck.salvage_value, 4500)
self.assertEqual(self.truck.children_ids.value_residual, 0)
self.assertEqual(self.truck.children_ids.salvage_value, 1500)
def test_asset_modify_value_04(self):
"Increase the residual value, decrease the salvage value; increase of book value"
self.env['asset.modify'].create({
'name': 'GODZILA IS REAL!',
'asset_id': self.truck.id,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'value_residual': 4000,
'salvage_value': 2000,
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
self.assertEqual(self.truck.value_residual, 3500)
self.assertEqual(self.truck.salvage_value, 2000)
self.assertEqual(self.truck.children_ids.value_residual, 200)
self.assertEqual(self.truck.children_ids.salvage_value, 0)
def test_asset_modify_report(self):
"""Test the asset value modification flows"""
# PY + - Final PY + - Final Bookvalue
# -6 0 10000 0 10000 0 750 0 750 9250
# -5 10000 0 0 10000 750 750 0 1500 8500
# -4 10000 0 0 10000 1500 750 0 2250 7750
# -3 10000 0 0 10000 2250 750 0 3000 7000
# -2 10000 0 0 10000 3000 750 0 3750 6250
# -1 10000 0 0 10000 3750 750 0 4500 5500
# 0 10000 0 0 10000 4500 750 0 5250 4750 <-- today
# 1 10000 0 0 10000 5250 750 0 6000 4000
# 2 10000 0 0 10000 6000 750 0 6750 3250
# 3 10000 0 0 10000 6750 750 0 7500 2500
today = fields.Date.today()
report = self.env.ref('account_asset.assets_report')
# TEST REPORT
# look at all period, with unposted entries
options = self._generate_options(report, today + relativedelta(years=-6, month=1, day=1), today + relativedelta(years=+4, month=12, day=31))
lines = report._get_lines({**options, **{'unfold_all': False, 'all_entries': True}})
self.assertListEqual([ 0.0, 10000.0, 0.0, 10000.0, 0.0, 7500.0, 0.0, 7500.0, 2500.0],
[x['no_format'] for x in lines[0]['columns'][4:]])
# look at all period, without unposted entries
options = self._generate_options(report, today + relativedelta(years=-6, month=1, day=1), today + relativedelta(years=+4, month=12, day=31))
lines = report._get_lines({**options, **{'unfold_all': False, 'all_entries': False}})
self.assertListEqual([ 0.0, 10000.0, 0.0, 10000.0, 0.0, 4500.0, 0.0, 4500.0, 5500.0],
[x['no_format'] for x in lines[0]['columns'][4:]])
# look only at this period
options = self._generate_options(report, today + relativedelta(years=0, month=1, day=1), today + relativedelta(years=0, month=12, day=31))
lines = report._get_lines({**options, **{'unfold_all': False, 'all_entries': True}})
self.assertListEqual([10000.0, 0.0, 0.0, 10000.0, 4500.0, 750.0, 0.0, 5250.0, 4750.0],
[x['no_format'] for x in lines[0]['columns'][4:]])
# test value increase
# PY + - Final PY + - Final Bookvalue
# -6 0 10000 0 10000 750 0 750 9250
# -5 10000 0 0 10000 750 750 0 1500 8500
# -4 10000 0 0 10000 1500 750 0 2250 7750
# -3 10000 0 0 10000 2250 750 0 3000 7000
# -2 10000 0 0 10000 3000 750 0 3750 6250
# -1 10000 0 0 10000 3750 750 0 4500 5500
# 0 10000 1500 0 11500 4500 1000 0 5500 6000 <-- today
# 1 11500 0 0 11500 5500 1000 0 6500 5000
# 2 11500 0 0 11500 6500 1000 0 7500 4000
# 3 11500 0 0 11500 7500 1000 0 8500 3000
self.assertEqual(self.truck.value_residual, 3000)
self.assertEqual(self.truck.salvage_value, 2500)
self.env['asset.modify'].create({
'name': 'New beautiful sticker :D',
'asset_id': self.truck.id,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'value_residual': 4000,
'salvage_value': 3000,
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
self.assertEqual(self.truck.value_residual + sum(self.truck.children_ids.mapped('value_residual')), 3400)
self.assertEqual(self.truck.salvage_value + sum(self.truck.children_ids.mapped('salvage_value')), 3000)
# look at all period, with unposted entries
options = self._generate_options(report, today + relativedelta(years=-6, months=-6), today + relativedelta(years=+4, month=12, day=31))
lines = report._get_lines({**options, **{'unfold_all': False, 'all_entries': True}})
self.assertListEqual([0.0, 11500.0, 0.0, 11500.0, 0.0, 8500.0, 0.0, 8500.0, 3000.0],
[x['no_format'] for x in lines[0]['columns'][4:]])
self.assertEqual('10 y', lines[1]['columns'][3]['name'], 'Depreciation Rate = 10%')
# look only at this period
options = self._generate_options(report, today + relativedelta(years=0, month=1, day=1), today + relativedelta(years=0, month=12, day=31))
lines = report._get_lines({**options, **{'unfold_all': False, 'all_entries': True}})
self.assertListEqual([11500.0, 0.0, 0.0, 11500.0, 5100.0, 850.0, 0.0, 5950.0, 5550.0],
[x['no_format'] for x in lines[0]['columns'][4:]])
# test value decrease
self.env['asset.modify'].create({
'name': "Huge scratch on beautiful sticker :'( It is ruined",
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'asset_id': self.truck.children_ids.id,
'value_residual': 0,
'salvage_value': 500,
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
self.env['asset.modify'].create({
'name': "Huge scratch on beautiful sticker :'( It went through...",
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'asset_id': self.truck.id,
'value_residual': 1000,
'salvage_value': 2500,
"account_asset_counterpart_id": self.assert_counterpart_account_id,
}).modify()
self.assertEqual(self.truck.value_residual + sum(self.truck.children_ids.mapped('value_residual')), 1000)
self.assertEqual(self.truck.salvage_value + sum(self.truck.children_ids.mapped('salvage_value')), 3000)
# look at all period, with unposted entries
options = self._generate_options(report, today + relativedelta(years=-6, month=1, day=1), today + relativedelta(years=+4, month=12, day=31))
lines = report._get_lines({**options, **{'unfold_all': False, 'all_entries': True}})
self.assertListEqual([0.0, 11500.0, 0.0, 11500.0, 0.0, 8500.0, 0.0, 8500.0, 3000.0],
[x['no_format'] for x in lines[0]['columns'][4:]])
# look only at previous period
options = self._generate_options(report, today + relativedelta(years=-1, month=1, day=1), today + relativedelta(years=-1, month=12, day=31))
lines = report._get_lines({**options, **{'unfold_all': False, 'all_entries': True}})
self.assertListEqual([11500.0, 0.0, 0.0, 11500.0, 4250.0, 3250.0, 0.0, 7500.0, 4000.0],
[x['no_format'] for x in lines[0]['columns'][4:]])
def test_asset_pause_resume(self):
"""Test that depreciation remains the same after a pause and resume at a later date"""
today = fields.Date.today()
self.assertEqual(len(self.truck.depreciation_move_ids.filtered(lambda e: e.state == 'draft')), 4)
self.env['asset.modify'].create({
'date': fields.Date.today() + relativedelta(days=-1),
'asset_id': self.truck.id,
}).pause()
self.assertEqual(len(self.truck.depreciation_move_ids.filtered(lambda e: e.state == 'draft')), 0)
with freeze_time(today) as frozen_time:
frozen_time.move_to(today + relativedelta(years=1))
self.env['asset.modify'].with_context(resume_after_pause=True).create({
'asset_id': self.truck.id,
}).modify()
self.assertEqual(len(self.truck.depreciation_move_ids.filtered(lambda e: e.state == 'posted')), 7)
self.assertEqual(
self.truck.depreciation_move_ids.filtered(lambda e: e.state == 'draft').mapped('amount_total'),
[375.0, 750.0, 750.0, 750.0])
def test_asset_modify_sell_profit(self):
"""Test that a credit is realised in the gain account when selling an asset for a sum greater than book value"""
closing_invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_line_ids': [(0, 0, {'price_unit': self.truck.book_value + 100})]
})
self.env['asset.modify'].create({
'asset_id': self.truck.id,
'invoice_line_ids': closing_invoice.invoice_line_ids,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'modify_action': 'sell',
}).sell_dispose()
closing_move = self.truck.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 10000,
'account_id': self.truck.account_asset_id.id,
}, {
'debit': 4500,
'credit': 0,
'account_id': self.truck.account_depreciation_id.id,
}, {
'debit': 5600,
'credit': 0,
'account_id': closing_invoice.invoice_line_ids.account_id.id,
}, {
'debit': 0,
'credit': 100,
'account_id': self.env.company.gain_account_id.id,
}])
def test_asset_modify_sell_loss(self):
"""Test that a debit is realised in the loss account when selling an asset for a sum less than book value"""
closing_invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_line_ids': [(0, 0, {'price_unit': self.truck.book_value - 100})]
})
self.env['asset.modify'].create({
'asset_id': self.truck.id,
'invoice_line_ids': closing_invoice.invoice_line_ids,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'modify_action': 'sell',
}).sell_dispose()
closing_move = self.truck.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 10000,
'account_id': self.truck.account_asset_id.id,
}, {
'debit': 4500,
'credit': 0,
'account_id': self.truck.account_depreciation_id.id,
}, {
'debit': 5400,
'credit': 0,
'account_id': closing_invoice.invoice_line_ids.account_id.id,
}, {
'debit': 100,
'credit': 0,
'account_id': self.env.company.loss_account_id.id,
}])
def test_asset_modify_dispose(self):
"""Test the loss of the remaining book_value when an asset is disposed using the wizard"""
self.env['asset.modify'].create({
'asset_id': self.truck.id,
'date': fields.Date.today() + relativedelta(months=-6, days=-1),
'modify_action': 'dispose',
}).sell_dispose()
closing_move = self.truck.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
self.assertRecordValues(closing_move.line_ids, [{
'debit': 0,
'credit': 10000,
'account_id': self.truck.account_asset_id.id,
}, {
'debit': 4500,
'credit': 0,
'account_id': self.truck.account_depreciation_id.id,
}, {
'debit': 5500,
'credit': 0,
'account_id': self.env.company.loss_account_id.id,
}])
def test_asset_reverse_depreciation(self):
"""Test the reversal of a depreciation move"""
self.assertEqual(sum(self.truck.depreciation_move_ids.filtered(lambda m: m.state == 'posted').mapped('depreciation_value')), 4500)
self.assertEqual(sum(self.truck.depreciation_move_ids.filtered(lambda m: m.state == 'draft').mapped('depreciation_value')), 3000)
self.assertEqual(max(self.truck.depreciation_move_ids.filtered(lambda m: m.state == 'posted'), key=lambda m: m.date).asset_remaining_value, 3000)
move_to_reverse = self.truck.depreciation_move_ids.filtered(lambda m: m.state == 'posted').sorted(lambda m: m.date)[-1]
reversed_move = move_to_reverse._reverse_moves()
# Check that the depreciation has been reported on the next move
min_date_draft = min(self.truck.depreciation_move_ids.filtered(lambda m: m.state == 'draft' and m.date > reversed_move.date), key=lambda m: m.date)
self.assertEqual(move_to_reverse.asset_remaining_value - min_date_draft.depreciation_value - reversed_move.depreciation_value, min_date_draft.asset_remaining_value)
self.assertEqual(move_to_reverse.asset_depreciated_value + min_date_draft.depreciation_value + reversed_move.depreciation_value, min_date_draft.asset_depreciated_value)
# The amount is still there, it only has been reversed. But it has been added on the next draft move to complete the depreciation table
self.assertEqual(sum(self.truck.depreciation_move_ids.filtered(lambda m: m.state == 'posted').mapped('depreciation_value')), 4500)
self.assertEqual(sum(self.truck.depreciation_move_ids.filtered(lambda m: m.state == 'draft').mapped('depreciation_value')), 3000)
# Check that the table shows fully depreciated at the end
self.assertEqual(max(self.truck.depreciation_move_ids, key=lambda m: m.date).asset_remaining_value, 0)
self.assertEqual(max(self.truck.depreciation_move_ids, key=lambda m: m.date).asset_depreciated_value, 7500)
def test_asset_reverse_original_move(self):
"""Test the reversal of a move that generated an asset"""
move_id = self.env['account.move'].create({
'ref': 'line1',
'line_ids': [
(0, 0, {
'account_id': self.company_data['default_account_expense'].id,
'debit': 300,
'name': 'Furniture',
}),
(0, 0, {
'account_id': self.company_data['default_account_assets'].id,
'credit': 300,
}),
]
})
move_id.action_post()
move_line_id = move_id.mapped('line_ids').filtered(lambda x: x.debit)
asset_form = Form(self.env['account.asset'].with_context(asset_type='purchase'))
asset_form._values['original_move_line_ids'] = [(6, 0, move_line_id.ids)]
asset_form._perform_onchange(['original_move_line_ids'])
asset_form.account_depreciation_expense_id = self.company_data['default_account_expense']
asset = asset_form.save()
self.assertTrue(asset.name, 'An asset should have been created')
reversed_move_id = move_id._reverse_moves()
reversed_move_id.action_post()
with self.assertRaises(MissingError, msg='The asset should have been deleted'):
asset.name
def test_asset_multiple_assets_from_one_move_line_00(self):
""" Test the creation of a as many assets as the value of
the quantity property of a move line. """
account = self.env['account.account'].create({
"name": "test account",
"code": "TEST",
"account_type": 'asset_non_current',
"create_asset": "draft",
"asset_type": "purchase",
"multiple_assets_per_line": True,
})
move = self.env['account.move'].create({
"partner_id": self.env['res.partner'].create({'name': 'Johny'}).id,
"ref": "line1",
"move_type": "in_invoice",
"invoice_date": "2020-12-31",
"invoice_line_ids": [
(0, 0, {
"account_id": account.id,
"price_unit": 400.0,
"name": "stuff",
"quantity": 2,
"product_uom_id": self.env.ref('uom.product_uom_unit').id,
"tax_ids": [],
}),
]
})
move.action_post()
assets = move.asset_ids
assets = sorted(assets, key=lambda i: i['original_value'], reverse=True)
self.assertEqual(len(assets), 2, '3 assets should have been created')
self.assertEqual(assets[0].original_value, 400.0)
self.assertEqual(assets[1].original_value, 400.0)
def test_asset_multiple_assets_from_one_move_line_01(self):
""" Test the creation of a as many assets as the value of
the quantity property of a move line. """
account = self.env['account.account'].create({
"name": "test account",
"code": "TEST",
"account_type": 'asset_non_current',
"create_asset": "draft",
"asset_type": "purchase",
"multiple_assets_per_line": True,
})
move = self.env['account.move'].create({
"partner_id": self.env['res.partner'].create({'name': 'Johny'}).id,
"ref": "line1",
"move_type": "in_invoice",
"invoice_date": "2020-12-31",
"invoice_line_ids": [
(0, 0, {
"account_id": account.id,
"name": "stuff",
"quantity": 3.0,
"price_unit": 1000.0,
"product_uom_id": self.env.ref('uom.product_uom_categ_unit').id,
}),
(0, 0, {
'account_id': self.company_data['default_account_assets'].id,
"name": "stuff",
'quantity': 1.0,
'price_unit': -500.0,
}),
]
})
move.action_post()
self.assertEqual(sum(asset.original_value for asset in move.asset_ids), move.line_ids[0].debit)
def test_asset_credit_note(self):
"""Test the generated entries created from an in_refund invoice with deferred expense."""
account_asset_model_fixedassets_test0 = self.env['account.asset'].create({
'account_depreciation_id': self.company_data['default_account_assets'].id,
'account_depreciation_expense_id': self.company_data['default_account_expense'].id,
'account_asset_id': self.company_data['default_account_assets'].id,
'journal_id': self.company_data['default_journal_purchase'].id,
'name': 'Hardware - 3 Years',
'method_number': 3,
'method_period': '12',
'state': 'model',
})
self.company_data['default_account_assets'].create_asset = "validate"
self.company_data['default_account_assets'].asset_model = account_asset_model_fixedassets_test0
invoice = self.env['account.move'].create({
'move_type': 'in_refund',
'invoice_date': '2020-01-01',
'date': '2020-01-01',
'partner_id': self.ref("base.res_partner_12"),
'invoice_line_ids': [(0, 0, {
'name': 'Refund Insurance claim',
'account_id': self.company_data['default_account_assets'].id,
'price_unit': 450,
'quantity': 1,
})],
})
invoice.action_post()
depreciation_lines = self.env['account.move.line'].search([
('account_id', '=', account_asset_model_fixedassets_test0.account_depreciation_id.id),
('move_id.asset_id', '=', invoice.asset_ids.id),
('debit', '=', 150),
])
self.assertEqual(
len(depreciation_lines), 3,
'Three entries with a debit of 150 must be created on the Deferred Expense Account'
)
def test_asset_partial_credit_note(self):
"""Test partial credit note on an in invoice that has generated draft assets.
Test case:
- Create in invoice with the following lines:
Product | Unit Price | Quantity | Multiple assets | # assets that will be deleted
--------------------------------------------------------------------------------------------
Product B | 200 | 4 | TRUE | 0
Product A | 100 | 7 | FALSE | 1
Product A | 100 | 5 | TRUE | 1
Product A | 150 | 6 | TRUE | 2
Product A | 100 | 7 | FALSE | 0
- Add a credit note with the following lines:
Product | Unit Price | Quantity
---------------------------------------
Product A | 100 | 1
Product A | 150 | 2
Product A | 100 | 7
"""
asset_model = self.env['account.asset'].create({
'account_depreciation_id': self.company_data['default_account_assets'].id,
'account_depreciation_expense_id': self.company_data['default_account_revenue'].id,
'journal_id': self.company_data['default_journal_sale'].id,
'name': 'Maintenance Contract - 3 Years',
'method_number': 3,
'method_period': '12',
'prorata_computation_type': 'none',
'asset_type': 'purchase',
'state': 'model',
})
self.company_data['default_account_assets'].create_asset = 'draft'
self.company_data['default_account_assets'].asset_model = asset_model
account_assets_multiple = self.company_data['default_account_assets'].copy()
account_assets_multiple.multiple_assets_per_line = True
product_a = self.env['product.product'].create({
'name': 'Product A',
'default_code': 'PA',
'lst_price': 100.0,
'standard_price': 100.0,
})
product_b = self.env['product.product'].create({
'name': 'Product B',
'default_code': 'PB',
'lst_price': 200.0,
'standard_price': 200.0,
})
invoice = self.env['account.move'].create({
'move_type': 'in_invoice',
'invoice_date': '2020-01-01',
'partner_id': self.ref("base.res_partner_12"),
'invoice_line_ids': [
(0, 0, {
'product_id': product_b.id,
'name': 'Product B',
'account_id': account_assets_multiple.id,
'price_unit': 200.0,
'quantity': 4,
}),
(0, 0, {
'product_id': product_a.id,
'name': 'Product A',
'account_id': self.company_data['default_account_assets'].id,
'price_unit': 100.0,
'quantity': 7,
}),
(0, 0, {
'product_id': product_a.id,
'name': 'Product A',
'account_id': account_assets_multiple.id,
'price_unit': 100.0,
'quantity': 5,
}),
(0, 0, {
'product_id': product_a.id,
'name': 'Product A',
'account_id': account_assets_multiple.id,
'price_unit': 150.0,
'quantity': 6,
}),
(0, 0, {
'product_id': product_a.id,
'name': 'Product A',
'account_id': self.company_data['default_account_assets'].id,
'price_unit': 100.0,
'quantity': 7,
}),
],
})
invoice.action_post()
product_a_100_lines = invoice.line_ids.filtered(lambda l: l.product_id == product_a and l.price_unit == 100.0)
product_a_150_lines = invoice.line_ids.filtered(lambda l: l.product_id == product_a and l.price_unit == 150.0)
product_b_lines = invoice.line_ids.filtered(lambda l: l.product_id == product_b)
self.assertEqual(len(invoice.line_ids.mapped(lambda l: l.asset_ids)), 17)
self.assertEqual(len(product_b_lines.asset_ids), 4)
self.assertEqual(len(product_a_100_lines.asset_ids), 7)
self.assertEqual(len(product_a_150_lines.asset_ids), 6)
credit_note = invoice._reverse_moves()
with Form(credit_note) as move_form:
move_form.invoice_date = move_form.date
move_form.invoice_line_ids.remove(0)
move_form.invoice_line_ids.remove(0)
with move_form.invoice_line_ids.edit(0) as line_form:
line_form.quantity = 1
with move_form.invoice_line_ids.edit(1) as line_form:
line_form.quantity = 2
credit_note.action_post()
self.assertEqual(len(invoice.line_ids.mapped(lambda l: l.asset_ids)), 13)
self.assertEqual(len(product_b_lines.asset_ids), 4)
self.assertEqual(len(product_a_100_lines.asset_ids), 5)
self.assertEqual(len(product_a_150_lines.asset_ids), 4)
def test_asset_with_non_deductible_tax(self):
"""Test that the assets' original_value and non_deductible_tax_value are correctly computed
from a move line with a non-deductible tax."""
asset_account = self.company_data['default_account_assets']
non_deductible_tax = self.env['account.tax'].create({
'name': 'Non-deductible Tax',
'amount': 21,
'amount_type': 'percent',
'type_tax_use': 'purchase',
'invoice_repartition_line_ids': [
Command.create({'repartition_type': 'base'}),
Command.create({
'factor_percent': 50,
'repartition_type': 'tax',
'use_in_tax_closing': False
}),
Command.create({
'factor_percent': 50,
'repartition_type': 'tax',
'use_in_tax_closing': True
}),
],
'refund_repartition_line_ids': [
Command.create({'repartition_type': 'base'}),
Command.create({
'factor_percent': 50,
'repartition_type': 'tax',
'use_in_tax_closing': False
}),
Command.create({
'factor_percent': 50,
'repartition_type': 'tax',
'use_in_tax_closing': True
}),
],
})
asset_account.tax_ids = non_deductible_tax
# 1. Automatic creation
asset_account.create_asset = 'draft'
asset_account.asset_model = self.account_asset_model_fixedassets.id
asset_account.multiple_assets_per_line = True
vendor_bill_auto = self.env['account.move'].create({
'move_type': 'in_invoice',
'invoice_date': '2020-01-01',
'partner_id': self.ref("base.res_partner_12"),
'invoice_line_ids': [Command.create({
'account_id': asset_account.id,
'name': 'Asus Laptop',
'price_unit': 1000.0,
'quantity': 2,
'tax_ids': [Command.set(non_deductible_tax.ids)],
})],
})
vendor_bill_auto.action_post()
new_assets_auto = vendor_bill_auto.asset_ids
self.assertEqual(len(new_assets_auto), 2)
self.assertEqual(new_assets_auto.mapped('original_value'), [1105.0, 1105.0])
self.assertEqual(new_assets_auto.mapped('non_deductible_tax_value'), [105.0, 105.0])
# 2. Manual creation
asset_account.create_asset = 'no'
asset_account.asset_model = None
asset_account.multiple_assets_per_line = False
vendor_bill_manu = self.env['account.move'].create({
'move_type': 'in_invoice',
'invoice_date': '2020-01-01',
'partner_id': self.ref("base.res_partner_12"),
'invoice_line_ids': [
Command.create({
'account_id': asset_account.id,
'name': 'Asus Laptop',
'price_unit': 1000.0,
'quantity': 2,
'tax_ids': [Command.set(non_deductible_tax.ids)]
}),
Command.create({
'account_id': asset_account.id,
'name': 'Lenovo Laptop',
'price_unit': 500.0,
'quantity': 3,
'tax_ids': [Command.set(non_deductible_tax.ids)]
}),
],
})
vendor_bill_manu.action_post()
# TOFIX: somewhere above this the field account.asset.asset_type is made
# dirty, but this field has to be flushed in a specific environment.
# This is because the field 'asset_type' is stored, computed and
# context-dependent, which explains why its value must be retrieved
# from the right environment.
self.env.flush_all()
move_line_ids = vendor_bill_manu.mapped('line_ids').filtered(lambda x: 'Laptop' in x.name)
asset_form = Form(self.env['account.asset'].with_context(
default_original_move_line_ids=move_line_ids.ids,
asset_type='purchase'
))
asset_form._values['original_move_line_ids'] = [Command.set(move_line_ids.ids)]
asset_form._perform_onchange(['original_move_line_ids'])
asset_form.account_depreciation_expense_id = self.company_data['default_account_expense']
new_assets_manu = asset_form.save()
self.assertEqual(len(new_assets_manu), 1)
self.assertEqual(new_assets_manu.original_value, 3867.5)
self.assertEqual(new_assets_manu.non_deductible_tax_value, 367.5)
def test_post_asset_with_passed_recognition_date(self):
"""
Check the state of an asset when the last recognition date
is passed at the moment of posting it.
"""
asset = self.env['account.asset'].create({
'account_asset_id': self.company_data['default_account_expense'].id,
'account_depreciation_id': self.company_data['default_account_assets'].copy().id,
'account_depreciation_expense_id': self.company_data['default_account_assets'].id,
'journal_id': self.company_data['default_journal_misc'].id,
'asset_type': 'expense',
'name': 'test',
'acquisition_date': fields.Date.today() - relativedelta(years=1, month=6, day=1),
'original_value': 10000,
'method_number': 5,
'method_period': '1',
'method': 'linear',
})
asset.compute_depreciation_board()
self.assertTrue(all(m.state == 'draft' for m in asset.depreciation_move_ids))
asset.validate()
self.assertTrue(all(m.state == 'posted' for m in asset.depreciation_move_ids))
self.assertEqual(asset.state, 'close')
def test_asset_degressive_01(self):
""" Check the computation of an asset with degressive method,
start at middle of the year
"""
asset = self.env['account.asset'].create({
'account_asset_id': self.company_data['default_account_expense'].id,
'account_depreciation_id': self.company_data['default_account_assets'].copy().id,
'account_depreciation_expense_id': self.company_data['default_account_assets'].id,
'journal_id': self.company_data['default_journal_misc'].id,
'asset_type': 'expense',
'name': 'Degressive',
'acquisition_date': '2021-07-01',
'prorata_computation_type': 'constant_periods',
'original_value': 10000,
'method_number': 5,
'method_period': '12',
'method': 'degressive',
'method_progress_factor': 0.5,
})
asset.validate()
self.assertEqual(asset.method_number + 1, len(asset.depreciation_move_ids))
self.assertRecordValues(asset.depreciation_move_ids.sorted(lambda l: (l.date, l.id)), [{
'amount_total': 2500,
'asset_remaining_value': 7500,
}, {
'amount_total': 3750,
'asset_remaining_value': 3750,
}, {
'amount_total': 1875,
'asset_remaining_value': 1875,
}, {
'amount_total': 937.5,
'asset_remaining_value': 937.5,
}, {
'amount_total': 468.75,
'asset_remaining_value': 468.75,
}, {
'amount_total': 468.75,
'asset_remaining_value': 0,
}])
def test_asset_degressive_02(self):
""" Check the computation of an asset with degressive method,
start at beginning of the year.
"""
asset = self.env['account.asset'].create({
'account_asset_id': self.company_data['default_account_expense'].id,
'account_depreciation_id': self.company_data['default_account_assets'].copy().id,
'account_depreciation_expense_id': self.company_data['default_account_assets'].id,
'journal_id': self.company_data['default_journal_misc'].id,
'asset_type': 'expense',
'name': 'Degressive',
'acquisition_date': '2021-01-01',
'original_value': 10000,
'method_number': 5,
'method_period': '12',
'method': 'degressive',
'method_progress_factor': 0.5,
})
asset.validate()
self.assertEqual(asset.method_number, len(asset.depreciation_move_ids))
self.assertRecordValues(asset.depreciation_move_ids.sorted(lambda l: (l.date, l.id)), [{
'amount_total': 5000,
'asset_remaining_value': 5000,
}, {
'amount_total': 2500,
'asset_remaining_value': 2500,
}, {
'amount_total': 1250,
'asset_remaining_value': 1250,
}, {
'amount_total': 625,
'asset_remaining_value': 625,
}, {
'amount_total': 625,
'asset_remaining_value': 0,
}])
def test_asset_negative_01(self):
""" Check the computation of an asset with negative value. """
asset = self.env['account.asset'].create({
'account_asset_id': self.company_data['default_account_expense'].id,
'account_depreciation_id': self.company_data['default_account_assets'].copy().id,
'account_depreciation_expense_id': self.company_data['default_account_assets'].id,
'journal_id': self.company_data['default_journal_misc'].id,
'asset_type': 'expense',
'name': 'Degressive Linear',
'acquisition_date': '2021-07-01',
'original_value': -10000,
'method_number': 5,
'method_period': '12',
'method': 'linear',
})
asset.prorata_computation_type = 'constant_periods'
asset.validate()
self.assertRecordValues(asset.depreciation_move_ids.sorted(lambda l: (l.date, l.id)), [{
'amount_total': 1000,
'asset_remaining_value': -9000,
}, {
'amount_total': 2000,
'asset_remaining_value': -7000,
}, {
'amount_total': 2000,
'asset_remaining_value': -5000,
}, {
'amount_total': 2000,
'asset_remaining_value': -3000,
}, {
'amount_total': 2000,
'asset_remaining_value': -1000,
}, {
'amount_total': 1000,
'asset_remaining_value': 0,
}])
def test_asset_daily_computation_01(self):
""" Check the computation of an asset with daily_computation. """
asset = self.env['account.asset'].create({
'account_asset_id': self.company_data['default_account_expense'].id,
'account_depreciation_id': self.company_data['default_account_assets'].copy().id,
'account_depreciation_expense_id': self.company_data['default_account_assets'].id,
'journal_id': self.company_data['default_journal_misc'].id,
'asset_type': 'expense',
'name': 'Degressive Linear',
'acquisition_date': '2021-07-01',
'prorata_computation_type': 'daily_computation',
'original_value': 10000,
'method_number': 5,
'method_period': '12',
'method': 'linear',
})
asset.validate()
self.assertRecordValues(asset.depreciation_move_ids.sorted(lambda l: (l.date, l.id)), [{
'amount_total': 1007.67,
'asset_remaining_value': 8992.33,
}, {
'amount_total': 1998.90,
'asset_remaining_value': 6993.43,
}, {
'amount_total': 1998.91,
'asset_remaining_value': 4994.52,
}, {
'amount_total': 2004.38,
'asset_remaining_value': 2990.14,
}, {
'amount_total': 1998.90,
'asset_remaining_value': 991.24,
}, {
'amount_total': 991.24,
'asset_remaining_value': 0,
}])
def test_decrement_book_value_with_negative_asset(self):
"""
Test the computation of book value and remaining value
when posting a depreciation move related with a negative asset
"""
depreciation_account = self.company_data['default_account_assets'].copy()
asset_model = self.env['account.asset'].create({
'name': 'test',
'state': 'model',
'active': True,
'asset_type': 'purchase',
'method': 'linear',
'method_number': 5,
'method_period': '1',
'prorata_computation_type': 'constant_periods',
'account_asset_id': self.company_data['default_account_expense'].id,
'account_depreciation_id': depreciation_account.id,
'account_depreciation_expense_id': self.company_data['default_account_assets'].id,
'journal_id': self.company_data['default_journal_purchase'].id,
})
depreciation_account.asset_type = 'purchase'
depreciation_account.can_create_asset = True
depreciation_account.create_asset = 'draft'
depreciation_account.asset_model = asset_model
refund = self.env['account.move'].create({
'move_type': 'in_refund',
'partner_id': self.partner_a.id,
'invoice_date': '2021-06-01',
'invoice_line_ids': [Command.create({'name': 'refund', 'account_id': depreciation_account.id, 'price_unit': 500, 'tax_ids': False})],
})
refund.action_post()
self.assertTrue(refund.asset_ids)
asset = refund.asset_ids
self.assertEqual(asset.book_value, -refund.amount_total)
self.assertEqual(asset.value_residual, -refund.amount_total)
asset.validate()
self.assertEqual(len(asset.depreciation_move_ids.filtered(lambda m: m.state == 'posted')), 1)
self.assertEqual(asset.book_value, -400.0)
self.assertEqual(asset.value_residual, -400.0)
def test_depreciation_schedule_report_with_negative_asset(self):
"""
Test the computation of book value and remaining value
when posting a depreciation move related with a negative asset
"""
depreciation_account = self.company_data['default_account_assets']
asset_model = self.env['account.asset'].create({
'name': 'test',
'state': 'model',
'active': True,
'asset_type': 'purchase',
'method': 'linear',
'method_number': 5,
'method_period': '1',
'prorata_computation_type': 'none',
'account_asset_id': depreciation_account.id,
'account_depreciation_id': depreciation_account.id,
'account_depreciation_expense_id': depreciation_account.copy().id,
'journal_id': self.company_data['default_journal_misc'].id,
})
depreciation_account.asset_type = 'purchase'
depreciation_account.can_create_asset = True
depreciation_account.create_asset = 'draft'
depreciation_account.asset_model = asset_model
refund = self.env['account.move'].create({
'move_type': 'in_refund',
'partner_id': self.ref("base.res_partner_12"),
'invoice_date': fields.Date.today() - relativedelta(months=1),
'invoice_line_ids': [(0, 0, {'name': 'refund', 'account_id': depreciation_account.id, 'price_unit': 500})],
})
refund.action_post()
self.assertTrue(refund.asset_ids)
asset = refund.asset_ids
asset.validate()
report = self.env.ref('account_asset.assets_report')
options = self._generate_options(report, fields.Date.today() + relativedelta(months=-7, day=1), fields.Date.today() + relativedelta(months=-6, day=31))
expected_values_open_asset = [
("refund", "", "", 500.0, -500.0, "", "", 100.0, -100.0, -400.0),
]
self.assertLinesValues(report._get_lines(options)[2:3], [0, 5, 6, 7, 8, 9, 10, 11, 12, 13], expected_values_open_asset)
invoice = self.env['account.move'].create({
'move_type': 'in_invoice',
'invoice_date': fields.Date.today(),
'partner_id': self.ref("base.res_partner_12"),
'invoice_line_ids': [
(0, 0, {
'name': 'Product B',
'account_id': self.company_data['default_account_revenue'].id,
'price_unit': 200.0,
'quantity': 4,
}),
]
})
res_move = self.env['asset.modify'].create({
'asset_id': asset.id,
'invoice_ids': [Command.set(invoice.ids)],
'modify_action': 'sell',
'gain_account_id': self.company_data['default_account_receivable'].id,
}).sell_dispose()
self.env['account.move'].search(res_move['domain']).action_post()
expected_values_closed_asset = [
("refund", "", 500.0, 500.0, "", "", 500.0, 500.0, "", ""),
]
options = self._generate_options(report, fields.Date.today() + relativedelta(months=-7, day=1), fields.Date.today())
self.assertLinesValues(report._get_lines(options)[2:3], [0, 5, 6, 7, 8, 9, 10, 11, 12, 13], expected_values_closed_asset)