From f92b1d424b67091e68fd7a3af44a52406268e44f Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Mon, 20 Jul 2015 11:34:30 +0200 Subject: [PATCH] [IMP] account_asset: various improvement. History management now use the chatter (removal of dedicated history object) Assets are now linked to created account.move instead of account.move.line (which was meaningless since we have the invoice reference and besides didn't show the whole entries). Added a feature to close or dispose of an asset Chatter messages have been improved. Fixed some demo data. Removed 'note' field of 'change duration' wizard because it was redundant with the name field. --- addons/account_asset/account_asset.py | 183 ++++++++++++------ addons/account_asset/account_asset_demo.yml | 4 +- addons/account_asset/account_asset_invoice.py | 6 - .../security/ir.model.access.csv | 2 - .../account_asset/tests/test_account_asset.py | 8 +- .../views/account_asset_view.xml | 67 ++----- .../wizard/account_asset_change_duration.py | 20 +- .../account_asset_change_duration_view.xml | 2 - 8 files changed, 144 insertions(+), 148 deletions(-) diff --git a/addons/account_asset/account_asset.py b/addons/account_asset/account_asset.py index df80e2279d0..16a585d2991 100644 --- a/addons/account_asset/account_asset.py +++ b/addons/account_asset/account_asset.py @@ -3,7 +3,6 @@ from datetime import date, datetime from dateutil.relativedelta import relativedelta -import openerp.addons.decimal_precision as dp from openerp import api, fields, models, _ from openerp.exceptions import UserError, ValidationError from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DF @@ -55,7 +54,7 @@ class AccountAssetAsset(models.Model): _description = 'Asset/Revenue Recognition' _inherit = ['mail.thread', 'ir.needaction_mixin'] - account_move_line_ids = fields.One2many('account.move.line', 'asset_id', string='Entries', readonly=True, states={'draft': [('readonly', False)]}) + account_move_ids = fields.One2many('account.move', 'asset_id', string='Entries', readonly=True, states={'draft': [('readonly', False)]}) entry_count = fields.Integer(compute='_entry_count', string='# Asset Entries') name = fields.Char(string='Asset Name', required=True, readonly=True, states={'draft': [('readonly', False)]}) code = fields.Char(string='Reference', size=32, readonly=True, states={'draft': [('readonly', False)]}) @@ -88,7 +87,6 @@ class AccountAssetAsset(models.Model): " * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond.") prorata = fields.Boolean(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 purchase date instead of the first January / Start date of fiscal year') - history_ids = fields.One2many('account.asset.history', 'asset_id', string='History', readonly=True) depreciation_line_ids = fields.One2many('account.asset.depreciation.line', 'asset_id', string='Depreciation Lines', readonly=True, states={'draft': [('readonly', False)], 'open': [('readonly', False)]}) salvage_value = fields.Float(string='Salvage Value', digits=0, readonly=True, states={'draft': [('readonly', False)]}, help="It is the amount you plan to have that you cannot depreciate.") @@ -100,8 +98,8 @@ class AccountAssetAsset(models.Model): for asset in self: if asset.state in ['open', 'close']: raise UserError(_('You cannot delete a document is in %s state.') % (asset.state,)) - if asset.account_move_line_ids: - raise UserError(_('You cannot delete a document that contains posted lines.')) + if asset.account_move_ids: + raise UserError(_('You cannot delete a document that contains posted entries.')) return super(AccountAssetAsset, self).unlink() @api.multi @@ -111,9 +109,9 @@ class AccountAssetAsset(models.Model): @return: Returns a dictionary of the effective dates of the last depreciation entry made for given asset ids. If there isn't any, return the purchase date of this asset """ self.env.cr.execute(""" - SELECT a.id as id, COALESCE(MAX(l.date),a.date) AS date + SELECT a.id as id, COALESCE(MAX(m.date),a.date) AS date FROM account_asset_asset a - LEFT JOIN account_move_line l ON (l.asset_id = a.id) + LEFT JOIN account_move m ON (m.asset_id = a.id) WHERE a.id IN %s GROUP BY a.id, a.date """, (tuple(self.ids),)) result = dict(self.env.cr.fetchall()) @@ -170,7 +168,7 @@ class AccountAssetAsset(models.Model): # Remove old unposted depreciation lines. We cannot use unlink() with One2many field commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids] - if self.value != 0.0: + if self.value_residual != 0.0: amount_to_depr = residual_amount = self.value_residual if self.prorata: depreciation_date = datetime.strptime(self._get_last_depreciation_date()[self.id], DF).date() @@ -217,15 +215,76 @@ class AccountAssetAsset(models.Model): @api.multi def validate(self): self.write({'state': 'open'}) + fields = [ + 'method', + 'method_number', + 'method_period', + 'method_end', + 'method_progress_factor', + 'method_time', + 'salvage_value', + 'invoice_id', + ] + ref_tracked_fields = self.env['account.asset.asset'].fields_get(fields) + for asset in self: + tracked_fields = ref_tracked_fields.copy() + if asset.method == 'linear': + del(tracked_fields['method_progress_factor']) + if asset.method_time != 'end': + del(tracked_fields['method_end']) + else: + del(tracked_fields['method_number']) + dummy, tracking_value_ids = asset._message_track(tracked_fields, dict.fromkeys(fields)) + asset.message_post(subject=_('Asset created'), tracking_value_ids=tracking_value_ids) @api.multi def set_to_close(self): - unposted_dep_line = self.env['account.asset.depreciation.line'].search_count( - [('asset_id', 'in', self.ids), ('move_check', '=', False)]) - if unposted_dep_line: - raise UserError(_('You cannot close a document which has unposted lines.')) - self.message_post(body=_("Document closed.")) - self.write({'state': 'close'}) + move_ids = [] + for asset in self: + unposted_depreciation_line_ids = asset.depreciation_line_ids.filtered(lambda x: not x.move_check) + if unposted_depreciation_line_ids: + old_values = { + 'method_end': asset.method_end, + 'method_number': asset.method_number, + } + + # Remove all unposted depr. lines + commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids] + + # Create a new depr. line with the residual amount and post it + sequence = len(asset.depreciation_line_ids) - len(unposted_depreciation_line_ids) + 1 + today = datetime.today().strftime(DF) + vals = { + 'amount': asset.value_residual, + 'asset_id': asset.id, + 'sequence': sequence, + 'name': (asset.code or '') + '/' + str(sequence), + 'remaining_value': 0, + 'depreciated_value': asset.value - asset.salvage_value, # the asset is completely depreciated + 'depreciation_date': today, + } + commands.append((0, False, vals)) + asset.write({'depreciation_line_ids': commands, 'method_end': today, 'method_number': sequence}) + tracked_fields = self.env['account.asset.asset'].fields_get(['method_number', 'method_end']) + changes, tracking_value_ids = asset._message_track(tracked_fields, old_values) + if changes: + asset.message_post(subject=_('Asset sold or disposed. Accounting entry awaiting for validation.'), tracking_value_ids=tracking_value_ids) + move_ids += asset.depreciation_line_ids[-1].create_move(post_move=False) + if move_ids: + name = _('Disposal Move') + view_mode = 'form' + if len(move_ids) > 1: + name = _('Disposal Moves') + view_mode = 'tree,form' + return { + 'name': name, + 'view_type': 'form', + 'view_mode': view_mode, + 'res_model': 'account.move', + 'type': 'ir.actions.act_window', + 'target': 'current', + 'res_id': move_ids[0], + } @api.multi def set_to_draft(self): @@ -245,10 +304,10 @@ class AccountAssetAsset(models.Model): self.currency_id = self.company_id.currency_id.id @api.multi - @api.depends('account_move_line_ids') + @api.depends('account_move_ids') def _entry_count(self): for asset in self: - asset.entry_count = self.env['account.move.line'].search_count([('asset_id', '=', asset.id)]) + asset.entry_count = self.env['account.move'].search_count([('asset_id', '=', asset.id)]) @api.one @api.constrains('prorata', 'method_time') @@ -314,10 +373,10 @@ class AccountAssetAsset(models.Model): @api.multi def open_entries(self): return { - 'name': _('Journal Items'), + 'name': _('Journal Entries'), 'view_type': 'form', 'view_mode': 'tree,form', - 'res_model': 'account.move.line', + 'res_model': 'account.move', 'view_id': False, 'type': 'ir.actions.act_window', 'context': dict(self.env.context or {}, search_default_asset_id=self.id, default_asset_id=self.id), @@ -345,9 +404,8 @@ class AccountAssetDepreciationLine(models.Model): self.move_check = bool(self.move_id) @api.multi - def create_move(self): - created_move_ids = [] - asset_ids = [] + def create_move(self, post_move=True): + created_moves = self.env['account.move'] for line in self: depreciation_date = self.env.context.get('depreciation_date') or line.depreciation_date or fields.Date.context_today(self) company_currency = line.asset_id.company_id.currency_id @@ -372,7 +430,6 @@ class AccountAssetDepreciationLine(models.Model): 'amount_currency': company_currency != current_currency and - sign * line.amount or 0.0, 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id if categ_type == 'sale' else False, 'date': depreciation_date, - 'asset_id': line.asset_id.id if categ_type == 'sale' else False, } move_line_2 = { 'name': asset_name, @@ -385,43 +442,52 @@ class AccountAssetDepreciationLine(models.Model): 'amount_currency': company_currency != current_currency and sign * line.amount or 0.0, 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id if categ_type == 'purchase' else False, 'date': depreciation_date, - 'asset_id': line.asset_id.id if categ_type == 'purchase' else False } move_vals = { 'ref': reference, 'date': depreciation_date or False, 'journal_id': line.asset_id.category_id.journal_id.id, 'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)], + 'asset_id': line.asset_id.id, } move = self.env['account.move'].create(move_vals) line.write({'move_id': move.id, 'move_check': True}) - created_move_ids.append(move.id) - move.post() - asset_ids.append(line.asset_id) - partner_name = line.asset_id.partner_id.name - currency_name = line.asset_id.currency_id.name + created_moves |= move - def _format_message(message_description, tracked_values): - message = '' - if message_description: - message = '%s' % message_description - for name, values in tracked_values.iteritems(): - message += '
    • %s: ' % name - message += '%s
' % values - return message + if post_move and created_moves: + created_moves.post() + return [x.id for x in created_moves] - msg_values = {_('Currency'): currency_name, _('Amount'): line.amount} - if partner_name: - msg_values[_('Partner')] = partner_name - msg = _format_message(_('Depreciation line posted.'), msg_values) - line.asset_id.message_post(body=msg) + @api.multi + def post_lines_and_close_asset(self): # we re-evaluate the assets to determine whether we can close them - for asset in asset_ids: + for line in self: + line.log_message_when_posted() + asset = line.asset_id if asset.currency_id.is_zero(asset.value_residual): asset.message_post(body=_("Document closed.")) asset.write({'state': 'close'}) - asset.compute_depreciation_board() - return created_move_ids + + @api.multi + def log_message_when_posted(self): + def _format_message(message_description, tracked_values): + message = '' + if message_description: + message = '%s' % message_description + for name, values in tracked_values.iteritems(): + message += '
    • %s: ' % name + message += '%s
' % values + return message + + for line in self: + if line.move_id and line.move_id.state == 'draft': + partner_name = line.asset_id.partner_id.name + currency_name = line.asset_id.currency_id.name + msg_values = {_('Currency'): currency_name, _('Amount'): line.amount} + if partner_name: + msg_values[_('Partner')] = partner_name + msg = _format_message(_('Depreciation line posted.'), msg_values) + line.asset_id.message_post(body=msg) @api.multi def unlink(self): @@ -435,25 +501,14 @@ class AccountAssetDepreciationLine(models.Model): return super(AccountAssetDepreciationLine, self).unlink() -class AccountMoveLine(models.Model): - _inherit = 'account.move.line' +class AccountMove(models.Model): + _inherit = 'account.move' + asset_id = fields.Many2one('account.asset.asset', string='Asset', ondelete="restrict") - -class AccountAssetHistory(models.Model): - _name = 'account.asset.history' - _description = 'Asset history' - _order = 'date desc' - - name = fields.Char(string='History name', index=True) - user_id = fields.Many2one('res.users', string='User', required=True, default=lambda self: self.env.user) - date = fields.Date(required=True, default=fields.Date.context_today) - asset_id = fields.Many2one('account.asset.asset', string='Asset', required=True) - method_time = fields.Selection([('number', 'Number of Depreciations'), ('end', 'Ending Date')], string='Time Method', required=True, - help="The method to use to compute the dates and number of depreciation lines.\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.") - method_number = fields.Integer(string='Number of Depreciations', help="The number of depreciations needed to depreciate your asset") - method_period = fields.Integer(string='Period Length', help="Time in months between two depreciations") - method_end = fields.Date(string='Ending date') - note = fields.Text() + @api.multi + def post(self): + for move in self: + if move.asset_id: + move.asset_id.depreciation_line_ids.post_lines_and_close_asset() + return super(AccountMove, self).post() diff --git a/addons/account_asset/account_asset_demo.yml b/addons/account_asset/account_asset_demo.yml index 11121257d42..13afa24c9e6 100644 --- a/addons/account_asset/account_asset_demo.yml +++ b/addons/account_asset/account_asset_demo.yml @@ -15,7 +15,7 @@ 'journal_id': journal_id[0], 'name': 'Hardware - 3 Years', 'method_number': 3, - 'account_asset_id': xfa_account_id[0], + 'account_asset_id': expense_account_id[0], 'account_depreciation_id': xfa_account_id[0], 'account_income_recognition_id': expense_account_id[0], } @@ -24,7 +24,7 @@ 'journal_id': journal_id[0], 'name': 'Cars - 5 Years', 'method_number': 5, - 'account_asset_id': xfa_account_id[0], + 'account_asset_id': expense_account_id[0], 'account_depreciation_id': xfa_account_id[0], 'account_income_recognition_id': expense_account_id[0], } diff --git a/addons/account_asset/account_asset_invoice.py b/addons/account_asset/account_asset_invoice.py index 2814f39d7ba..276a6a22d43 100644 --- a/addons/account_asset/account_asset_invoice.py +++ b/addons/account_asset/account_asset_invoice.py @@ -17,12 +17,6 @@ class AccountInvoice(models.Model): inv.invoice_line_ids.asset_create() return result - @api.model - def line_get_convert(self, line, part): - res = super(AccountInvoice, self).line_get_convert(line, part) - res['asset_id'] = line.get('asset_id', False) - return res - class AccountInvoiceLine(models.Model): _inherit = 'account.invoice.line' diff --git a/addons/account_asset/security/ir.model.access.csv b/addons/account_asset/security/ir.model.access.csv index e09f65aa1d8..de884b5720b 100644 --- a/addons/account_asset/security/ir.model.access.csv +++ b/addons/account_asset/security/ir.model.access.csv @@ -1,10 +1,8 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_account_asset_category,account.asset.category,model_account_asset_category,account.group_account_user,1,0,0,0 access_account_asset_asset,account.asset.asset,model_account_asset_asset,account.group_account_user,1,0,0,0 -access_account_asset_history,account.asset.history,model_account_asset_history,account.group_account_user,1,0,0,0 access_account_asset_category_manager,account.asset.category,model_account_asset_category,account.group_account_manager,1,1,1,1 access_account_asset_asset_manager,account.asset.asset,model_account_asset_asset,account.group_account_manager,1,1,1,1 -access_account_asset_history_manager,account.asset.history,model_account_asset_history,account.group_account_manager,1,1,1,1 access_account_asset_depreciation_line,account.asset.depreciation.line,model_account_asset_depreciation_line,account.group_account_user,1,0,0,0 access_account_asset_depreciation_line_manager,account.asset.depreciation.line,model_account_asset_depreciation_line,account.group_account_manager,1,1,1,1 access_asset_asset_report,asset.asset.report,model_asset_asset_report,account.group_account_user,1,0,0,0 diff --git a/addons/account_asset/tests/test_account_asset.py b/addons/account_asset/tests/test_account_asset.py index afb2a1d4aa2..e4871e39680 100644 --- a/addons/account_asset/tests/test_account_asset.py +++ b/addons/account_asset/tests/test_account_asset.py @@ -42,7 +42,7 @@ class TestAccountAsset(common.TransactionCase): # I check the move line is created. asset = self.env['account.asset.asset'].browse([self.ref("account_asset.account_asset_asset_vehicles_test0")])[0] - self.assertEqual(len(asset.depreciation_line_ids), len(asset.account_move_line_ids), + self.assertEqual(len(asset.depreciation_line_ids), len(asset.account_move_ids), 'Move lines not created correctly') # I Check that After creating all the moves of depreciation lines the state "Close". @@ -86,13 +86,13 @@ class TestAccountAsset(common.TransactionCase): line.create_move() # I check that move line is created from posted installment lines. - self.assertEqual(len(recognition.depreciation_line_ids), len(recognition.account_move_line_ids), + self.assertEqual(len(recognition.depreciation_line_ids), len(recognition.account_move_ids), 'Move lines not created correctly.') # I check data in move line and installment line. first_installment_line = recognition.depreciation_line_ids[0] - first_move_line = recognition.account_move_line_ids[0] - self.assertEqual(first_installment_line.amount, first_move_line.credit, + first_move = recognition.account_move_ids[0] + self.assertEqual(first_installment_line.amount, first_move.amount, 'First installment line amount is incorrect.') remaining_value = recognition.value - first_installment_line.amount self.assertEqual(first_installment_line.remaining_value, recognition.value - first_installment_line.amount, diff --git a/addons/account_asset/views/account_asset_view.xml b/addons/account_asset/views/account_asset_view.xml index e7adecc52c4..d351d3e5865 100644 --- a/addons/account_asset/views/account_asset_view.xml +++ b/addons/account_asset/views/account_asset_view.xml @@ -92,7 +92,7 @@