# -*- 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, '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 cls.env.user.groups_id += cls.env.ref('analytic.group_analytic_accounting') analytic_plan = cls.env['account.analytic.plan'].create({ 'name': "Default Plan", }) cls.analytic_account = cls.env['account.analytic.account'].create({ 'name': "Test Account", 'plan_id': analytic_plan.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_account_asset_no_tax(self): self.account_asset_model_fixedassets.account_depreciation_expense_id.tax_ids = self.tax_purchase_a CEO_car = self.env['account.asset'].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() self.assertFalse(any(CEO_car.depreciation_move_ids.line_ids.mapped('tax_line_id'))) def test_00_account_asset(self): """Test the lifecycle of an asset""" CEO_car = self.env['account.asset'].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': 0, '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'].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': 0, '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 = 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, 'journal_id': self.company_data['default_journal_misc'].id, 'name': 'Typical car - 3 Years', 'method_number': 3, 'method_period': '12', 'prorata_computation_type': 'daily_computation', '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 invoice = self.env['account.move'].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': 'Very little red car', 'account_id': self.company_data['default_account_assets'].id, 'price_unit': 450, 'quantity': 1, })], }) invoice.action_post() asset = invoice.asset_ids self.assertEqual(len(asset), 1, 'One and only one asset should have been created from invoice.') self.assertTrue(asset.state == 'open', 'Asset should be in Open state') first_invoice_line = invoice.invoice_line_ids[0] self.assertEqual(asset.original_value, first_invoice_line.price_subtotal, 'Asset value is not same as invoice line.') # I check data in move line and depreciation line. first_depreciation_line = asset.depreciation_move_ids.sorted(lambda r: r.id)[0] self.assertAlmostEqual(first_depreciation_line.asset_remaining_value, asset.original_value - first_depreciation_line.amount_total, msg='Remaining value is incorrect.') self.assertAlmostEqual(first_depreciation_line.asset_depreciated_value, first_depreciation_line.amount_total, msg='Depreciated value is incorrect.') # I check next installment date. last_depreciation_date = first_depreciation_line.date installment_date = last_depreciation_date + relativedelta(months=+int(asset.method_period)) self.assertEqual(asset.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'].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': 0, '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'].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'].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'].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""" asset_account = self.env['account.account'].create({ "name": "test_06_account_asset", "code": "test.06.account.asset", "account_type": 'asset_non_current', "create_asset": "no", "multiple_assets_per_line": True, }) CEO_car = self.env['account.asset'].create({ 'salvage_value': 0, 'state': 'draft', 'method_period': '12', 'method_number': 4, 'name': "CEO's Car", 'original_value': 1000.0, 'acquisition_date': fields.Date.today() - relativedelta(years=3), 'account_asset_id': asset_account.id, 'account_depreciation_id': self.company_data['default_account_assets'].copy().id, 'account_depreciation_expense_id': asset_account.id, 'journal_id': self.company_data['default_journal_misc'].id, 'prorata_computation_type': 'none', }) 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'].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, 'acquisition_date': today + relativedelta(years=-3, month=1, day=1), }) 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", 'acquisition_date': today + relativedelta(years=-3, month=1, day=1), }) 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']) 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_form.prorata_computation_type = 'none' 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_entry_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_form.original_move_line_ids = 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.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']) self.assertEqual(asset.acquisition_date, min(move_ids.mapped('date'))) def test_asset_from_bill_move_line_form(self): """Test that the asset is correcly created from a move line""" move_ids = self.env['account.move'].create([{ 'move_type': 'in_invoice', 'partner_id': self.partner_a.id, 'ref': 'line1', 'date': '2020-06-01', 'invoice_date': '2020-06-15', 'invoice_line_ids': [ Command.create({ 'account_id': self.company_data['default_account_expense'].id, 'price_unit': 300, 'name': 'Furniture', 'tax_ids': [], }), ] }, { 'move_type': 'in_invoice', 'partner_id': self.partner_a.id, 'ref': 'line2', 'date': '2020-06-01', 'invoice_date': '2020-06-14', 'invoice_line_ids': [ Command.create({ 'account_id': self.company_data['default_account_expense'].id, 'price_unit': 600, 'name': 'Furniture too', 'tax_ids': [], }), ] }, ]) 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_form.original_move_line_ids = 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.assertRecordValues(asset, [{ 'name': 'Furniture', 'account_asset_id': self.company_data['default_account_expense'].id, 'account_depreciation_id': self.company_data['default_account_expense'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'acquisition_date': min(move_ids.mapped('invoice_date')), }]) def test_asset_from_bill_move_line_form_multicurrency(self): """Test that the asset is correcly created from a move line using a foreign currency""" 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 asset_account.create_asset = 'no' asset_account.asset_model = None asset_account.multiple_assets_per_line = False vendor_bill = self.env['account.move'].create({ 'move_type': 'in_invoice', 'currency_id': self.currency_data['currency'].id, 'invoice_date': '2020-01-01', 'partner_id': self.partner_a.id, 'invoice_line_ids': [ Command.create({ 'account_id': asset_account.id, 'currency_id': self.currency_data['currency'].id, 'name': 'Asus Laptop', 'price_unit': 1000.0, 'quantity': 1, 'tax_ids': [Command.set(non_deductible_tax.ids)] }), Command.create({ 'account_id': asset_account.id, 'currency_id': self.currency_data['currency'].id, 'name': 'Lenovo Laptop', 'price_unit': 500.0, 'quantity': 1, 'tax_ids': [Command.set(non_deductible_tax.ids)] }), ], }) vendor_bill.action_post() self.env.flush_all() move_line_ids = vendor_bill.mapped('line_ids').filtered(lambda x: x.name and '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.original_move_line_ids = move_line_ids asset_form.account_depreciation_expense_id = self.company_data['default_account_expense'] new_assets = asset_form.save() self.assertEqual(len(new_assets), 1) self.assertEqual(new_assets.original_value, 828.75) self.assertEqual(new_assets.non_deductible_tax_value, 78.75) 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, "account_depreciation_id": self.company_data['default_account_assets'].id, }).modify() self.assertEqual(self.truck.value_residual, 3000) self.assertEqual(self.truck.salvage_value, 2500) self.assertEqual(self.truck.children_ids.value_residual, 1000) self.assertEqual(self.truck.children_ids.salvage_value, 500) self.assertEqual(self.truck.account_depreciation_id.id, self.company_data['default_account_assets'].id) 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, 500) 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 1500 0 10000 3750 950 0 4700 6800 # 0 10000 0 0 11500 4700 950 0 5650 5850 <-- today # 1 11500 0 0 11500 5650 950 0 6600 4900 # 2 11500 0 0 11500 6600 950 0 7550 3950 # 3 11500 0 0 11500 7550 950 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(years=-1, months=-6, days=-1), 'value_residual': 4750, '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')), 3800) 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, 4700.0, 950.0, 0.0, 5650.0, 5850.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([10000.0, 1500.0, 0.0, 11500.0, 3750.0, 3750.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_sale_same_account_as_invoice(self): """Test the sale of an asset with an invoice that has the same account as the Depreciation Account""" closing_invoice = self.env['account.move'].create({ 'move_type': 'out_invoice', 'invoice_line_ids': [ Command.create({ 'account_id': self.truck.account_depreciation_id.id, '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, }]) self.assertEqual(closing_move.depreciation_value, 3000, "Should be the remaining amount before the sale") 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) report = self.env.ref('account_asset.assets_report') today = fields.Date.today() 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) reversed_move.action_post() options = self._generate_options(report, today + relativedelta(years=0, month=7, day=1), today + relativedelta(years=0, month=7, day=31)) lines = report._get_lines({**options, 'unfold_all': False, 'all_entries': True}) # We take the reversal entry into account self.assertListEqual([10000.0, 0.0, 0.0, 10000.0, 4500.0, -750.0, 0.0, 3750.0, 6250.0], [x['no_format'] for x in lines[0]['columns'][4:]]) 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}) # With the report on the next entry, we get a normal depreciation amount for the year 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:]]) def test_credit_note_out_refund(self): """ Test the behaviour of the asset creation when a credit note is created. The asset created from the credit note should be the same as the one created from the invoice with a negative value. """ depreciation_account = self.company_data['default_account_assets'].copy() revenue_model = self.env['account.asset'].create({ 'account_depreciation_id': depreciation_account.id, 'account_depreciation_expense_id': self.company_data['default_account_revenue'].id, 'journal_id': self.company_data['default_journal_misc'].id, 'name': 'Hardware - 5 Years', 'method_number': 5, 'method_period': '12', 'state': 'model', }) depreciation_account.write({'create_asset': 'draft', 'asset_model': revenue_model.id}) invoice = self.env['account.move'].create({ 'invoice_date': '2019-07-01', 'move_type': 'in_invoice', 'partner_id': self.partner_a.id, 'invoice_line_ids': [(0, 0, { 'name': 'Hardware', 'account_id': depreciation_account.id, 'price_unit': 5000, 'quantity': 1, 'tax_ids': False, })], }) invoice.action_post() self.assertTrue(invoice.asset_ids) credit_note = invoice._reverse_moves([{'invoice_date': fields.Date.today()}]) credit_note.action_post() invoice_asset = invoice.asset_ids credit_note_asset = credit_note.asset_ids # check if invoice_asset still exists after validate the credit note self.assertTrue(invoice_asset) self.assertTrue(credit_note_asset) (invoice_asset + credit_note_asset).validate() self.assertRecordValues(credit_note_asset, [ { 'acquisition_date': invoice_asset.acquisition_date, 'book_value': -invoice_asset.book_value, 'value_residual': -invoice_asset.value_residual, } ]) for invoice_asset_move, credit_note_asset_move in zip(invoice_asset.depreciation_move_ids.sorted('date'), credit_note_asset.depreciation_move_ids.sorted('date')): self.assertRecordValues(credit_note_asset_move, [ { 'date': invoice_asset_move.date, 'state': invoice_asset_move.state, 'depreciation_value': -invoice_asset_move.depreciation_value, } ]) 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", "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", "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 asset""" 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_expense'].id, 'account_asset_id': self.company_data['default_account_assets'].id, 'journal_id': self.company_data['default_journal_purchase'].id, 'name': 'Small car - 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 = asset_model invoice = self.env['account.move'].create({ 'move_type': 'in_refund', 'invoice_date': '2020-01-01', 'date': '2020-01-01', 'partner_id': self.partner_a.id, 'invoice_line_ids': [(0, 0, { 'name': 'Very little red car', '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', '=', asset_model.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 --------------------------------------------------------- Product B | 200 | 4 | TRUE Product A | 100 | 7 | FALSE Product A | 100 | 5 | TRUE Product A | 150 | 6 | TRUE Product A | 100 | 7 | FALSE - 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_expense'].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', '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.partner_a.id, '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)), 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) 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.partner_a.id, '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.partner_a.id, '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: x.name and 'Laptop' in x.name) asset_form = Form(self.env['account.asset'].with_context( default_original_move_line_ids=move_line_ids.ids, )) asset_form.original_move_line_ids = 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_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_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, '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': 625.00, 'asset_remaining_value': 312.50, }, { 'amount_total': 312.50, '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_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, '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_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, '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_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, '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, 'method': 'linear', 'method_number': 5, 'method_period': '1', 'prorata_computation_type': 'constant_periods', 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': depreciation_account.id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_purchase'].id, }) 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 depreciation schedule with negative asset """ asset = self.env['account.asset'].create({ 'name': 'test', 'original_value': -500, 'method': 'linear', 'method_number': 5, 'method_period': '1', 'acquisition_date': fields.Date.today() + relativedelta(months=-1), 'prorata_computation_type': 'none', 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, }) 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 = [ ("test", 0, 0, 500.0, -500.0, 0, 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, options) expense_account_copy = self.company_data['default_account_expense'].copy() disposal_action_view = self.env['asset.modify'].create({ 'asset_id': asset.id, 'modify_action': 'dispose', 'loss_account_id': expense_account_copy.id, 'date': fields.Date.today() }).sell_dispose() self.env['account.move'].browse(disposal_action_view['res_id']).action_post() expected_values_closed_asset = [ ("test", 0, 500.0, 500.0, 0, 0, 500.0, 500.0, 0, 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, options) def test_depreciation_schedule_hierarchy(self): # Remove previously existing assets. assets = self.env['account.asset'].search([ ('company_id', '=', self.env.company.id), ('state', '!=', 'model'), ]) assets.state = 'draft' assets.mapped('depreciation_move_ids').state = 'draft' assets.unlink() # Create the account groups. self.env['account.group'].create([ {'name': 'Group 1', 'code_prefix_start': '1', 'code_prefix_end': '1'}, {'name': 'Group 11', 'code_prefix_start': '11', 'code_prefix_end': '11'}, {'name': 'Group 12', 'code_prefix_start': '12', 'code_prefix_end': '12'}, ]) # Create the accounts. account_a, account_a1, account_b, account_c, account_d, account_e = self.env['account.account'].create([ {'code': '1100', 'name': 'Account A', 'account_type': 'asset_non_current'}, {'code': '1110', 'name': 'Account A1', 'account_type': 'asset_non_current'}, {'code': '1200', 'name': 'Account B', 'account_type': 'asset_non_current'}, {'code': '1300', 'name': 'Account C', 'account_type': 'asset_non_current'}, {'code': '1400', 'name': 'Account D', 'account_type': 'asset_non_current'}, {'code': '9999', 'name': 'Account E', 'account_type': 'asset_non_current'}, ]) # Create and validate the assets, and post the depreciation entries. self.env['account.asset'].create([ { 'account_asset_id': account_id, 'account_depreciation_id': account_id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, 'name': name, 'acquisition_date': fields.Date.to_date('2020-07-01'), 'original_value': original_value, 'method': 'linear', 'prorata_computation_type': 'none', } for account_id, name, original_value in [ (account_a.id, 'ZenBook', 1250), (account_a.id, 'ThinkBook', 1500), (account_a1.id, 'XPS', 1750), (account_b.id, 'MacBook', 2000), (account_c.id, 'Aspire', 1600), (account_d.id, 'Playstation', 550), (account_e.id, 'Xbox', 500), ] ]).validate() # Configure the depreciation schedule report. report = self.env.ref('account_asset.assets_report') options = self._generate_options(report, '2022-01-01', '2022-12-31') options['hierarchy'] = True self.env.company.totals_below_sections = True # Generate and compare actual VS expected values. lines = [ { 'name': line['name'], 'level': line['level'], 'book_value': line['columns'][-1]['name'] } for line in (report._get_lines(options)) ] expected_values = [ # pylint: disable=C0326 {'name': '1 Group 1', 'level': 1, 'book_value': '$\xa06,920.00'}, {'name': '11 Group 11', 'level': 2, 'book_value': '$\xa03,600.00'}, {'name': '1100 Account A', 'level': 3, 'book_value': '$\xa02,200.00'}, {'name': 'ZenBook', 'level': 4, 'book_value': '$\xa01,000.00'}, {'name': 'ThinkBook', 'level': 4, 'book_value': '$\xa01,200.00'}, {'name': 'Total 1100 Account A', 'level': 3, 'book_value': '$\xa02,200.00'}, {'name': '1110 Account A1', 'level': 3, 'book_value': '$\xa01,400.00'}, {'name': 'XPS', 'level': 4, 'book_value': '$\xa01,400.00'}, {'name': 'Total 1110 Account A1', 'level': 3, 'book_value': '$\xa01,400.00'}, {'name': 'Total 11 Group 11', 'level': 2, 'book_value': '$\xa03,600.00'}, {'name': '12 Group 12', 'level': 2, 'book_value': '$\xa01,600.00'}, {'name': '1200 Account B', 'level': 3, 'book_value': '$\xa01,600.00'}, {'name': 'MacBook', 'level': 4, 'book_value': '$\xa01,600.00'}, {'name': 'Total 1200 Account B', 'level': 3, 'book_value': '$\xa01,600.00'}, {'name': 'Total 12 Group 12', 'level': 2, 'book_value': '$\xa01,600.00'}, {'name': '1300 Account C', 'level': 2, 'book_value': '$\xa01,280.00'}, {'name': 'Aspire', 'level': 3, 'book_value': '$\xa01,280.00'}, {'name': 'Total 1300 Account C', 'level': 2, 'book_value': '$\xa01,280.00'}, {'name': '1400 Account D', 'level': 2, 'book_value': '$\xa0440.00'}, {'name': 'Playstation', 'level': 3, 'book_value': '$\xa0440.00'}, {'name': 'Total 1400 Account D', 'level': 2, 'book_value': '$\xa0440.00'}, {'name': 'Total 1 Group 1', 'level': 1, 'book_value': '$\xa06,920.00'}, {'name': '(No Group)', 'level': 1, 'book_value': '$\xa0400.00'}, {'name': '9999 Account E', 'level': 2, 'book_value': '$\xa0400.00'}, {'name': 'Xbox', 'level': 3, 'book_value': '$\xa0400.00'}, {'name': 'Total 9999 Account E', 'level': 2, 'book_value': '$\xa0400.00'}, {'name': 'Total (No Group)', 'level': 1, 'book_value': '$\xa0400.00'}, {'name': 'Total', 'level': 1, 'book_value': '$\xa07,320.00'}, ] self.assertEqual(len(lines), len(expected_values)) self.assertEqual(lines, expected_values) def test_depreciation_schedule_disposal_move_unposted(self): """ Test the computation of values when disposing an asset, and the difference if the disposal move is posted """ asset = self.env['account.asset'].create({ 'name': 'test asset', 'method': 'linear', 'original_value': 1000, 'method_number': 5, 'method_period': '12', 'acquisition_date': fields.Date.today() + relativedelta(years=-2, month=1, day=1), 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, }) asset.validate() expense_account_copy = self.company_data['default_account_expense'].copy() disposal_action_view = self.env['asset.modify'].create({ 'asset_id': asset.id, 'modify_action': 'dispose', 'loss_account_id': expense_account_copy.id, 'date': fields.Date.today() + relativedelta(days=-1) }).sell_dispose() report = self.env.ref('account_asset.assets_report') options = self._generate_options(report, '2021-01-01', '2021-12-31') # The disposal move is in draft and should not be considered (depreciation and book value) # Values are: name, assets_before, assets+, assets-, assets_after, depreciation_before, depreciation+, depreciation-, depreciation_after, book_value expected_values_asset_disposal_unposted = [ ("test asset", 1000.0, 0.0, 0, 1000.0, 400.0, 100.0, 0.0, 500.0, 500.0), ] self.assertLinesValues(report._get_lines(options)[2:3], [0, 5, 6, 7, 8, 9, 10, 11, 12, 13], expected_values_asset_disposal_unposted, options) self.env['account.move'].browse(disposal_action_view.get('res_id')).action_post() expected_values_asset_disposal_posted = [ ("test asset", 1000.0, 0.0, 1000.0, 0.0, 400.0, 100.0, 500.0, 0.0, 0.0), ] self.assertLinesValues(report._get_lines(options)[2:3], [0, 5, 6, 7, 8, 9, 10, 11, 12, 13], expected_values_asset_disposal_posted, options) def test_depreciation_schedule_disposal_move_unposted_with_non_depreciable_value(self): """ Test the computation of values when disposing an asset with non-depreciable value, and the difference if the disposal move is posted """ asset = self.env['account.asset'].create({ 'name': 'test asset', 'method': 'linear', 'original_value': 10000, 'salvage_value': 8000, 'method_number': 24, 'method_period': '1', 'acquisition_date': fields.Date.today() + relativedelta(months=-1, day=1), 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, }) asset.validate() report = self.env.ref('account_asset.assets_report') options = self._generate_options(report, '2021-07-01', '2021-07-31') expected_values_asset_disposal_unposted = [ ("test asset", 10000.0, 0.0, 0.0, 10000.0, 83.33, 0.0, 0.0, 83.33, 9916.67), ] self.assertLinesValues(report._get_lines(options)[2:3], [0, 5, 6, 7, 8, 9, 10, 11, 12, 13], expected_values_asset_disposal_unposted, options) expense_account_copy = self.company_data['default_account_expense'].copy() disposal_action_view = self.env['asset.modify'].create({ 'asset_id': asset.id, 'modify_action': 'dispose', 'loss_account_id': expense_account_copy.id, 'date': fields.Date.today() }).sell_dispose() expected_values_asset_disposal_unposted = [ ("test asset", 10000.0, 0.0, 0.0, 10000.0, 83.33, 2.69, 0.0, 86.02, 9913.98), ] self.assertLinesValues(report._get_lines(options)[2:3], [0, 5, 6, 7, 8, 9, 10, 11, 12, 13], expected_values_asset_disposal_unposted, options) self.env['account.move'].browse(disposal_action_view['res_id']).action_post() expected_values_asset_disposal_posted = [ ("test asset", 10000.0, 0.0, 10000.0, 0.0, 83.33, 2.69, 86.02, 0.0, 0.0), ] self.assertLinesValues(report._get_lines(options)[2:3], [0, 5, 6, 7, 8, 9, 10, 11, 12, 13], expected_values_asset_disposal_posted, options) def test_asset_analytic_on_lines(self): CEO_car = self.env['account.asset'].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': '2020-01-01', }) CEO_car._onchange_model_id() CEO_car.method_number = 5 CEO_car.analytic_distribution = {self.analytic_account.id: 100} # In order to test the process of Account Asset, I perform a action to confirm Account Asset. CEO_car.validate() for move in CEO_car.depreciation_move_ids: self.assertRecordValues(move.line_ids, [ { 'analytic_distribution': {str(self.analytic_account.id): 100}, }, { 'analytic_distribution': {str(self.analytic_account.id): 100}, }, ]) CEO_car.analytic_distribution = {str(self.analytic_account.id): 200} # Only draft moves should have a changed analytic distribution for move in CEO_car.depreciation_move_ids.filtered(lambda m: m.state == 'posted'): self.assertRecordValues(move.line_ids, [ { 'analytic_distribution': {str(self.analytic_account.id): 100}, }, { 'analytic_distribution': {str(self.analytic_account.id): 100}, }, ]) for move in CEO_car.depreciation_move_ids.filtered(lambda m: m.state == 'draft'): self.assertRecordValues(move.line_ids, [ { 'analytic_distribution': {str(self.analytic_account.id): 200}, }, { 'analytic_distribution': {str(self.analytic_account.id): 200}, }, ]) def test_asset_analytic_filter(self): """ Test that the analytic filter works correctly. """ truck_b = self.truck.copy() truck_b.acquisition_date = self.truck.acquisition_date truck_b.validate() self.truck.analytic_distribution = {self.analytic_account.id: 100} self.env['account.move']._autopost_draft_entries() self.env.company.totals_below_sections = False report = self.env.ref('account_asset.assets_report') # No prefix group, no group by account options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_groupby_account': False, 'unfold_all': False}) # without Analytic Filter self.assertLinesValues( # pylint: disable=C0326 report._get_lines(options), # Name Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value [ 0, 5, 6, 7, 8, 9, 10, 11, 12, 13], [ ('truck', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('truck (copy)', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('Total', 20000, 0, 0, 20000, 9000, 0, 0, 9000, 11000,), ], options ) # with Analytic Filter options['analytic_accounts'] = [self.analytic_account.id] self.assertLinesValues( # pylint: disable=C0326 report._get_lines(options), # Name Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value [ 0, 5, 6, 7, 8, 9, 10, 11, 12, 13], [ ('truck', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('Total', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ], options ) def test_asset_analytic_groupby(self): """ Test that the analytic groupby works correctly. """ truck_b = self.truck.copy() truck_b.acquisition_date = self.truck.acquisition_date truck_b.validate() self.truck.analytic_distribution = {self.analytic_account.id: 100} self.env['account.move']._autopost_draft_entries() self.env.company.totals_below_sections = False report = self.env.ref('account_asset.assets_report') report.filter_analytic_groupby = True # No prefix group, no group by account options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_groupby_account': False, 'unfold_all': False}) # without Analytic Groupby self.assertLinesValues( # pylint: disable=C0326 report._get_lines(options), # Name Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value [ 0, 5, 6, 7, 8, 9, 10, 11, 12, 13], [ ('truck', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('truck (copy)', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('Total', 20000, 0, 0, 20000, 9000, 0, 0, 9000, 11000,), ], options ) # with Analytic Groupby options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={ 'assets_groupby_account': False, 'unfold_all': False, 'analytic_accounts_groupby': [self.analytic_account.id], }) self.assertLinesValues( # pylint: disable=C0326 report._get_lines(options), # Group | ANALYTIC | | ALL | # Name Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value [ 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 18, 19, 20, 21, 22, 23, 24, 25, 26], [ ('truck', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500, 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500), ('truck (copy)', '', '', '', '', '', '', '', '', '', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500), ('Total', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500, 20000, 0, 0, 20000, 9000, 0, 0, 9000, 11000), ], options ) def test_depreciation_schedule_report_first_depreciation(self): """Test that the depreciation schedule report displays the correct first depreciation date.""" # check that the truck's first depreciation date is correct: # the truck has a yearly linear depreciation and it's prorate_date is 2015-01-01 # therefore we expect it's first depreciation date to be the last day of 2015 today = fields.Date.today() 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}}) self.assertEqual(lines[1]['columns'][1]['name'], '12/31/2015') def test_asset_modify_sell_multicurrency(self): """ Test that the closing invoice's currency is taken into account when selling an asset. """ closing_invoice = self.env['account.move'].create({ 'move_type': 'out_invoice', 'currency_id': self.currency_data['currency'].id, 'invoice_line_ids': [Command.create({'price_unit': 5000})] }) 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': 2500, 'credit': 0, 'account_id': closing_invoice.invoice_line_ids.account_id.id, }, { 'debit': 3000, 'credit': 0, 'account_id': self.env.company.loss_account_id.id, }]) def test_depreciation_schedule_prefix_groups(self): for i in range(1, 3): asset = self.env['account.asset'].create({ 'method_period': '12', 'method_number': 4, 'name': f"Asset {i}", 'original_value': i * 100.0, 'acquisition_date': fields.Date.today() - relativedelta(years=3), 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].copy().id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, 'prorata_computation_type': 'none', }) asset.validate() self.env['account.move']._autopost_draft_entries() self.env.company.totals_below_sections = False report = self.env.ref('account_asset.assets_report') # No prefix group, no group by account options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_groupby_account': False}) self.assertLinesValues( # pylint: disable=C0326 report._get_lines(options), # Name Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value [ 0, 5, 6, 7, 8, 9, 10, 11, 12, 13], [ ('truck', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('Asset 1', 100, 0, 0, 100, 75, 0, 0, 75, 25,), ('Asset 2', 200, 0, 0, 200, 150, 0, 0, 150, 50,), ('Total', 10300, 0, 0, 10300, 4725, 0, 0, 4725, 5575,), ], options, ) # No prefix group, group by account options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_groupby_account': True}) options['unfold_all'] = True self.assertLinesValues( # pylint: disable=C0326 report._get_lines(options), # Name Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value [ 0, 5, 6, 7, 8, 9, 10, 11, 12, 13], [ ('151000 Fixed Asset', 10300, 0, 0, 10300, 4725, 0, 0, 4725, 5575,), ('truck', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('Asset 1', 100, 0, 0, 100, 75, 0, 0, 75, 25,), ('Asset 2', 200, 0, 0, 200, 150, 0, 0, 150, 50,), ('Total', 10300, 0, 0, 10300, 4725, 0, 0, 4725, 5575,), ], options, ) report.prefix_groups_threshold = 3 # Prefix group, no group by account options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_groupby_account': False, 'unfold_all': True}) options['unfold_all'] = True self.assertLinesValues( # pylint: disable=C0326 report._get_lines(options), # Name Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value [ 0, 5, 6, 7, 8, 9, 10, 11, 12, 13], [ ('A (2 lines)', 300, 0, 0, 300, 225, 0, 0, 225, 75,), ('Asset 1', 100, 0, 0, 100, 75, 0, 0, 75, 25,), ('Asset 2', 200, 0, 0, 200, 150, 0, 0, 150, 50,), ('T (1 line)', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('truck', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('Total', 10300, 0, 0, 10300, 4725, 0, 0, 4725, 5575,), ], options, ) # Prefix group, group by account options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_groupby_account': True, 'unfold_all': True}) options['unfold_all'] = True self.assertLinesValues( # pylint: disable=C0326 report._get_lines(options), # Name Assets/start Assets/+ Assets/- Assets/end Depreciation/start Depreciation/+ Depreciation/- Depreciation/end Book Value [ 0, 5, 6, 7, 8, 9, 10, 11, 12, 13], [ ('151000 Fixed Asset', 10300, 0, 0, 10300, 4725, 0, 0, 4725, 5575,), ('A (2 lines)', 300, 0, 0, 300, 225, 0, 0, 225, 75,), ('Asset 1', 100, 0, 0, 100, 75, 0, 0, 75, 25,), ('Asset 2', 200, 0, 0, 200, 150, 0, 0, 150, 50,), ('T (1 line)', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('truck', 10000, 0, 0, 10000, 4500, 0, 0, 4500, 5500,), ('Total', 10300, 0, 0, 10300, 4725, 0, 0, 4725, 5575,), ], options, ) def test_archive_asset_model(self): """ Test that we can archive an asset model. """ self.account_asset_model_fixedassets.active = False self.assertFalse(self.account_asset_model_fixedassets.active) def test_asset_increase_with_lock_year(self): """ Test the dates at which the moves are posted even with increase, with lock date""" self.company_data['company'].fiscalyear_lock_date = fields.Date.to_date('2021-03-01') asset = self.env['account.asset'].create({ 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].copy().id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, 'name': 'Car', 'acquisition_date': fields.Date.today() + relativedelta(months=-6), 'original_value': 12000, 'method_number': 12, 'method_period': '1', 'method': 'linear', }) asset.validate() self.assertRecordValues( asset.depreciation_move_ids.sorted(lambda l: (l.date, l.id)), [ {'date': fields.Date.to_date('2021-03-31')}, {'date': fields.Date.to_date('2021-03-31')}, {'date': fields.Date.to_date('2021-03-31')}, {'date': fields.Date.to_date('2021-04-30')}, {'date': fields.Date.to_date('2021-05-31')}, {'date': fields.Date.to_date('2021-06-30')}, {'date': fields.Date.to_date('2021-07-31')}, {'date': fields.Date.to_date('2021-08-31')}, {'date': fields.Date.to_date('2021-09-30')}, {'date': fields.Date.to_date('2021-10-31')}, {'date': fields.Date.to_date('2021-11-30')}, {'date': fields.Date.to_date('2021-12-31')} ] ) self.assertEqual(asset.book_value, 6000) self.env['asset.modify'].create({ 'asset_id': asset.id, 'name': 'Test increase with lock date', 'value_residual': 8000.0, 'date': fields.Date.today() + relativedelta(days=-1), "account_asset_counterpart_id": self.assert_counterpart_account_id, }).modify() self.assertEqual(asset.book_value, 8000) self.assertRecordValues( asset.children_ids.depreciation_move_ids.sorted(lambda dep: (dep.date, dep.id)), [ {'date': fields.Date.to_date('2021-07-31'), 'depreciation_value': 333.33}, {'date': fields.Date.to_date('2021-08-31'), 'depreciation_value': 333.34}, {'date': fields.Date.to_date('2021-09-30'), 'depreciation_value': 333.33}, {'date': fields.Date.to_date('2021-10-31'), 'depreciation_value': 333.33}, {'date': fields.Date.to_date('2021-11-30'), 'depreciation_value': 333.34}, {'date': fields.Date.to_date('2021-12-31'), 'depreciation_value': 333.33} ] ) def test_asset_decrease_with_lock_year(self): """ Test the dates and values for the moves that are posted with decrease and lock date""" self.company_data['company'].fiscalyear_lock_date = fields.Date.to_date('2021-03-01') asset = self.env['account.asset'].create({ 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].copy().id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, 'name': 'Car', 'acquisition_date': fields.Date.today() + relativedelta(months=-6), 'original_value': 12000, 'method_number': 12, 'method_period': '1', 'method': 'linear', }) asset.validate() self.assertEqual(asset.book_value, 6000) self.env['asset.modify'].create({ 'asset_id': asset.id, 'name': 'Test decrease with lock date', 'value_residual': 4000.0, 'date': fields.Date.today() + relativedelta(days=-1), "account_asset_counterpart_id": self.assert_counterpart_account_id, }).modify() self.assertEqual(asset.book_value, 4000) self.assertRecordValues( asset.depreciation_move_ids.sorted(lambda dep: (dep.date, dep.id)), [ {'date': fields.Date.to_date('2021-03-31'), 'depreciation_value': 1000}, {'date': fields.Date.to_date('2021-03-31'), 'depreciation_value': 1000}, {'date': fields.Date.to_date('2021-03-31'), 'depreciation_value': 1000}, {'date': fields.Date.to_date('2021-04-30'), 'depreciation_value': 1000}, {'date': fields.Date.to_date('2021-05-31'), 'depreciation_value': 1000}, {'date': fields.Date.to_date('2021-06-30'), 'depreciation_value': 1000}, {'date': fields.Date.to_date('2021-06-30'), 'depreciation_value': 2000}, {'date': fields.Date.to_date('2021-07-31'), 'depreciation_value': 666.67}, {'date': fields.Date.to_date('2021-08-31'), 'depreciation_value': 666.66}, {'date': fields.Date.to_date('2021-09-30'), 'depreciation_value': 666.67}, {'date': fields.Date.to_date('2021-10-31'), 'depreciation_value': 666.67}, {'date': fields.Date.to_date('2021-11-30'), 'depreciation_value': 666.66}, {'date': fields.Date.to_date('2021-12-31'), 'depreciation_value': 666.67} ] ) def test_asset_onchange_model(self): """ Test the changes of account_asset_id when changing asset models """ account_asset = self.company_data['default_account_assets'].copy() asset_model = self.env['account.asset'].create({ 'name': 'test model', 'state': 'model', 'active': True, 'method': 'linear', 'method_number': 5, 'method_period': '1', 'prorata_computation_type': 'none', 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'account_asset_id': account_asset.id, 'journal_id': self.company_data['default_journal_misc'].id, }) asset_model_with_account = self.env['account.asset'].create({ 'name': 'test model with account', 'state': 'model', 'active': True, 'method': 'linear', 'method_number': 5, 'method_period': '1', 'prorata_computation_type': 'none', 'account_depreciation_id': self.company_data['default_account_assets'].id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, }) asset_form = Form(self.env['account.asset']) asset_form.name = "Test Asset" asset_form.original_value = 10000 asset_form.model_id = asset_model self.assertEqual(asset_form.account_asset_id, account_asset, "The account_asset_id should be the one from the model") asset_form.model_id = asset_model_with_account self.assertEqual(asset_form.account_asset_id, self.company_data['default_account_assets'], "The account_asset_id should be computed from the depreciation account from the model") other_account_on_bill = self.company_data['default_account_assets'].copy() other_account_on_bill.create_asset = 'draft' other_account_on_bill.asset_model = asset_model invoice = self.env['account.move'].create({ 'move_type': 'in_invoice', 'invoice_date': '2020-12-31', 'partner_id': self.partner_a.id, 'invoice_line_ids': [ (0, 0, { 'name': 'A beautiful small bomb', 'account_id': other_account_on_bill.id, 'price_unit': 200.0, 'quantity': 1, }), ], }) invoice.action_post() self.assertEqual(invoice.asset_ids.account_asset_id, other_account_on_bill, "The account should be the one from the bill, not the model") asset_form = Form(invoice.asset_ids) asset_form.model_id = asset_model self.assertEqual(asset_form.account_asset_id, other_account_on_bill, "We keep the account from the bill") def test_asset_reevaluation_degressive_linear(self): """ Tests the reevaluation of an asset in degressive_then_linear with a gross increase""" asset = self.env['account.asset'].create({ 'method_period': '12', 'method_number': 5, 'name': "Car with purple sticker", 'original_value': 10000.0, 'acquisition_date': fields.Date.today() - relativedelta(years=2), 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].copy().id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, 'prorata_computation_type': 'none', 'method': 'degressive_then_linear', 'method_progress_factor': 0.4, }) asset.validate() self.assertRecordValues(asset.depreciation_move_ids, [{ 'depreciation_value': 4000, 'asset_remaining_value': 6000, 'state': 'posted', }, { 'depreciation_value': 2400, 'asset_remaining_value': 3600, 'state': 'posted', }, { 'depreciation_value': 2000, 'asset_remaining_value': 1600, 'state': 'draft', }, { 'depreciation_value': 1600, 'asset_remaining_value': 0, 'state': 'draft', }]) self.env['asset.modify'].create({ 'name': "Inflation made it take 20%!", 'date': fields.Date.today() + relativedelta(months=-6, days=-1), 'asset_id': asset.id, 'value_residual': 5600, "account_asset_counterpart_id": self.assert_counterpart_account_id, }).modify() self.assertRecordValues(asset.children_ids[0].depreciation_move_ids.sorted(lambda mv: (mv.date, mv.id)), [{ # (2000 + 2000*6400/3600) / 5 'depreciation_value': 1111.11, 'asset_remaining_value': 888.89, 'state': 'draft', }, { 'depreciation_value': 888.89, 'asset_remaining_value': 0, 'state': 'draft', }]) def test_asset_already_depreciated(self): asset = self.env['account.asset'].create({ 'method_period': '12', 'method_number': 5, 'name': "Car with purple sticker", 'original_value': 10000.0, 'acquisition_date': fields.Date.today() - relativedelta(years=1), 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].copy().id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, 'prorata_computation_type': 'none', 'already_depreciated_amount_import': 3000, }) asset.validate() self.env['asset.modify'].create({ 'asset_id': asset.id, 'date': fields.Date.today() - relativedelta(days=1), 'name': 'Test reason', }).modify() self.assertRecordValues(asset.depreciation_move_ids, [{ 'depreciation_value': 1000, 'date': fields.Date.to_date('2021-12-31'), }, { 'depreciation_value': 2000, 'date': fields.Date.to_date('2022-12-31'), }, { 'depreciation_value': 2000, 'date': fields.Date.to_date('2023-12-31'), }, { 'depreciation_value': 2000, 'date': fields.Date.to_date('2024-12-31'), }, ]) fully_depreciated_asset = self.env['account.asset'].create({ 'method_period': '12', 'method_number': 5, 'name': "Car with purple sticker", 'original_value': 10000.0, 'acquisition_date': fields.Date.today() - relativedelta(years=2), 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].copy().id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, 'prorata_computation_type': 'none', 'salvage_value': 4000, 'already_depreciated_amount_import': 6000, }) fully_depreciated_asset.validate() self.env['asset.modify'].create({ 'asset_id': fully_depreciated_asset.id, 'date': fields.Date.today(), 'modify_action': 'dispose', }).sell_dispose() self.assertEqual(len(fully_depreciated_asset.depreciation_move_ids), 1, "Only the disposal should be created") def test_asset_acquisition_date_from_bill(self): """Test that the invoice date is used as acquisition date instead of date""" self.company_data['default_account_assets'].create_asset = 'draft' self.company_data['default_account_assets'].asset_model = self.account_asset_model_fixedassets bill = self.env['account.move'].with_context(asset_type='purchase').create({ 'move_type': 'in_invoice', 'partner_id': self.partner_a.id, 'date': '2020-06-15', 'invoice_date': '2020-06-01', 'invoice_line_ids': [Command.create({ 'name': 'Insurance claim', 'account_id': self.company_data['default_account_assets'].id, 'price_unit': 450, 'quantity': 1, })], }) bill.action_post() asset = bill.asset_ids self.assertEqual(asset.acquisition_date, bill.invoice_date) def test_asset_write_multi_company(self): assets = self.env['account.asset'].create([ { 'company_id': company_data['company'].id, 'name': 'test asset', } for company_data in [self.company_data, self.company_data_2] ]) self.assertEqual(assets[0].company_id, self.company_data['company']) self.assertEqual(assets[1].company_id, self.company_data_2['company']) assets.validate() def test_depreciation_moves_company_with_sub_company(self): """The depreciation moves should have the company of the asset, even in multicompany setup""" company = self.env.company branch_x = self.env['res.company'].create({ 'name': 'Branch X', 'country_id': company.country_id.id, 'parent_id': company.id, }) asset_vals = { 'method_period': '12', 'method_number': 5, 'name': "Car with purple sticker", 'original_value': 10000.0, 'acquisition_date': fields.Date.today() - relativedelta(years=1), 'account_asset_id': self.company_data['default_account_assets'].id, 'account_depreciation_id': self.company_data['default_account_assets'].copy().id, 'account_depreciation_expense_id': self.company_data['default_account_expense'].id, 'journal_id': self.company_data['default_journal_misc'].id, } setup_list = [ {'company_ids': (company + branch_x).ids, 'company_id': branch_x.id}, {'company_ids': branch_x.ids, 'company_id': branch_x.id}, {'company_ids': (company + branch_x).ids, 'company_id': company.id}, {'company_ids': company.ids, 'company_id': company.id}, ] expected_vals_list = [branch_x, branch_x, company, company] for setup, expected in zip(setup_list, expected_vals_list): with self.subTest(setup=setup, expected_company=expected): self.env.user.write({ 'company_ids': [Command.set(setup['company_ids'])], 'company_id': setup['company_id'], }) asset = self.env['account.asset'].create(asset_vals) asset.compute_depreciation_board() self.assertEqual(asset.depreciation_move_ids.mapped('company_id'), expected)