2
0

[MIG] account_asset_management: Migration to 11.0

This commit is contained in:
Akim Juillerat 2018-10-01 11:57:07 +02:00 committed by Rodrigo
parent 49d1c36679
commit a7cdfdd14d
113 changed files with 40986 additions and 43415 deletions

View File

@ -1,10 +1,29 @@
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png =================
:target: https://www.gnu.org/licenses/agpl Assets Management
:alt: License: AGPL-3 =================
========================== .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Financial asset management !! This file is generated by oca-gen-addon-readme !!
========================== !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github
:target: https://github.com/OCA/account-financial-tools/tree/11.0/account_asset_management
:alt: OCA/account-financial-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/account-financial-tools-11-0/account-financial-tools-11-0-account_asset_management
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/92/11.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This Module manages the assets owned by a company. It will keep This Module manages the assets owned by a company. It will keep
track of depreciation's occurred on those assets. And it allows to create track of depreciation's occurred on those assets. And it allows to create
@ -20,6 +39,11 @@ Excel based reporting is available via the 'account_asset_management_xls' module
The module contains a large number of functional enhancements compared to The module contains a large number of functional enhancements compared to
the standard account_asset module from Odoo. the standard account_asset module from Odoo.
**Table of contents**
.. contents::
:local:
Configuration Configuration
============= =============
@ -29,46 +53,50 @@ creation of separate assets per Supplier Invoice Line.
Usage Usage
===== =====
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/92/10.0
Known issues
============
The module in NOT compatible with the standard account_asset module. The module in NOT compatible with the standard account_asset module.
Bug Tracker Bug Tracker
=========== ===========
Bugs are tracked on `GitHub Issues Bugs are tracked on `GitHub Issues <https://github.com/OCA/account-financial-tools/issues>`_.
<https://github.com/OCA/account-financial-tools/issues>`_. In case of trouble, please In case of trouble, please check there if your issue has already been reported.
check there if your issue has already been reported. If you spotted it first, If you spotted it first, help us smashing it by providing a detailed and welcomed
help us smash it by providing detailed and welcomed feedback. `feedback <https://github.com/OCA/account-financial-tools/issues/new?body=module:%20account_asset_management%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits Credits
======= =======
Authors
~~~~~~~
* Noviat
Contributors Contributors
------------ ~~~~~~~~~~~~
- OpenERP SA - OpenERP SA
- Luc De Meyer (Noviat) - Luc De Meyer (Noviat)
- Frédéric Clementi (camptocamp) - Frédéric Clementi (camptocamp)
- Florian Dacosta (Akretion) - Florian Dacosta (Akretion)
- Stéphane Bidoul (Acsone) - Stéphane Bidoul (Acsone)
- Adrien Peiffer (Acsone) - Adrien Peiffer (Acsone)
- Akim Juillerat <akim.juillerat@camptocamp.com>
Maintainer Maintainers
---------- ~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png .. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association :alt: Odoo Community Association
:target: https://odoo-community.org :target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use. promote its widespread use.
To contribute to this module, please visit http://odoo-community.org. This module is part of the `OCA/account-financial-tools <https://github.com/OCA/account-financial-tools/tree/11.0/account_asset_management>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
from . import models from . import models
from . import wizard from . import wizard

View File

@ -1,19 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2018 Noviat # Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': 'Assets Management', 'name': 'Assets Management',
'version': '10.0.3.1.0', 'version': '11.0.1.0.0',
'license': 'AGPL-3', 'license': 'AGPL-3',
'depends': [ 'depends': [
'account_fiscal_year', 'account_fiscal_year',
], ],
'conflicts': ['account_asset'], 'conflicts': ['account_asset'],
'author': "Noviat,Odoo Community Association (OCA)", 'author': "Noviat,Odoo Community Association (OCA)",
'website': 'http://www.noviat.com', 'website': 'https://github.com/OCA/account-financial-tools',
'category': 'Accounting & Finance', 'category': 'Accounting & Finance',
'sequence': 32,
'data': [ 'data': [
'security/account_asset_security.xml', 'security/account_asset_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
@ -22,14 +20,11 @@
'views/account_account.xml', 'views/account_account.xml',
'views/account_asset.xml', 'views/account_asset.xml',
'views/account_asset_profile.xml', 'views/account_asset_profile.xml',
'views/account_config_settings.xml', 'views/res_config_settings.xml',
'views/account_invoice.xml', 'views/account_invoice.xml',
'views/account_invoice_line.xml', 'views/account_invoice_line.xml',
'views/account_move.xml', 'views/account_move.xml',
'views/account_move_line.xml', 'views/account_move_line.xml',
'views/menuitem.xml', 'views/menuitem.xml',
], ],
'auto_install': False,
'installable': True,
'application': True,
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,148 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2017 Noviat
# Copyright OpenUpgrade contributors (https://github.com/oca/openupgradelib)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
_logger = logging.getLogger(__name__)
_logger.setLevel(logging.DEBUG)
column_renames = {
'account_account': [
('asset_category_id', 'asset_profile_id', 'Asset Profile'),
],
'account_asset': [
('asset_value', 'depreciation_base', 'Depreciation Base'),
('category_id', 'profile_id', 'Asset Profile'),
],
'account_invoice_line': [
('asset_category_id', 'asset_profile_id', 'Asset Profile'),
],
'account_move_line': [
('asset_category_id', 'asset_profile_id', 'Asset Profile'),
],
}
table_renames = [
('account_asset_asset', 'account_asset'),
('account_asset_category', 'account_asset_profile'),
('account_asset_depreciation_line', 'account_asset_line')]
model_renames = [
('account.asset.asset', 'account.asset'),
('account.asset.category', 'account.asset.profile'),
('account.asset.depreciation.line', 'account.asset.line')]
view_refs = [
'account_asset_management.view_account_asset_form',
'account_asset_management.view_invoice_asset_category',
'account_asset_management.view_account_invoice_asset_form',
'account_asset_management.view_account_move_line_form_inherit',
'account_asset_management.view_account_move_asset_form'
]
def is_module_installed(cr, module):
""" Check if `module` is installed.
:return: True / False
"""
cr.execute(
"SELECT id FROM ir_module_module "
"WHERE name=%s and state IN ('installed', 'to upgrade')", (module,))
return bool(cr.fetchone())
def table_exists(cr, table):
""" Check whether a certain table or view exists """
cr.execute('SELECT 1 FROM pg_class WHERE relname = %s', (table,))
return cr.fetchone()
def rename_columns(cr, column_spec):
for table in column_spec.keys():
for (old, new, comment) in column_spec[table]:
cr.execute(
"SELECT column_name "
"FROM information_schema.columns "
"WHERE table_name=%s "
"AND column_name=%s",
(table, new))
res = cr.fetchone()
if not res:
_logger.info("table %s, column %s: renaming to %s",
table, old, new)
cr.execute(
'ALTER TABLE "%s" RENAME "%s" TO "%s"'
% (table, old, new,))
cr.execute('DROP INDEX IF EXISTS "%s_%s_index"' % (table, old))
if comment:
cr.execute(
"COMMENT ON COLUMN %s.%s IS '%s'"
% (table, new, comment))
def rename_tables(cr, table_spec):
to_rename = [x[0] for x in table_spec]
for old, new in list(table_spec):
if (table_exists(cr, old + '_id_seq') and
old + '_id_seq' not in to_rename):
table_spec.append((old + '_id_seq', new + '_id_seq'))
for (old, new) in table_spec:
_logger.info("table %s: renaming to %s",
old, new)
if table_exists(cr, old) and not table_exists(cr, new):
cr.execute('ALTER TABLE "%s" RENAME TO "%s"' % (old, new))
def rename_models(cr, model_spec):
for (old, new) in model_spec:
_logger.info("model %s: renaming to %s",
old, new)
cr.execute('UPDATE ir_model SET model = %s '
'WHERE model = %s', (new, old,))
cr.execute('UPDATE ir_model_fields SET relation = %s '
'WHERE relation = %s', (new, old,))
cr.execute('UPDATE ir_model_data SET model = %s '
'WHERE model = %s', (new, old,))
cr.execute('UPDATE ir_attachment SET res_model = %s '
'WHERE res_model = %s', (new, old,))
cr.execute('UPDATE ir_model_fields SET model = %s '
'WHERE model = %s', (new, old,))
cr.execute('UPDATE ir_translation set '
"name=%s || substr(name, strpos(name, ',')) "
'where name like %s',
(new, old + ',%'),)
if is_module_installed(cr, 'mail'):
# fortunately, the data model didn't change up to now
cr.execute(
'UPDATE mail_message SET model=%s where model=%s', (new, old),
)
if table_exists(cr, 'mail_followers'):
cr.execute(
'UPDATE mail_followers SET res_model=%s '
'WHERE res_model=%s',
(new, old),
)
def remove_views(cr, view_refs):
model = 'ir.ui.view'
for view_ref in view_refs:
module, name = view_ref.split('.')
cr.execute(
'SELECT res_id FROM ir_model_data '
'WHERE module = %s AND name = %s AND model = %s',
(module, name, model)
)
res = cr.fetchone()
if res:
cr.execute(
'DELETE FROM ir_ui_view WHERE id = %s', (res[0],))
def migrate(cr, version):
remove_views(cr, view_refs)
rename_tables(cr, table_renames)
rename_models(cr, model_renames)
rename_columns(cr, column_renames)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from . import account_account from . import account_account
from . import account_asset from . import account_asset
from . import account_asset_profile from . import account_asset_profile
@ -7,4 +6,4 @@ from . import account_asset_recompute_trigger
from . import account_invoice from . import account_invoice
from . import account_move from . import account_move
from . import date_range from . import date_range
from . import res_config from . import res_config_settings

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2017 Noviat # Copyright 2009-2017 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2018 Noviat # Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@ -13,6 +12,7 @@ from odoo import api, fields, models, _
import odoo.addons.decimal_precision as dp import odoo.addons.decimal_precision as dp
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.osv import expression from odoo.osv import expression
from functools import reduce
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -83,7 +83,9 @@ class AccountAsset(models.Model):
string='Parent Asset', readonly=True, string='Parent Asset', readonly=True,
states={'draft': [('readonly', False)]}, states={'draft': [('readonly', False)]},
domain=[('type', '=', 'view')], domain=[('type', '=', 'view')],
ondelete='restrict') ondelete='restrict',
index=True,
)
parent_left = fields.Integer(index=True) parent_left = fields.Integer(index=True)
parent_right = fields.Integer(index=True) parent_right = fields.Integer(index=True)
child_ids = fields.One2many( child_ids = fields.One2many(
@ -104,7 +106,7 @@ class AccountAsset(models.Model):
('open', 'Running'), ('open', 'Running'),
('close', 'Close'), ('close', 'Close'),
('removed', 'Removed'), ('removed', 'Removed'),
], string='Status', required=True, default='draft', copy='draft', ], string='Status', required=True, default='draft', copy=False,
help="When an asset is created, the status is 'Draft'.\n" help="When an asset is created, the status is 'Draft'.\n"
"If the asset is confirmed, the status goes in 'Running' " "If the asset is confirmed, the status goes in 'Running' "
"and the depreciation lines can be posted " "and the depreciation lines can be posted "
@ -206,7 +208,7 @@ class AccountAsset(models.Model):
for line in asset.depreciation_line_ids: for line in asset.depreciation_line_ids:
if line.move_id: if line.move_id:
asset.move_line_check = True asset.move_line_check = True
continue break
@api.depends('purchase_value', 'salvage_value', 'type', 'method') @api.depends('purchase_value', 'salvage_value', 'type', 'method')
@api.multi @api.multi
@ -234,17 +236,20 @@ class AccountAsset(models.Model):
lambda l: l.type in ('depreciate', 'remove') and lambda l: l.type in ('depreciate', 'remove') and
(l.init_entry or l.move_check)) (l.init_entry or l.move_check))
value_depreciated = sum([l.amount for l in lines]) value_depreciated = sum([l.amount for l in lines])
asset.value_residual = \ residual = asset.depreciation_base - value_depreciated
asset.depreciation_base - value_depreciated depreciated = value_depreciated
asset.value_depreciated = value_depreciated
else: else:
asset.value_residual = 0.0 residual = 0.0
asset.value_depreciated = 0.0 depreciated = 0.0
asset.update({
'value_residual': residual,
'value_depreciated': depreciated
})
@api.multi @api.multi
@api.constrains('parent_id') @api.constrains('parent_id')
def _check_recursion(self, parent=None): def _check_recursion(self, parent=None):
res = super(AccountAsset, self)._check_recursion(parent=parent) res = super()._check_recursion(parent=parent)
if not res: if not res:
raise UserError( raise UserError(
_("Error ! You can not create recursive assets.")) _("Error ! You can not create recursive assets."))
@ -279,9 +284,10 @@ class AccountAsset(models.Model):
dl_create_line = self.depreciation_line_ids.filtered( dl_create_line = self.depreciation_line_ids.filtered(
lambda r: r.type == 'create') lambda r: r.type == 'create')
if dl_create_line: if dl_create_line:
dl_create_line.write({ dl_create_line.update({
'amount': self.depreciation_base, 'amount': self.depreciation_base,
'line_date': self.date_start}) 'line_date': self.date_start
})
@api.onchange('profile_id') @api.onchange('profile_id')
def _onchange_profile_id(self): def _onchange_profile_id(self):
@ -290,16 +296,18 @@ class AccountAsset(models.Model):
raise UserError( raise UserError(
_("You cannot change the profile of an asset " _("You cannot change the profile of an asset "
"with accounting entries.")) "with accounting entries."))
if self.profile_id: profile = self.profile_id
profile = self.profile_id if profile:
self.parent_id = profile.parent_id self.update({
self.method = profile.method 'parent_id': profile.parent_id,
self.method_number = profile.method_number 'method': profile.method,
self.method_time = profile.method_time 'method_number': profile.method_number,
self.method_period = profile.method_period 'method_time': profile.method_time,
self.method_progress_factor = profile.method_progress_factor 'method_period': profile.method_period,
self.prorata = profile.prorata 'method_progress_factor': profile.method_progress_factor,
self.account_analytic_id = profile.account_analytic_id 'prorata': profile.prorata,
'account_analytic_id': profile.account_analytic_id,
})
@api.onchange('method_time') @api.onchange('method_time')
def _onchange_method_time(self): def _onchange_method_time(self):
@ -309,10 +317,12 @@ class AccountAsset(models.Model):
@api.onchange('type') @api.onchange('type')
def _onchange_type(self): def _onchange_type(self):
if self.type == 'view': if self.type == 'view':
self.date_start = False self.update({
self.profile_id = False 'date_start': False,
self.purchase_value = False 'profile_id': False,
self.salvage_value = False 'purchase_value': False,
'salvage_value': False,
})
if self.depreciation_line_ids: if self.depreciation_line_ids:
self.depreciation_line_ids.unlink() self.depreciation_line_ids.unlink()
@ -322,8 +332,8 @@ class AccountAsset(models.Model):
vals['prorata'] = True vals['prorata'] = True
if vals.get('type') == 'view': if vals.get('type') == 'view':
vals['date_start'] = False vals['date_start'] = False
asset = super(AccountAsset, self).create(vals) asset = super().create(vals)
if self._context.get('create_asset_from_move_line'): if self.env.context.get('create_asset_from_move_line'):
# Trigger compute of depreciation_base # Trigger compute of depreciation_base
asset.salvage_value = 0.0 asset.salvage_value = 0.0
if asset.type == 'normal': if asset.type == 'normal':
@ -335,20 +345,19 @@ class AccountAsset(models.Model):
if vals.get('method_time'): if vals.get('method_time'):
if vals['method_time'] != 'year' and not vals.get('prorata'): if vals['method_time'] != 'year' and not vals.get('prorata'):
vals['prorata'] = True vals['prorata'] = True
super(AccountAsset, self).write(vals) res = super().write(vals)
for asset in self: for asset in self:
asset_type = vals.get('type') or asset.type asset_type = vals.get('type') or asset.type
if asset_type == 'view' or \ if asset_type == 'view' or \
self._context.get('asset_validate_from_write'): self.env.context.get('asset_validate_from_write'):
continue continue
asset._create_first_asset_line() asset._create_first_asset_line()
if asset.profile_id.open_asset and \ if asset.profile_id.open_asset and \
self._context.get('create_asset_from_move_line'): self.env.context.get('create_asset_from_move_line'):
asset.compute_depreciation_board() asset.compute_depreciation_board()
# extra context to avoid recursion # extra context to avoid recursion
ctx = dict(self._context, asset_validate_from_write=True) asset.with_context(asset_validate_from_write=True).validate()
asset.with_context(ctx).validate() return res
return True
def _create_first_asset_line(self): def _create_first_asset_line(self):
self.ensure_one() self.ensure_one()
@ -364,8 +373,8 @@ class AccountAsset(models.Model):
'type': 'create', 'type': 'create',
} }
asset_line = asset_line_obj.create(asset_line_vals) asset_line = asset_line_obj.create(asset_line_vals)
if self._context.get('create_asset_from_move_line'): if self.env.context.get('create_asset_from_move_line'):
asset_line.move_id = self._context['move_id'] asset_line.move_id = self.env.context['move_id']
@api.multi @api.multi
def unlink(self): def unlink(self):
@ -379,11 +388,11 @@ class AccountAsset(models.Model):
_("You cannot delete an asset that contains " _("You cannot delete an asset that contains "
"posted depreciation lines.")) "posted depreciation lines."))
# update accounting entries linked to lines of type 'create' # update accounting entries linked to lines of type 'create'
ctx = dict(self._context, allow_asset_removal=True, amls = self.with_context(
from_parent_object=True) allow_asset_removal=True, from_parent_object=True
amls = self.with_context(ctx).mapped('account_move_line_ids') ).mapped('account_move_line_ids')
amls.write({'asset_id': False}) amls.write({'asset_id': False})
return super(AccountAsset, self).unlink() return super().unlink()
@api.model @api.model
def name_search(self, name, args=None, operator='ilike', limit=100): def name_search(self, name, args=None, operator='ilike', limit=100):
@ -422,7 +431,7 @@ class AccountAsset(models.Model):
@api.multi @api.multi
def remove(self): def remove(self):
self.ensure_one() self.ensure_one()
ctx = dict(self._context, active_ids=self.ids, active_id=self.id) ctx = dict(self.env.context, active_ids=self.ids, active_id=self.id)
early_removal = False early_removal = False
if self.method in ['linear-limit', 'degr-limit']: if self.method in ['linear-limit', 'degr-limit']:
@ -441,7 +450,6 @@ class AccountAsset(models.Model):
'target': 'new', 'target': 'new',
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'context': ctx, 'context': ctx,
'nodestroy': True,
} }
@api.multi @api.multi
@ -461,13 +469,17 @@ class AccountAsset(models.Model):
'res_model': 'account.move', 'res_model': 'account.move',
'view_id': False, 'view_id': False,
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'context': self._context, 'context': self.env.context,
'nodestroy': True,
'domain': [('id', 'in', am_ids)], 'domain': [('id', 'in', am_ids)],
} }
@api.multi @api.multi
def compute_depreciation_board(self): def compute_depreciation_board(self):
def group_lines(x, y):
y.update({'amount': x['amount'] + y['amount']})
return y
line_obj = self.env['account.asset.line'] line_obj = self.env['account.asset.line']
digits = self.env['decimal.precision'].precision_get('Account') digits = self.env['decimal.precision'].precision_get('Account')
@ -498,8 +510,8 @@ class AccountAsset(models.Model):
continue continue
# group lines prior to depreciation start period # group lines prior to depreciation start period
depreciation_start_date = datetime.strptime( depreciation_start_date = fields.Datetime.from_string(
asset.date_start, '%Y-%m-%d') asset.date_start)
lines = table[0]['lines'] lines = table[0]['lines']
lines1 = [] lines1 = []
lines2 = [] lines2 = []
@ -512,9 +524,6 @@ class AccountAsset(models.Model):
else: else:
lines2.append(line) lines2.append(line)
if lines1: if lines1:
def group_lines(x, y):
y.update({'amount': x['amount'] + y['amount']})
return y
lines1 = [reduce(group_lines, lines1)] lines1 = [reduce(group_lines, lines1)]
lines1[0]['depreciated_value'] = 0.0 lines1[0]['depreciated_value'] = 0.0
table[0]['lines'] = lines1 + lines2 table[0]['lines'] = lines1 + lines2
@ -523,8 +532,8 @@ class AccountAsset(models.Model):
# recompute in case of deviation # recompute in case of deviation
depreciated_value_posted = depreciated_value = 0.0 depreciated_value_posted = depreciated_value = 0.0
if posted_lines: if posted_lines:
last_depreciation_date = datetime.strptime( last_depreciation_date = fields.Datetime.from_string(
last_line.line_date, '%Y-%m-%d') 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: if last_date_in_table <= last_depreciation_date:
raise UserError( raise UserError(
@ -616,8 +625,8 @@ class AccountAsset(models.Model):
- years: duration in calendar years, considering also leap years - years: duration in calendar years, considering also leap years
""" """
fy = self.env['date.range'].browse(fy_id) fy = self.env['date.range'].browse(fy_id)
fy_date_start = datetime.strptime(fy.date_start, '%Y-%m-%d') fy_date_start = fields.Datetime.from_string(fy.date_start)
fy_date_stop = datetime.strptime(fy.date_end, '%Y-%m-%d') fy_date_stop = fields.Datetime.from_string(fy.date_end)
days = (fy_date_stop - fy_date_start).days + 1 days = (fy_date_stop - fy_date_start).days + 1
months = (fy_date_stop.year - fy_date_start.year) * 12 \ months = (fy_date_stop.year - fy_date_start.year) * 12 \
+ (fy_date_stop.month - fy_date_start.month) + 1 + (fy_date_stop.month - fy_date_start.month) + 1
@ -639,7 +648,7 @@ class AccountAsset(models.Model):
factor = float(duration) / cy_days factor = float(duration) / cy_days
elif i == cnt - 1: # last year elif i == cnt - 1: # last year
duration = ( duration = (
fy_date_stop - datetime(year, 01, 01)).days + 1 fy_date_stop - datetime(year, 1, 1)).days + 1
factor += float(duration) / cy_days factor += float(duration) / cy_days
else: else:
factor += 1.0 factor += 1.0
@ -655,8 +664,8 @@ class AccountAsset(models.Model):
fy_id = entry['fy_id'] fy_id = entry['fy_id']
if self.prorata: if self.prorata:
if firstyear: if firstyear:
depreciation_date_start = datetime.strptime( depreciation_date_start = fields.Datetime.from_string(
self.date_start, '%Y-%m-%d') self.date_start)
fy_date_stop = entry['date_stop'] fy_date_stop = entry['date_stop']
first_fy_asset_days = \ first_fy_asset_days = \
(fy_date_stop - depreciation_date_start).days + 1 (fy_date_stop - depreciation_date_start).days + 1
@ -689,10 +698,10 @@ class AccountAsset(models.Model):
if the fiscal year starts in the middle of a month. if the fiscal year starts in the middle of a month.
""" """
if self.prorata: if self.prorata:
depreciation_start_date = datetime.strptime( depreciation_start_date = fields.Datetime.from_string(
self.date_start, '%Y-%m-%d') self.date_start)
else: else:
fy_date_start = datetime.strptime(fy.date_start, '%Y-%m-%d') fy_date_start = fields.Datetime.from_string(fy.date_start)
depreciation_start_date = datetime( depreciation_start_date = datetime(
fy_date_start.year, fy_date_start.month, 1) fy_date_start.year, fy_date_start.month, 1)
return depreciation_start_date return depreciation_start_date
@ -717,8 +726,8 @@ class AccountAsset(models.Model):
depreciation_stop_date = depreciation_start_date + \ depreciation_stop_date = depreciation_start_date + \
relativedelta(years=self.method_number, days=-1) relativedelta(years=self.method_number, days=-1)
elif self.method_time == 'end': elif self.method_time == 'end':
depreciation_stop_date = datetime.strptime( depreciation_stop_date = fields.Datetime.from_string(
self.method_end, '%Y-%m-%d') self.method_end)
return depreciation_stop_date return depreciation_stop_date
def _get_first_period_amount(self, table, entry, depreciation_start_date, def _get_first_period_amount(self, table, entry, depreciation_start_date,
@ -729,7 +738,7 @@ class AccountAsset(models.Model):
""" """
amount = entry.get('period_amount') amount = entry.get('period_amount')
if self.prorata and self.method_time == 'year': if self.prorata and self.method_time == 'year':
dates = filter(lambda x: x <= entry['date_stop'], line_dates) dates = [x for x in line_dates if x <= entry['date_stop']]
full_periods = len(dates) - 1 full_periods = len(dates) - 1
amount = entry['fy_amount'] - amount * full_periods amount = entry['fy_amount'] - amount * full_periods
return amount return amount
@ -812,7 +821,7 @@ class AccountAsset(models.Model):
depreciation_stop_date, line_dates): depreciation_stop_date, line_dates):
digits = self.env['decimal.precision'].precision_get('Account') digits = self.env['decimal.precision'].precision_get('Account')
asset_sign = self.depreciation_base >= 0 and 1 or -1 asset_sign = 1 if self.depreciation_base >= 0 else -1
i_max = len(table) - 1 i_max = len(table) - 1
remaining_value = self.depreciation_base remaining_value = self.depreciation_base
depreciated_value = 0.0 depreciated_value = 0.0
@ -902,7 +911,7 @@ class AccountAsset(models.Model):
asset_date_start = datetime.strptime(self.date_start, '%Y-%m-%d') asset_date_start = datetime.strptime(self.date_start, '%Y-%m-%d')
fy = company.find_daterange_fy(asset_date_start) fy = company.find_daterange_fy(asset_date_start)
fiscalyear_lock_date = company.fiscalyear_lock_date fiscalyear_lock_date = company.fiscalyear_lock_date
if fiscalyear_lock_date >= self.date_start: if fiscalyear_lock_date and fiscalyear_lock_date >= self.date_start:
init_flag = True init_flag = True
if fy: if fy:
fy_id = fy.id fy_id = fy.id
@ -957,14 +966,20 @@ class AccountAsset(models.Model):
fy_date_start = fy_date_stop + relativedelta(days=1) fy_date_start = fy_date_stop + relativedelta(days=1)
fy = company.find_daterange_fy(fy_date_start) fy = company.find_daterange_fy(fy_date_start)
if fy: if fy:
if fiscalyear_lock_date >= fy.date_end: if (
fiscalyear_lock_date and
fiscalyear_lock_date >= fy.date_end
):
init_flag = True init_flag = True
else: else:
init_flag = False init_flag = False
fy_date_stop = datetime.strptime(fy.date_end, '%Y-%m-%d') fy_date_stop = datetime.strptime(fy.date_end, '%Y-%m-%d')
else: else:
fy_date_stop = fy_date_stop + relativedelta(years=1) fy_date_stop = fy_date_stop + relativedelta(years=1)
if fiscalyear_lock_date >= fy_date_stop.strftime('%Y-%m-%d'): if (
fiscalyear_lock_date and
fiscalyear_lock_date >= fy_date_stop.strftime('%Y-%m-%d')
):
init_flag = True init_flag = True
else: else:
init_flag = False init_flag = False
@ -1035,7 +1050,7 @@ class AccountAsset(models.Model):
@api.multi @api.multi
def _compute_entries(self, date_end, check_triggers=False): def _compute_entries(self, date_end, check_triggers=False):
# To DO : add ir_cron job calling this method to # TODO : add ir_cron job calling this method to
# generate periodical accounting entries # generate periodical accounting entries
result = [] result = []
error_log = '' error_log = ''
@ -1057,9 +1072,9 @@ class AccountAsset(models.Model):
order='line_date') order='line_date')
for depreciation in depreciations: for depreciation in depreciations:
try: try:
with self._cr.savepoint(): with self.env.cr.savepoint():
result += depreciation.create_move() result += depreciation.create_move()
except: except Exception:
e = exc_info()[0] e = exc_info()[0]
tb = ''.join(format_exception(*exc_info())) tb = ''.join(format_exception(*exc_info()))
asset_ref = depreciation.asset_id.code and '%s (ref: %s)' \ asset_ref = depreciation.asset_id.code and '%s (ref: %s)' \

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2018 Noviat # Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@ -23,16 +22,15 @@ class AccountAssetLine(models.Model):
string='Previous Depreciation Line', string='Previous Depreciation Line',
readonly=True) readonly=True)
parent_state = fields.Selection( parent_state = fields.Selection(
selection=[
('draft', 'Draft'),
('open', 'Running'),
('close', 'Close'),
('removed', 'Removed')],
related='asset_id.state', related='asset_id.state',
string='State of Asset') string='State of Asset',
readonly=True,
)
depreciation_base = fields.Float( depreciation_base = fields.Float(
related='asset_id.depreciation_base', related='asset_id.depreciation_base',
string='Depreciation Base') string='Depreciation Base',
readonly=True,
)
amount = fields.Float( amount = fields.Float(
string='Amount', digits=dp.get_precision('Account'), string='Amount', digits=dp.get_precision('Account'),
required=True) required=True)
@ -63,17 +61,17 @@ class AccountAssetLine(models.Model):
init_entry = fields.Boolean( init_entry = fields.Boolean(
string='Initial Balance Entry', string='Initial Balance Entry',
help="Set this flag for entries of previous fiscal years " help="Set this flag for entries of previous fiscal years "
"for which OpenERP 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 @api.multi
def _compute_values(self): def _compute_values(self):
dlines = self dlines = self
if self._context.get('no_compute_asset_line_ids'): if self.env.context.get('no_compute_asset_line_ids'):
# skip compute for lines in unlink # skip compute for lines in unlink
exclude_ids = self._context['no_compute_asset_line_ids'] exclude_ids = self.env.context['no_compute_asset_line_ids']
dlines = dlines.filtered(lambda l: l.id not in exclude_ids) dlines = self.filtered(lambda l: l.id not in exclude_ids)
dlines = self.filtered(lambda l: l.type == 'depreciate') dlines = dlines.filtered(lambda l: l.type == 'depreciate')
dlines = dlines.sorted(key=lambda l: l.line_date) dlines = dlines.sorted(key=lambda l: l.line_date)
for i, dl in enumerate(dlines): for i, dl in enumerate(dlines):
@ -107,20 +105,21 @@ class AccountAssetLine(models.Model):
for dl in self: for dl in self:
if vals.get('line_date'): if vals.get('line_date'):
if isinstance(vals['line_date'], datetime.date): if isinstance(vals['line_date'], datetime.date):
vals['line_date'] = vals['line_date'].strftime('%Y-%m-%d') vals['line_date'] = fields.Date.to_string(
vals['line_date'])
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 asset_lines = dl.asset_id.depreciation_line_ids
if 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 # allow to remove an accounting entry via the
# 'Delete Move' button on the depreciation lines. # 'Delete Move' button on the depreciation lines.
if not self._context.get('unlink_from_asset'): if not self.env.context.get('unlink_from_asset'):
raise UserError(_( raise UserError(_(
"You are not allowed to remove an accounting entry " "You are not allowed to remove an accounting entry "
"linked to an asset." "linked to an asset."
"\nYou should remove such entries from the asset.")) "\nYou should remove such entries from the asset."))
elif vals.keys() == ['asset_id']: elif list(vals.keys()) == ['asset_id']:
continue continue
elif dl.move_id and not self._context.get( elif dl.move_id and not self.env.context.get(
'allow_asset_line_update'): 'allow_asset_line_update'):
raise UserError(_( raise UserError(_(
"You cannot change a depreciation line " "You cannot change a depreciation line "
@ -152,7 +151,7 @@ class AccountAssetLine(models.Model):
raise UserError(_( raise UserError(_(
"You cannot set the date on a depreciation line " "You cannot set the date on a depreciation line "
"prior to already posted entries.")) "prior to already posted entries."))
return super(AccountAssetLine, self).write(vals) return super().write(vals)
@api.multi @api.multi
def unlink(self): def unlink(self):
@ -166,13 +165,12 @@ class AccountAssetLine(models.Model):
"You cannot delete a depreciation line with " "You cannot delete a depreciation line with "
"an associated accounting entry.")) "an associated accounting entry."))
previous = dl.previous_id previous = dl.previous_id
next = dl.asset_id.depreciation_line_ids.filtered( 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: if next_line:
next.previous_id = previous next_line.previous_id = previous
ctx = dict(self._context, no_compute_asset_line_ids=self.ids) return super(AccountAssetLine, self.with_context(
return super( no_compute_asset_line_ids=self.ids)).unlink()
AccountAssetLine, self.with_context(ctx)).unlink()
def _setup_move_data(self, depreciation_date): def _setup_move_data(self, depreciation_date):
asset = self.asset_id asset = self.asset_id
@ -184,14 +182,14 @@ class AccountAssetLine(models.Model):
} }
return move_data return move_data
def _setup_move_line_data(self, depreciation_date, account, type, move): def _setup_move_line_data(self, depreciation_date, account, ml_type, move):
asset = self.asset_id asset = self.asset_id
amount = self.amount amount = self.amount
analytic_id = False analytic_id = False
if type == 'depreciation': if ml_type == 'depreciation':
debit = amount < 0 and -amount or 0.0 debit = amount < 0 and -amount or 0.0
credit = amount > 0 and amount or 0.0 credit = amount > 0 and amount or 0.0
elif type == 'expense': elif ml_type == 'expense':
debit = amount > 0 and amount or 0.0 debit = amount > 0 and amount or 0.0
credit = amount < 0 and -amount or 0.0 credit = amount < 0 and -amount or 0.0
analytic_id = asset.account_analytic_id.id analytic_id = asset.account_analytic_id.id
@ -213,8 +211,9 @@ class AccountAssetLine(models.Model):
@api.multi @api.multi
def create_move(self): def create_move(self):
created_move_ids = [] created_move_ids = []
asset_ids = [] asset_ids = set()
ctx = dict(self._context, allow_asset=True, check_move_validity=False) ctx = dict(self.env.context,
allow_asset=True, check_move_validity=False)
for line in self: for line in self:
asset = line.asset_id asset = line.asset_id
depreciation_date = line.line_date depreciation_date = line.line_date
@ -229,14 +228,14 @@ class AccountAssetLine(models.Model):
depreciation_date, exp_acc, 'expense', move) depreciation_date, exp_acc, 'expense', move)
self.env['account.move.line'].with_context(ctx).create(aml_e_vals) self.env['account.move.line'].with_context(ctx).create(aml_e_vals)
move.post() move.post()
write_ctx = dict(self._context, allow_asset_line_update=True) line.with_context(allow_asset_line_update=True).write({
line.with_context(write_ctx).write({'move_id': move.id}) 'move_id': move.id
})
created_move_ids.append(move.id) created_move_ids.append(move.id)
asset_ids.append(asset.id) asset_ids.add(asset.id)
# we re-evaluate the assets to determine if we can close them # we re-evaluate the assets to determine if we can close them
for asset in self.env['account.asset'].browse( for asset in self.env['account.asset'].browse(list(asset_ids)):
list(set(asset_ids))): if asset.company_currency_id.is_zero(asset.value_residual):
if asset.company_id.currency_id.is_zero(asset.value_residual):
asset.state = 'close' asset.state = 'close'
return created_move_ids return created_move_ids
@ -250,8 +249,7 @@ class AccountAssetLine(models.Model):
'res_model': 'account.move', 'res_model': 'account.move',
'view_id': False, 'view_id': False,
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'context': self._context, 'context': self.env.context,
'nodestroy': True,
'domain': [('id', '=', self.move_id.id)], 'domain': [('id', '=', self.move_id.id)],
} }

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2018 Noviat # Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@ -11,7 +10,7 @@ class AccountAssetProfile(models.Model):
_description = 'Asset profile' _description = 'Asset profile'
_order = 'name' _order = 'name'
name = fields.Char(string='Name', size=64, required=True, index=1) name = fields.Char(string='Name', size=64, required=True, index=True)
note = fields.Text() note = fields.Text()
account_analytic_id = fields.Many2one( account_analytic_id = fields.Many2one(
comodel_name='account.analytic.account', comodel_name='account.analytic.account',
@ -158,7 +157,7 @@ class AccountAssetProfile(models.Model):
def create(self, vals): def create(self, vals):
if vals.get('method_time') != 'year' and not vals.get('prorata'): if vals.get('method_time') != 'year' and not vals.get('prorata'):
vals['prorata'] = True vals['prorata'] = True
profile = super(AccountAssetProfile, self).create(vals) profile = super().create(vals)
acc_id = vals.get('account_asset_id') acc_id = vals.get('account_asset_id')
if acc_id: if acc_id:
account = self.env['account.account'].browse(acc_id) account = self.env['account.account'].browse(acc_id)
@ -171,11 +170,11 @@ class AccountAssetProfile(models.Model):
if vals.get('method_time'): if vals.get('method_time'):
if vals['method_time'] != 'year' and not vals.get('prorata'): if vals['method_time'] != 'year' and not vals.get('prorata'):
vals['prorata'] = True vals['prorata'] = True
super(AccountAssetProfile, self).write(vals) res = super().write(vals)
for profile in self: # TODO last profile in self is defined as default on the related
acc_id = vals.get('account_asset_id') # account. must be improved.
if acc_id: account = self.env['account.account'].browse(
account = self.env['account.account'].browse(acc_id) vals.get('account_asset_id'))
if not account.asset_profile_id: if self and account and not account.asset_profile_id:
account.write({'asset_profile_id': profile.id}) account.write({'asset_profile_id': self[-1].id})
return True return res

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2018 Noviat # Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2018 Noviat # Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@ -12,8 +11,7 @@ class AccountInvoice(models.Model):
@api.multi @api.multi
def finalize_invoice_move_lines(self, move_lines): def finalize_invoice_move_lines(self, move_lines):
move_lines = super(AccountInvoice, self) \ move_lines = super().finalize_invoice_move_lines(move_lines)
.finalize_invoice_move_lines(move_lines)
new_lines = [] new_lines = []
for line_tuple in move_lines: for line_tuple in move_lines:
line = line_tuple[2] line = line_tuple[2]
@ -74,11 +72,9 @@ class AccountInvoice(models.Model):
@api.multi @api.multi
def action_move_create(self): def action_move_create(self):
res = super(AccountInvoice, self).action_move_create() res = super().action_move_create()
for inv in self: for inv in self:
move = inv.move_id assets = inv.move_id.line_ids.mapped('asset_id')
assets = [aml.asset_id for aml in
filter(lambda x: x.asset_id, move.line_ids)]
for asset in assets: for asset in assets:
asset.code = inv.move_name asset.code = inv.move_name
asset_line_name = asset._get_depreciation_entry_name(0) asset_line_name = asset._get_depreciation_entry_name(0)
@ -92,15 +88,15 @@ class AccountInvoice(models.Model):
assets = self.env['account.asset'] assets = self.env['account.asset']
for inv in self: for inv in self:
move = inv.move_id move = inv.move_id
assets = move.line_ids.mapped('asset_id') assets |= move.line_ids.mapped('asset_id')
super(AccountInvoice, self).action_cancel() super().action_cancel()
if assets: if assets:
assets.unlink() assets.unlink()
return True return True
@api.model @api.model
def line_get_convert(self, line, part): def line_get_convert(self, line, part):
res = super(AccountInvoice, self).line_get_convert(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 # skip empty debit/credit
if res.get('debit') or res.get('credit'): if res.get('debit') or res.get('credit'):
@ -109,19 +105,20 @@ class AccountInvoice(models.Model):
@api.model @api.model
def inv_line_characteristic_hashcode(self, invoice_line): def inv_line_characteristic_hashcode(self, invoice_line):
res = super(AccountInvoice, self).inv_line_characteristic_hashcode( res = super().inv_line_characteristic_hashcode(
invoice_line) invoice_line)
res += '-%s' % invoice_line.get('asset_profile_id', 'False') res += '-%s' % invoice_line.get('asset_profile_id', 'False')
return res return res
@api.model @api.model
def invoice_line_move_line_get(self): def invoice_line_move_line_get(self):
res = super(AccountInvoice, self).invoice_line_move_line_get() 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: for vals in res:
invline = invoice_line_obj.browse(vals['invl_id']) if vals.get('invl_id'):
if invline.asset_profile_id: invline = invoice_line_obj.browse(vals['invl_id'])
vals['asset_profile_id'] = invline.asset_profile_id.id if invline.asset_profile_id:
vals['asset_profile_id'] = invline.asset_profile_id.id
return res return res
@ -144,4 +141,4 @@ class AccountInvoiceLine(models.Model):
@api.onchange('account_id') @api.onchange('account_id')
def _onchange_account_id(self): def _onchange_account_id(self):
self.asset_profile_id = self.account_id.asset_profile_id.id self.asset_profile_id = self.account_id.asset_profile_id.id
return super(AccountInvoiceLine, self)._onchange_account_id() return super()._onchange_account_id()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2018 Noviat # Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@ -23,31 +22,30 @@ class AccountMove(models.Model):
_inherit = 'account.move' _inherit = 'account.move'
@api.multi @api.multi
def unlink(self, **kwargs): def unlink(self):
for move in self: # for move in self:
deprs = self.env['account.asset.line'].search( deprs = self.env['account.asset.line'].search(
[('move_id', '=', move.id), [('move_id', 'in', self.ids),
('type', 'in', ['depreciate', 'remove'])]) ('type', 'in', ['depreciate', 'remove'])])
if deprs and not self._context.get('unlink_from_asset'): if deprs and not self.env.context.get('unlink_from_asset'):
raise UserError( raise UserError(
_("You are not allowed to remove an accounting entry " _("You are not allowed to remove an accounting entry "
"linked to an asset." "linked to an asset."
"\nYou should remove such entries from the asset.")) "\nYou should remove such entries from the asset."))
# trigger store function # trigger store function
deprs.write({'move_id': False}) deprs.write({'move_id': False})
return super(AccountMove, self).unlink(**kwargs) return super().unlink()
@api.multi @api.multi
def write(self, vals): def write(self, vals):
if set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE): if set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE):
for move in self: deprs = self.env['account.asset.line'].search(
deprs = self.env['account.asset.line'].search( [('move_id', 'in', self.ids), ('type', '=', 'depreciate')])
[('move_id', '=', move.id), ('type', '=', 'depreciate')]) if deprs:
if deprs: raise UserError(
raise UserError( _("You cannot change an accounting entry "
_("You cannot change an accounting entry " "linked to an asset depreciation line."))
"linked to an asset depreciation line.")) return super().write(vals)
return super(AccountMove, self).write(vals)
class AccountMoveLine(models.Model): class AccountMoveLine(models.Model):
@ -65,8 +63,8 @@ class AccountMoveLine(models.Model):
self.asset_profile_id = self.account_id.asset_profile_id self.asset_profile_id = self.account_id.asset_profile_id
@api.model @api.model
def create(self, vals, **kwargs): def create(self, vals):
if vals.get('asset_id') and not self._context.get('allow_asset'): if vals.get('asset_id') and not self.env.context.get('allow_asset'):
raise UserError( raise UserError(
_("You are not allowed to link " _("You are not allowed to link "
"an accounting entry to an asset." "an accounting entry to an asset."
@ -83,70 +81,78 @@ class AccountMoveLine(models.Model):
'partner_id': vals['partner_id'], 'partner_id': vals['partner_id'],
'date_start': move.date, 'date_start': move.date,
} }
if self._context.get('company_id'): if self.env.context.get('company_id'):
temp_vals['company_id'] = self._context['company_id'] temp_vals['company_id'] = self.env.context['company_id']
temp_asset = asset_obj.new(temp_vals) temp_asset = asset_obj.new(temp_vals)
temp_asset._onchange_profile_id() temp_asset._onchange_profile_id()
asset_vals = temp_asset._convert_to_write(temp_asset._cache) asset_vals = temp_asset._convert_to_write(temp_asset._cache)
self._get_asset_analytic_values(vals, asset_vals) self._get_asset_analytic_values(vals, asset_vals)
ctx = dict(self._context, create_asset_from_move_line=True,
move_id=vals['move_id'])
asset = asset_obj.with_context( asset = asset_obj.with_context(
ctx).create(asset_vals) create_asset_from_move_line=True,
move_id=vals['move_id']).create(asset_vals)
vals['asset_id'] = asset.id vals['asset_id'] = asset.id
return super(AccountMoveLine, self).create(vals, **kwargs) return super().create(vals)
@api.multi @api.multi
def write(self, vals, **kwargs): def _prepare_asset_create(self, vals):
for aml in self: self.ensure_one()
if aml.asset_id: debit = 'debit' in vals and vals.get('debit', 0.0) or self.debit
if set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE_LINE): credit = 'credit' in vals and \
if not (self.env.context.get('allow_asset_removal') and vals.get('credit', 0.0) or self.credit
vals.keys() == ['asset_id']): depreciation_base = debit - credit
raise UserError( partner_id = 'partner' in vals and \
_("You cannot change an accounting item " vals.get('partner', False) or self.partner_id.id
"linked to an asset depreciation line.")) 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,
}
@api.multi
def write(self, vals):
if (
self.mapped('asset_id') and
set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE_LINE) and
not (
self.env.context.get('allow_asset_removal') and
list(vals.keys()) == ['asset_id'])
):
raise UserError(
_("You cannot change an accounting item "
"linked to an asset depreciation line."))
if vals.get('asset_id'): if vals.get('asset_id'):
raise UserError( raise UserError(
_("You are not allowed to link " _("You are not allowed to link "
"an accounting entry to an asset." "an accounting entry to an asset."
"\nYou should generate such entries from the asset.")) "\nYou should generate such entries from the asset."))
if vals.get('asset_profile_id'): if vals.get('asset_profile_id'):
assert len(self.ids) == 1, \ if len(self) == 1:
'This option should only be used for a single id at a time.' raise AssertionError(_(
'This option should only be used for a single id at a '
'time.'))
asset_obj = self.env['account.asset'] asset_obj = self.env['account.asset']
for aml in self: 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 continue
# create asset # create asset
debit = 'debit' in vals and vals.get('debit', 0.0) or aml.debit asset_vals = aml._prepare_asset_create(vals)
credit = 'credit' in vals and \
vals.get('credit', 0.0) or aml.credit
depreciation_base = debit - credit
partner_id = 'partner' in vals and \
vals.get('partner', False) or aml.partner_id.id
date_start = 'date' in vals and \
vals.get('date', False) or aml.date
asset_vals = {
'name': vals.get('name') or aml.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 aml.company_id.id,
}
self._play_onchange_profile_id(asset_vals) self._play_onchange_profile_id(asset_vals)
self._get_asset_analytic_values(vals, asset_vals) self._get_asset_analytic_values(vals, asset_vals)
ctx = dict(self._context, create_asset_from_move_line=True, asset = asset_obj.with_context(
move_id=aml.move_id.id) create_asset_from_move_line=True,
asset = asset_obj.with_context(ctx).create(asset_vals) move_id=aml.move_id.id).create(asset_vals)
vals['asset_id'] = asset.id vals['asset_id'] = asset.id
return super(AccountMoveLine, self).write(vals, **kwargs) return super().write(vals)
@api.model @api.model
def _get_asset_analytic_values(self, vals, asset_vals): def _get_asset_analytic_values(self, vals, asset_vals):
asset_vals['account_analytic_id'] = \ asset_vals['account_analytic_id'] = vals.get(
vals.get('analytic_account_id', False) 'analytic_account_id', False)
@api.model @api.model
def _play_onchange_profile_id(self, vals): def _play_onchange_profile_id(self, vals):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2009-2017 Noviat # Copyright 2009-2017 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@ -29,7 +28,7 @@ class DateRange(models.Model):
} }
self.env['account.asset.recompute.trigger'].sudo().create( self.env['account.asset.recompute.trigger'].sudo().create(
recompute_vals) recompute_vals)
return super(DateRange, self).create(vals) return super().create(vals)
@api.multi @api.multi
def write(self, vals): def write(self, vals):
@ -48,4 +47,4 @@ class DateRange(models.Model):
} }
self.env['account.asset.recompute.trigger'].sudo().\ self.env['account.asset.recompute.trigger'].sudo().\
create(recompute_vals) create(recompute_vals)
return super(DateRange, self).write(vals) return super().write(vals)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2014 ACSONE SA/NV (http://acsone.eu). # Copyright (c) 2014 ACSONE SA/NV (http://acsone.eu).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@ -6,7 +5,7 @@ from odoo import fields, models
class Config(models.TransientModel): class Config(models.TransientModel):
_inherit = 'account.config.settings' _inherit = 'res.config.settings'
module_account_asset_management = fields.Boolean( module_account_asset_management = fields.Boolean(
string='Assets management (OCA)', string='Assets management (OCA)',

View File

@ -0,0 +1,2 @@
It is recommended to configure your Purchase Journal with "Group Invoice Lines" to avoid the
creation of separate assets per Supplier Invoice Line.

View File

@ -0,0 +1,7 @@
- OpenERP SA
- Luc De Meyer (Noviat)
- Frédéric Clementi (camptocamp)
- Florian Dacosta (Akretion)
- Stéphane Bidoul (Acsone)
- Adrien Peiffer (Acsone)
- Akim Juillerat <akim.juillerat@camptocamp.com>

View File

@ -0,0 +1,13 @@
This Module manages the assets owned by a company. It will keep
track of depreciation's occurred on those assets. And it allows to create
accounting entries from the depreciation lines.
The full asset life-cycle is managed (from asset creation to asset removal).
Assets can be created manually as well as automatically
(via the creation of an accounting entry on the asset account).
Excel based reporting is available via the 'account_asset_management_xls' module.
The module contains a large number of functional enhancements compared to
the standard account_asset module from Odoo.

View File

@ -0,0 +1 @@
The module in NOT compatible with the standard account_asset module.

View File

@ -0,0 +1,444 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" />
<title>Assets Management</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="assets-management">
<h1 class="title">Assets Management</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/account-financial-tools/tree/11.0/account_asset_management"><img alt="OCA/account-financial-tools" src="https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/account-financial-tools-11-0/account-financial-tools-11-0-account_asset_management"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/92/11.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This Module manages the assets owned by a company. It will keep
track of depreciations occurred on those assets. And it allows to create
accounting entries from the depreciation lines.</p>
<p>The full asset life-cycle is managed (from asset creation to asset removal).</p>
<p>Assets can be created manually as well as automatically
(via the creation of an accounting entry on the asset account).</p>
<p>Excel based reporting is available via the account_asset_management_xls module.</p>
<p>The module contains a large number of functional enhancements compared to
the standard account_asset module from Odoo.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="id1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id1">Configuration</a></h1>
<p>It is recommended to configure your Purchase Journal with “Group Invoice Lines” to avoid the
creation of separate assets per Supplier Invoice Line.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id2">Usage</a></h1>
<p>The module in NOT compatible with the standard account_asset module.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/account-financial-tools/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/account-financial-tools/issues/new?body=module:%20account_asset_management%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>Noviat</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li>OpenERP SA</li>
<li>Luc De Meyer (Noviat)</li>
<li>Frédéric Clementi (camptocamp)</li>
<li>Florian Dacosta (Akretion)</li>
<li>Stéphane Bidoul (Acsone)</li>
<li>Adrien Peiffer (Acsone)</li>
<li>Akim Juillerat &lt;<a class="reference external" href="mailto:akim.juillerat&#64;camptocamp.com">akim.juillerat&#64;camptocamp.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/account-financial-tools/tree/11.0/account_asset_management">OCA/account-financial-tools</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
from . import test_account_asset_management from . import test_account_asset_management

View File

@ -1,4 +1,4 @@
<?xml version="1.0" ?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data noupdate="1"> <data noupdate="1">

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2014 ACSONE SA/NV (acsone.eu). # Copyright (c) 2014 ACSONE SA/NV (acsone.eu).
# Copyright 2009-2018 Noviat # Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@ -7,87 +6,89 @@ import calendar
from datetime import date, datetime from datetime import date, datetime
import time import time
import odoo.tests.common as common from odoo.tests.common import SavepointCase
from odoo import tools from odoo import tools
from odoo.modules.module import get_resource_path from odoo.modules.module import get_resource_path
class TestAssetManagement(common.TransactionCase): class TestAssetManagement(SavepointCase):
def _load(self, module, *args): @classmethod
tools.convert_file(self.cr, module, def _load(cls, module, *args):
tools.convert_file(cls.cr, module,
get_resource_path(module, *args), get_resource_path(module, *args),
{}, 'init', False, 'test', {}, 'init', False, 'test',
self.registry._assertion_report) cls.registry._assertion_report)
def setUp(self): @classmethod
super(TestAssetManagement, self).setUp() def setUpClass(cls):
super().setUpClass()
self._load('account', 'test', 'account_minimal_test.xml') cls._load('account', 'test', 'account_minimal_test.xml')
self._load('account_asset_management', 'tests', cls._load('account_asset_management', 'tests',
'account_asset_test_data.xml') 'account_asset_test_data.xml')
# ENVIRONEMENTS # ENVIRONEMENTS
self.asset_model = self.env['account.asset'] cls.asset_model = cls.env['account.asset']
self.dl_model = self.env['account.asset.line'] cls.dl_model = cls.env['account.asset.line']
self.remove_model = self.env['account.asset.remove'] cls.remove_model = cls.env['account.asset.remove']
self.account_invoice = self.env['account.invoice'] cls.account_invoice = cls.env['account.invoice']
self.account_move_line = self.env['account.move.line'] cls.account_move_line = cls.env['account.move.line']
self.account_account = self.env['account.account'] cls.account_account = cls.env['account.account']
self.account_journal = self.env['account.journal'] cls.account_journal = cls.env['account.journal']
self.account_invoice_line = self.env['account.invoice.line'] cls.account_invoice_line = cls.env['account.invoice.line']
# INSTANCES # INSTANCES
# Instance: company # Instance: company
self.company = self.env.ref('base.main_company') cls.company = cls.env.ref('base.main_company')
# Instance: account type (receivable) # Instance: account type (receivable)
self.type_recv = self.env.ref('account.data_account_type_receivable') cls.type_recv = cls.env.ref('account.data_account_type_receivable')
# Instance: account type (payable) # Instance: account type (payable)
self.type_payable = self.env.ref('account.data_account_type_payable') cls.type_payable = cls.env.ref('account.data_account_type_payable')
# Instance: account (receivable) # Instance: account (receivable)
self.account_recv = self.account_account.create({ cls.account_recv = cls.account_account.create({
'name': 'test_account_receivable', 'name': 'test_account_receivable',
'code': '123', 'code': '123',
'user_type_id': self.type_recv.id, 'user_type_id': cls.type_recv.id,
'company_id': self.company.id, 'company_id': cls.company.id,
'reconcile': True}) 'reconcile': True})
# Instance: account (payable) # Instance: account (payable)
self.account_payable = self.account_account.create({ cls.account_payable = cls.account_account.create({
'name': 'test_account_payable', 'name': 'test_account_payable',
'code': '321', 'code': '321',
'user_type_id': self.type_payable.id, 'user_type_id': cls.type_payable.id,
'company_id': self.company.id, 'company_id': cls.company.id,
'reconcile': True}) 'reconcile': True})
# Instance: partner # Instance: partner
self.partner = self.env.ref('base.res_partner_2') cls.partner = cls.env.ref('base.res_partner_2')
# Instance: journal # Instance: journal
self.journal = self.account_journal.search( cls.journal = cls.account_journal.search(
[('type', '=', 'purchase')])[0] [('type', '=', 'purchase')])[0]
# Instance: product # Instance: product
self.product = self.env.ref('product.product_product_4') cls.product = cls.env.ref('product.product_product_4')
# Instance: invoice line # Instance: invoice line
self.invoice_line = self.account_invoice_line.create({ cls.invoice_line = cls.account_invoice_line.create({
'name': 'test', 'name': 'test',
'account_id': self.account_payable.id, 'account_id': cls.account_payable.id,
'price_unit': 2000.00, 'price_unit': 2000.00,
'quantity': 1, 'quantity': 1,
'product_id': self.product.id}) 'product_id': cls.product.id})
# Instance: invoice # Instance: invoice
self.invoice = self.account_invoice.create({ cls.invoice = cls.account_invoice.create({
'partner_id': self.partner.id, 'partner_id': cls.partner.id,
'account_id': self.account_recv.id, 'account_id': cls.account_recv.id,
'journal_id': self.journal.id, 'journal_id': cls.journal.id,
'invoice_line_ids': [(4, self.invoice_line.id)]}) 'invoice_line_ids': [(4, cls.invoice_line.id)]})
def test_01_nonprorata_basic(self): def test_01_nonprorata_basic(self):
"""Basic tests of depreciation board computations and postings.""" """Basic tests of depreciation board computations and postings."""
@ -96,30 +97,30 @@ class TestAssetManagement(common.TransactionCase):
# #
ict0 = self.browse_ref('account_asset_management.' ict0 = self.browse_ref('account_asset_management.'
'account_asset_asset_ict0') 'account_asset_asset_ict0')
self.assertEquals(ict0.state, 'draft') self.assertEqual(ict0.state, 'draft')
self.assertEquals(ict0.purchase_value, 1500) self.assertEqual(ict0.purchase_value, 1500)
self.assertEquals(ict0.salvage_value, 0) self.assertEqual(ict0.salvage_value, 0)
self.assertEquals(ict0.depreciation_base, 1500) self.assertEqual(ict0.depreciation_base, 1500)
self.assertEquals(len(ict0.depreciation_line_ids), 1) self.assertEqual(len(ict0.depreciation_line_ids), 1)
vehicle0 = self.browse_ref('account_asset_management.' vehicle0 = self.browse_ref('account_asset_management.'
'account_asset_asset_vehicle0') 'account_asset_asset_vehicle0')
self.assertEquals(vehicle0.state, 'draft') self.assertEqual(vehicle0.state, 'draft')
self.assertEquals(vehicle0.purchase_value, 12000) self.assertEqual(vehicle0.purchase_value, 12000)
self.assertEquals(vehicle0.salvage_value, 2000) self.assertEqual(vehicle0.salvage_value, 2000)
self.assertEquals(vehicle0.depreciation_base, 10000) self.assertEqual(vehicle0.depreciation_base, 10000)
self.assertEquals(len(vehicle0.depreciation_line_ids), 1) self.assertEqual(len(vehicle0.depreciation_line_ids), 1)
# #
# I compute the depreciation boards # I compute the depreciation boards
# #
ict0.compute_depreciation_board() ict0.compute_depreciation_board()
ict0.refresh() ict0.refresh()
self.assertEquals(len(ict0.depreciation_line_ids), 4) self.assertEqual(len(ict0.depreciation_line_ids), 4)
self.assertEquals(ict0.depreciation_line_ids[1].amount, 500) self.assertEqual(ict0.depreciation_line_ids[1].amount, 500)
vehicle0.compute_depreciation_board() vehicle0.compute_depreciation_board()
vehicle0.refresh() vehicle0.refresh()
self.assertEquals(len(vehicle0.depreciation_line_ids), 6) self.assertEqual(len(vehicle0.depreciation_line_ids), 6)
self.assertEquals(vehicle0.depreciation_line_ids[1].amount, 2000) self.assertEqual(vehicle0.depreciation_line_ids[1].amount, 2000)
# #
# I post the first depreciation line # I post the first depreciation line
@ -127,15 +128,15 @@ class TestAssetManagement(common.TransactionCase):
ict0.validate() ict0.validate()
ict0.depreciation_line_ids[1].create_move() ict0.depreciation_line_ids[1].create_move()
ict0.refresh() ict0.refresh()
self.assertEquals(ict0.state, 'open') self.assertEqual(ict0.state, 'open')
self.assertEquals(ict0.value_depreciated, 500) self.assertEqual(ict0.value_depreciated, 500)
self.assertEquals(ict0.value_residual, 1000) self.assertEqual(ict0.value_residual, 1000)
vehicle0.validate() vehicle0.validate()
vehicle0.depreciation_line_ids[1].create_move() vehicle0.depreciation_line_ids[1].create_move()
vehicle0.refresh() vehicle0.refresh()
self.assertEquals(vehicle0.state, 'open') self.assertEqual(vehicle0.state, 'open')
self.assertEquals(vehicle0.value_depreciated, 2000) self.assertEqual(vehicle0.value_depreciated, 2000)
self.assertEquals(vehicle0.value_residual, 8000) self.assertEqual(vehicle0.value_residual, 8000)
def test_02_prorata_basic(self): def test_02_prorata_basic(self):
"""Prorata temporis depreciation basic test.""" """Prorata temporis depreciation basic test."""
@ -199,7 +200,7 @@ class TestAssetManagement(common.TransactionCase):
'type': 'depreciate', 'type': 'depreciate',
'init_entry': True, 'init_entry': True,
}) })
self.assertEquals(len(asset.depreciation_line_ids), 2) self.assertEqual(len(asset.depreciation_line_ids), 2)
asset.compute_depreciation_board() asset.compute_depreciation_board()
asset.refresh() asset.refresh()
# I check the depreciated value is the initial value # I check the depreciated value is the initial value
@ -245,7 +246,7 @@ class TestAssetManagement(common.TransactionCase):
'type': 'depreciate', 'type': 'depreciate',
'init_entry': True, 'init_entry': True,
}) })
self.assertEquals(len(asset.depreciation_line_ids), 2) self.assertEqual(len(asset.depreciation_line_ids), 2)
asset.compute_depreciation_board() asset.compute_depreciation_board()
asset.refresh() asset.refresh()
# I check the depreciated value is the initial value # I check the depreciated value is the initial value
@ -289,7 +290,7 @@ class TestAssetManagement(common.TransactionCase):
asset.refresh() asset.refresh()
# check values in the depreciation board # check values in the depreciation board
self.assertEquals(len(asset.depreciation_line_ids), 5) self.assertEqual(len(asset.depreciation_line_ids), 5)
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, self.assertAlmostEqual(asset.depreciation_line_ids[1].amount,
400.00, places=2) 400.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, self.assertAlmostEqual(asset.depreciation_line_ids[2].amount,
@ -318,7 +319,7 @@ class TestAssetManagement(common.TransactionCase):
asset.refresh() asset.refresh()
# check values in the depreciation board # check values in the depreciation board
self.assertEquals(len(asset.depreciation_line_ids), 15) self.assertEqual(len(asset.depreciation_line_ids), 15)
# lines prior to asset start period are grouped in the first entry # lines prior to asset start period are grouped in the first entry
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, self.assertAlmostEqual(asset.depreciation_line_ids[1].amount,
300.00, places=2) 300.00, places=2)
@ -349,7 +350,7 @@ class TestAssetManagement(common.TransactionCase):
asset.refresh() asset.refresh()
# check values in the depreciation board # check values in the depreciation board
self.assertEquals(len(asset.depreciation_line_ids), 6) self.assertEqual(len(asset.depreciation_line_ids), 6)
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, self.assertAlmostEqual(asset.depreciation_line_ids[1].amount,
400.00, places=2) 400.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, self.assertAlmostEqual(asset.depreciation_line_ids[2].amount,
@ -380,7 +381,7 @@ class TestAssetManagement(common.TransactionCase):
asset.refresh() asset.refresh()
# check values in the depreciation board # check values in the depreciation board
self.assertEquals(len(asset.depreciation_line_ids), 6) self.assertEqual(len(asset.depreciation_line_ids), 6)
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, self.assertAlmostEqual(asset.depreciation_line_ids[1].amount,
200.00, places=2) 200.00, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount, self.assertAlmostEqual(asset.depreciation_line_ids[-1].amount,
@ -415,7 +416,7 @@ class TestAssetManagement(common.TransactionCase):
}) })
wiz.remove() wiz.remove()
asset.refresh() asset.refresh()
self.assertEquals(len(asset.depreciation_line_ids), 3) self.assertEqual(len(asset.depreciation_line_ids), 3)
self.assertAlmostEqual(asset.depreciation_line_ids[1].amount, self.assertAlmostEqual(asset.depreciation_line_ids[1].amount,
81.46, places=2) 81.46, places=2)
self.assertAlmostEqual(asset.depreciation_line_ids[2].amount, self.assertAlmostEqual(asset.depreciation_line_ids[2].amount,

View File

@ -1,4 +1,4 @@
<?xml version="1.0"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<record id="view_account_form" model="ir.ui.view"> <record id="view_account_form" model="ir.ui.view">

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<record model="ir.ui.view" id="account_asset_view_form"> <record model="ir.ui.view" id="account_asset_view_form">
@ -17,8 +18,8 @@
attrs="{'invisible': [('type', '=', 'view')]}"/> attrs="{'invisible': [('type', '=', 'view')]}"/>
</header> </header>
<sheet> <sheet>
<div class="oe_button_box oe_right"> <div class="oe_button_box">
<button name="open_entries" string="Journal Entries" type="object" class="oe_inline" <button name="open_entries" string="Journal Entries" type="object" class="oe_stat_button" icon="fa-bars"
attrs="{'invisible': [('type', '=', 'view')]}"/> attrs="{'invisible': [('type', '=', 'view')]}"/>
</div> </div>
<div class="oe_title"> <div class="oe_title">
@ -77,20 +78,20 @@
<group> <group>
<separator string="Depreciation Method" colspan="2"/> <separator string="Depreciation Method" colspan="2"/>
<field name="method"/> <field name="method"/>
<field name="method_progress_factor" digits="(14, 4)" <field name="method_progress_factor"
attrs="{'invisible': [('method', 'in', ['linear', 'linear-limit'])], 'required': [('method', 'in', ['degressive', 'degr-linear', 'degr-limit'])]}"/> attrs="{'invisible': [('method', 'in', ['linear', 'linear-limit'])], 'required': [('method', 'in', ['degressive', 'degr-linear', 'degr-limit'])]}"/>
<field name="prorata" attrs="{'readonly': [('method_time', '!=', 'year')]}"/> <field name="prorata" attrs="{'readonly': [('method_time', '!=', 'year')]}"/>
</group> </group>
</group> </group>
</page> </page>
<page string="Depreciation Board"> <page string="Depreciation Board">
<header> <div>
<button type="object" name="compute_depreciation_board" <button type="object" name="compute_depreciation_board"
string="Compute" icon="fa-gears" colspan="2" string=" Compute" icon="fa-gears"
attrs="{'invisible': [('state', 'in', ['close', 'removed'])]}"/> attrs="{'invisible': [('state', 'in', ['close', 'removed'])]}"/>
</header> </div>
<field name="depreciation_line_ids" mode="tree" options="{'reload_on_button': true}"> <field name="depreciation_line_ids" mode="tree" options="{'reload_on_button': true}">
<tree string="Asset Lines" colors="blue:(move_check == False) and (init_entry == False)" create="false"> <tree string="Asset Lines" decoration-info="(move_check == False) and (init_entry == False)" create="false">
<field name="type"/> <field name="type"/>
<field name="line_date"/> <field name="line_date"/>
<field name="depreciated_value" readonly="1"/> <field name="depreciated_value" readonly="1"/>
@ -172,7 +173,7 @@
<field name="name">account.asset.tree</field> <field name="name">account.asset.tree</field>
<field name="model">account.asset</field> <field name="model">account.asset</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Assets" colors="blue:(type == 'view')"> <tree string="Assets" decoration-info="type == 'view'">
<field name="name"/> <field name="name"/>
<field name="type" invisible="1"/> <field name="type" invisible="1"/>
<field name="code"/> <field name="code"/>
@ -194,9 +195,9 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Account Asset"> <search string="Account Asset">
<filter string="Draft" name="draft" domain="[('state', '=', 'draft')]" help="Draft Assets"/> <filter string="Draft" name="draft" domain="[('state', '=', 'draft')]" help="Draft Assets"/>
<filter string="Running" domain="[('state', '=', 'open')]" help="Assets in Running State"/> <filter string="Running" name="running" domain="[('state', '=', 'open')]" help="Assets in Running State"/>
<filter string="Close" domain="[('state', '=', 'close')]" help="Assets in Close State"/> <filter string="Close" name="close" domain="[('state', '=', 'close')]" help="Assets in Close State"/>
<filter string="Removed" domain="[('state', '=', 'removed')]" help="Assets which have been removed"/> <filter string="Removed" name="removed" domain="[('state', '=', 'removed')]" help="Assets which have been removed"/>
<separator orientation="vertical"/> <separator orientation="vertical"/>
<field name="name" string="Asset"/> <field name="name" string="Asset"/>
<field name="code"/> <field name="code"/>
@ -219,37 +220,6 @@
<field name="search_view_id" ref="account_asset_view_search"/> <field name="search_view_id" ref="account_asset_view_search"/>
</record> </record>
<record model="ir.ui.view" id="account_asset_view_tree_hierarchy">
<field name="name">account.asset.hierarchy</field>
<field name="model">account.asset</field>
<field name="field_parent">child_ids</field>
<field name="arch" type="xml">
<tree string="Assets" colors="blue:(type == 'view')">
<field name="name"/>
<field name="type" invisible="1"/>
<field name="code"/>
<!--
<field name="depreciation_base" attrs="{'invisible': [('type', '=', 'view')]}"/>
<field name="value_depreciated" attrs="{'invisible': [('type', '=', 'view')]}"/>
<field name="value_residual" attrs="{'invisible': [('type', '=', 'view')]}"/>
<field name="date_start"/>
<field name="date_remove"/>
<field name="profile_id"/>
<field name="state" attrs="{'invisible': [('type', '=', 'view')]}"/>
<field name="company_id" groups="base.group_multi_company"/>
-->
</tree>
</field>
</record>
<record id="account_asset_action_hierarchy" model="ir.actions.act_window">
<field name="name">Asset Hierarchy</field>
<field name="res_model">account.asset</field>
<field name="view_type">tree</field>
<field name="domain">[('parent_id','=',False)]</field>
<field name="view_id" ref="account_asset_view_tree_hierarchy"/>
</record>
<act_window id="act_entries_open" <act_window id="act_entries_open"
name="Journal Items" name="Journal Items"
src_model="account.asset" src_model="account.asset"

Some files were not shown because too many files have changed in this diff Show More