# -*- coding: utf-8 -*-

import time

from dateutil.relativedelta import relativedelta
from odoo import fields, Command
from odoo.exceptions import UserError, MissingError
from odoo.tests import Form, tagged, freeze_time
from odoo.addons.fusion_accounting.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_ids = 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()
        self.assertTrue(False not in Hashed_car.depreciation_move_ids[:3].mapped('inalterable_hash'))

        for i in range(0, 4):
            self.assertFalse(Hashed_car.depreciation_move_ids[i].reversal_move_ids)

        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_ids.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('fusion_accounting.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_ids 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.multiple_assets_per_line = False

        vendor_bill = self.env['account.move'].create({
            'move_type': 'in_invoice',
            'currency_id': self.other_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.other_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.other_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: '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('fusion_accounting.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, [{
            'ref': 'truck: Sale',
            'debit': 0,
            'credit': 10000,
            'account_id': self.truck.account_asset_id.id,
        }, {
            'ref': 'truck: Sale',
            'debit': 4500,
            'credit': 0,
            'account_id': self.truck.account_depreciation_id.id,
        }, {
            'ref': 'truck: Sale',
            'debit': 5600,
            'credit': 0,
            'account_id': closing_invoice.invoice_line_ids.account_id.id,
        }, {
            'ref': 'truck: Sale',
            '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, [{
            'ref': 'truck: Sale',
            'debit': 0,
            'credit': 10000,
            'account_id': self.truck.account_asset_id.id,
        }, {
            'ref': 'truck: Sale',
            'debit': 4500,
            'credit': 0,
            'account_id': self.truck.account_depreciation_id.id,
        }, {
            'ref': 'truck: Sale',
            'debit': 5400,
            'credit': 0,
            'account_id': closing_invoice.invoice_line_ids.account_id.id,
        }, {
            'ref': 'truck: Sale',
            '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, [{
            'ref': 'truck: Sale',
            'debit': 0,
            'credit': 10000,
            'account_id': self.truck.account_asset_id.id,
        }, {
            'ref': 'truck: Sale',
            'debit': 4500,
            'credit': 0,
            'account_id': self.truck.account_depreciation_id.id,
        }, {
            'ref': 'truck: Sale',
            'debit': 5400,
            'credit': 0,
            'account_id': closing_invoice.invoice_line_ids.account_id.id,
        }, {
            'ref': 'truck: Sale',
            '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, [{
            'ref': 'truck: Disposal',
            'debit': 0,
            'credit': 10000,
            'account_id': self.truck.account_asset_id.id,
        }, {
            'ref': 'truck: Disposal',
            'debit': 4500,
            'credit': 0,
            'account_id': self.truck.account_depreciation_id.id,
        }, {
            'ref': 'truck: Disposal',
            '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('fusion_accounting.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_ref_asset_depreciation(self):
        """Test that the reference used in depreciation moves is correct"""

        for ref in self.truck.depreciation_move_ids.mapped('ref'):
            self.assertEqual(ref, 'truck: Depreciation')

    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_ids': revenue_model})

        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_ids = 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_ids = 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_ids = self.account_asset_model_fixedassets
        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_ids = 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: '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_ids = 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('fusion_accounting.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('fusion_accounting.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('fusion_accounting.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('fusion_accounting.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('fusion_accounting.assets_report')

        # No prefix group, no group by account
        options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_grouping_field': 'none', '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('fusion_accounting.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_grouping_field': 'none', '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_grouping_field': 'none',
            '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('fusion_accounting.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.other_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):
        asset_group = self.env['account.asset.group'].create({'name': 'Odoo Office'})
        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,
                'asset_group_id': asset_group.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('fusion_accounting.assets_report')

        # No prefix group, no group by account
        options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_grouping_field': 'none'})
        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_grouping_field': 'account_id'})
        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_grouping_field': 'none', '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_grouping_field': 'account_id', '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,
        )

        # No prefix group, group by asset group
        options = self._generate_options(report, '2021-01-01', '2021-12-31', default_options={'assets_grouping_field': 'asset_group_id'})
        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],
            [
                ('(No Asset Group)',            10000,          0,       0,       10000,      4500,               0,             0,            4500,             5500),
                ('truck',                       10000,          0,       0,       10000,      4500,               0,             0,            4500,             5500),
                ('Odoo Office',                 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),
                ('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': fusion_accounting.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_ids = 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_move_type(self):
        """ Test the field asset_move_type set on account.move describing the
            relation that a move can have towards an asset
        """
        asset_account_id = self.company_data['default_account_assets'].id

        bill = self.env['account.move'].create([
            {
                'move_type': 'in_invoice',
                'invoice_date': fields.Date.today() + relativedelta(months=-6, days=-1),
                'date': fields.Date.today() + relativedelta(months=-6, days=-1),
                'partner_id': self.partner_a.id,
                'invoice_line_ids': [Command.create({
                    'name': 'Truck',
                    'account_id': asset_account_id,
                    'quantity': 1.0,
                    'price_unit': 1000.0,
                    'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
                })],
            },
        ])
        bill.action_post()
        asset_line = bill.line_ids.filtered(lambda x: x.account_id.id == asset_account_id)
        asset_form = Form(self.env['account.asset'].with_context(default_original_move_line_ids=asset_line.ids))
        asset_form.original_move_line_ids = asset_line
        asset_form.account_depreciation_expense_id = self.company_data['default_account_expense']
        car = asset_form.save()
        car.validate()

        # All depreciation move must be defined as depreciation
        self.assertTrue(all(car.depreciation_move_ids.mapped(lambda m: m.asset_move_type == 'depreciation')))

        # Negative revaluation
        self.env['asset.modify'].create({
            'name': 'Little scratch :(',
            'asset_id': car.id,
            'value_residual': car.book_value - 150,
            'date': fields.Date.today(),
        }).modify()

        # Ensure that the added depreciation moves are one 'depreciation' and the other is 'negative_revaluation'
        added_move_on_revaluation = car.depreciation_move_ids.filtered(lambda m: m.date == fields.Date.today())
        self.assertRecordValues(added_move_on_revaluation.sorted(lambda mv: mv.id), [
            {'asset_move_type': 'depreciation'},
            {'asset_move_type': 'negative_revaluation'}
        ])

        # Sell
        closing_invoice = self.env['account.move'].create({
            'move_type': 'out_invoice',
            'invoice_line_ids': [Command.create(
                {'price_unit': car.book_value + 100}  # selling price: 849.46, net_gain_on_sale: 100.45
            )]
        })

        self.env['asset.modify'].create({
            'asset_id': car.id,
            'modify_action': 'sell',
            'invoice_line_ids': closing_invoice.invoice_line_ids,
            'date': fields.Date.today(),
        }).sell_dispose()
        selling_move = car.depreciation_move_ids.filtered(lambda l: l.state == 'draft')
        selling_move.action_post()

        # Ensure that the added depreciation moves are one 'depreciation' and the other is 'sale'
        added_move_on_sale = car.depreciation_move_ids.filtered(lambda m: m.date == fields.Date.today()) - added_move_on_revaluation
        self.assertTrue(added_move_on_sale.asset_move_type == 'sale')
        self.assertEqual(car.net_gain_on_sale, 100)

        # Create new asset to test positive revaluation and disposal
        new_car = car.copy()
        new_car.validate()

        # Positive revaluation
        self.env['asset.modify'].create({
            'name': 'New beautiful sticker :D',
            'asset_id': new_car.id,
            'value_residual': new_car.book_value + 50,
            'salvage_value': 0,
            'date': fields.Date.today(),
            "account_asset_counterpart_id": self.assert_counterpart_account_id,
        }).modify()

        self.assertEqual(
            new_car.children_ids.original_move_line_ids.move_id.asset_move_type,
            'positive_revaluation',
            "the original move of the child asset is set as 'positive_revaluation'"
        )

        disposal_action_view = self.env['asset.modify'].create({
            'asset_id': new_car.id,
            'modify_action': 'dispose',
            'date': fields.Date.today()
        }).sell_dispose()

        self.env['account.move'].browse(disposal_action_view['res_id']).action_post()
        self.assertEqual(self.env['account.move'].browse(disposal_action_view['res_id']).asset_move_type, 'disposal')

    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_ids = 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)
