diff --git a/account_asset_management/__manifest__.py b/account_asset_management/__manifest__.py index c26c8a26..ace4d309 100644 --- a/account_asset_management/__manifest__.py +++ b/account_asset_management/__manifest__.py @@ -3,30 +3,28 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Assets Management', - 'version': '12.0.2.1.3', - 'license': 'AGPL-3', - 'depends': [ - 'account', - ], - 'excludes': ['account_asset'], - 'author': "Noviat,Odoo Community Association (OCA)", - 'website': 'https://github.com/OCA/account-financial-tools', - 'category': 'Accounting & Finance', - 'data': [ - 'security/account_asset_security.xml', - 'security/ir.model.access.csv', - 'wizard/account_asset_compute.xml', - 'wizard/account_asset_remove.xml', - 'views/account_account.xml', - 'views/account_asset.xml', - 'views/account_asset_group.xml', - 'views/account_asset_profile.xml', - 'views/res_config_settings.xml', - 'views/account_invoice.xml', - 'views/account_invoice_line.xml', - 'views/account_move.xml', - 'views/account_move_line.xml', - 'views/menuitem.xml', + "name": "Assets Management", + "version": "12.0.2.1.3", + "license": "AGPL-3", + "depends": ["account"], + "excludes": ["account_asset"], + "author": "Noviat,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-financial-tools", + "category": "Accounting & Finance", + "data": [ + "security/account_asset_security.xml", + "security/ir.model.access.csv", + "wizard/account_asset_compute.xml", + "wizard/account_asset_remove.xml", + "views/account_account.xml", + "views/account_asset.xml", + "views/account_asset_group.xml", + "views/account_asset_profile.xml", + "views/res_config_settings.xml", + "views/account_invoice.xml", + "views/account_invoice_line.xml", + "views/account_move.xml", + "views/account_move_line.xml", + "views/menuitem.xml", ], } diff --git a/account_asset_management/migrations/12.0.1.0.0/openupgrade_analysis.txt b/account_asset_management/migrations/12.0.1.0.0/openupgrade_analysis.txt index eb8fb2ce..d667a9f1 100644 --- a/account_asset_management/migrations/12.0.1.0.0/openupgrade_analysis.txt +++ b/account_asset_management/migrations/12.0.1.0.0/openupgrade_analysis.txt @@ -154,4 +154,4 @@ DEL ir.ui.view: account_asset.view_account_asset_search DEL ir.ui.view: account_asset.view_asset_asset_report_search DEL ir.ui.view: account_asset.view_asset_depreciation_confirmation_wizard DEL ir.ui.view: account_asset.view_invoice_asset_category -DEL ir.ui.view: account_asset.view_product_template_form_inherit \ No newline at end of file +DEL ir.ui.view: account_asset.view_product_template_form_inherit diff --git a/account_asset_management/migrations/12.0.1.0.0/post-migration.py b/account_asset_management/migrations/12.0.1.0.0/post-migration.py index 4800d5e1..07d2f0f1 100644 --- a/account_asset_management/migrations/12.0.1.0.0/post-migration.py +++ b/account_asset_management/migrations/12.0.1.0.0/post-migration.py @@ -3,6 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from openupgradelib import openupgrade from psycopg2 import sql + from odoo.tools import float_is_zero @@ -12,31 +13,33 @@ def adjust_asset_values(env): """ # Copy analytic account value openupgrade.logged_query( - env.cr, """ + env.cr, + """ UPDATE account_asset aa SET account_analytic_id = aap.account_analytic_id FROm account_asset_profile aap WHERE aa.profile_id = aap.id""", ) # Adjust method_time, method_number and method_period - number = sql.Identifier(openupgrade.get_legacy_name('method_number')) - period = sql.Identifier(openupgrade.get_legacy_name('method_period')) - for table in ['account_asset_profile', 'account_asset']: + number = sql.Identifier(openupgrade.get_legacy_name("method_number")) + period = sql.Identifier(openupgrade.get_legacy_name("method_period")) + for table in ["account_asset_profile", "account_asset"]: table = sql.Identifier(table) openupgrade.logged_query( - env.cr, sql.SQL(""" + env.cr, + sql.SQL( + """ UPDATE {table} SET method_time = 'year', method_number = ({number} * {period}) / 12 WHERE MOD({number} * {period}, 12) = 0 - """).format( - number=number, - period=period, - table=table, - ), + """ + ).format(number=number, period=period, table=table), ) openupgrade.logged_query( - env.cr, sql.SQL(""" + env.cr, + sql.SQL( + """ UPDATE {table} SET method_period = (CASE WHEN {period} = 1 THEN 'month' @@ -44,16 +47,15 @@ def adjust_asset_values(env): WHEN {period} = 12 THEN 'year' END) WHERE {period} IN (1, 3, 12) - """).format( - period=period, - table=table, - ), + """ + ).format(period=period, table=table), ) def adjust_aml_values(env): openupgrade.logged_query( - env.cr, """ + env.cr, + """ UPDATE account_move_line aml SET asset_id = aa.id, asset_profile_id = aa.profile_id @@ -70,29 +72,31 @@ def handle_account_asset_disposal_migration(env): In this phase we set the last asset line to the type remove on the corresponding assets. """ - column_name = openupgrade.get_legacy_name('disposal_move_id') - if not openupgrade.column_exists(env.cr, 'account_asset', column_name): + column_name = openupgrade.get_legacy_name("disposal_move_id") + if not openupgrade.column_exists(env.cr, "account_asset", column_name): return env.cr.execute( - sql.SQL( - "SELECT id FROM account_asset WHERE {col} IS NOT NULL" - ).format(col=sql.Identifier(column_name)) + sql.SQL("SELECT id FROM account_asset WHERE {col} IS NOT NULL").format( + col=sql.Identifier(column_name) + ) ) - assets = env['account.asset'].with_context( - allow_asset_line_update=True, - ).browse(x[0] for x in env.cr.fetchall()) - assets.mapped('depreciation_line_ids').filtered( + assets = ( + env["account.asset"] + .with_context(allow_asset_line_update=True) + .browse(x[0] for x in env.cr.fetchall()) + ) + assets.mapped("depreciation_line_ids").filtered( lambda x: float_is_zero( x.remaining_value, precision_rounding=x.asset_id.company_currency_id.rounding, ) - ).write({'type': 'remove'}) + ).write({"type": "remove"}) @openupgrade.migrate() def migrate(env, version): - copied_column = openupgrade.get_legacy_name('method_time') - if not openupgrade.column_exists(env.cr, 'account_asset', copied_column): + copied_column = openupgrade.get_legacy_name("method_time") + if not openupgrade.column_exists(env.cr, "account_asset", copied_column): # We avoid this migration if `account_asset` was not installed in v11 return adjust_asset_values(env) diff --git a/account_asset_management/migrations/12.0.1.0.0/pre-migration.py b/account_asset_management/migrations/12.0.1.0.0/pre-migration.py index 5254b3bf..e2255401 100644 --- a/account_asset_management/migrations/12.0.1.0.0/pre-migration.py +++ b/account_asset_management/migrations/12.0.1.0.0/pre-migration.py @@ -4,47 +4,46 @@ from openupgradelib import openupgrade _model_renames = [ - ('account.asset.category', 'account.asset.profile'), - ('account.asset.depreciation.line', 'account.asset.line'), - ('account.asset.asset', 'account.asset'), + ("account.asset.category", "account.asset.profile"), + ("account.asset.depreciation.line", "account.asset.line"), + ("account.asset.asset", "account.asset"), ] _table_renames = [ - (old.replace('.', '_'), new.replace('.', '_')) - for (old, new) in _model_renames + (old.replace(".", "_"), new.replace(".", "_")) for (old, new) in _model_renames ] _column_copies = { - 'account_asset': [ - ('method_number', None, None), - ('method_time', None, None), - ], - 'account_asset_profile': [ - ('method_number', None, None), - ('method_time', None, None), + "account_asset": [("method_number", None, None), ("method_time", None, None)], + "account_asset_profile": [ + ("method_number", None, None), + ("method_time", None, None), ], } _column_renames = { - 'account_asset': [ - ('method_period', None), - ], - 'account_asset_profile': [ - ('method_period', None), - ], + "account_asset": [("method_period", None)], + "account_asset_profile": [("method_period", None)], } _field_renames = [ - ('account.asset', 'account_asset', 'category_id', 'profile_id'), - ('account.asset', 'account_asset', 'currency_id', 'company_currency_id'), - ('account.asset', 'account_asset', 'date', 'date_start'), - ('account.asset', 'account_asset', 'value', 'purchase_value'), - ('account.asset.line', 'account_asset_line', - 'depreciation_date', 'line_date'), - ('account.asset.profile', 'account_asset_profile', - 'account_depreciation_expense_id', 'account_expense_depreciation_id'), - ('account.invoice.line', 'account_invoice_line', - 'asset_category_id', 'asset_profile_id'), + ("account.asset", "account_asset", "category_id", "profile_id"), + ("account.asset", "account_asset", "currency_id", "company_currency_id"), + ("account.asset", "account_asset", "date", "date_start"), + ("account.asset", "account_asset", "value", "purchase_value"), + ("account.asset.line", "account_asset_line", "depreciation_date", "line_date"), + ( + "account.asset.profile", + "account_asset_profile", + "account_depreciation_expense_id", + "account_expense_depreciation_id", + ), + ( + "account.invoice.line", + "account_invoice_line", + "asset_category_id", + "asset_profile_id", + ), ] @@ -54,20 +53,27 @@ def handle_account_asset_disposal_migration(env): In this phase we rename stuff for adapting to the new data structure. """ cr = env.cr - if not openupgrade.column_exists(cr, 'account_asset', 'disposal_move_id'): + if not openupgrade.column_exists(cr, "account_asset", "disposal_move_id"): return - openupgrade.copy_columns(cr, {'account_asset': [('state', None, None)]}) - openupgrade.rename_columns( - cr, {'account_asset': [('disposal_move_id', None)]}) + openupgrade.copy_columns(cr, {"account_asset": [("state", None, None)]}) + openupgrade.rename_columns(cr, {"account_asset": [("disposal_move_id", None)]}) openupgrade.map_values( - cr, openupgrade.get_legacy_name('state'), 'state', - [('disposed', 'removed')], table='account_asset', + cr, + openupgrade.get_legacy_name("state"), + "state", + [("disposed", "removed")], + table="account_asset", ) openupgrade.rename_fields( - env, [ - ('account.asset', 'account_asset', 'disposal_date', 'date_remove'), - ('account.asset.profile', 'account_asset_profile', - 'account_loss_id', 'account_residual_value_id'), + env, + [ + ("account.asset", "account_asset", "disposal_date", "date_remove"), + ( + "account.asset.profile", + "account_asset_profile", + "account_loss_id", + "account_residual_value_id", + ), ], ) @@ -75,9 +81,9 @@ def handle_account_asset_disposal_migration(env): @openupgrade.migrate() def migrate(env, version): cr = env.cr - if openupgrade.table_exists(cr, 'account_asset_asset'): + if openupgrade.table_exists(cr, "account_asset_asset"): # `account_asset` module was installed in v11 - if openupgrade.table_exists(cr, 'account_asset'): + if openupgrade.table_exists(cr, "account_asset"): # `account_asset_management` module also installed in v11 # TODO: Merge in existing tables assets if both module installed pass @@ -88,9 +94,16 @@ def migrate(env, version): openupgrade.rename_columns(cr, _column_renames) openupgrade.rename_fields(env, _field_renames) handle_account_asset_disposal_migration(env) - if openupgrade.column_exists(cr, 'account_asset', - 'analytic_account_id'): + if openupgrade.column_exists(cr, "account_asset", "analytic_account_id"): # account_asset_analytic module in OCA/account_analytic openupgrade.rename_fields( - env, [('account.asset', 'account_asset', - 'analytic_account_id', 'account_analytic_id')]) + env, + [ + ( + "account.asset", + "account_asset", + "analytic_account_id", + "account_analytic_id", + ) + ], + ) diff --git a/account_asset_management/migrations/12.0.2.0.0/post-migration.py b/account_asset_management/migrations/12.0.2.0.0/post-migration.py index be01f6e8..bb759106 100644 --- a/account_asset_management/migrations/12.0.2.0.0/post-migration.py +++ b/account_asset_management/migrations/12.0.2.0.0/post-migration.py @@ -7,17 +7,15 @@ from psycopg2 import sql def create_asset_groups(cr): # Add a supporting column for indicating the source asset view - origin_column = sql.Identifier( - openupgrade.get_legacy_name('view_asset_id')) + origin_column = sql.Identifier(openupgrade.get_legacy_name("view_asset_id")) openupgrade.logged_query( - cr, sql.SQL("ALTER TABLE account_asset_group ADD {} int4").format( - origin_column, - ), + cr, sql.SQL("ALTER TABLE account_asset_group ADD {} int4").format(origin_column) ) # Now fill new table recursively attending parents - parent_column = sql.Identifier(openupgrade.get_legacy_name('parent_id')) - parent_group_ids = ('NULL', ) - query_sql = sql.SQL(""" + parent_column = sql.Identifier(openupgrade.get_legacy_name("parent_id")) + parent_group_ids = ("NULL",) + query_sql = sql.SQL( + """ INSERT INTO account_asset_group ( name, code, company_id, parent_id, create_uid, create_date, write_date, write_uid, {origin_column} @@ -29,56 +27,54 @@ def create_asset_groups(cr): ON aag2.{origin_column} = va.{parent_column} WHERE {parent_column} {rest_sql} RETURNING id - """) + """ + ) isnull = sql.SQL("IS NULL") inids = sql.SQL("IN %(ids)s") while parent_group_ids: query = query_sql.format( origin_column=origin_column, - table=sql.Identifier( - openupgrade.get_legacy_name('account_asset_view') - ), + table=sql.Identifier(openupgrade.get_legacy_name("account_asset_view")), parent_column=parent_column, - rest_sql=isnull if parent_group_ids == ('NULL', ) else inids + rest_sql=isnull if parent_group_ids == ("NULL",) else inids, ) - openupgrade.logged_query(cr, query, {'ids': parent_group_ids}) + openupgrade.logged_query(cr, query, {"ids": parent_group_ids}) parent_group_ids = tuple(x[0] for x in cr.fetchall()) def update_asset_group_links(cr): - parent_column = sql.Identifier(openupgrade.get_legacy_name('parent_id')) - origin_column = sql.Identifier( - openupgrade.get_legacy_name('view_asset_id')) + parent_column = sql.Identifier(openupgrade.get_legacy_name("parent_id")) + origin_column = sql.Identifier(openupgrade.get_legacy_name("view_asset_id")) openupgrade.logged_query( - cr, sql.SQL(""" + cr, + sql.SQL( + """ INSERT INTO account_asset_profile_group_rel (profile_id, group_id) SELECT aap.id, aag.id FROM account_asset_profile aap JOIN account_asset_group aag - ON aag.{origin_column} = aap.{parent_column}""").format( - parent_column=parent_column, - origin_column=origin_column, - ), + ON aag.{origin_column} = aap.{parent_column}""" + ).format(parent_column=parent_column, origin_column=origin_column), ) openupgrade.logged_query( - cr, sql.SQL(""" + cr, + sql.SQL( + """ INSERT INTO account_asset_group_rel (asset_id, group_id) SELECT aa.id, aag.id FROM account_asset aa JOIN account_asset_group aag - ON aag.{origin_column} = aa.{parent_column}""").format( - parent_column=parent_column, - origin_column=origin_column, - ), + ON aag.{origin_column} = aa.{parent_column}""" + ).format(parent_column=parent_column, origin_column=origin_column), ) @openupgrade.migrate() def migrate(env, version): - column = openupgrade.get_legacy_name('parent_id') - if openupgrade.column_exists(env.cr, 'account_asset', column): + column = openupgrade.get_legacy_name("parent_id") + if openupgrade.column_exists(env.cr, "account_asset", column): # if migrating directly from v11 `account_asset` module, there are no # view assets nor parents create_asset_groups(env.cr) diff --git a/account_asset_management/migrations/12.0.2.0.0/pre-migration.py b/account_asset_management/migrations/12.0.2.0.0/pre-migration.py index 91cbb159..a56908df 100644 --- a/account_asset_management/migrations/12.0.2.0.0/pre-migration.py +++ b/account_asset_management/migrations/12.0.2.0.0/pre-migration.py @@ -3,14 +3,9 @@ from openupgradelib import openupgrade from psycopg2 import sql - _column_renames = { - 'account_asset_profile': [ - ('parent_id', None), - ], - 'account_asset': [ - ('parent_id', None), - ], + "account_asset_profile": [("parent_id", None)], + "account_asset": [("parent_id", None)], } @@ -18,31 +13,35 @@ def move_view_assets(cr): """Copy view assets to other table for preserving them, but outside of the main table, so remove them from there. """ - temp_table = sql.Identifier( - openupgrade.get_legacy_name('account_asset_view')) + temp_table = sql.Identifier(openupgrade.get_legacy_name("account_asset_view")) openupgrade.logged_query( - cr, sql.SQL(""" + cr, + sql.SQL( + """ CREATE TABLE {} AS ( SELECT * FROM account_asset WHERE type='view' - )""").format(temp_table), + )""" + ).format(temp_table), ) openupgrade.logged_query(cr, "DELETE FROM account_asset WHERE type='view'") @openupgrade.migrate() def migrate(env, version): - if openupgrade.column_exists(env.cr, 'account_asset', 'parent_id'): + if openupgrade.column_exists(env.cr, "account_asset", "parent_id"): # if migrating directly from v11 `account_asset` module, there are no # view assets nor parents openupgrade.rename_columns(env.cr, _column_renames) openupgrade.logged_query( - env.cr, """ + env.cr, + """ ALTER TABLE account_asset DROP CONSTRAINT account_asset_parent_id_fkey""", ) openupgrade.logged_query( - env.cr, """ + env.cr, + """ ALTER TABLE account_asset_profile DROP CONSTRAINT account_asset_profile_parent_id_fkey""", ) diff --git a/account_asset_management/models/account_account.py b/account_asset_management/models/account_account.py index 92a2681d..53610f6f 100644 --- a/account_asset_management/models/account_account.py +++ b/account_asset_management/models/account_account.py @@ -1,25 +1,30 @@ # Copyright 2009-2017 Noviat # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class AccountAccount(models.Model): - _inherit = 'account.account' + _inherit = "account.account" asset_profile_id = fields.Many2one( - comodel_name='account.asset.profile', - string='Asset Profile', - help="Default Asset Profile when creating invoice lines " - "with this account.") + comodel_name="account.asset.profile", + string="Asset Profile", + help="Default Asset Profile when creating invoice lines " "with this account.", + ) @api.multi - @api.constrains('asset_profile_id') + @api.constrains("asset_profile_id") def _check_asset_profile(self): for account in self: - if account.asset_profile_id and \ - account.asset_profile_id.account_asset_id != account: - raise ValidationError(_( - "The Asset Account defined in the Asset Profile " - "must be equal to the account.")) + if ( + account.asset_profile_id + and account.asset_profile_id.account_asset_id != account + ): + raise ValidationError( + _( + "The Asset Account defined in the Asset Profile " + "must be equal to the account." + ) + ) diff --git a/account_asset_management/models/account_asset.py b/account_asset_management/models/account_asset.py index 1d6a8570..5e2f7ea3 100644 --- a/account_asset_management/models/account_asset.py +++ b/account_asset_management/models/account_asset.py @@ -3,17 +3,19 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import calendar -from datetime import date -from dateutil.relativedelta import relativedelta import logging +from datetime import date +from functools import reduce from sys import exc_info from traceback import format_exception -from odoo import api, fields, models, _ -import odoo.addons.decimal_precision as dp +from dateutil.relativedelta import relativedelta + +from odoo import _, api, fields, models from odoo.exceptions import UserError from odoo.osv import expression -from functools import reduce + +import odoo.addons.decimal_precision as dp _logger = logging.getLogger(__name__) @@ -25,187 +27,241 @@ class DummyFy(object): class AccountAsset(models.Model): - _name = 'account.asset' - _description = 'Asset' - _order = 'date_start desc, code, name' + _name = "account.asset" + _description = "Asset" + _order = "date_start desc, code, name" account_move_line_ids = fields.One2many( - comodel_name='account.move.line', - inverse_name='asset_id', - string='Entries', readonly=True, copy=False) - move_line_check = fields.Boolean( - compute='_compute_move_line_check', - string='Has accounting entries') - name = fields.Char( - string='Asset Name', size=64, required=True, - readonly=True, states={'draft': [('readonly', False)]}) - code = fields.Char( - string='Reference', size=32, readonly=True, - states={'draft': [('readonly', False)]}) - purchase_value = fields.Float( - string='Purchase Value', required=True, readonly=True, - states={'draft': [('readonly', False)]}, - help="This amount represent the initial value of the asset." - "\nThe Depreciation Base is calculated as follows:" - "\nPurchase Value - Salvage Value.") - salvage_value = fields.Float( - string='Salvage Value', digits=dp.get_precision('Account'), + comodel_name="account.move.line", + inverse_name="asset_id", + string="Entries", readonly=True, - states={'draft': [('readonly', False)]}, + copy=False, + ) + move_line_check = fields.Boolean( + compute="_compute_move_line_check", string="Has accounting entries" + ) + name = fields.Char( + string="Asset Name", + size=64, + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + code = fields.Char( + string="Reference", + size=32, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + purchase_value = fields.Float( + string="Purchase Value", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + help="This amount represent the initial value of the asset." + "\nThe Depreciation Base is calculated as follows:" + "\nPurchase Value - Salvage Value.", + ) + salvage_value = fields.Float( + string="Salvage Value", + digits=dp.get_precision("Account"), + readonly=True, + states={"draft": [("readonly", False)]}, help="The estimated value that an asset will realize upon " - "its sale at the end of its useful life.\n" - "This value is used to determine the depreciation amounts.") + "its sale at the end of its useful life.\n" + "This value is used to determine the depreciation amounts.", + ) depreciation_base = fields.Float( - compute='_compute_depreciation_base', - digits=dp.get_precision('Account'), - string='Depreciation Base', + compute="_compute_depreciation_base", + digits=dp.get_precision("Account"), + string="Depreciation Base", store=True, help="This amount represent the depreciation base " - "of the asset (Purchase Value - Salvage Value.") + "of the asset (Purchase Value - Salvage Value.", + ) value_residual = fields.Float( - compute='_compute_depreciation', - digits=dp.get_precision('Account'), - string='Residual Value', - store=True) + compute="_compute_depreciation", + digits=dp.get_precision("Account"), + string="Residual Value", + store=True, + ) value_depreciated = fields.Float( - compute='_compute_depreciation', - digits=dp.get_precision('Account'), - string='Depreciated Value', - store=True) - note = fields.Text('Note') + compute="_compute_depreciation", + digits=dp.get_precision("Account"), + string="Depreciated Value", + store=True, + ) + note = fields.Text("Note") profile_id = fields.Many2one( - comodel_name='account.asset.profile', - string='Asset Profile', + comodel_name="account.asset.profile", + string="Asset Profile", change_default=True, readonly=True, required=True, - states={'draft': [('readonly', False)]}) + states={"draft": [("readonly", False)]}, + ) group_ids = fields.Many2many( - comodel_name='account.asset.group', + comodel_name="account.asset.group", relation="account_asset_group_rel", column1="asset_id", column2="group_id", - string='Asset Groups') + string="Asset Groups", + ) date_start = fields.Date( - string='Asset Start Date', + string="Asset Start Date", readonly=True, required=True, - states={'draft': [('readonly', False)]}, + states={"draft": [("readonly", False)]}, help="You should manually add depreciation lines " - "with the depreciations of previous fiscal years " - "if the Depreciation Start Date is different from the date " - "for which accounting entries need to be generated.") - date_remove = fields.Date(string='Asset Removal Date', readonly=True) + "with the depreciations of previous fiscal years " + "if the Depreciation Start Date is different from the date " + "for which accounting entries need to be generated.", + ) + date_remove = fields.Date(string="Asset Removal Date", readonly=True) state = fields.Selection( selection=[ - ('draft', 'Draft'), - ('open', 'Running'), - ('close', 'Close'), - ('removed', 'Removed'), - ], string='Status', required=True, default='draft', copy=False, + ("draft", "Draft"), + ("open", "Running"), + ("close", "Close"), + ("removed", "Removed"), + ], + string="Status", + required=True, + default="draft", + copy=False, help="When an asset is created, the status is 'Draft'.\n" - "If the asset is confirmed, the status goes in 'Running' " - "and the depreciation lines can be posted " - "to the accounting.\n" - "If the last depreciation line is posted, " - "the asset goes into the 'Close' status.\n" - "When the removal entries are generated, " - "the asset goes into the 'Removed' status.") + "If the asset is confirmed, the status goes in 'Running' " + "and the depreciation lines can be posted " + "to the accounting.\n" + "If the last depreciation line is posted, " + "the asset goes into the 'Close' status.\n" + "When the removal entries are generated, " + "the asset goes into the 'Removed' status.", + ) active = fields.Boolean(default=True) partner_id = fields.Many2one( - comodel_name='res.partner', string='Partner', readonly=True, - states={'draft': [('readonly', False)]}) + comodel_name="res.partner", + string="Partner", + readonly=True, + states={"draft": [("readonly", False)]}, + ) method = fields.Selection( - selection=lambda self: self.env[ - 'account.asset.profile']._selection_method(), - string='Computation Method', - required=True, readonly=True, - states={'draft': [('readonly', False)]}, default='linear', + selection=lambda self: self.env["account.asset.profile"]._selection_method(), + string="Computation Method", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + default="linear", help="Choose the method to use to compute " - "the amount of depreciation lines.\n" - " * Linear: Calculated on basis of: " - "Gross Value / Number of Depreciations\n" - " * Degressive: Calculated on basis of: " - "Residual Value * Degressive Factor" - " * Degressive-Linear (only for Time Method = Year): " - "Degressive becomes linear when the annual linear " - "depreciation exceeds the annual degressive depreciation") + "the amount of depreciation lines.\n" + " * Linear: Calculated on basis of: " + "Gross Value / Number of Depreciations\n" + " * Degressive: Calculated on basis of: " + "Residual Value * Degressive Factor" + " * Degressive-Linear (only for Time Method = Year): " + "Degressive becomes linear when the annual linear " + "depreciation exceeds the annual degressive depreciation", + ) method_number = fields.Integer( - string='Number of Years', readonly=True, - states={'draft': [('readonly', False)]}, default=5, - help="The number of years needed to depreciate your asset") + string="Number of Years", + readonly=True, + states={"draft": [("readonly", False)]}, + default=5, + help="The number of years needed to depreciate your asset", + ) method_period = fields.Selection( selection=lambda self: self.env[ - 'account.asset.profile']._selection_method_period(), - string='Period Length', - required=True, readonly=True, - states={'draft': [('readonly', False)]}, default='year', - help="Period length for the depreciation accounting entries") + "account.asset.profile" + ]._selection_method_period(), + string="Period Length", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + default="year", + help="Period length for the depreciation accounting entries", + ) method_end = fields.Date( - string='Ending Date', readonly=True, - states={'draft': [('readonly', False)]}) + string="Ending Date", readonly=True, states={"draft": [("readonly", False)]} + ) method_progress_factor = fields.Float( - string='Degressive Factor', readonly=True, - states={'draft': [('readonly', False)]}, default=0.3) + string="Degressive Factor", + readonly=True, + states={"draft": [("readonly", False)]}, + default=0.3, + ) method_time = fields.Selection( selection=lambda self: self.env[ - 'account.asset.profile']._selection_method_time(), - string='Time Method', - required=True, readonly=True, - states={'draft': [('readonly', False)]}, default='year', + "account.asset.profile" + ]._selection_method_time(), + string="Time Method", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + default="year", help="Choose the method to use to compute the dates and " - "number of depreciation lines.\n" - " * Number of Years: Specify the number of years " - "for the depreciation.\n" - # " * Number of Depreciations: Fix the number of " - # "depreciation lines and the time between 2 depreciations.\n" - # " * Ending Date: Choose the time between 2 depreciations " - # "and the date the depreciations won't go beyond." + "number of depreciation lines.\n" + " * Number of Years: Specify the number of years " + "for the depreciation.\n" + # " * Number of Depreciations: Fix the number of " + # "depreciation lines and the time between 2 depreciations.\n" + # " * Ending Date: Choose the time between 2 depreciations " + # "and the date the depreciations won't go beyond." ) days_calc = fields.Boolean( - string='Calculate by days', + string="Calculate by days", default=False, help="Use number of days to calculate depreciation amount", ) use_leap_years = fields.Boolean( - string='Use leap years', + string="Use leap years", default=False, help="If not set, the system will distribute evenly the amount to " - "amortize across the years, based on the number of years. " - "So the amount per year will be the " - "depreciation base / number of years.\n " - "If set, the system will consider if the current year " - "is a leap year. The amount to depreciate per year will be " - "calculated as depreciation base / (depreciation end date - " - "start date + 1) * days in the current year.", + "amortize across the years, based on the number of years. " + "So the amount per year will be the " + "depreciation base / number of years.\n " + "If set, the system will consider if the current year " + "is a leap year. The amount to depreciate per year will be " + "calculated as depreciation base / (depreciation end date - " + "start date + 1) * days in the current year.", ) prorata = fields.Boolean( - string='Prorata Temporis', readonly=True, - states={'draft': [('readonly', False)]}, + string="Prorata Temporis", + readonly=True, + states={"draft": [("readonly", False)]}, help="Indicates that the first depreciation entry for this asset " - "have to be done from the depreciation start date instead " - "of the first day of the fiscal year.") + "have to be done from the depreciation start date instead " + "of the first day of the fiscal year.", + ) depreciation_line_ids = fields.One2many( - comodel_name='account.asset.line', - inverse_name='asset_id', - string='Depreciation Lines', copy=False, - readonly=True, states={'draft': [('readonly', False)]}) + comodel_name="account.asset.line", + inverse_name="asset_id", + string="Depreciation Lines", + copy=False, + readonly=True, + states={"draft": [("readonly", False)]}, + ) company_id = fields.Many2one( - comodel_name='res.company', - string='Company', required=True, readonly=True, - default=lambda self: self._default_company_id()) + comodel_name="res.company", + string="Company", + required=True, + readonly=True, + default=lambda self: self._default_company_id(), + ) company_currency_id = fields.Many2one( - comodel_name='res.currency', - related='company_id.currency_id', - string='Company Currency', - store=True, readonly=True) + comodel_name="res.currency", + related="company_id.currency_id", + string="Company Currency", + store=True, + readonly=True, + ) account_analytic_id = fields.Many2one( - comodel_name='account.analytic.account', - string='Analytic account') + comodel_name="account.analytic.account", string="Analytic account" + ) @api.model def _default_company_id(self): - return self.env['res.company']._company_default_get('account.asset') + return self.env["res.company"]._company_default_get("account.asset") @api.multi def _compute_move_line_check(self): @@ -215,113 +271,116 @@ class AccountAsset(models.Model): asset.move_line_check = True break - @api.depends('purchase_value', 'salvage_value', 'method') + @api.depends("purchase_value", "salvage_value", "method") @api.multi def _compute_depreciation_base(self): for asset in self: - if asset.method in ['linear-limit', 'degr-limit']: + if asset.method in ["linear-limit", "degr-limit"]: asset.depreciation_base = asset.purchase_value else: - asset.depreciation_base = \ - asset.purchase_value - asset.salvage_value + asset.depreciation_base = asset.purchase_value - asset.salvage_value @api.multi - @api.depends('depreciation_base', - 'depreciation_line_ids.type', - 'depreciation_line_ids.amount', - 'depreciation_line_ids.previous_id', - 'depreciation_line_ids.init_entry', - 'depreciation_line_ids.move_check',) + @api.depends( + "depreciation_base", + "depreciation_line_ids.type", + "depreciation_line_ids.amount", + "depreciation_line_ids.previous_id", + "depreciation_line_ids.init_entry", + "depreciation_line_ids.move_check", + ) def _compute_depreciation(self): for asset in self: lines = asset.depreciation_line_ids.filtered( - lambda l: l.type in ('depreciate', 'remove') and - (l.init_entry or l.move_check)) + lambda l: l.type in ("depreciate", "remove") + and (l.init_entry or l.move_check) + ) value_depreciated = sum([l.amount for l in lines]) residual = asset.depreciation_base - value_depreciated depreciated = value_depreciated - asset.update({ - 'value_residual': residual, - 'value_depreciated': depreciated - }) + asset.update({"value_residual": residual, "value_depreciated": depreciated}) @api.multi - @api.constrains('method', 'method_time') + @api.constrains("method", "method_time") def _check_method(self): for asset in self: - if asset.method == 'degr-linear' and asset.method_time != 'year': + if asset.method == "degr-linear" and asset.method_time != "year": raise UserError( - _("Degressive-Linear is only supported for Time Method = " - "Year.")) + _("Degressive-Linear is only supported for Time Method = " "Year.") + ) @api.multi - @api.constrains('date_start', 'method_end', 'method_time') + @api.constrains("date_start", "method_end", "method_time") def _check_dates(self): for asset in self: - if asset.method_time == 'end': + if asset.method_time == "end": if asset.method_end <= asset.date_start: - raise UserError( - _("The Start Date must precede the Ending Date.")) + raise UserError(_("The Start Date must precede the Ending Date.")) - @api.onchange('purchase_value', 'salvage_value', 'date_start', 'method') + @api.onchange("purchase_value", "salvage_value", "date_start", "method") def _onchange_purchase_salvage_value(self): - if self.method in ['linear-limit', 'degr-limit']: + if self.method in ["linear-limit", "degr-limit"]: self.depreciation_base = self.purchase_value or 0.0 else: purchase_value = self.purchase_value or 0.0 salvage_value = self.salvage_value or 0.0 self.depreciation_base = purchase_value - salvage_value dl_create_line = self.depreciation_line_ids.filtered( - lambda r: r.type == 'create') + lambda r: r.type == "create" + ) if dl_create_line: - dl_create_line.update({ - 'amount': self.depreciation_base, - 'line_date': self.date_start - }) + dl_create_line.update( + {"amount": self.depreciation_base, "line_date": self.date_start} + ) - @api.onchange('profile_id') + @api.onchange("profile_id") def _onchange_profile_id(self): for line in self.depreciation_line_ids: if line.move_id: raise UserError( - _("You cannot change the profile of an asset " - "with accounting entries.")) + _( + "You cannot change the profile of an asset " + "with accounting entries." + ) + ) profile = self.profile_id if profile: - self.update({ - 'method': profile.method, - 'method_number': profile.method_number, - 'method_time': profile.method_time, - 'method_period': profile.method_period, - 'days_calc': profile.days_calc, - 'use_leap_years': profile.use_leap_years, - 'method_progress_factor': profile.method_progress_factor, - 'prorata': profile.prorata, - 'account_analytic_id': profile.account_analytic_id, - 'group_ids': profile.group_ids, - }) + self.update( + { + "method": profile.method, + "method_number": profile.method_number, + "method_time": profile.method_time, + "method_period": profile.method_period, + "days_calc": profile.days_calc, + "use_leap_years": profile.use_leap_years, + "method_progress_factor": profile.method_progress_factor, + "prorata": profile.prorata, + "account_analytic_id": profile.account_analytic_id, + "group_ids": profile.group_ids, + } + ) - @api.onchange('method_time') + @api.onchange("method_time") def _onchange_method_time(self): - if self.method_time != 'year': + if self.method_time != "year": self.prorata = True - @api.onchange('method_number') + @api.onchange("method_number") def _onchange_method_number(self): if self.method_number and self.method_end: self.method_end = False - @api.onchange('method_end') + @api.onchange("method_end") def _onchange_method_end(self): if self.method_end and self.method_number: self.method_number = 0 @api.model def create(self, vals): - if vals.get('method_time') != 'year' and not vals.get('prorata'): - vals['prorata'] = True + if vals.get("method_time") != "year" and not vals.get("prorata"): + vals["prorata"] = True asset = super().create(vals) - if self.env.context.get('create_asset_from_move_line'): + if self.env.context.get("create_asset_from_move_line"): # Trigger compute of depreciation_base asset.salvage_value = 0.0 asset._create_first_asset_line() @@ -329,16 +388,17 @@ class AccountAsset(models.Model): @api.multi def write(self, vals): - if vals.get('method_time'): - if vals['method_time'] != 'year' and not vals.get('prorata'): - vals['prorata'] = True + if vals.get("method_time"): + if vals["method_time"] != "year" and not vals.get("prorata"): + vals["prorata"] = True res = super().write(vals) for asset in self: - if self.env.context.get('asset_validate_from_write'): + if self.env.context.get("asset_validate_from_write"): continue asset._create_first_asset_line() - if asset.profile_id.open_asset and \ - self.env.context.get('create_asset_from_move_line'): + if asset.profile_id.open_asset and self.env.context.get( + "create_asset_from_move_line" + ): asset.compute_depreciation_board() # extra context to avoid recursion asset.with_context(asset_validate_from_write=True).validate() @@ -347,59 +407,60 @@ class AccountAsset(models.Model): def _create_first_asset_line(self): self.ensure_one() if self.depreciation_base and not self.depreciation_line_ids: - asset_line_obj = self.env['account.asset.line'] + asset_line_obj = self.env["account.asset.line"] line_name = self._get_depreciation_entry_name(0) asset_line_vals = { - 'amount': self.depreciation_base, - 'asset_id': self.id, - 'name': line_name, - 'line_date': self.date_start, - 'init_entry': True, - 'type': 'create', + "amount": self.depreciation_base, + "asset_id": self.id, + "name": line_name, + "line_date": self.date_start, + "init_entry": True, + "type": "create", } asset_line = asset_line_obj.create(asset_line_vals) - if self.env.context.get('create_asset_from_move_line'): - asset_line.move_id = self.env.context['move_id'] + if self.env.context.get("create_asset_from_move_line"): + asset_line.move_id = self.env.context["move_id"] @api.multi def unlink(self): for asset in self: - if asset.state != 'draft': - raise UserError( - _("You can only delete assets in draft state.")) + if asset.state != "draft": + raise UserError(_("You can only delete assets in draft state.")) if asset.depreciation_line_ids.filtered( - lambda r: r.type == 'depreciate' and r.move_check): + lambda r: r.type == "depreciate" and r.move_check + ): raise UserError( - _("You cannot delete an asset that contains " - "posted depreciation lines.")) + _( + "You cannot delete an asset that contains " + "posted depreciation lines." + ) + ) # update accounting entries linked to lines of type 'create' - amls = self.with_context( - allow_asset_removal=True - ).mapped('account_move_line_ids') - amls.write({'asset_id': False}) + amls = self.with_context(allow_asset_removal=True).mapped( + "account_move_line_ids" + ) + amls.write({"asset_id": False}) return super().unlink() @api.model - def name_search(self, name, args=None, operator='ilike', limit=100): + def name_search(self, name, args=None, operator="ilike", limit=100): args = args or [] domain = [] if name: - domain = ['|', - ('code', '=ilike', name + '%'), - ('name', operator, name)] + domain = ["|", ("code", "=ilike", name + "%"), ("name", operator, name)] if operator in expression.NEGATIVE_TERM_OPERATORS: - domain = ['&', '!'] + domain[1:] + domain = ["&", "!"] + domain[1:] assets = self.search(domain + args, limit=limit) return assets.name_get() @api.multi - @api.depends('name', 'code') + @api.depends("name", "code") def name_get(self): result = [] for asset in self: name = asset.name if asset.code: - name = ' - '.join([asset.code, name]) + name = " - ".join([asset.code, name]) result.append((asset.id, name)) return result @@ -407,9 +468,9 @@ class AccountAsset(models.Model): def validate(self): for asset in self: if asset.company_currency_id.is_zero(asset.value_residual): - asset.state = 'close' + asset.state = "close" else: - asset.state = 'open' + asset.state = "open" return True @api.multi @@ -418,73 +479,76 @@ class AccountAsset(models.Model): ctx = dict(self.env.context, active_ids=self.ids, active_id=self.id) early_removal = False - if self.method in ['linear-limit', 'degr-limit']: + if self.method in ["linear-limit", "degr-limit"]: if self.value_residual != self.salvage_value: early_removal = True elif self.value_residual: early_removal = True if early_removal: - ctx.update({'early_removal': True}) + ctx.update({"early_removal": True}) return { - 'name': _("Generate Asset Removal entries"), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'account.asset.remove', - 'target': 'new', - 'type': 'ir.actions.act_window', - 'context': ctx, + "name": _("Generate Asset Removal entries"), + "view_type": "form", + "view_mode": "form", + "res_model": "account.asset.remove", + "target": "new", + "type": "ir.actions.act_window", + "context": ctx, } @api.multi def set_to_draft(self): - return self.write({'state': 'draft'}) + return self.write({"state": "draft"}) @api.multi def open_entries(self): self.ensure_one() - amls = self.env['account.move.line'].search( - [('asset_id', '=', self.id)], order='date ASC') + amls = self.env["account.move.line"].search( + [("asset_id", "=", self.id)], order="date ASC" + ) am_ids = [l.move_id.id for l in amls] return { - 'name': _("Journal Entries"), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move', - 'view_id': False, - 'type': 'ir.actions.act_window', - 'context': self.env.context, - 'domain': [('id', 'in', am_ids)], + "name": _("Journal Entries"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.move", + "view_id": False, + "type": "ir.actions.act_window", + "context": self.env.context, + "domain": [("id", "in", am_ids)], } @api.multi def compute_depreciation_board(self): - def group_lines(x, y): - y.update({'amount': x['amount'] + y['amount']}) + y.update({"amount": x["amount"] + y["amount"]}) return y - line_obj = self.env['account.asset.line'] - digits = self.env['decimal.precision'].precision_get('Account') + line_obj = self.env["account.asset.line"] + digits = self.env["decimal.precision"].precision_get("Account") for asset in self: if asset.value_residual == 0.0: continue domain = [ - ('asset_id', '=', asset.id), - ('type', '=', 'depreciate'), - '|', ('move_check', '=', True), ('init_entry', '=', True)] - posted_lines = line_obj.search( - domain, order='line_date desc') + ("asset_id", "=", asset.id), + ("type", "=", "depreciate"), + "|", + ("move_check", "=", True), + ("init_entry", "=", True), + ] + posted_lines = line_obj.search(domain, order="line_date desc") if posted_lines: last_line = posted_lines[0] else: last_line = line_obj domain = [ - ('asset_id', '=', asset.id), - ('type', '=', 'depreciate'), - ('move_id', '=', False), - ('init_entry', '=', False)] + ("asset_id", "=", asset.id), + ("type", "=", "depreciate"), + ("move_id", "=", False), + ("init_entry", "=", False), + ] old_lines = line_obj.search(domain) if old_lines: old_lines.unlink() @@ -495,67 +559,72 @@ class AccountAsset(models.Model): # group lines prior to depreciation start period depreciation_start_date = asset.date_start - lines = table[0]['lines'] + lines = table[0]["lines"] lines1 = [] lines2 = [] - flag = lines[0]['date'] < depreciation_start_date + flag = lines[0]["date"] < depreciation_start_date for line in lines: if flag: lines1.append(line) - if line['date'] >= depreciation_start_date: + if line["date"] >= depreciation_start_date: flag = False else: lines2.append(line) if lines1: lines1 = [reduce(group_lines, lines1)] - lines1[0]['depreciated_value'] = 0.0 - table[0]['lines'] = lines1 + lines2 + lines1[0]["depreciated_value"] = 0.0 + table[0]["lines"] = lines1 + lines2 # check table with posted entries and # recompute in case of deviation depreciated_value_posted = depreciated_value = 0.0 if posted_lines: last_depreciation_date = last_line.line_date - last_date_in_table = table[-1]['lines'][-1]['date'] + last_date_in_table = table[-1]["lines"][-1]["date"] if last_date_in_table <= last_depreciation_date: raise UserError( - _("The duration of the asset conflicts with the " - "posted depreciation table entry dates.")) + _( + "The duration of the asset conflicts with the " + "posted depreciation table entry dates." + ) + ) for table_i, entry in enumerate(table): - residual_amount_table = \ - entry['lines'][-1]['remaining_value'] - if entry['date_start'] <= last_depreciation_date \ - <= entry['date_stop']: + residual_amount_table = entry["lines"][-1]["remaining_value"] + if ( + entry["date_start"] + <= last_depreciation_date + <= entry["date_stop"] + ): break - if entry['date_stop'] == last_depreciation_date: + if entry["date_stop"] == last_depreciation_date: table_i += 1 line_i = 0 else: entry = table[table_i] - date_min = entry['date_start'] - for line_i, line in enumerate(entry['lines']): - residual_amount_table = line['remaining_value'] - if date_min <= last_depreciation_date <= line['date']: + date_min = entry["date_start"] + for line_i, line in enumerate(entry["lines"]): + residual_amount_table = line["remaining_value"] + if date_min <= last_depreciation_date <= line["date"]: break - date_min = line['date'] - if line['date'] == last_depreciation_date: + date_min = line["date"] + if line["date"] == last_depreciation_date: line_i += 1 table_i_start = table_i line_i_start = line_i # check if residual value corresponds with table # and adjust table when needed - depreciated_value_posted = depreciated_value = \ - sum([l.amount for l in posted_lines]) + depreciated_value_posted = depreciated_value = sum( + [l.amount for l in posted_lines] + ) residual_amount = asset.depreciation_base - depreciated_value - amount_diff = round( - residual_amount_table - residual_amount, digits) + amount_diff = round(residual_amount_table - residual_amount, digits) if amount_diff: # compensate in first depreciation entry # after last posting - line = table[table_i_start]['lines'][line_i_start] - line['amount'] -= amount_diff + line = table[table_i_start]["lines"][line_i_start] + line["amount"] -= amount_diff else: # no posted lines table_i_start = 0 @@ -563,32 +632,31 @@ class AccountAsset(models.Model): seq = len(posted_lines) depr_line = last_line - last_date = table[-1]['lines'][-1]['date'] + last_date = table[-1]["lines"][-1]["date"] depreciated_value = depreciated_value_posted for entry in table[table_i_start:]: - for line in entry['lines'][line_i_start:]: + for line in entry["lines"][line_i_start:]: seq += 1 name = asset._get_depreciation_entry_name(seq) - if line['date'] == last_date: + if line["date"] == last_date: # ensure that the last entry of the table always # depreciates the remaining value - if asset.method in ['linear-limit', 'degr-limit']: - depr_max = asset.depreciation_base \ - - asset.salvage_value + if asset.method in ["linear-limit", "degr-limit"]: + depr_max = asset.depreciation_base - asset.salvage_value else: depr_max = asset.depreciation_base amount = depr_max - depreciated_value else: - amount = line['amount'] + amount = line["amount"] if amount: vals = { - 'previous_id': depr_line.id, - 'amount': round(amount, digits), - 'asset_id': asset.id, - 'name': name, - 'line_date': line['date'], - 'line_days': line['days'], - 'init_entry': entry['init'], + "previous_id": depr_line.id, + "amount": round(amount, digits), + "asset_id": asset.id, + "name": name, + "line_date": line["date"], + "line_days": line["days"], + "init_entry": entry["init"], } depreciated_value += round(amount, digits) depr_line = line_obj.create(vals) @@ -598,7 +666,7 @@ class AccountAsset(models.Model): return True - def _get_fy_duration(self, fy, option='days'): + def _get_fy_duration(self, fy, option="days"): """Returns fiscal year duration. @param option: @@ -610,13 +678,16 @@ class AccountAsset(models.Model): fy_date_start = fy.date_from fy_date_stop = fy.date_to days = (fy_date_stop - fy_date_start).days + 1 - months = (fy_date_stop.year - fy_date_start.year) * 12 \ - + (fy_date_stop.month - fy_date_start.month) + 1 - if option == 'days': + months = ( + (fy_date_stop.year - fy_date_start.year) * 12 + + (fy_date_stop.month - fy_date_start.month) + + 1 + ) + if option == "days": return days - elif option == 'months': + elif option == "months": return months - elif option == 'years': + elif option == "years": year = fy_date_start.year cnt = fy_date_stop.year - fy_date_start.year + 1 for i in range(cnt): @@ -625,12 +696,10 @@ class AccountAsset(models.Model): if fy_date_stop.year == year: duration = (fy_date_stop - fy_date_start).days + 1 else: - duration = ( - date(year, 12, 31) - fy_date_start).days + 1 + duration = (date(year, 12, 31) - fy_date_start).days + 1 factor = float(duration) / cy_days elif i == cnt - 1: # last year - duration = ( - fy_date_stop - date(year, 1, 1)).days + 1 + duration = (fy_date_stop - date(year, 1, 1)).days + 1 factor += float(duration) / cy_days else: factor += 1.0 @@ -643,23 +712,23 @@ class AccountAsset(models.Model): calculate the impact of extended/shortened fiscal years """ duration_factor = 1.0 - fy = entry['fy'] + fy = entry["fy"] if self.prorata: if firstyear: depreciation_date_start = self.date_start - fy_date_stop = entry['date_stop'] - first_fy_asset_days = \ - (fy_date_stop - depreciation_date_start).days + 1 - first_fy_duration = self._get_fy_duration(fy, option='days') - first_fy_year_factor = self._get_fy_duration( - fy, option='years') - duration_factor = \ - float(first_fy_asset_days) / first_fy_duration \ + fy_date_stop = entry["date_stop"] + first_fy_asset_days = (fy_date_stop - depreciation_date_start).days + 1 + first_fy_duration = self._get_fy_duration(fy, option="days") + first_fy_year_factor = self._get_fy_duration(fy, option="years") + duration_factor = ( + float(first_fy_asset_days) + / first_fy_duration * first_fy_year_factor + ) else: - duration_factor = self._get_fy_duration(fy, option='years') + duration_factor = self._get_fy_duration(fy, option="years") else: - fy_months = self._get_fy_duration(fy, option='months') + fy_months = self._get_fy_duration(fy, option="months") duration_factor = float(fy_months) / 12 return duration_factor @@ -675,90 +744,99 @@ class AccountAsset(models.Model): return depreciation_start_date def _get_depreciation_stop_date(self, depreciation_start_date): - if self.method_time == 'year' and not self.method_end: - depreciation_stop_date = depreciation_start_date + \ - relativedelta(years=self.method_number, days=-1) - elif self.method_time == 'number': - if self.method_period == 'month': - depreciation_stop_date = depreciation_start_date + \ - relativedelta(months=self.method_number, days=-1) - elif self.method_period == 'quarter': - m = [x for x in [3, 6, 9, 12] - if x >= depreciation_start_date.month][0] - first_line_date = depreciation_start_date \ - + relativedelta(month=m, day=31) + if self.method_time == "year" and not self.method_end: + depreciation_stop_date = depreciation_start_date + relativedelta( + years=self.method_number, days=-1 + ) + elif self.method_time == "number": + if self.method_period == "month": + depreciation_stop_date = depreciation_start_date + relativedelta( + months=self.method_number, days=-1 + ) + elif self.method_period == "quarter": + m = [x for x in [3, 6, 9, 12] if x >= depreciation_start_date.month][0] + first_line_date = depreciation_start_date + relativedelta( + month=m, day=31 + ) months = self.method_number * 3 - depreciation_stop_date = first_line_date \ - + relativedelta(months=months - 1, days=-1) - elif self.method_period == 'year': - depreciation_stop_date = depreciation_start_date + \ - relativedelta(years=self.method_number, days=-1) - elif self.method_time == 'year' and self.method_end: + depreciation_stop_date = first_line_date + relativedelta( + months=months - 1, days=-1 + ) + elif self.method_period == "year": + depreciation_stop_date = depreciation_start_date + relativedelta( + years=self.method_number, days=-1 + ) + elif self.method_time == "year" and self.method_end: depreciation_stop_date = self.method_end return depreciation_stop_date - def _get_first_period_amount(self, table, entry, depreciation_start_date, - line_dates): + def _get_first_period_amount( + self, table, entry, depreciation_start_date, line_dates + ): """ Return prorata amount for Time Method 'Year' in case of 'Prorata Temporis' """ - amount = entry.get('period_amount') - if self.prorata and self.method_time == 'year': - dates = [x for x in line_dates if x <= entry['date_stop']] + amount = entry.get("period_amount") + if self.prorata and self.method_time == "year": + dates = [x for x in line_dates if x <= entry["date_stop"]] full_periods = len(dates) - 1 - amount = entry['fy_amount'] - amount * full_periods + amount = entry["fy_amount"] - amount * full_periods return amount def _get_amount_linear( - self, depreciation_start_date, depreciation_stop_date, entry): + self, depreciation_start_date, depreciation_stop_date, entry + ): """ Override this method if you want to compute differently the yearly amount. """ if not self.use_leap_years and self.method_number: return self.depreciation_base / self.method_number - year = entry['date_stop'].year + year = entry["date_stop"].year cy_days = calendar.isleap(year) and 366 or 365 days = (depreciation_stop_date - depreciation_start_date).days + 1 return (self.depreciation_base / days) * cy_days - def _compute_year_amount(self, residual_amount, depreciation_start_date, - depreciation_stop_date, entry): + def _compute_year_amount( + self, residual_amount, depreciation_start_date, depreciation_stop_date, entry + ): """ Localization: override this method to change the degressive-linear calculation logic according to local legislation. """ - if self.method_time != 'year': + if self.method_time != "year": raise UserError( - _("The '_compute_year_amount' method is only intended for " - "Time Method 'Number of Years.")) + _( + "The '_compute_year_amount' method is only intended for " + "Time Method 'Number of Years." + ) + ) year_amount_linear = self._get_amount_linear( - depreciation_start_date, depreciation_stop_date, entry) - if self.method == 'linear': + depreciation_start_date, depreciation_stop_date, entry + ) + if self.method == "linear": return year_amount_linear - if self.method == 'linear-limit': + if self.method == "linear-limit": if (residual_amount - year_amount_linear) < self.salvage_value: return residual_amount - self.salvage_value else: return year_amount_linear - year_amount_degressive = residual_amount * \ - self.method_progress_factor - if self.method == 'degressive': + year_amount_degressive = residual_amount * self.method_progress_factor + if self.method == "degressive": return year_amount_degressive - if self.method == 'degr-linear': + if self.method == "degr-linear": if year_amount_linear > year_amount_degressive: return min(year_amount_linear, residual_amount) else: return min(year_amount_degressive, residual_amount) - if self.method == 'degr-limit': + if self.method == "degr-limit": if (residual_amount - year_amount_degressive) < self.salvage_value: return residual_amount - self.salvage_value else: return year_amount_degressive else: - raise UserError( - _("Illegal value %s in asset.method.") % self.method) + raise UserError(_("Illegal value %s in asset.method.") % self.method) def _compute_line_dates(self, table, start_date, stop_date): """ @@ -772,28 +850,27 @@ class AccountAsset(models.Model): """ line_dates = [] - if self.method_period == 'month': + if self.method_period == "month": line_date = start_date + relativedelta(day=31) - if self.method_period == 'quarter': + if self.method_period == "quarter": m = [x for x in [3, 6, 9, 12] if x >= start_date.month][0] line_date = start_date + relativedelta(month=m, day=31) - elif self.method_period == 'year': - line_date = table[0]['date_stop'] + elif self.method_period == "year": + line_date = table[0]["date_stop"] i = 1 while line_date < stop_date: line_dates.append(line_date) - if self.method_period == 'month': + if self.method_period == "month": line_date = line_date + relativedelta(months=1, day=31) - elif self.method_period == 'quarter': + elif self.method_period == "quarter": line_date = line_date + relativedelta(months=3, day=31) - elif self.method_period == 'year': - line_date = table[i]['date_stop'] + elif self.method_period == "year": + line_date = table[i]["date_stop"] i += 1 # last entry - if not (self.method_time == 'number' and - len(line_dates) == self.method_number): + if not (self.method_time == "number" and len(line_dates) == self.method_number): if self.days_calc: line_dates.append(stop_date) else: @@ -802,9 +879,9 @@ class AccountAsset(models.Model): return line_dates def _compute_depreciation_amount_per_fiscal_year( - self, table, line_dates, depreciation_start_date, - depreciation_stop_date): - digits = self.env['decimal.precision'].precision_get('Account') + self, table, line_dates, depreciation_start_date, depreciation_stop_date + ): + digits = self.env["decimal.precision"].precision_get("Account") fy_residual_amount = self.depreciation_base i_max = len(table) - 1 asset_sign = self.depreciation_base >= 0 and 1 or -1 @@ -814,25 +891,27 @@ class AccountAsset(models.Model): day_amount = self.depreciation_base / days for i, entry in enumerate(table): - if self.method_time == 'year': + if self.method_time == "year": year_amount = self._compute_year_amount( - fy_residual_amount, depreciation_start_date, - depreciation_stop_date, entry) - if self.method_period == 'year': + fy_residual_amount, + depreciation_start_date, + depreciation_stop_date, + entry, + ) + if self.method_period == "year": period_amount = year_amount - elif self.method_period == 'quarter': + elif self.method_period == "quarter": period_amount = year_amount / 4 - elif self.method_period == 'month': + elif self.method_period == "month": period_amount = year_amount / 12 if i == i_max: - if self.method in ['linear-limit', 'degr-limit']: + if self.method in ["linear-limit", "degr-limit"]: fy_amount = fy_residual_amount - self.salvage_value else: fy_amount = fy_residual_amount else: firstyear = i == 0 and True or False - fy_factor = self._get_fy_duration_factor( - entry, firstyear) + fy_factor = self._get_fy_duration_factor(entry, firstyear) fy_amount = year_amount * fy_factor if asset_sign * (fy_amount - fy_residual_amount) > 0: fy_amount = fy_residual_amount @@ -840,28 +919,31 @@ class AccountAsset(models.Model): fy_amount = round(fy_amount, digits) else: fy_amount = False - if self.method_time == 'number': + if self.method_time == "number": number = self.method_number else: number = len(line_dates) period_amount = round(self.depreciation_base / number, digits) - entry.update({ - 'period_amount': period_amount, - 'fy_amount': fy_amount, - 'day_amount': day_amount, - }) - if self.method_time == 'year': + entry.update( + { + "period_amount": period_amount, + "fy_amount": fy_amount, + "day_amount": day_amount, + } + ) + if self.method_time == "year": fy_residual_amount -= fy_amount if round(fy_residual_amount, digits) == 0: break i_max = i - table = table[:i_max + 1] + table = table[: i_max + 1] return table - def _compute_depreciation_table_lines(self, table, depreciation_start_date, - depreciation_stop_date, line_dates): + def _compute_depreciation_table_lines( + self, table, depreciation_start_date, depreciation_stop_date, line_dates + ): - digits = self.env['decimal.precision'].precision_get('Account') + digits = self.env["decimal.precision"].precision_get("Account") asset_sign = 1 if self.depreciation_base >= 0 else -1 i_max = len(table) - 1 remaining_value = self.depreciation_base @@ -871,38 +953,41 @@ class AccountAsset(models.Model): lines = [] fy_amount_check = 0.0 - fy_amount = entry['fy_amount'] + fy_amount = entry["fy_amount"] li_max = len(line_dates) - 1 - prev_date = max(entry['date_start'], depreciation_start_date) + prev_date = max(entry["date_start"], depreciation_start_date) for li, line_date in enumerate(line_dates): line_days = (line_date - prev_date).days + 1 if round(remaining_value, digits) == 0.0: break - if (line_date > min(entry['date_stop'], - depreciation_stop_date) and not - (i == i_max and li == li_max)): + if line_date > min(entry["date_stop"], depreciation_stop_date) and not ( + i == i_max and li == li_max + ): prev_date = line_date break else: prev_date = line_date + relativedelta(days=1) - if self.method == 'degr-linear' \ - and asset_sign * (fy_amount - fy_amount_check) < 0: + if ( + self.method == "degr-linear" + and asset_sign * (fy_amount - fy_amount_check) < 0 + ): break if i == 0 and li == 0: - if entry.get('day_amount') > 0.0: - amount = line_days * entry.get('day_amount') + if entry.get("day_amount") > 0.0: + amount = line_days * entry.get("day_amount") else: amount = self._get_first_period_amount( - table, entry, depreciation_start_date, line_dates) + table, entry, depreciation_start_date, line_dates + ) amount = round(amount, digits) else: - if entry.get('day_amount') > 0.0: - amount = line_days * entry.get('day_amount') + if entry.get("day_amount") > 0.0: + amount = line_days * entry.get("day_amount") else: - amount = entry.get('period_amount') + amount = entry.get("period_amount") # last year, last entry # Handle rounding deviations. @@ -913,11 +998,11 @@ class AccountAsset(models.Model): remaining_value -= amount fy_amount_check += amount line = { - 'date': line_date, - 'days': line_days, - 'amount': amount, - 'depreciated_value': depreciated_value, - 'remaining_value': remaining_value, + "date": line_date, + "days": line_days, + "amount": amount, + "depreciated_value": depreciated_value, + "remaining_value": remaining_value, } lines.append(line) depreciated_value += amount @@ -930,66 +1015,72 @@ class AccountAsset(models.Model): # was compensated in the first FY depreciation line. # The code has now been simplified with compensation # always in last FT depreciation line. - if self.method_time == 'year' and not entry.get('day_amount'): + if self.method_time == "year" and not entry.get("day_amount"): if round(fy_amount_check - fy_amount, digits) != 0: diff = fy_amount_check - fy_amount amount = amount - diff remaining_value += diff - lines[-1].update({ - 'amount': amount, - 'remaining_value': remaining_value, - }) + lines[-1].update( + {"amount": amount, "remaining_value": remaining_value} + ) depreciated_value -= diff if not lines: table.pop(i) else: - entry['lines'] = lines + entry["lines"] = lines line_dates = line_dates[li:] for i, entry in enumerate(table): - if not entry['fy_amount']: - entry['fy_amount'] = sum( - [l['amount'] for l in entry['lines']]) + if not entry["fy_amount"]: + entry["fy_amount"] = sum([l["amount"] for l in entry["lines"]]) def _get_fy_info(self, date): """Return an homogeneus data structure for fiscal years.""" fy_info = self.company_id.compute_fiscalyear_dates(date) - if 'record' not in fy_info: - fy_info['record'] = DummyFy( - date_from=fy_info['date_from'], - date_to=fy_info['date_to'], + if "record" not in fy_info: + fy_info["record"] = DummyFy( + date_from=fy_info["date_from"], date_to=fy_info["date_to"] ) return fy_info def _compute_depreciation_table(self): table = [] - if self.method_time in ['year', 'number'] \ - and not self.method_number and not self.method_end: + if ( + self.method_time in ["year", "number"] + and not self.method_number + and not self.method_end + ): return table company = self.company_id asset_date_start = self.date_start - fiscalyear_lock_date = ( - company.fiscalyear_lock_date or fields.Date.to_date('1901-01-01')) + fiscalyear_lock_date = company.fiscalyear_lock_date or fields.Date.to_date( + "1901-01-01" + ) depreciation_start_date = self._get_depreciation_start_date( - self._get_fy_info(asset_date_start)['record']) + self._get_fy_info(asset_date_start)["record"] + ) depreciation_stop_date = self._get_depreciation_stop_date( - depreciation_start_date) + depreciation_start_date + ) fy_date_start = asset_date_start while fy_date_start <= depreciation_stop_date: fy_info = self._get_fy_info(fy_date_start) - table.append({ - 'fy': fy_info['record'], - 'date_start': fy_info['date_from'], - 'date_stop': fy_info['date_to'], - 'init': fiscalyear_lock_date >= fy_info['date_from'], - }) - fy_date_start = fy_info['date_to'] + relativedelta(days=1) + table.append( + { + "fy": fy_info["record"], + "date_start": fy_info["date_from"], + "date_stop": fy_info["date_to"], + "init": fiscalyear_lock_date >= fy_info["date_from"], + } + ) + fy_date_start = fy_info["date_to"] + relativedelta(days=1) # Step 1: # Calculate depreciation amount per fiscal year. # This is calculation is skipped for method_time != 'year'. line_dates = self._compute_line_dates( - table, depreciation_start_date, depreciation_stop_date) + table, depreciation_start_date, depreciation_stop_date + ) table = self._compute_depreciation_amount_per_fiscal_year( table, line_dates, depreciation_start_date, depreciation_stop_date ) @@ -997,64 +1088,67 @@ class AccountAsset(models.Model): # Spread depreciation amount per fiscal year # over the depreciation periods. self._compute_depreciation_table_lines( - table, depreciation_start_date, depreciation_stop_date, - line_dates) + table, depreciation_start_date, depreciation_stop_date, line_dates + ) return table def _get_depreciation_entry_name(self, seq): """ use this method to customise the name of the accounting entry """ - return (self.code or str(self.id)) + '/' + str(seq) + return (self.code or str(self.id)) + "/" + str(seq) @api.multi def _compute_entries(self, date_end, check_triggers=False): # TODO : add ir_cron job calling this method to # generate periodical accounting entries result = [] - error_log = '' + error_log = "" if check_triggers: - recompute_obj = self.env['account.asset.recompute.trigger'] - recomputes = recompute_obj.sudo().search([('state', '=', 'open')]) + recompute_obj = self.env["account.asset.recompute.trigger"] + recomputes = recompute_obj.sudo().search([("state", "=", "open")]) if check_triggers and recomputes: - trigger_companies = recomputes.mapped('company_id') + trigger_companies = recomputes.mapped("company_id") for asset in self: if asset.company_id.id in trigger_companies.ids: asset.compute_depreciation_board() - depreciations = self.env['account.asset.line'].search([ - ('asset_id', 'in', self.ids), - ('type', '=', 'depreciate'), - ('init_entry', '=', False), - ('line_date', '<=', date_end), - ('move_check', '=', False)], - order='line_date') + depreciations = self.env["account.asset.line"].search( + [ + ("asset_id", "in", self.ids), + ("type", "=", "depreciate"), + ("init_entry", "=", False), + ("line_date", "<=", date_end), + ("move_check", "=", False), + ], + order="line_date", + ) for depreciation in depreciations: try: with self.env.cr.savepoint(): result += depreciation.create_move() except Exception: e = exc_info()[0] - tb = ''.join(format_exception(*exc_info())) + tb = "".join(format_exception(*exc_info())) asset_ref = depreciation.asset_id.name if depreciation.asset_id.code: - asset_ref = '[%s] %s' % ( - depreciation.asset_id.code, asset_ref) - error_log += _( - "\nError while processing asset '%s': %s" - ) % (asset_ref, str(e)) - error_msg = _( - "Error while processing asset '%s': \n\n%s" - ) % (asset_ref, tb) + asset_ref = "[{}] {}".format(depreciation.asset_id.code, asset_ref) + error_log += _("\nError while processing asset '%s': %s") % ( + asset_ref, + str(e), + ) + error_msg = _("Error while processing asset '%s': \n\n%s") % ( + asset_ref, + tb, + ) _logger.error("%s, %s", self._name, error_msg) if check_triggers and recomputes: - companies = recomputes.mapped('company_id') - triggers = recomputes.filtered( - lambda r: r.company_id.id in companies.ids) + companies = recomputes.mapped("company_id") + triggers = recomputes.filtered(lambda r: r.company_id.id in companies.ids) if triggers: recompute_vals = { - 'date_completed': fields.Datetime.now(), - 'state': 'done', + "date_completed": fields.Datetime.now(), + "state": "done", } triggers.sudo().write(recompute_vals) diff --git a/account_asset_management/models/account_asset_group.py b/account_asset_management/models/account_asset_group.py index d05870ce..d8319e03 100644 --- a/account_asset_management/models/account_asset_group.py +++ b/account_asset_management/models/account_asset_group.py @@ -6,24 +6,26 @@ from odoo import api, fields, models class AccountAssetGroup(models.Model): - _name = 'account.asset.group' - _description = 'Asset Group' - _order = 'name' + _name = "account.asset.group" + _description = "Asset Group" + _order = "name" _parent_store = True - name = fields.Char(string='Name', size=64, required=True, index=True) + name = fields.Char(string="Name", size=64, required=True, index=True) code = fields.Char(index=True) parent_path = fields.Char(index=True) company_id = fields.Many2one( - comodel_name='res.company', - string='Company', + comodel_name="res.company", + string="Company", required=True, - default=lambda self: self._default_company_id()) + default=lambda self: self._default_company_id(), + ) parent_id = fields.Many2one( - comodel_name='account.asset.group', - string='Parent Asset Group', - ondelete='restrict') + comodel_name="account.asset.group", + string="Parent Asset Group", + ondelete="restrict", + ) @api.model def _default_company_id(self): - return self.env['res.company']._company_default_get('account.asset') + return self.env["res.company"]._company_default_get("account.asset") diff --git a/account_asset_management/models/account_asset_line.py b/account_asset_management/models/account_asset_line.py index 326f9c7a..1794fc16 100644 --- a/account_asset_management/models/account_asset_line.py +++ b/account_asset_management/models/account_asset_line.py @@ -1,87 +1,86 @@ # Copyright 2009-2018 Noviat # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ -import odoo.addons.decimal_precision as dp +from odoo import _, api, fields, models from odoo.exceptions import UserError +import odoo.addons.decimal_precision as dp + class AccountAssetLine(models.Model): - _name = 'account.asset.line' - _description = 'Asset depreciation table line' - _order = 'type, line_date' + _name = "account.asset.line" + _description = "Asset depreciation table line" + _order = "type, line_date" - name = fields.Char(string='Depreciation Name', size=64, readonly=True) + name = fields.Char(string="Depreciation Name", size=64, readonly=True) asset_id = fields.Many2one( - comodel_name='account.asset', string='Asset', - required=True, ondelete='cascade') + comodel_name="account.asset", string="Asset", required=True, ondelete="cascade" + ) previous_id = fields.Many2one( - comodel_name='account.asset.line', - string='Previous Depreciation Line', - readonly=True) - parent_state = fields.Selection( - related='asset_id.state', - string='State of Asset', + comodel_name="account.asset.line", + string="Previous Depreciation Line", readonly=True, ) + parent_state = fields.Selection( + related="asset_id.state", string="State of Asset", readonly=True + ) depreciation_base = fields.Float( - related='asset_id.depreciation_base', - string='Depreciation Base', - readonly=True, + related="asset_id.depreciation_base", string="Depreciation Base", readonly=True ) amount = fields.Float( - string='Amount', digits=dp.get_precision('Account'), - required=True) - remaining_value = fields.Float( - compute='_compute_values', - digits=dp.get_precision('Account'), - string='Next Period Depreciation', - store=True) - depreciated_value = fields.Float( - compute='_compute_values', - digits=dp.get_precision('Account'), - string='Amount Already Depreciated', - store=True) - line_date = fields.Date(string='Date', required=True) - line_days = fields.Integer( - string='Days', - readonly=True, + string="Amount", digits=dp.get_precision("Account"), required=True ) + remaining_value = fields.Float( + compute="_compute_values", + digits=dp.get_precision("Account"), + string="Next Period Depreciation", + store=True, + ) + depreciated_value = fields.Float( + compute="_compute_values", + digits=dp.get_precision("Account"), + string="Amount Already Depreciated", + store=True, + ) + line_date = fields.Date(string="Date", required=True) + line_days = fields.Integer(string="Days", readonly=True) move_id = fields.Many2one( - comodel_name='account.move', - string='Depreciation Entry', readonly=True) + comodel_name="account.move", string="Depreciation Entry", readonly=True + ) move_check = fields.Boolean( - compute='_compute_move_check', - string='Posted', - store=True) + compute="_compute_move_check", string="Posted", store=True + ) type = fields.Selection( selection=[ - ('create', 'Depreciation Base'), - ('depreciate', 'Depreciation'), - ('remove', 'Asset Removal')], - readonly=True, default='depreciate') + ("create", "Depreciation Base"), + ("depreciate", "Depreciation"), + ("remove", "Asset Removal"), + ], + readonly=True, + default="depreciate", + ) init_entry = fields.Boolean( - string='Initial Balance Entry', + string="Initial Balance Entry", help="Set this flag for entries of previous fiscal years " - "for which Odoo has not generated accounting entries.") + "for which Odoo has not generated accounting entries.", + ) - @api.depends('amount', 'previous_id', 'type') + @api.depends("amount", "previous_id", "type") @api.multi def _compute_values(self): dlines = self - if self.env.context.get('no_compute_asset_line_ids'): + if self.env.context.get("no_compute_asset_line_ids"): # skip compute for lines in unlink - exclude_ids = self.env.context['no_compute_asset_line_ids'] + exclude_ids = self.env.context["no_compute_asset_line_ids"] dlines = self.filtered(lambda l: l.id not in exclude_ids) - dlines = dlines.filtered(lambda l: l.type == 'depreciate') + dlines = dlines.filtered(lambda l: l.type == "depreciate") dlines = dlines.sorted(key=lambda l: l.line_date) # Group depreciation lines per asset - asset_ids = dlines.mapped('asset_id') + asset_ids = dlines.mapped("asset_id") grouped_dlines = [] for asset in asset_ids: - grouped_dlines.append( - dlines.filtered(lambda l: l.asset_id.id == asset.id)) + grouped_dlines.append(dlines.filtered(lambda l: l.asset_id.id == asset.id)) for dlines in grouped_dlines: for i, dl in enumerate(dlines): @@ -89,102 +88,125 @@ class AccountAssetLine(models.Model): depreciation_base = dl.depreciation_base tmp = depreciation_base - dl.previous_id.remaining_value depreciated_value = dl.previous_id and tmp or 0.0 - remaining_value = \ - depreciation_base - depreciated_value - dl.amount + remaining_value = depreciation_base - depreciated_value - dl.amount else: depreciated_value += dl.previous_id.amount remaining_value -= dl.amount dl.depreciated_value = depreciated_value dl.remaining_value = remaining_value - @api.depends('move_id') + @api.depends("move_id") @api.multi def _compute_move_check(self): for line in self: line.move_check = bool(line.move_id) - @api.onchange('amount') + @api.onchange("amount") def _onchange_amount(self): - if self.type == 'depreciate': - self.remaining_value = self.depreciation_base - \ - self.depreciated_value - self.amount + if self.type == "depreciate": + self.remaining_value = ( + self.depreciation_base - self.depreciated_value - self.amount + ) @api.multi def write(self, vals): for dl in self: - line_date = vals.get('line_date') or dl.line_date + line_date = vals.get("line_date") or dl.line_date asset_lines = dl.asset_id.depreciation_line_ids - if list(vals.keys()) == ['move_id'] and not vals['move_id']: + if list(vals.keys()) == ["move_id"] and not vals["move_id"]: # allow to remove an accounting entry via the # 'Delete Move' button on the depreciation lines. - if not self.env.context.get('unlink_from_asset'): - raise UserError(_( - "You are not allowed to remove an accounting entry " - "linked to an asset." - "\nYou should remove such entries from the asset.")) - elif list(vals.keys()) == ['asset_id']: + if not self.env.context.get("unlink_from_asset"): + raise UserError( + _( + "You are not allowed to remove an accounting entry " + "linked to an asset." + "\nYou should remove such entries from the asset." + ) + ) + elif list(vals.keys()) == ["asset_id"]: continue - elif dl.move_id and not self.env.context.get( - 'allow_asset_line_update'): - raise UserError(_( - "You cannot change a depreciation line " - "with an associated accounting entry.")) - elif vals.get('init_entry'): + elif dl.move_id and not self.env.context.get("allow_asset_line_update"): + raise UserError( + _( + "You cannot change a depreciation line " + "with an associated accounting entry." + ) + ) + elif vals.get("init_entry"): check = asset_lines.filtered( - lambda l: l.move_check and l.type == 'depreciate' and - l.line_date <= line_date) + lambda l: l.move_check + and l.type == "depreciate" + and l.line_date <= line_date + ) if check: - raise UserError(_( - "You cannot set the 'Initial Balance Entry' flag " - "on a depreciation line " - "with prior posted entries.")) - elif vals.get('line_date'): - if dl.type == 'create': + raise UserError( + _( + "You cannot set the 'Initial Balance Entry' flag " + "on a depreciation line " + "with prior posted entries." + ) + ) + elif vals.get("line_date"): + if dl.type == "create": check = asset_lines.filtered( - lambda l: l.type != 'create' and - (l.init_entry or l.move_check) and - l.line_date < fields.Date.to_date(vals['line_date'])) + lambda l: l.type != "create" + and (l.init_entry or l.move_check) + and l.line_date < fields.Date.to_date(vals["line_date"]) + ) if check: raise UserError( - _("You cannot set the Asset Start Date " - "after already posted entries.")) + _( + "You cannot set the Asset Start Date " + "after already posted entries." + ) + ) else: check = asset_lines.filtered( - lambda l: l != dl and - (l.init_entry or l.move_check) and - l.line_date > fields.Date.to_date(vals['line_date'])) + lambda l: l != dl + and (l.init_entry or l.move_check) + and l.line_date > fields.Date.to_date(vals["line_date"]) + ) if check: - raise UserError(_( - "You cannot set the date on a depreciation line " - "prior to already posted entries.")) + raise UserError( + _( + "You cannot set the date on a depreciation line " + "prior to already posted entries." + ) + ) return super().write(vals) @api.multi def unlink(self): for dl in self: - if dl.type == 'create' and dl.amount: - raise UserError(_( - "You cannot remove an asset line " - "of type 'Depreciation Base'.")) + if dl.type == "create" and dl.amount: + raise UserError( + _("You cannot remove an asset line " "of type 'Depreciation Base'.") + ) elif dl.move_id: - raise UserError(_( - "You cannot delete a depreciation line with " - "an associated accounting entry.")) + raise UserError( + _( + "You cannot delete a depreciation line with " + "an associated accounting entry." + ) + ) previous = dl.previous_id next_line = dl.asset_id.depreciation_line_ids.filtered( - lambda l: l.previous_id == dl and l not in self) + lambda l: l.previous_id == dl and l not in self + ) if next_line: next_line.previous_id = previous - return super(AccountAssetLine, self.with_context( - no_compute_asset_line_ids=self.ids)).unlink() + return super( + AccountAssetLine, self.with_context(no_compute_asset_line_ids=self.ids) + ).unlink() def _setup_move_data(self, depreciation_date): asset = self.asset_id move_data = { - 'name': asset.name, - 'date': depreciation_date, - 'ref': self.name, - 'journal_id': asset.profile_id.journal_id.id, + "name": asset.name, + "date": depreciation_date, + "ref": self.name, + "journal_id": asset.profile_id.journal_id.id, } return move_data @@ -192,25 +214,25 @@ class AccountAssetLine(models.Model): asset = self.asset_id amount = self.amount analytic_id = False - if ml_type == 'depreciation': + if ml_type == "depreciation": debit = amount < 0 and -amount or 0.0 credit = amount > 0 and amount or 0.0 - elif ml_type == 'expense': + elif ml_type == "expense": debit = amount > 0 and amount or 0.0 credit = amount < 0 and -amount or 0.0 analytic_id = asset.account_analytic_id.id move_line_data = { - 'name': asset.name, - 'ref': self.name, - 'move_id': move.id, - 'account_id': account.id, - 'credit': credit, - 'debit': debit, - 'journal_id': asset.profile_id.journal_id.id, - 'partner_id': asset.partner_id.id, - 'analytic_account_id': analytic_id, - 'date': depreciation_date, - 'asset_id': asset.id, + "name": asset.name, + "ref": self.name, + "move_id": move.id, + "account_id": account.id, + "credit": credit, + "debit": debit, + "journal_id": asset.profile_id.journal_id.id, + "partner_id": asset.partner_id.id, + "analytic_account_id": analytic_id, + "date": depreciation_date, + "asset_id": asset.id, } return move_line_data @@ -218,63 +240,58 @@ class AccountAssetLine(models.Model): def create_move(self): created_move_ids = [] asset_ids = set() - ctx = dict(self.env.context, - allow_asset=True, check_move_validity=False) + ctx = dict(self.env.context, allow_asset=True, check_move_validity=False) for line in self: asset = line.asset_id depreciation_date = line.line_date am_vals = line._setup_move_data(depreciation_date) - move = self.env['account.move'].with_context(ctx).create(am_vals) + move = self.env["account.move"].with_context(ctx).create(am_vals) depr_acc = asset.profile_id.account_depreciation_id exp_acc = asset.profile_id.account_expense_depreciation_id aml_d_vals = line._setup_move_line_data( - depreciation_date, depr_acc, 'depreciation', move) - self.env['account.move.line'].with_context(ctx).create(aml_d_vals) + depreciation_date, depr_acc, "depreciation", move + ) + self.env["account.move.line"].with_context(ctx).create(aml_d_vals) aml_e_vals = line._setup_move_line_data( - depreciation_date, exp_acc, 'expense', move) - self.env['account.move.line'].with_context(ctx).create(aml_e_vals) + depreciation_date, exp_acc, "expense", move + ) + self.env["account.move.line"].with_context(ctx).create(aml_e_vals) move.post() - line.with_context(allow_asset_line_update=True).write({ - 'move_id': move.id - }) + line.with_context(allow_asset_line_update=True).write({"move_id": move.id}) created_move_ids.append(move.id) asset_ids.add(asset.id) # we re-evaluate the assets to determine if we can close them - for asset in self.env['account.asset'].browse(list(asset_ids)): + for asset in self.env["account.asset"].browse(list(asset_ids)): if asset.company_currency_id.is_zero(asset.value_residual): - asset.state = 'close' + asset.state = "close" return created_move_ids @api.multi def open_move(self): self.ensure_one() return { - 'name': _("Journal Entry"), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move', - 'view_id': False, - 'type': 'ir.actions.act_window', - 'context': self.env.context, - 'domain': [('id', '=', self.move_id.id)], + "name": _("Journal Entry"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.move", + "view_id": False, + "type": "ir.actions.act_window", + "context": self.env.context, + "domain": [("id", "=", self.move_id.id)], } @api.multi def unlink_move(self): for line in self: move = line.move_id - if move.state == 'posted': + if move.state == "posted": move.button_cancel() move.with_context(unlink_from_asset=True).unlink() # trigger store function - line.with_context(unlink_from_asset=True).write( - {'move_id': False}) - if line.parent_state == 'close': - line.asset_id.write({'state': 'open'}) - elif line.parent_state == 'removed' and line.type == 'remove': - line.asset_id.write({ - 'state': 'close', - 'date_remove': False, - }) + line.with_context(unlink_from_asset=True).write({"move_id": False}) + if line.parent_state == "close": + line.asset_id.write({"state": "open"}) + elif line.parent_state == "removed" and line.type == "remove": + line.asset_id.write({"state": "close", "date_remove": False}) line.unlink() return True diff --git a/account_asset_management/models/account_asset_profile.py b/account_asset_management/models/account_asset_profile.py index cd3b1f65..a751ffcc 100644 --- a/account_asset_management/models/account_asset_profile.py +++ b/account_asset_management/models/account_asset_profile.py @@ -1,150 +1,169 @@ # Copyright 2009-2018 Noviat # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError class AccountAssetProfile(models.Model): - _name = 'account.asset.profile' - _description = 'Asset profile' - _order = 'name' + _name = "account.asset.profile" + _description = "Asset profile" + _order = "name" - name = fields.Char(string='Name', size=64, required=True, index=True) + name = fields.Char(string="Name", size=64, required=True, index=True) note = fields.Text() account_analytic_id = fields.Many2one( - comodel_name='account.analytic.account', - string='Analytic account') + comodel_name="account.analytic.account", string="Analytic account" + ) account_asset_id = fields.Many2one( - comodel_name='account.account', - domain=[('deprecated', '=', False)], - string='Asset Account', required=True) + comodel_name="account.account", + domain=[("deprecated", "=", False)], + string="Asset Account", + required=True, + ) account_depreciation_id = fields.Many2one( - comodel_name='account.account', - domain=[('deprecated', '=', False)], - string='Depreciation Account', required=True) + comodel_name="account.account", + domain=[("deprecated", "=", False)], + string="Depreciation Account", + required=True, + ) account_expense_depreciation_id = fields.Many2one( - comodel_name='account.account', - domain=[('deprecated', '=', False)], - string='Depr. Expense Account', required=True) + comodel_name="account.account", + domain=[("deprecated", "=", False)], + string="Depr. Expense Account", + required=True, + ) account_plus_value_id = fields.Many2one( - comodel_name='account.account', - domain=[('deprecated', '=', False)], - string='Plus-Value Account') + comodel_name="account.account", + domain=[("deprecated", "=", False)], + string="Plus-Value Account", + ) account_min_value_id = fields.Many2one( - comodel_name='account.account', - domain=[('deprecated', '=', False)], - string='Min-Value Account') + comodel_name="account.account", + domain=[("deprecated", "=", False)], + string="Min-Value Account", + ) account_residual_value_id = fields.Many2one( - comodel_name='account.account', - domain=[('deprecated', '=', False)], - string='Residual Value Account') + comodel_name="account.account", + domain=[("deprecated", "=", False)], + string="Residual Value Account", + ) journal_id = fields.Many2one( - comodel_name='account.journal', - domain=[('type', '=', 'general')], - string='Journal', required=True) + comodel_name="account.journal", + domain=[("type", "=", "general")], + string="Journal", + required=True, + ) company_id = fields.Many2one( - comodel_name='res.company', - string='Company', required=True, - default=lambda self: self._default_company_id()) + comodel_name="res.company", + string="Company", + required=True, + default=lambda self: self._default_company_id(), + ) group_ids = fields.Many2many( - comodel_name='account.asset.group', + comodel_name="account.asset.group", relation="account_asset_profile_group_rel", column1="profile_id", column2="group_id", - string='Asset Groups') + string="Asset Groups", + ) method = fields.Selection( selection=lambda self: self._selection_method(), - string='Computation Method', + string="Computation Method", required=True, help="Choose the method to use to compute the depreciation lines.\n" - " * Linear: Calculated on basis of: " - "Depreciation Base / Number of Depreciations. " - "Depreciation Base = Purchase Value - Salvage Value.\n" - " * Linear-Limit: Linear up to Salvage Value. " - "Depreciation Base = Purchase Value.\n" - " * Degressive: Calculated on basis of: " - "Residual Value * Degressive Factor.\n" - " * Degressive-Linear (only for Time Method = Year): " - "Degressive becomes linear when the annual linear " - "depreciation exceeds the annual degressive depreciation.\n" - " * Degressive-Limit: Degressive up to Salvage Value. " - "The Depreciation Base is equal to the asset value.", - default='linear') + " * Linear: Calculated on basis of: " + "Depreciation Base / Number of Depreciations. " + "Depreciation Base = Purchase Value - Salvage Value.\n" + " * Linear-Limit: Linear up to Salvage Value. " + "Depreciation Base = Purchase Value.\n" + " * Degressive: Calculated on basis of: " + "Residual Value * Degressive Factor.\n" + " * Degressive-Linear (only for Time Method = Year): " + "Degressive becomes linear when the annual linear " + "depreciation exceeds the annual degressive depreciation.\n" + " * Degressive-Limit: Degressive up to Salvage Value. " + "The Depreciation Base is equal to the asset value.", + default="linear", + ) method_number = fields.Integer( - string='Number of Years', + string="Number of Years", help="The number of years needed to depreciate your asset", - default=5) + default=5, + ) method_period = fields.Selection( selection=lambda self: self._selection_method_period(), - string='Period Length', required=True, - default='year', - help="Period length for the depreciation accounting entries") - method_progress_factor = fields.Float( - string='Degressive Factor', default=0.3) + string="Period Length", + required=True, + default="year", + help="Period length for the depreciation accounting entries", + ) + method_progress_factor = fields.Float(string="Degressive Factor", default=0.3) method_time = fields.Selection( selection=lambda self: self._selection_method_time(), - string='Time Method', required=True, - default='year', + string="Time Method", + required=True, + default="year", help="Choose the method to use to compute the dates and " - "number of depreciation lines.\n" - " * Number of Years: Specify the number of years " - "for the depreciation.\n") + "number of depreciation lines.\n" + " * Number of Years: Specify the number of years " + "for the depreciation.\n", + ) days_calc = fields.Boolean( - string='Calculate by days', + string="Calculate by days", default=False, - help="Use number of days to calculate depreciation amount") + help="Use number of days to calculate depreciation amount", + ) use_leap_years = fields.Boolean( - string='Use leap years', + string="Use leap years", default=False, help="If not set, the system will distribute evenly the amount to " - "amortize across the years, based on the number of years. " - "So the amount per year will be the " - "depreciation base / number of years.\n " - "If set, the system will consider if the current year " - "is a leap year. The amount to depreciate per year will be " - "calculated as depreciation base / (depreciation end date - " - "start date + 1) * days in the current year.", + "amortize across the years, based on the number of years. " + "So the amount per year will be the " + "depreciation base / number of years.\n " + "If set, the system will consider if the current year " + "is a leap year. The amount to depreciate per year will be " + "calculated as depreciation base / (depreciation end date - " + "start date + 1) * days in the current year.", ) prorata = fields.Boolean( - string='Prorata Temporis', + string="Prorata Temporis", help="Indicates that the first depreciation entry for this asset " - "has to be done from the depreciation start date instead of " - "the first day of the fiscal year.") + "has to be done from the depreciation start date instead of " + "the first day of the fiscal year.", + ) open_asset = fields.Boolean( - string='Skip Draft State', + string="Skip Draft State", help="Check this if you want to automatically confirm the assets " - "of this profile when created by invoices.") + "of this profile when created by invoices.", + ) asset_product_item = fields.Boolean( - string='Create an asset by product item', + string="Create an asset by product item", help="By default during the validation of an invoice, an asset " - "is created by invoice line as long as an accounting entry is " - "created by invoice line. " - "With this setting, an accounting entry will be created by " - "product item. So, there will be an asset by product item.") + "is created by invoice line as long as an accounting entry is " + "created by invoice line. " + "With this setting, an accounting entry will be created by " + "product item. So, there will be an asset by product item.", + ) active = fields.Boolean(default=True) @api.model def _default_company_id(self): - return self.env['res.company']._company_default_get('account.asset') + return self.env["res.company"]._company_default_get("account.asset") @api.model def _selection_method(self): - return[ - ('linear', _('Linear')), - ('linear-limit', _('Linear up to Salvage Value')), - ('degressive', _('Degressive')), - ('degr-linear', _('Degressive-Linear')), - ('degr-limit', _('Degressive up to Salvage Value')), + return [ + ("linear", _("Linear")), + ("linear-limit", _("Linear up to Salvage Value")), + ("degressive", _("Degressive")), + ("degr-linear", _("Degressive-Linear")), + ("degr-limit", _("Degressive up to Salvage Value")), ] @api.model def _selection_method_period(self): - return [ - ('month', _('Month')), - ('quarter', _('Quarter')), - ('year', _('Year')), - ] + return [("month", _("Month")), ("quarter", _("Quarter")), ("year", _("Year"))] @api.model def _selection_method_time(self): @@ -152,47 +171,43 @@ class AccountAssetProfile(models.Model): Install the 'account_asset_management_method_number_end' to enable the 'Number' and 'End' Time Methods. """ - return [ - ('year', _('Number of Years or end date')), - ] + return [("year", _("Number of Years or end date"))] @api.multi - @api.constrains('method') + @api.constrains("method") def _check_method(self): for profile in self: - if profile.method == 'degr-linear' and \ - profile.method_time != 'year': + if profile.method == "degr-linear" and profile.method_time != "year": raise UserError( - _("Degressive-Linear is only supported for Time Method = " - "Year.")) + _("Degressive-Linear is only supported for Time Method = " "Year.") + ) - @api.onchange('method_time') + @api.onchange("method_time") def _onchange_method_time(self): - if self.method_time != 'year': + if self.method_time != "year": self.prorata = True @api.model def create(self, vals): - if vals.get('method_time') != 'year' and not vals.get('prorata'): - vals['prorata'] = True + if vals.get("method_time") != "year" and not vals.get("prorata"): + vals["prorata"] = True profile = super().create(vals) - acc_id = vals.get('account_asset_id') + acc_id = vals.get("account_asset_id") if acc_id: - account = self.env['account.account'].browse(acc_id) + account = self.env["account.account"].browse(acc_id) if not account.asset_profile_id: - account.write({'asset_profile_id': profile.id}) + account.write({"asset_profile_id": profile.id}) return profile @api.multi def write(self, vals): - if vals.get('method_time'): - if vals['method_time'] != 'year' and not vals.get('prorata'): - vals['prorata'] = True + if vals.get("method_time"): + if vals["method_time"] != "year" and not vals.get("prorata"): + vals["prorata"] = True res = super().write(vals) # TODO last profile in self is defined as default on the related # account. must be improved. - account = self.env['account.account'].browse( - vals.get('account_asset_id')) + account = self.env["account.account"].browse(vals.get("account_asset_id")) if self and account and not account.asset_profile_id: - account.write({'asset_profile_id': self[-1].id}) + account.write({"asset_profile_id": self[-1].id}) return res diff --git a/account_asset_management/models/account_asset_recompute_trigger.py b/account_asset_management/models/account_asset_recompute_trigger.py index 6f0f1b38..6403b276 100644 --- a/account_asset_management/models/account_asset_recompute_trigger.py +++ b/account_asset_management/models/account_asset_recompute_trigger.py @@ -5,21 +5,20 @@ from odoo import fields, models class AccountAssetRecomputeTrigger(models.Model): - _name = 'account.asset.recompute.trigger' + _name = "account.asset.recompute.trigger" _description = "Asset table recompute triggers" - reason = fields.Char( - string='Reason', required=True) - company_id = fields.Many2one( - 'res.company', string='Company', required=True) + reason = fields.Char(string="Reason", required=True) + company_id = fields.Many2one("res.company", string="Company", required=True) date_trigger = fields.Datetime( - 'Trigger Date', + "Trigger Date", readonly=True, - help="Date of the event triggering the need to " - "recompute the Asset Tables.") - date_completed = fields.Datetime( - 'Completion Date', readonly=True) + help="Date of the event triggering the need to " "recompute the Asset Tables.", + ) + date_completed = fields.Datetime("Completion Date", readonly=True) state = fields.Selection( - selection=[('open', 'Open'), ('done', 'Done')], - string='State', default='open', - readonly=True) + selection=[("open", "Open"), ("done", "Done")], + string="State", + default="open", + readonly=True, + ) diff --git a/account_asset_management/models/account_fiscal_year.py b/account_asset_management/models/account_fiscal_year.py index 764bf759..36ca404f 100644 --- a/account_asset_management/models/account_fiscal_year.py +++ b/account_asset_management/models/account_fiscal_year.py @@ -4,6 +4,7 @@ import logging import time from datetime import datetime + from dateutil.relativedelta import relativedelta from odoo import api, models @@ -13,37 +14,33 @@ _logger = logging.getLogger(__name__) class AccountFiscalYear(models.Model): - _inherit = 'account.fiscal.year' + _inherit = "account.fiscal.year" @api.model def create(self, vals): - date_from = datetime.strptime(vals.get('date_from'), '%Y-%m-%d') - date_to = datetime.strptime(vals.get('date_to'), '%Y-%m-%d') + date_from = datetime.strptime(vals.get("date_from"), "%Y-%m-%d") + date_to = datetime.strptime(vals.get("date_to"), "%Y-%m-%d") if not date_to == date_from + relativedelta(years=1, days=-1): recompute_vals = { - 'reason': 'creation of fiscalyear %s' % vals.get('name'), - 'company_id': - vals.get('company_id') or - self.env.user.company_id.id, - 'date_trigger': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'state': 'open', + "reason": "creation of fiscalyear %s" % vals.get("name"), + "company_id": vals.get("company_id") or self.env.user.company_id.id, + "date_trigger": time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), + "state": "open", } - self.env['account.asset.recompute.trigger'].sudo().create( - recompute_vals) + self.env["account.asset.recompute.trigger"].sudo().create(recompute_vals) return super().create(vals) @api.multi def write(self, vals): - if vals.get('date_from') or vals.get('date_to'): + if vals.get("date_from") or vals.get("date_to"): for fy in self: recompute_vals = { - 'reason': - 'duration change of fiscalyear %s' % fy.name, - 'company_id': fy.company_id.id, - 'date_trigger': - time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'state': 'open', + "reason": "duration change of fiscalyear %s" % fy.name, + "company_id": fy.company_id.id, + "date_trigger": time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), + "state": "open", } - self.env['account.asset.recompute.trigger'].sudo().\ - create(recompute_vals) + self.env["account.asset.recompute.trigger"].sudo().create( + recompute_vals + ) return super().write(vals) diff --git a/account_asset_management/models/account_invoice.py b/account_asset_management/models/account_invoice.py index f5229f1b..2c1266ec 100644 --- a/account_asset_management/models/account_invoice.py +++ b/account_asset_management/models/account_invoice.py @@ -7,7 +7,7 @@ from odoo import api, fields, models class AccountInvoice(models.Model): - _inherit = 'account.invoice' + _inherit = "account.invoice" @api.multi def finalize_invoice_move_lines(self, move_lines): @@ -15,27 +15,32 @@ class AccountInvoice(models.Model): new_lines = [] for line_tuple in move_lines: line = line_tuple[2] - dp = self.env['decimal.precision'] - if line.get('asset_profile_id') and \ - line.get('quantity', 0.0) > 1.0: - profile = self.env['account.asset.profile'].browse( - [line.get('asset_profile_id')]) + dp = self.env["decimal.precision"] + if line.get("asset_profile_id") and line.get("quantity", 0.0) > 1.0: + profile = self.env["account.asset.profile"].browse( + [line.get("asset_profile_id")] + ) if profile.asset_product_item: origin_line = copy.deepcopy(line) - line_qty = line.get('quantity') - line['quantity'] = round(line['quantity'] / line_qty, - dp.precision_get('Account')) - line['debit'] = round(line['debit'] / line_qty, - dp.precision_get('Account')) - line['credit'] = round(line['credit'] / line_qty, - dp.precision_get('Account')) - for analytic_line_tuple in line['analytic_line_ids']: + line_qty = line.get("quantity") + line["quantity"] = round( + line["quantity"] / line_qty, dp.precision_get("Account") + ) + line["debit"] = round( + line["debit"] / line_qty, dp.precision_get("Account") + ) + line["credit"] = round( + line["credit"] / line_qty, dp.precision_get("Account") + ) + for analytic_line_tuple in line["analytic_line_ids"]: analytic_line = analytic_line_tuple[2] - analytic_line['amount'] = round( - analytic_line['amount'] / line_qty, - dp.precision_get('Account')) - analytic_line['unit_amount'] = round( - analytic_line['unit_amount'] / line_qty, 2) + analytic_line["amount"] = round( + analytic_line["amount"] / line_qty, + dp.precision_get("Account"), + ) + analytic_line["unit_amount"] = round( + analytic_line["unit_amount"] / line_qty, 2 + ) line_to_create = line_qty while line_to_create > 1: line_to_create -= 1 @@ -43,29 +48,31 @@ class AccountInvoice(models.Model): new_lines.append(new_line) # Compute rounding difference and apply it on the first # line - line['quantity'] += round( - origin_line['quantity'] - line['quantity'] * line_qty, - 2) - line['debit'] += round( - origin_line['debit'] - line['debit'] * line_qty, - dp.precision_get('Account')) - line['credit'] += round( - origin_line['credit'] - line['credit'] * line_qty, - dp.precision_get('Account')) + line["quantity"] += round( + origin_line["quantity"] - line["quantity"] * line_qty, 2 + ) + line["debit"] += round( + origin_line["debit"] - line["debit"] * line_qty, + dp.precision_get("Account"), + ) + line["credit"] += round( + origin_line["credit"] - line["credit"] * line_qty, + dp.precision_get("Account"), + ) i = 0 - for analytic_line_tuple in line['analytic_line_ids']: + for analytic_line_tuple in line["analytic_line_ids"]: analytic_line = analytic_line_tuple[2] - origin_analytic_line = \ - origin_line['analytic_line_ids'][i][2] - analytic_line['amount'] += round( - origin_analytic_line['amount'] - analytic_line[ - 'amount'] * line_qty, - dp.precision_get('Account')) - analytic_line['unit_amount'] += round( - origin_analytic_line['unit_amount'] - - analytic_line[ - 'unit_amount'] * line_qty, - dp.precision_get('Account')) + origin_analytic_line = origin_line["analytic_line_ids"][i][2] + analytic_line["amount"] += round( + origin_analytic_line["amount"] + - analytic_line["amount"] * line_qty, + dp.precision_get("Account"), + ) + analytic_line["unit_amount"] += round( + origin_analytic_line["unit_amount"] + - analytic_line["unit_amount"] * line_qty, + dp.precision_get("Account"), + ) i += 1 move_lines.extend(new_lines) return move_lines @@ -74,21 +81,21 @@ class AccountInvoice(models.Model): def action_move_create(self): res = super().action_move_create() for inv in self: - assets = inv.move_id.line_ids.mapped('asset_id') + assets = inv.move_id.line_ids.mapped("asset_id") for asset in assets: asset.code = inv.move_name asset_line_name = asset._get_depreciation_entry_name(0) asset.depreciation_line_ids[0].with_context( - {'allow_asset_line_update': True} + {"allow_asset_line_update": True} ).name = asset_line_name return res @api.multi def action_cancel(self): - assets = self.env['account.asset'] + assets = self.env["account.asset"] for inv in self: move = inv.move_id - assets |= move.line_ids.mapped('asset_id') + assets |= move.line_ids.mapped("asset_id") super().action_cancel() if assets: assets.unlink() @@ -97,47 +104,47 @@ class AccountInvoice(models.Model): @api.model def line_get_convert(self, line, part): res = super().line_get_convert(line, part) - if line.get('asset_profile_id'): + if line.get("asset_profile_id"): # skip empty debit/credit - if res.get('debit') or res.get('credit'): - res['asset_profile_id'] = line['asset_profile_id'] + if res.get("debit") or res.get("credit"): + res["asset_profile_id"] = line["asset_profile_id"] return res @api.model def inv_line_characteristic_hashcode(self, invoice_line): - res = super().inv_line_characteristic_hashcode( - invoice_line) - res += '-%s' % invoice_line.get('asset_profile_id', 'False') + res = super().inv_line_characteristic_hashcode(invoice_line) + res += "-%s" % invoice_line.get("asset_profile_id", "False") return res @api.model def invoice_line_move_line_get(self): res = super().invoice_line_move_line_get() - invoice_line_obj = self.env['account.invoice.line'] + invoice_line_obj = self.env["account.invoice.line"] for vals in res: - if vals.get('invl_id'): - invline = invoice_line_obj.browse(vals['invl_id']) + if vals.get("invl_id"): + invline = invoice_line_obj.browse(vals["invl_id"]) if invline.asset_profile_id: - vals['asset_profile_id'] = invline.asset_profile_id.id + vals["asset_profile_id"] = invline.asset_profile_id.id return res class AccountInvoiceLine(models.Model): - _inherit = 'account.invoice.line' + _inherit = "account.invoice.line" asset_profile_id = fields.Many2one( - comodel_name='account.asset.profile', - string='Asset Profile') + comodel_name="account.asset.profile", string="Asset Profile" + ) asset_id = fields.Many2one( - comodel_name='account.asset', - string='Asset', - domain=[('state', 'in', ['open', 'close'])], + comodel_name="account.asset", + string="Asset", + domain=[("state", "in", ["open", "close"])], help="Complete this field when selling an asset " - "in order to facilitate the creation of the " - "asset removal accounting entries via the " - "asset 'Removal' button") + "in order to facilitate the creation of the " + "asset removal accounting entries via the " + "asset 'Removal' button", + ) - @api.onchange('account_id') + @api.onchange("account_id") def _onchange_account_id(self): self.asset_profile_id = self.account_id.asset_profile_id.id return super()._onchange_account_id() diff --git a/account_asset_management/models/account_move.py b/account_asset_management/models/account_move.py index e9cb04b1..e1c50d2b 100644 --- a/account_asset_management/models/account_move.py +++ b/account_asset_management/models/account_move.py @@ -3,123 +3,136 @@ import logging -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError _logger = logging.getLogger(__name__) # List of move's fields that can't be modified if move is linked # with a depreciation line -FIELDS_AFFECTS_ASSET_MOVE = set(['journal_id', 'date']) +FIELDS_AFFECTS_ASSET_MOVE = {"journal_id", "date"} # List of move line's fields that can't be modified if move is linked # with a depreciation line -FIELDS_AFFECTS_ASSET_MOVE_LINE = \ - set(['credit', 'debit', 'account_id', 'journal_id', 'date', - 'asset_profile_id', 'asset_id']) +FIELDS_AFFECTS_ASSET_MOVE_LINE = { + "credit", + "debit", + "account_id", + "journal_id", + "date", + "asset_profile_id", + "asset_id", +} class AccountMove(models.Model): - _inherit = 'account.move' + _inherit = "account.move" @api.multi def unlink(self): # for move in self: - deprs = self.env['account.asset.line'].search( - [('move_id', 'in', self.ids), - ('type', 'in', ['depreciate', 'remove'])]) - if deprs and not self.env.context.get('unlink_from_asset'): + deprs = self.env["account.asset.line"].search( + [("move_id", "in", self.ids), ("type", "in", ["depreciate", "remove"])] + ) + if deprs and not self.env.context.get("unlink_from_asset"): raise UserError( - _("You are not allowed to remove an accounting entry " - "linked to an asset." - "\nYou should remove such entries from the asset.")) + _( + "You are not allowed to remove an accounting entry " + "linked to an asset." + "\nYou should remove such entries from the asset." + ) + ) # trigger store function - deprs.write({'move_id': False}) + deprs.write({"move_id": False}) return super().unlink() @api.multi def write(self, vals): if set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE): - deprs = self.env['account.asset.line'].search( - [('move_id', 'in', self.ids), ('type', '=', 'depreciate')]) + deprs = self.env["account.asset.line"].search( + [("move_id", "in", self.ids), ("type", "=", "depreciate")] + ) if deprs: raise UserError( - _("You cannot change an accounting entry " - "linked to an asset depreciation line.")) + _( + "You cannot change an accounting entry " + "linked to an asset depreciation line." + ) + ) return super().write(vals) class AccountMoveLine(models.Model): - _inherit = 'account.move.line' + _inherit = "account.move.line" asset_profile_id = fields.Many2one( - comodel_name='account.asset.profile', - string='Asset Profile') + comodel_name="account.asset.profile", string="Asset Profile" + ) asset_id = fields.Many2one( - comodel_name='account.asset', - string='Asset', ondelete='restrict') + comodel_name="account.asset", string="Asset", ondelete="restrict" + ) - @api.onchange('account_id') + @api.onchange("account_id") def _onchange_account_id(self): self.asset_profile_id = self.account_id.asset_profile_id @api.model def create(self, vals): - if vals.get('asset_id') and not self.env.context.get('allow_asset'): + if vals.get("asset_id") and not self.env.context.get("allow_asset"): raise UserError( - _("You are not allowed to link " - "an accounting entry to an asset." - "\nYou should generate such entries from the asset.")) - if vals.get('asset_profile_id'): + _( + "You are not allowed to link " + "an accounting entry to an asset." + "\nYou should generate such entries from the asset." + ) + ) + if vals.get("asset_profile_id"): # create asset - asset_obj = self.env['account.asset'] - move = self.env['account.move'].browse(vals['move_id']) - depreciation_base = vals['debit'] or -vals['credit'] + asset_obj = self.env["account.asset"] + move = self.env["account.move"].browse(vals["move_id"]) + depreciation_base = vals["debit"] or -vals["credit"] temp_vals = { - 'name': vals['name'], - 'profile_id': vals['asset_profile_id'], - 'purchase_value': depreciation_base, - 'partner_id': vals['partner_id'], - 'date_start': move.date, + "name": vals["name"], + "profile_id": vals["asset_profile_id"], + "purchase_value": depreciation_base, + "partner_id": vals["partner_id"], + "date_start": move.date, } - if self.env.context.get('company_id'): - temp_vals['company_id'] = self.env.context['company_id'] + if self.env.context.get("company_id"): + temp_vals["company_id"] = self.env.context["company_id"] temp_asset = asset_obj.new(temp_vals) temp_asset._onchange_profile_id() asset_vals = temp_asset._convert_to_write(temp_asset._cache) self._get_asset_analytic_values(vals, asset_vals) asset = asset_obj.with_context( - create_asset_from_move_line=True, - move_id=vals['move_id']).create(asset_vals) - vals['asset_id'] = asset.id + create_asset_from_move_line=True, move_id=vals["move_id"] + ).create(asset_vals) + vals["asset_id"] = asset.id return super().create(vals) @api.multi def _prepare_asset_create(self, vals): self.ensure_one() - debit = 'debit' in vals and vals.get('debit', 0.0) or self.debit - credit = 'credit' in vals and \ - vals.get('credit', 0.0) or self.credit + debit = "debit" in vals and vals.get("debit", 0.0) or self.debit + credit = "credit" in vals and vals.get("credit", 0.0) or self.credit depreciation_base = debit - credit - partner_id = 'partner' in vals and \ - vals.get('partner', False) or self.partner_id.id - date_start = 'date' in vals and \ - vals.get('date', False) or self.date + partner_id = ( + "partner" in vals and vals.get("partner", False) or self.partner_id.id + ) + date_start = "date" in vals and vals.get("date", False) or self.date return { - 'name': vals.get('name') or self.name, - 'profile_id': vals['asset_profile_id'], - 'purchase_value': depreciation_base, - 'partner_id': partner_id, - 'date_start': date_start, - 'company_id': vals.get('company_id') or self.company_id.id, + "name": vals.get("name") or self.name, + "profile_id": vals["asset_profile_id"], + "purchase_value": depreciation_base, + "partner_id": partner_id, + "date_start": date_start, + "company_id": vals.get("company_id") or self.company_id.id, } @api.multi def write(self, vals): - if ( - set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE_LINE) and - not ( - self.env.context.get('allow_asset_removal') and - list(vals.keys()) == ['asset_id']) + if set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE_LINE) and not ( + self.env.context.get("allow_asset_removal") + and list(vals.keys()) == ["asset_id"] ): # Check if at least one asset is linked to a move linked_asset = False @@ -127,43 +140,49 @@ class AccountMoveLine(models.Model): linked_asset = move.asset_id if linked_asset: raise UserError( - _("You cannot change an accounting item " - "linked to an asset depreciation line.")) - if vals.get('asset_id'): + _( + "You cannot change an accounting item " + "linked to an asset depreciation line." + ) + ) + if vals.get("asset_id"): raise UserError( - _("You are not allowed to link " - "an accounting entry to an asset." - "\nYou should generate such entries from the asset.")) - if vals.get('asset_profile_id'): + _( + "You are not allowed to link " + "an accounting entry to an asset." + "\nYou should generate such entries from the asset." + ) + ) + if vals.get("asset_profile_id"): if len(self) == 1: - raise AssertionError(_( - 'This option should only be used for a single id at a ' - 'time.')) - asset_obj = self.env['account.asset'] + raise AssertionError( + _("This option should only be used for a single id at a " "time.") + ) + asset_obj = self.env["account.asset"] for aml in self: - if vals['asset_profile_id'] == aml.asset_profile_id.id: + if vals["asset_profile_id"] == aml.asset_profile_id.id: continue # create asset asset_vals = aml._prepare_asset_create(vals) self._play_onchange_profile_id(asset_vals) self._get_asset_analytic_values(vals, asset_vals) asset = asset_obj.with_context( - create_asset_from_move_line=True, - move_id=aml.move_id.id).create(asset_vals) - vals['asset_id'] = asset.id + create_asset_from_move_line=True, move_id=aml.move_id.id + ).create(asset_vals) + vals["asset_id"] = asset.id return super().write(vals) @api.model def _get_asset_analytic_values(self, vals, asset_vals): - asset_vals['account_analytic_id'] = vals.get( - 'analytic_account_id', False) + asset_vals["account_analytic_id"] = vals.get("analytic_account_id", False) @api.model def _play_onchange_profile_id(self, vals): - asset_obj = self.env['account.asset'] + asset_obj = self.env["account.asset"] asset_temp = asset_obj.new(vals) asset_temp._onchange_profile_id() for field in asset_temp._fields: if field not in vals and asset_temp[field]: - vals[field] = asset_temp._fields[field].\ - convert_to_write(asset_temp[field], asset_temp) + vals[field] = asset_temp._fields[field].convert_to_write( + asset_temp[field], asset_temp + ) diff --git a/account_asset_management/models/res_config_settings.py b/account_asset_management/models/res_config_settings.py index 2842e4c2..55b0942c 100644 --- a/account_asset_management/models/res_config_settings.py +++ b/account_asset_management/models/res_config_settings.py @@ -5,12 +5,13 @@ from odoo import fields, models class Config(models.TransientModel): - _inherit = 'res.config.settings' + _inherit = "res.config.settings" module_account_asset_management = fields.Boolean( - string='Assets management (OCA)', + string="Assets management (OCA)", help="""This allows you to manage the assets owned by a company or a person. It keeps track of the depreciation occurred on those assets, and creates account move for those depreciation lines. - This installs the module account_asset_management.""") + This installs the module account_asset_management.""", + ) diff --git a/account_asset_management/tests/test_account_asset_management.py b/account_asset_management/tests/test_account_asset_management.py index 46de4edd..f5458724 100644 --- a/account_asset_management/tests/test_account_asset_management.py +++ b/account_asset_management/tests/test_account_asset_management.py @@ -3,128 +3,154 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import calendar -from datetime import date, datetime import time +from datetime import date, datetime -from odoo.tests.common import SavepointCase from odoo import tools from odoo.modules.module import get_resource_path +from odoo.tests.common import SavepointCase class TestAssetManagement(SavepointCase): - @classmethod def _load(cls, module, *args): - tools.convert_file(cls.cr, module, - get_resource_path(module, *args), - {}, 'init', False, 'test', - cls.registry._assertion_report) + tools.convert_file( + cls.cr, + module, + get_resource_path(module, *args), + {}, + "init", + False, + "test", + cls.registry._assertion_report, + ) @classmethod def setUpClass(cls): super().setUpClass() - cls._load('account', 'test', 'account_minimal_test.xml') - cls._load('account_asset_management', 'tests', - 'account_asset_test_data.xml') + cls._load("account", "test", "account_minimal_test.xml") + cls._load("account_asset_management", "tests", "account_asset_test_data.xml") # ENVIRONEMENTS - cls.asset_model = cls.env['account.asset'] - cls.dl_model = cls.env['account.asset.line'] - cls.remove_model = cls.env['account.asset.remove'] - cls.account_invoice = cls.env['account.invoice'] - cls.account_move_line = cls.env['account.move.line'] - cls.account_account = cls.env['account.account'] - cls.account_journal = cls.env['account.journal'] - cls.account_invoice_line = cls.env['account.invoice.line'] + cls.asset_model = cls.env["account.asset"] + cls.dl_model = cls.env["account.asset.line"] + cls.remove_model = cls.env["account.asset.remove"] + cls.account_invoice = cls.env["account.invoice"] + cls.account_move_line = cls.env["account.move.line"] + cls.account_account = cls.env["account.account"] + cls.account_journal = cls.env["account.journal"] + cls.account_invoice_line = cls.env["account.invoice.line"] # INSTANCES # Instance: company - cls.company = cls.env.ref('base.main_company') + cls.company = cls.env.ref("base.main_company") # Instance: account type (receivable) - cls.type_recv = cls.env.ref('account.data_account_type_receivable') + cls.type_recv = cls.env.ref("account.data_account_type_receivable") # Instance: account type (payable) - cls.type_payable = cls.env.ref('account.data_account_type_payable') + cls.type_payable = cls.env.ref("account.data_account_type_payable") # Instance: account (receivable) - cls.account_recv = cls.account_account.create({ - 'name': 'test_account_receivable', - 'code': '123', - 'user_type_id': cls.type_recv.id, - 'company_id': cls.company.id, - 'reconcile': True}) + cls.account_recv = cls.account_account.create( + { + "name": "test_account_receivable", + "code": "123", + "user_type_id": cls.type_recv.id, + "company_id": cls.company.id, + "reconcile": True, + } + ) # Instance: account (payable) - cls.account_payable = cls.account_account.create({ - 'name': 'test_account_payable', - 'code': '321', - 'user_type_id': cls.type_payable.id, - 'company_id': cls.company.id, - 'reconcile': True}) + cls.account_payable = cls.account_account.create( + { + "name": "test_account_payable", + "code": "321", + "user_type_id": cls.type_payable.id, + "company_id": cls.company.id, + "reconcile": True, + } + ) # Instance: partner - cls.partner = cls.env.ref('base.res_partner_2') + cls.partner = cls.env.ref("base.res_partner_2") # Instance: journal - cls.journal = cls.account_journal.search( - [('type', '=', 'purchase')])[0] + cls.journal = cls.account_journal.search([("type", "=", "purchase")])[0] # Instance: product - cls.product = cls.env.ref('product.product_product_4') + cls.product = cls.env.ref("product.product_product_4") # Instance: invoice line - cls.invoice_line = cls.account_invoice_line.create({ - 'name': 'test', - 'account_id': cls.account_payable.id, - 'price_unit': 2000.00, - 'quantity': 1, - 'product_id': cls.product.id}) + cls.invoice_line = cls.account_invoice_line.create( + { + "name": "test", + "account_id": cls.account_payable.id, + "price_unit": 2000.00, + "quantity": 1, + "product_id": cls.product.id, + } + ) # Instance: invoice - cls.invoice = cls.account_invoice.create({ - 'partner_id': cls.partner.id, - 'account_id': cls.account_recv.id, - 'journal_id': cls.journal.id, - 'invoice_line_ids': [(4, cls.invoice_line.id)]}) + cls.invoice = cls.account_invoice.create( + { + "partner_id": cls.partner.id, + "account_id": cls.account_recv.id, + "journal_id": cls.journal.id, + "invoice_line_ids": [(4, cls.invoice_line.id)], + } + ) - cls.invoice_line_2 = cls.account_invoice_line.create({ - 'name': 'test 2', - 'account_id': cls.account_payable.id, - 'price_unit': 10000.00, - 'quantity': 1, - 'product_id': cls.product.id}) - cls.invoice_line_3 = cls.account_invoice_line.create({ - 'name': 'test 3', - 'account_id': cls.account_payable.id, - 'price_unit': 20000.00, - 'quantity': 1, - 'product_id': cls.product.id}) + cls.invoice_line_2 = cls.account_invoice_line.create( + { + "name": "test 2", + "account_id": cls.account_payable.id, + "price_unit": 10000.00, + "quantity": 1, + "product_id": cls.product.id, + } + ) + cls.invoice_line_3 = cls.account_invoice_line.create( + { + "name": "test 3", + "account_id": cls.account_payable.id, + "price_unit": 20000.00, + "quantity": 1, + "product_id": cls.product.id, + } + ) - cls.invoice_2 = cls.account_invoice.create({ - 'partner_id': cls.partner.id, - 'account_id': cls.account_recv.id, - 'journal_id': cls.journal.id, - 'invoice_line_ids': [(4, cls.invoice_line_2.id), - (4, cls.invoice_line_3.id)]}) + cls.invoice_2 = cls.account_invoice.create( + { + "partner_id": cls.partner.id, + "account_id": cls.account_recv.id, + "journal_id": cls.journal.id, + "invoice_line_ids": [ + (4, cls.invoice_line_2.id), + (4, cls.invoice_line_3.id), + ], + } + ) def test_01_nonprorata_basic(self): """Basic tests of depreciation board computations and postings.""" # # first load demo assets and do some sanity checks # - ict0 = self.browse_ref('account_asset_management.' - 'account_asset_asset_ict0') - self.assertEqual(ict0.state, 'draft') + ict0 = self.browse_ref("account_asset_management." "account_asset_asset_ict0") + self.assertEqual(ict0.state, "draft") self.assertEqual(ict0.purchase_value, 1500) self.assertEqual(ict0.salvage_value, 0) self.assertEqual(ict0.depreciation_base, 1500) self.assertEqual(len(ict0.depreciation_line_ids), 1) - vehicle0 = self.browse_ref('account_asset_management.' - 'account_asset_asset_vehicle0') - self.assertEqual(vehicle0.state, 'draft') + vehicle0 = self.browse_ref( + "account_asset_management." "account_asset_asset_vehicle0" + ) + self.assertEqual(vehicle0.state, "draft") self.assertEqual(vehicle0.purchase_value, 12000) self.assertEqual(vehicle0.salvage_value, 2000) self.assertEqual(vehicle0.depreciation_base, 10000) @@ -148,305 +174,320 @@ class TestAssetManagement(SavepointCase): ict0.validate() ict0.depreciation_line_ids[1].create_move() ict0.refresh() - self.assertEqual(ict0.state, 'open') + self.assertEqual(ict0.state, "open") self.assertEqual(ict0.value_depreciated, 500) self.assertEqual(ict0.value_residual, 1000) vehicle0.validate() vehicle0.depreciation_line_ids[1].create_move() vehicle0.refresh() - self.assertEqual(vehicle0.state, 'open') + self.assertEqual(vehicle0.state, "open") self.assertEqual(vehicle0.value_depreciated, 2000) self.assertEqual(vehicle0.value_residual, 8000) def test_02_prorata_basic(self): """Prorata temporis depreciation basic test.""" - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 3333, - 'salvage_value': 0, - 'date_start': time.strftime('%Y-07-07'), - 'method_time': 'year', - 'method_number': 5, - 'method_period': 'month', - 'prorata': True, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 3333, + "salvage_value": 0, + "date_start": time.strftime("%Y-07-07"), + "method_time": "year", + "method_number": 5, + "method_period": "month", + "prorata": True, + } + ) asset.compute_depreciation_board() asset.refresh() if calendar.isleap(date.today().year): - self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, - 46.44, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[1].amount, 46.44, places=2 + ) else: - self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, - 47.33, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, - 55.55, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, - 55.55, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, - 55.55, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[5].amount, - 55.55, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[6].amount, - 55.55, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[1].amount, 47.33, places=2 + ) + self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, 55.55, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 55.55, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, 55.55, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[5].amount, 55.55, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[6].amount, 55.55, places=2) if calendar.isleap(date.today().year): - self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, - 9.11, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[-1].amount, 9.11, places=2 + ) else: - self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, - 8.22, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[-1].amount, 8.22, places=2 + ) def test_03_proprata_init_prev_year(self): """Prorata temporis depreciation with init value in prev year.""" # I create an asset in current year - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 3333, - 'salvage_value': 0, - 'date_start': '%d-07-07' % (datetime.now().year - 1,), - 'method_time': 'year', - 'method_number': 5, - 'method_period': 'month', - 'prorata': True, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 3333, + "salvage_value": 0, + "date_start": "%d-07-07" % (datetime.now().year - 1,), + "method_time": "year", + "method_number": 5, + "method_period": "month", + "prorata": True, + } + ) # I create a initial depreciation line in previous year - self.dl_model.create({ - 'asset_id': asset.id, - 'amount': 325.08, - 'line_date': '%d-12-31' % (datetime.now().year - 1,), - 'type': 'depreciate', - 'init_entry': True, - }) + self.dl_model.create( + { + "asset_id": asset.id, + "amount": 325.08, + "line_date": "%d-12-31" % (datetime.now().year - 1,), + "type": "depreciate", + "init_entry": True, + } + ) self.assertEqual(len(asset.depreciation_line_ids), 2) asset.compute_depreciation_board() asset.refresh() # I check the depreciated value is the initial value - self.assertAlmostEqual(asset.value_depreciated, 325.08, - places=2) + self.assertAlmostEqual(asset.value_depreciated, 325.08, places=2) # I check computed values in the depreciation board - self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 55.55, - places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 55.55, places=2) if calendar.isleap(date.today().year - 1): # for leap years the first year depreciation amount of 325.08 # is too high and hence a correction is applied to the next # entry of the table - self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, - 54.66, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, - 55.55, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, - 9.11, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[2].amount, 54.66, places=2 + ) + self.assertAlmostEqual( + asset.depreciation_line_ids[3].amount, 55.55, places=2 + ) + self.assertAlmostEqual( + asset.depreciation_line_ids[-1].amount, 9.11, places=2 + ) else: - self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, - 55.55, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, - 8.22, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[2].amount, 55.55, places=2 + ) + self.assertAlmostEqual( + asset.depreciation_line_ids[-1].amount, 8.22, places=2 + ) def test_04_prorata_init_cur_year(self): """Prorata temporis depreciation with init value in curent year.""" - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 3333, - 'salvage_value': 0, - 'date_start': time.strftime('%Y-07-07'), - 'method_time': 'year', - 'method_number': 5, - 'method_period': 'month', - 'prorata': True, - }) - self.dl_model.create({ - 'asset_id': asset.id, - 'amount': 279.44, - 'line_date': time.strftime('%Y-11-30'), - 'type': 'depreciate', - 'init_entry': True, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 3333, + "salvage_value": 0, + "date_start": time.strftime("%Y-07-07"), + "method_time": "year", + "method_number": 5, + "method_period": "month", + "prorata": True, + } + ) + self.dl_model.create( + { + "asset_id": asset.id, + "amount": 279.44, + "line_date": time.strftime("%Y-11-30"), + "type": "depreciate", + "init_entry": True, + } + ) self.assertEqual(len(asset.depreciation_line_ids), 2) asset.compute_depreciation_board() asset.refresh() # I check the depreciated value is the initial value - self.assertAlmostEqual(asset.value_depreciated, 279.44, - places=2) + self.assertAlmostEqual(asset.value_depreciated, 279.44, places=2) # I check computed values in the depreciation board if calendar.isleap(date.today().year): - self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, - 44.75, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[2].amount, 44.75, places=2 + ) else: - self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, - 45.64, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, - 55.55, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[2].amount, 45.64, places=2 + ) + self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 55.55, places=2) if calendar.isleap(date.today().year): - self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, - 9.11, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[-1].amount, 9.11, places=2 + ) else: - self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, - 8.22, places=2) + self.assertAlmostEqual( + asset.depreciation_line_ids[-1].amount, 8.22, places=2 + ) def test_05_degressive_linear(self): """Degressive-Linear with annual and quarterly depreciation.""" # annual depreciation - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 1000, - 'salvage_value': 0, - 'date_start': time.strftime('%Y-07-07'), - 'method_time': 'year', - 'method': 'degr-linear', - 'method_progress_factor': 0.40, - 'method_number': 5, - 'method_period': 'year', - 'prorata': False, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 1000, + "salvage_value": 0, + "date_start": time.strftime("%Y-07-07"), + "method_time": "year", + "method": "degr-linear", + "method_progress_factor": 0.40, + "method_number": 5, + "method_period": "year", + "prorata": False, + } + ) asset.compute_depreciation_board() asset.refresh() # check values in the depreciation board self.assertEqual(len(asset.depreciation_line_ids), 5) - self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, - 400.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, - 240.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, - 200.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, - 160.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 400.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, 240.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 200.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, 160.00, places=2) # quarterly depreciation - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 1000, - 'salvage_value': 0, - 'date_start': time.strftime('%Y-07-07'), - 'method_time': 'year', - 'method': 'degr-linear', - 'method_progress_factor': 0.40, - 'method_number': 5, - 'method_period': 'quarter', - 'prorata': False, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 1000, + "salvage_value": 0, + "date_start": time.strftime("%Y-07-07"), + "method_time": "year", + "method": "degr-linear", + "method_progress_factor": 0.40, + "method_number": 5, + "method_period": "quarter", + "prorata": False, + } + ) asset.compute_depreciation_board() asset.refresh() # check values in the depreciation board self.assertEqual(len(asset.depreciation_line_ids), 15) # lines prior to asset start period are grouped in the first entry - self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, - 300.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, - 60.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[7].amount, - 50.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[13].amount, - 40.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 300.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 60.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[7].amount, 50.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[13].amount, 40.00, places=2) def test_06_degressive_limit(self): """Degressive with annual depreciation.""" - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 1000, - 'salvage_value': 100, - 'date_start': time.strftime('%Y-07-07'), - 'method_time': 'year', - 'method': 'degr-limit', - 'method_progress_factor': 0.40, - 'method_number': 5, - 'method_period': 'year', - 'prorata': False, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 1000, + "salvage_value": 100, + "date_start": time.strftime("%Y-07-07"), + "method_time": "year", + "method": "degr-limit", + "method_progress_factor": 0.40, + "method_number": 5, + "method_period": "year", + "prorata": False, + } + ) asset.compute_depreciation_board() asset.refresh() # check values in the depreciation board self.assertEqual(len(asset.depreciation_line_ids), 6) - self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, - 400.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, - 240.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, - 144.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, - 86.40, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[5].amount, - 29.60, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 400.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, 240.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[3].amount, 144.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[4].amount, 86.40, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[5].amount, 29.60, places=2) def test_07_linear_limit(self): """Degressive with annual depreciation.""" - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 1000, - 'salvage_value': 100, - 'date_start': time.strftime('%Y-07-07'), - 'method_time': 'year', - 'method': 'linear-limit', - 'method_number': 5, - 'method_period': 'year', - 'prorata': False, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 1000, + "salvage_value": 100, + "date_start": time.strftime("%Y-07-07"), + "method_time": "year", + "method": "linear-limit", + "method_number": 5, + "method_period": "year", + "prorata": False, + } + ) asset.compute_depreciation_board() asset.refresh() # check values in the depreciation board self.assertEqual(len(asset.depreciation_line_ids), 6) - self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, - 200.00, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, - 100.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 200.00, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, 100.00, places=2) def test_08_asset_removal(self): """Asset removal""" - asset = self.asset_model.create({ - 'name': 'test asset removal', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 5000, - 'salvage_value': 0, - 'date_start': '2019-01-01', - 'method_time': 'year', - 'method_number': 5, - 'method_period': 'quarter', - 'prorata': False, - }) + asset = self.asset_model.create( + { + "name": "test asset removal", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 5000, + "salvage_value": 0, + "date_start": "2019-01-01", + "method_time": "year", + "method_number": 5, + "method_period": "quarter", + "prorata": False, + } + ) asset.compute_depreciation_board() asset.validate() - wiz_ctx = { - 'active_id': asset.id, - 'early_removal': True, - } - wiz = self.remove_model.with_context(wiz_ctx).create({ - 'date_remove': '2019-01-31', - 'sale_value': 0.0, - 'posting_regime': 'gain_loss_on_sale', - 'account_plus_value_id': self.ref('account.a_sale'), - 'account_min_value_id': self.ref('account.a_expense'), - }) + wiz_ctx = {"active_id": asset.id, "early_removal": True} + wiz = self.remove_model.with_context(wiz_ctx).create( + { + "date_remove": "2019-01-31", + "sale_value": 0.0, + "posting_regime": "gain_loss_on_sale", + "account_plus_value_id": self.ref("account.a_sale"), + "account_min_value_id": self.ref("account.a_expense"), + } + ) wiz.remove() asset.refresh() self.assertEqual(len(asset.depreciation_line_ids), 3) - self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, - 81.46, places=2) - self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, - 4918.54, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, 81.46, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, 4918.54, places=2) def test_09_asset_from_invoice(self): - all_asset = self.env['account.asset'].search([]) + all_asset = self.env["account.asset"].search([]) invoice = self.invoice asset_profile = self.env.ref( - 'account_asset_management.account_asset_profile_car_5Y') + "account_asset_management.account_asset_profile_car_5Y" + ) asset_profile.asset_product_item = False self.assertTrue(len(invoice.invoice_line_ids) > 0) line = invoice.invoice_line_ids[0] @@ -455,21 +496,22 @@ class TestAssetManagement(SavepointCase): line.asset_profile_id = asset_profile invoice.action_invoice_open() # I get all asset after invoice validation - current_asset = self.env['account.asset'].search([]) + current_asset = self.env["account.asset"].search([]) # I get the new asset new_asset = current_asset - all_asset # I check that a new asset is created self.assertEqual(len(new_asset), 1) # I check that the new asset has the correct purchase value - self.assertAlmostEqual(new_asset.purchase_value, - -line.price_unit * line.quantity, - places=2) + self.assertAlmostEqual( + new_asset.purchase_value, -line.price_unit * line.quantity, places=2 + ) def test_10_asset_from_invoice_product_item(self): - all_asset = self.env['account.asset'].search([]) + all_asset = self.env["account.asset"].search([]) invoice = self.invoice asset_profile = self.env.ref( - 'account_asset_management.account_asset_profile_car_5Y') + "account_asset_management.account_asset_profile_car_5Y" + ) asset_profile.asset_product_item = True self.assertTrue(len(invoice.invoice_line_ids) > 0) line = invoice.invoice_line_ids[0] @@ -478,123 +520,134 @@ class TestAssetManagement(SavepointCase): line.asset_profile_id = asset_profile invoice.action_invoice_open() # I get all asset after invoice validation - current_asset = self.env['account.asset'].search([]) + current_asset = self.env["account.asset"].search([]) # I get the new asset new_asset = current_asset - all_asset # I check that a new asset is created self.assertEqual(len(new_asset), line.quantity) for asset in new_asset: # I check that the new asset has the correct purchase value - self.assertAlmostEqual( - asset.purchase_value, -line.price_unit, places=2) + self.assertAlmostEqual(asset.purchase_value, -line.price_unit, places=2) def test_11_assets_from_invoice(self): - all_assets = self.env['account.asset'].search([]) + all_assets = self.env["account.asset"].search([]) invoice = self.invoice_2 asset_profile = self.env.ref( - 'account_asset_management.account_asset_profile_car_5Y') + "account_asset_management.account_asset_profile_car_5Y" + ) asset_profile.asset_product_item = True # Compute depreciation lines on invoice validation asset_profile.open_asset = True self.assertTrue(len(invoice.invoice_line_ids) == 2) - invoice.invoice_line_ids.write({ - 'quantity': 1, - 'asset_profile_id': asset_profile.id, - }) + invoice.invoice_line_ids.write( + {"quantity": 1, "asset_profile_id": asset_profile.id} + ) invoice.action_invoice_open() # Retrieve all assets after invoice validation - current_assets = self.env['account.asset'].search([]) + current_assets = self.env["account.asset"].search([]) # What are the new assets? new_assets = current_assets - all_assets self.assertEqual(len(new_assets), 2) for asset in new_assets: dlines = asset.depreciation_line_ids.filtered( - lambda l: l.type == 'depreciate') + lambda l: l.type == "depreciate" + ) dlines = dlines.sorted(key=lambda l: l.line_date) self.assertAlmostEqual(dlines[0].depreciated_value, 0.0) self.assertAlmostEqual(dlines[-1].remaining_value, 0.0) def test_12_prorata_days_calc(self): """Prorata temporis depreciation with days calc option.""" - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 3333, - 'salvage_value': 0, - 'date_start': '2019-07-07', - 'method_time': 'year', - 'method_number': 5, - 'method_period': 'month', - 'prorata': True, - 'days_calc': True, - 'use_leap_years': False, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 3333, + "salvage_value": 0, + "date_start": "2019-07-07", + "method_time": "year", + "method_number": 5, + "method_period": "month", + "prorata": True, + "days_calc": True, + "use_leap_years": False, + } + ) asset.compute_depreciation_board() asset.refresh() day_rate = 3333 / 1827 # 3333 / 1827 depreciation days for i in range(1, 10): self.assertAlmostEqual( asset.depreciation_line_ids[i].amount, - asset.depreciation_line_ids[i].line_days * day_rate, places=2) + asset.depreciation_line_ids[i].line_days * day_rate, + places=2, + ) # Last depreciation remaining - self.assertAlmostEqual( - asset.depreciation_line_ids[-1].amount, 11.05, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, 11.05, places=2) def test_13_use_leap_year(self): # When you use the depreciation with years method and using lap years, # the depreciation amount is calculated as 10000 / 1826 days * 365 days # = yearly depreciation amount of 1998.90. # Then 1998.90 / 12 = 166.58 - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 10000, - 'salvage_value': 0, - 'date_start': time.strftime('2019-01-01'), - 'method_time': 'year', - 'method_number': 5, - 'method_period': 'month', - 'prorata': False, - 'days_calc': False, - 'use_leap_years': True, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 10000, + "salvage_value": 0, + "date_start": time.strftime("2019-01-01"), + "method_time": "year", + "method_number": 5, + "method_period": "month", + "prorata": False, + "days_calc": False, + "use_leap_years": True, + } + ) asset.compute_depreciation_board() asset.refresh() for i in range(2, 11): self.assertAlmostEqual( - asset.depreciation_line_ids[i].amount, 166.58, places=2) + asset.depreciation_line_ids[i].amount, 166.58, places=2 + ) self.assertAlmostEqual( - asset.depreciation_line_ids[13].depreciated_value, 1998.90, - places=2) + asset.depreciation_line_ids[13].depreciated_value, 1998.90, places=2 + ) def test_14_not_use_leap_year(self): # When you run a depreciation with method = 'year' and no not use # lap years you divide 1000 / 5 years = 2000, then divided by 12 months # to get 166.67 per month, equal for all periods. - asset = self.asset_model.create({ - 'name': 'test asset', - 'profile_id': self.ref('account_asset_management.' - 'account_asset_profile_car_5Y'), - 'purchase_value': 10000, - 'salvage_value': 0, - 'date_start': time.strftime('2019-01-01'), - 'method_time': 'year', - 'method_number': 5, - 'method_period': 'month', - 'prorata': False, - 'days_calc': False, - 'use_leap_years': False, - }) + asset = self.asset_model.create( + { + "name": "test asset", + "profile_id": self.ref( + "account_asset_management." "account_asset_profile_car_5Y" + ), + "purchase_value": 10000, + "salvage_value": 0, + "date_start": time.strftime("2019-01-01"), + "method_time": "year", + "method_number": 5, + "method_period": "month", + "prorata": False, + "days_calc": False, + "use_leap_years": False, + } + ) asset.compute_depreciation_board() asset.refresh() for i in range(1, 11): self.assertAlmostEqual( - asset.depreciation_line_ids[1].amount, 166.67, places=2) + asset.depreciation_line_ids[1].amount, 166.67, places=2 + ) # In the last month of the fiscal year we compensate for the small # deviations if that is necessary. - self.assertAlmostEqual( - asset.depreciation_line_ids[12].amount, 166.63, places=2) + self.assertAlmostEqual(asset.depreciation_line_ids[12].amount, 166.63, places=2) diff --git a/account_asset_management/wizard/account_asset_compute.py b/account_asset_management/wizard/account_asset_compute.py index 17c48e6f..ba22c8e0 100644 --- a/account_asset_management/wizard/account_asset_compute.py +++ b/account_asset_management/wizard/account_asset_compute.py @@ -1,65 +1,65 @@ # Copyright 2009-2018 Noviat # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models class AccountAssetCompute(models.TransientModel): - _name = 'account.asset.compute' + _name = "account.asset.compute" _description = "Compute Assets" date_end = fields.Date( - string='Date', required=True, + string="Date", + required=True, default=fields.Date.today, help="All depreciation lines prior to this date will be automatically" - " posted") + " posted", + ) note = fields.Text() @api.multi def asset_compute(self): - assets = self.env['account.asset'].search( - [('state', '=', 'open')]) + assets = self.env["account.asset"].search([("state", "=", "open")]) created_move_ids, error_log = assets._compute_entries( - self.date_end, check_triggers=True) + self.date_end, check_triggers=True + ) if error_log: - module = __name__.split('addons.')[1].split('.')[0] - result_view = self.env.ref( - '%s.%s_view_form_result' - % (module, self._table)) - self.note = _("Compute Assets errors") + ':\n' + error_log + module = __name__.split("addons.")[1].split(".")[0] + result_view = self.env.ref("{}.{}_view_form_result".format(module, self._table)) + self.note = _("Compute Assets errors") + ":\n" + error_log return { - 'name': _('Compute Assets result'), - 'res_id': self.id, - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'account.asset.compute', - 'view_id': result_view.id, - 'target': 'new', - 'type': 'ir.actions.act_window', - 'context': {'asset_move_ids': created_move_ids}, + "name": _("Compute Assets result"), + "res_id": self.id, + "view_type": "form", + "view_mode": "form", + "res_model": "account.asset.compute", + "view_id": result_view.id, + "target": "new", + "type": "ir.actions.act_window", + "context": {"asset_move_ids": created_move_ids}, } return { - 'name': _('Created Asset Moves'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move', - 'view_id': False, - 'domain': [('id', 'in', created_move_ids)], - 'type': 'ir.actions.act_window', + "name": _("Created Asset Moves"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.move", + "view_id": False, + "domain": [("id", "in", created_move_ids)], + "type": "ir.actions.act_window", } @api.multi def view_asset_moves(self): self.ensure_one() - domain = [('id', 'in', self.env.context.get('asset_move_ids', []))] + domain = [("id", "in", self.env.context.get("asset_move_ids", []))] return { - 'name': _('Created Asset Moves'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move', - 'view_id': False, - 'domain': domain, - 'type': 'ir.actions.act_window', + "name": _("Created Asset Moves"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.move", + "view_id": False, + "domain": domain, + "type": "ir.actions.act_window", } diff --git a/account_asset_management/wizard/account_asset_remove.py b/account_asset_management/wizard/account_asset_remove.py index 498d2a1f..2bf32a65 100644 --- a/account_asset_management/wizard/account_asset_remove.py +++ b/account_asset_management/wizard/account_asset_remove.py @@ -1,159 +1,170 @@ # Copyright 2009-2018 Noviat # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from dateutil.relativedelta import relativedelta import logging -from odoo import api, fields, models, _ +from dateutil.relativedelta import relativedelta + +from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError _logger = logging.getLogger(__name__) class AccountAssetRemove(models.TransientModel): - _name = 'account.asset.remove' - _description = 'Remove Asset' + _name = "account.asset.remove" + _description = "Remove Asset" date_remove = fields.Date( - string='Asset Removal Date', required=True, + string="Asset Removal Date", + required=True, default=fields.Date.today, help="Removal date must be after the last posted entry " - "in case of early removal") - force_date = fields.Date( - string='Force accounting date') + "in case of early removal", + ) + force_date = fields.Date(string="Force accounting date") sale_value = fields.Float( - string='Sale Value', - default=lambda self: self._default_sale_value()) + string="Sale Value", default=lambda self: self._default_sale_value() + ) account_sale_id = fields.Many2one( - comodel_name='account.account', - string='Asset Sale Account', - domain=[('deprecated', '=', False)], - default=lambda self: self._default_account_sale_id()) + comodel_name="account.account", + string="Asset Sale Account", + domain=[("deprecated", "=", False)], + default=lambda self: self._default_account_sale_id(), + ) account_plus_value_id = fields.Many2one( - comodel_name='account.account', - string='Plus-Value Account', - domain=[('deprecated', '=', False)], - default=lambda self: self._default_account_plus_value_id()) + comodel_name="account.account", + string="Plus-Value Account", + domain=[("deprecated", "=", False)], + default=lambda self: self._default_account_plus_value_id(), + ) account_min_value_id = fields.Many2one( - comodel_name='account.account', - string='Min-Value Account', - domain=[('deprecated', '=', False)], - default=lambda self: self._default_account_min_value_id()) + comodel_name="account.account", + string="Min-Value Account", + domain=[("deprecated", "=", False)], + default=lambda self: self._default_account_min_value_id(), + ) account_residual_value_id = fields.Many2one( - comodel_name='account.account', - string='Residual Value Account', - domain=[('deprecated', '=', False)], - default=lambda self: self._default_account_residual_value_id()) + comodel_name="account.account", + string="Residual Value Account", + domain=[("deprecated", "=", False)], + default=lambda self: self._default_account_residual_value_id(), + ) posting_regime = fields.Selection( selection=lambda self: self._selection_posting_regime(), - string='Removal Entry Policy', + string="Removal Entry Policy", required=True, default=lambda self: self._get_posting_regime(), help="Removal Entry Policy \n" - " * Residual Value: The non-depreciated value will be " - "posted on the 'Residual Value Account' \n" - " * Gain/Loss on Sale: The Gain or Loss will be posted on " - "the 'Plus-Value Account' or 'Min-Value Account' ") - note = fields.Text('Notes') + " * Residual Value: The non-depreciated value will be " + "posted on the 'Residual Value Account' \n" + " * Gain/Loss on Sale: The Gain or Loss will be posted on " + "the 'Plus-Value Account' or 'Min-Value Account' ", + ) + note = fields.Text("Notes") - @api.constrains('sale_value') + @api.constrains("sale_value") def _check_sale_value(self): if self.sale_value < 0: - raise ValidationError(_('The Sale Value must be positive!')) + raise ValidationError(_("The Sale Value must be positive!")) @api.model def _default_sale_value(self): - return self._get_sale()['sale_value'] + return self._get_sale()["sale_value"] @api.model def _default_account_sale_id(self): - return self._get_sale()['account_sale_id'] + return self._get_sale()["account_sale_id"] def _get_sale(self): - asset_id = self.env.context.get('active_id') + asset_id = self.env.context.get("active_id") sale_value = 0.0 account_sale_id = False - inv_lines = self.env['account.invoice.line'].search( - [('asset_id', '=', asset_id)]) + inv_lines = self.env["account.invoice.line"].search( + [("asset_id", "=", asset_id)] + ) for line in inv_lines: inv = line.invoice_id comp_curr = inv.company_id.currency_id inv_curr = inv.currency_id - if line.invoice_id.state in ['open', 'paid']: + if line.invoice_id.state in ["open", "paid"]: account_sale_id = line.account_id.id amount = line.price_subtotal if inv_curr != comp_curr: amount = comp_curr.compute(amount) sale_value += amount - return {'sale_value': sale_value, 'account_sale_id': account_sale_id} + return {"sale_value": sale_value, "account_sale_id": account_sale_id} @api.model def _default_account_plus_value_id(self): - asset_id = self.env.context.get('active_id') - asset = self.env['account.asset'].browse(asset_id) + asset_id = self.env.context.get("active_id") + asset = self.env["account.asset"].browse(asset_id) return asset.profile_id.account_plus_value_id @api.model def _default_account_min_value_id(self): - asset_id = self.env.context.get('active_id') - asset = self.env['account.asset'].browse(asset_id) + asset_id = self.env.context.get("active_id") + asset = self.env["account.asset"].browse(asset_id) return asset.profile_id.account_min_value_id @api.model def _default_account_residual_value_id(self): - asset_id = self.env.context.get('active_id') - asset = self.env['account.asset'].browse(asset_id) + asset_id = self.env.context.get("active_id") + asset = self.env["account.asset"].browse(asset_id) return asset.profile_id.account_residual_value_id @api.model def _selection_posting_regime(self): - return[ - ('residual_value', _('Residual Value')), - ('gain_loss_on_sale', _('Gain/Loss on Sale')), + return [ + ("residual_value", _("Residual Value")), + ("gain_loss_on_sale", _("Gain/Loss on Sale")), ] @api.model def _get_posting_regime(self): - asset_obj = self.env['account.asset'] - asset = asset_obj.browse(self.env.context.get('active_id')) + asset_obj = self.env["account.asset"] + asset = asset_obj.browse(self.env.context.get("active_id")) country = asset and asset.company_id.country_id.code or False if country in self._residual_value_regime_countries(): - return 'residual_value' + return "residual_value" else: - return 'gain_loss_on_sale' + return "gain_loss_on_sale" def _residual_value_regime_countries(self): - return ['FR'] + return ["FR"] @api.multi def remove(self): self.ensure_one() - asset_line_obj = self.env['account.asset.line'] + asset_line_obj = self.env["account.asset.line"] - asset_id = self.env.context.get('active_id') - asset = self.env['account.asset'].browse(asset_id) - asset_ref = asset.code and '%s (ref: %s)' \ - % (asset.name, asset.code) or asset.name + asset_id = self.env.context.get("active_id") + asset = self.env["account.asset"].browse(asset_id) + asset_ref = ( + asset.code and "{} (ref: {})".format(asset.name, asset.code) or asset.name + ) - if self.env.context.get('early_removal'): + if self.env.context.get("early_removal"): residual_value = self._prepare_early_removal(asset) else: residual_value = asset.value_residual dlines = asset_line_obj.search( - [('asset_id', '=', asset.id), ('type', '=', 'depreciate')], - order='line_date desc') + [("asset_id", "=", asset.id), ("type", "=", "depreciate")], + order="line_date desc", + ) if dlines: last_date = dlines[0].line_date else: create_dl = asset_line_obj.search( - [('asset_id', '=', asset.id), ('type', '=', 'create')])[0] + [("asset_id", "=", asset.id), ("type", "=", "create")] + )[0] last_date = create_dl.line_date if self.date_remove < last_date: raise UserError( - _("The removal date must be after " - "the last depreciation date.")) + _("The removal date must be after " "the last depreciation date.") + ) line_name = asset._get_depreciation_entry_name(len(dlines) + 1) journal_id = asset.profile_id.journal_id.id @@ -164,39 +175,39 @@ class AccountAssetRemove(models.TransientModel): # create move move_vals = { - 'name': asset.name, - 'date': date_remove, - 'ref': line_name, - 'journal_id': journal_id, - 'narration': self.note, + "name": asset.name, + "date": date_remove, + "ref": line_name, + "journal_id": journal_id, + "narration": self.note, } - move = self.env['account.move'].create(move_vals) + move = self.env["account.move"].create(move_vals) # create asset line asset_line_vals = { - 'amount': residual_value, - 'asset_id': asset_id, - 'name': line_name, - 'line_date': self.date_remove, - 'move_id': move.id, - 'type': 'remove', + "amount": residual_value, + "asset_id": asset_id, + "name": line_name, + "line_date": self.date_remove, + "move_id": move.id, + "type": "remove", } asset_line_obj.create(asset_line_vals) - asset.write({'state': 'removed', 'date_remove': self.date_remove}) + asset.write({"state": "removed", "date_remove": self.date_remove}) # create move lines move_lines = self._get_removal_data(asset, residual_value) - move.with_context(allow_asset=True).write({'line_ids': move_lines}) + move.with_context(allow_asset=True).write({"line_ids": move_lines}) return { - 'name': _("Asset '%s' Removal Journal Entry") % asset_ref, - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move', - 'view_id': False, - 'type': 'ir.actions.act_window', - 'context': self.env.context, - 'domain': [('id', '=', move.id)], + "name": _("Asset '%s' Removal Journal Entry") % asset_ref, + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.move", + "view_id": False, + "type": "ir.actions.act_window", + "context": self.env.context, + "domain": [("id", "=", move.id)], } def _prepare_early_removal(self, asset): @@ -204,15 +215,17 @@ class AccountAssetRemove(models.TransientModel): Generate last depreciation entry on the day before the removal date. """ date_remove = self.date_remove - asset_line_obj = self.env['account.asset.line'] + asset_line_obj = self.env["account.asset.line"] - digits = self.env['decimal.precision'].precision_get('Account') + digits = self.env["decimal.precision"].precision_get("Account") def _dlines(asset): lines = asset.depreciation_line_ids dlines = lines.filtered( - lambda l: l.type == 'depreciate' and not - l.init_entry and not l.move_check) + lambda l: l.type == "depreciate" + and not l.init_entry + and not l.move_check + ) dlines = dlines.sorted(key=lambda l: l.line_date) return dlines @@ -225,28 +238,32 @@ class AccountAssetRemove(models.TransientModel): first_date = first_to_depreciate_dl.line_date if date_remove > first_date: raise UserError( - _("You can't make an early removal if all the depreciation " - "lines for previous periods are not posted.")) + _( + "You can't make an early removal if all the depreciation " + "lines for previous periods are not posted." + ) + ) if first_to_depreciate_dl.previous_id: last_depr_date = first_to_depreciate_dl.previous_id.line_date else: create_dl = asset_line_obj.search( - [('asset_id', '=', asset.id), ('type', '=', 'create')]) + [("asset_id", "=", asset.id), ("type", "=", "create")] + ) last_depr_date = create_dl.line_date period_number_days = (first_date - last_depr_date).days new_line_date = date_remove + relativedelta(days=-1) to_depreciate_days = (new_line_date - last_depr_date).days to_depreciate_amount = round( - float(to_depreciate_days) / float(period_number_days) * - first_to_depreciate_dl.amount, digits) + float(to_depreciate_days) + / float(period_number_days) + * first_to_depreciate_dl.amount, + digits, + ) residual_value = asset.value_residual - to_depreciate_amount if to_depreciate_amount: - update_vals = { - 'amount': to_depreciate_amount, - 'line_date': new_line_date - } + update_vals = {"amount": to_depreciate_amount, "line_date": new_line_date} first_to_depreciate_dl.write(update_vals) dlines[0].create_move() dlines -= dlines[0] @@ -262,64 +279,64 @@ class AccountAssetRemove(models.TransientModel): depr_amount = asset.depreciation_base - residual_value if depr_amount: move_line_vals = { - 'name': asset.name, - 'account_id': profile.account_depreciation_id.id, - 'debit': depr_amount > 0 and depr_amount or 0.0, - 'credit': depr_amount < 0 and -depr_amount or 0.0, - 'partner_id': partner_id, - 'asset_id': asset.id + "name": asset.name, + "account_id": profile.account_depreciation_id.id, + "debit": depr_amount > 0 and depr_amount or 0.0, + "credit": depr_amount < 0 and -depr_amount or 0.0, + "partner_id": partner_id, + "asset_id": asset.id, } move_lines.append((0, 0, move_line_vals)) move_line_vals = { - 'name': asset.name, - 'account_id': profile.account_asset_id.id, - 'debit': (asset.depreciation_base < 0 and -asset - .depreciation_base or 0.0), - 'credit': (asset.depreciation_base > 0 and asset - .depreciation_base or 0.0), - 'partner_id': partner_id, - 'asset_id': asset.id + "name": asset.name, + "account_id": profile.account_asset_id.id, + "debit": (asset.depreciation_base < 0 and -asset.depreciation_base or 0.0), + "credit": (asset.depreciation_base > 0 and asset.depreciation_base or 0.0), + "partner_id": partner_id, + "asset_id": asset.id, } move_lines.append((0, 0, move_line_vals)) if residual_value: - if self.posting_regime == 'residual_value': + if self.posting_regime == "residual_value": move_line_vals = { - 'name': asset.name, - 'account_id': self.account_residual_value_id.id, - 'analytic_account_id': asset.account_analytic_id.id, - 'debit': residual_value, - 'credit': 0.0, - 'partner_id': partner_id, - 'asset_id': asset.id + "name": asset.name, + "account_id": self.account_residual_value_id.id, + "analytic_account_id": asset.account_analytic_id.id, + "debit": residual_value, + "credit": 0.0, + "partner_id": partner_id, + "asset_id": asset.id, } move_lines.append((0, 0, move_line_vals)) - elif self.posting_regime == 'gain_loss_on_sale': + elif self.posting_regime == "gain_loss_on_sale": if self.sale_value: sale_value = self.sale_value move_line_vals = { - 'name': asset.name, - 'account_id': self.account_sale_id.id, - 'analytic_account_id': asset.account_analytic_id.id, - 'debit': sale_value, - 'credit': 0.0, - 'partner_id': partner_id, - 'asset_id': asset.id + "name": asset.name, + "account_id": self.account_sale_id.id, + "analytic_account_id": asset.account_analytic_id.id, + "debit": sale_value, + "credit": 0.0, + "partner_id": partner_id, + "asset_id": asset.id, } move_lines.append((0, 0, move_line_vals)) balance = self.sale_value - residual_value - account_id = (self.account_plus_value_id.id - if balance > 0 - else self.account_min_value_id.id) + account_id = ( + self.account_plus_value_id.id + if balance > 0 + else self.account_min_value_id.id + ) move_line_vals = { - 'name': asset.name, - 'account_id': account_id, - 'analytic_account_id': asset.account_analytic_id.id, - 'debit': balance < 0 and -balance or 0.0, - 'credit': balance > 0 and balance or 0.0, - 'partner_id': partner_id, - 'asset_id': asset.id + "name": asset.name, + "account_id": account_id, + "analytic_account_id": asset.account_analytic_id.id, + "debit": balance < 0 and -balance or 0.0, + "credit": balance > 0 and balance or 0.0, + "partner_id": partner_id, + "asset_id": asset.id, } move_lines.append((0, 0, move_line_vals)) return move_lines