diff --git a/account_asset_management/__openerp__.py b/account_asset_management/__openerp__.py index f27e83a93..1284fe1d8 100644 --- a/account_asset_management/__openerp__.py +++ b/account_asset_management/__openerp__.py @@ -22,8 +22,9 @@ ############################################################################## { 'name': 'Assets Management', - 'version': '2.1', + 'version': '2.2', 'depends': ['account'], + 'conflicts': ['account_asset'], 'author': 'OpenERP & Noviat', 'description': """ Financial asset management. @@ -45,6 +46,14 @@ The module contains a large number of functional enhancements compared to the standard account_asset module from OpenERP/Odoo. The module in NOT compatible with the standard account_asset module. + +Contributors +------------ +- OpenERP SA +- Luc De Meyer (Noviat) +- Frédéric Clementi (camptocamp) +- Florian Dacosta (Akretion) +- Stéphane Bidoul (Acsone) """, 'website': 'http://www.noviat.com', 'category': 'Accounting & Finance', diff --git a/account_asset_management/account.py b/account_asset_management/account.py index 1a5e20399..4ab1b4438 100644 --- a/account_asset_management/account.py +++ b/account_asset_management/account.py @@ -58,6 +58,9 @@ class account_fiscalyear(orm.Model): _inherit = 'account.fiscalyear' def create(self, cr, uid, vals, context=None): + # To DO : + # change logic to avoid table recompute overhead + # when a regular (duration = 1 year) new FY is created recompute_obj = self.pool.get('account.asset.recompute.trigger') user_obj = self.pool.get('res.users') recompute_vals = { @@ -93,5 +96,3 @@ class account_fiscalyear(orm.Model): cr, SUPERUSER_ID, recompute_vals, context=context) return super(account_fiscalyear, self).write( cr, uid, ids, vals, context=context) - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/account_asset_management/account_asset.py b/account_asset_management/account_asset.py index 8557fe31a..84b83f196 100644 --- a/account_asset_management/account_asset.py +++ b/account_asset_management/account_asset.py @@ -70,11 +70,23 @@ class account_asset_category(orm.Model): 'account_analytic_id': fields.many2one( 'account.analytic.account', 'Analytic account'), 'account_asset_id': fields.many2one( - 'account.account', 'Asset Account', required=True), + 'account.account', 'Asset Account', required=True, + domain=[('type', '=', 'other')]), 'account_depreciation_id': fields.many2one( - 'account.account', 'Depreciation Account', required=True), + 'account.account', 'Depreciation Account', required=True, + domain=[('type', '=', 'other')]), 'account_expense_depreciation_id': fields.many2one( - 'account.account', 'Depr. Expense Account', required=True), + 'account.account', 'Depr. Expense Account', required=True, + domain=[('type', '=', 'other')]), + 'account_plus_value_id': fields.many2one( + 'account.account', 'Plus-Value Account', required=True, + domain=[('type', '=', 'other')]), + 'account_min_value_id': fields.many2one( + 'account.account', 'Min-Value Account', required=True, + domain=[('type', '=', 'other')]), + 'account_residual_value_id': fields.many2one( + 'account.account', 'Residual Value Account', + domain=[('type', '=', 'other')]), 'journal_id': fields.many2one( 'account.journal', 'Journal', required=True), 'company_id': fields.many2one( @@ -219,6 +231,7 @@ class account_asset_asset(orm.Model): _name = 'account.asset.asset' _description = 'Asset' _order = 'date_start desc, name' + _parent_store = True def unlink(self, cr, uid, ids, context=None): for asset in self.browse(cr, uid, ids, context=context): @@ -798,36 +811,11 @@ class account_asset_asset(orm.Model): asset.write({'state': 'open'}, context=context) return True - def set_to_close(self, cr, uid, ids, context=None): - # TO DO: - # The 'set_to_close' button is currently removed from the UI - # since the state goes automatically to close upon posting of - # the last depreciation line. - # We could put this button back and launch a wizard to generate - # the last depreciation entry and - # adapt the depreciation board accordingly. - # At this moment, the end user has to manually adjust the - # deprecation table and generate the last depreciation entry manually - # via the 'create_move' button in the table. - for asset in self.browse(cr, uid, ids, context): - if asset.value_residual: - raise orm.except_orm( - _('Operation not allowed!'), - _("You cannot close an asset which has not been " - "fully depreciated." - "\nPlease create the remaining depreciation entry " - "via the Depreciation Board.")) - return self.write(cr, uid, ids, {'state': 'close'}, context=context) - def remove(self, cr, uid, ids, context=None): for asset in self.browse(cr, uid, ids, context): + ctx = dict(context, active_ids=ids, active_id=ids[0]) if asset.value_residual: - raise orm.except_orm( - _('Operation not allowed!'), - _("You cannot remove an asset which has not been " - "fully depreciated." - "\nPlease create the remaining depreciation entry " - "via the Depreciation Board.")) + ctx.update({'early_removal': True}) return { 'name': _("Generate Asset Removal entries"), 'view_type': 'form', @@ -835,7 +823,7 @@ class account_asset_asset(orm.Model): 'res_model': 'account.asset.remove', 'target': 'new', 'type': 'ir.actions.act_window', - 'context': dict(context, active_ids=ids, active_id=ids[0]), + 'context': ctx, 'nodestroy': True, } @@ -868,39 +856,28 @@ class account_asset_asset(orm.Model): res[asset.id] = _value_get(asset) return res - def _residual_compute(self, cr, uid, asset, context=None): - if asset.type == 'view': - return 0.0 - cr.execute( - "SELECT COALESCE(SUM(amount),0.0) AS amount " - "FROM account_asset_depreciation_line " - "WHERE asset_id = %s AND type='depreciate' " - "AND (init_entry=TRUE OR move_check=TRUE)", - (asset.id,)) - amount = cr.fetchone()[0] - return asset.asset_value - amount - - def _residual(self, cr, uid, ids, name, args, context=None): + def _compute_depreciation(self, cr, uid, ids, name, args, context=None): res = {} - for asset in self.browse(cr, uid, ids, context): - if asset.type == 'normal': - res[asset.id] = self._residual_compute(cr, uid, asset, context) + for asset in self.browse(cr, uid, ids, context=context): + res[asset.id] = {} + child_ids = self.search(cr, uid, + [('parent_id', 'child_of', [asset.id]), + ('type', '=', 'normal')], + context=context) + if child_ids: + cr.execute( + "SELECT COALESCE(SUM(amount),0.0) AS amount " + "FROM account_asset_depreciation_line " + "WHERE asset_id in %s AND type='depreciate' " + "AND (init_entry=TRUE OR move_check=TRUE)", + (tuple(child_ids),)) + value_depreciated = cr.fetchone()[0] else: - def _residual_get(record): - residual = self._residual_compute(cr, uid, asset, context) - for rec in record.child_ids: - residual += \ - rec.type == 'normal' and \ - self._residual_compute(cr, uid, rec, context) or \ - _residual_get(rec) - return residual - res[asset.id] = _residual_get(asset) - return res - - def _depreciated(self, cr, uid, ids, name, args, context=None): - res = {} - for asset in self.browse(cr, uid, ids, context): - res[asset.id] = asset.asset_value - asset.value_residual + value_depreciated = 0.0 + res[asset.id]['value_residual'] = \ + asset.asset_value - value_depreciated + res[asset.id]['value_depreciated'] = \ + value_depreciated return res def _move_line_check(self, cr, uid, ids, name, args, context=None): @@ -1001,7 +978,7 @@ class account_asset_asset(orm.Model): }, help="This amount represent the initial value of the asset."), 'value_residual': fields.function( - _residual, method=True, + _compute_depreciation, method=True, multi='cd', digits_compute=dp.get_precision('Account'), string='Residual Value', store={ @@ -1015,7 +992,7 @@ class account_asset_asset(orm.Model): ['amount', 'init_entry', 'move_id'], 20), }), 'value_depreciated': fields.function( - _depreciated, method=True, + _compute_depreciation, method=True, multi='cd', digits_compute=dp.get_precision('Account'), string='Depreciated Value', store={ @@ -1043,7 +1020,10 @@ class account_asset_asset(orm.Model): 'parent_id': fields.many2one( 'account.asset.asset', 'Parent Asset', readonly=True, states={'draft': [('readonly', False)]}, - domain=[('type', '=', 'view')]), + domain=[('type', '=', 'view')], + ondelete='restrict'), + 'parent_left': fields.integer('Parent Left', select=1), + 'parent_right': fields.integer('Parent Right', select=1), 'child_ids': fields.one2many( 'account.asset.asset', 'parent_id', 'Child Assets'), 'date_start': fields.date( @@ -1233,7 +1213,8 @@ class account_asset_asset(orm.Model): default.update({ 'depreciation_line_ids': [], 'account_move_line_ids': [], - 'state': 'draft'}) + 'state': 'draft', + 'history_ids': []}) return super(account_asset_asset, self).copy( cr, uid, id, default, context=context) @@ -1573,19 +1554,19 @@ class account_asset_depreciation_line(orm.Model): } def _setup_move_data(self, depreciation_line, depreciation_date, - period_ids, context): + period_id, context): asset = depreciation_line.asset_id move_data = { 'name': asset.name, 'date': depreciation_date, 'ref': depreciation_line.name, - 'period_id': period_ids, + 'period_id': period_id, 'journal_id': asset.category_id.journal_id.id, } return move_data def _setup_move_line_data(self, depreciation_line, depreciation_date, - period_ids, account_id, type, move_id, context): + period_id, account_id, type, move_id, context): asset = depreciation_line.asset_id amount = depreciation_line.amount analytic_id = False @@ -1603,7 +1584,7 @@ class account_asset_depreciation_line(orm.Model): 'account_id': account_id, 'credit': credit, 'debit': debit, - 'period_id': period_ids, + 'period_id': period_id, 'journal_id': asset.category_id.journal_id.id, 'partner_id': asset.partner_id.id, 'analytic_account_id': analytic_id, @@ -1739,5 +1720,3 @@ class account_asset_history(orm.Model): 'date': lambda *args: time.strftime('%Y-%m-%d'), 'user_id': lambda self, cr, uid, ctx: uid } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/account_asset_management/account_asset_invoice.py b/account_asset_management/account_asset_invoice.py index d4f4ce2ab..fb5b4814d 100644 --- a/account_asset_management/account_asset_invoice.py +++ b/account_asset_management/account_asset_invoice.py @@ -86,6 +86,12 @@ class account_invoice_line(orm.Model): _columns = { 'asset_category_id': fields.many2one( 'account.asset.category', 'Asset Category'), + 'asset_id': fields.many2one( + 'account.asset.asset', 'Asset', + help="Complete this field when selling an asset " + "in order to facilitate the creation of the " + "asset removal accounting entries via the " + "asset 'Removal' button"), } def onchange_account_id(self, cr, uid, ids, product_id, diff --git a/account_asset_management/account_asset_invoice_view.xml b/account_asset_management/account_asset_invoice_view.xml index 05144025e..5704c487a 100644 --- a/account_asset_management/account_asset_invoice_view.xml +++ b/account_asset_management/account_asset_invoice_view.xml @@ -24,5 +24,16 @@ + + account.invoice.customer.form + account.invoice + + + + + + + + diff --git a/account_asset_management/account_asset_view.xml b/account_asset_management/account_asset_view.xml index 590b4c210..40c910cd5 100644 --- a/account_asset_management/account_asset_view.xml +++ b/account_asset_management/account_asset_view.xml @@ -20,6 +20,9 @@ + + + @@ -76,10 +79,9 @@
@@ -194,6 +196,7 @@ + @@ -209,7 +212,7 @@ - + diff --git a/account_asset_management/account_move.py b/account_asset_management/account_move.py index e4edabdd3..db85fc71e 100644 --- a/account_asset_management/account_move.py +++ b/account_asset_management/account_move.py @@ -37,7 +37,8 @@ class account_move(orm.Model): for move_id in ids: depr_ids = depr_obj.search( cr, uid, - [('move_id', '=', move_id), ('type', '=', 'depreciate')]) + [('move_id', '=', move_id), + ('type', 'in', ['depreciate', 'remove'])]) if depr_ids and not context.get('unlink_from_asset'): raise orm.except_orm( _('Error!'), diff --git a/account_asset_management/doc/changelog.rst b/account_asset_management/doc/changelog.rst index a467bb2f6..59539875d 100644 --- a/account_asset_management/doc/changelog.rst +++ b/account_asset_management/doc/changelog.rst @@ -100,4 +100,12 @@ Enhancements/changes made by Noviat (www.noviat.com) Enhancements/changes made by Noviat (www.noviat.com) -- Support assets without depreciation table (e.g. properties that keep their value). Specify 'method_number' = 0 for such assets. \ No newline at end of file +- Support assets without depreciation table (e.g. properties that keep their value). Specify 'method_number' = 0 for such assets. + +`V2.2` +------ + +Enhancements/changes + +- Generation of accounting entries in case of early removal. + diff --git a/account_asset_management/tests/__init__.py b/account_asset_management/tests/__init__.py new file mode 100644 index 000000000..2b8fcadd1 --- /dev/null +++ b/account_asset_management/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# account_asset_management tests +# +# Copyright (c) 2014 ACSONE SA/NV (acsone.eu). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import test_account_asset_management + +checks = [ + test_account_asset_management, +] diff --git a/account_asset_management/tests/test_account_asset_management.py b/account_asset_management/tests/test_account_asset_management.py new file mode 100644 index 000000000..264061055 --- /dev/null +++ b/account_asset_management/tests/test_account_asset_management.py @@ -0,0 +1,118 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# account_asset_management tests +# +# Copyright (c) 2014 ACSONE SA/NV (acsone.eu). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import openerp.tests.common as common + + +class TestAssetManagement(common.TransactionCase): + + def setUp(self): + super(TestAssetManagement, self).setUp() + self.asset_model = self.registry('account.asset.asset') + + def test_1(self): + """ Compute depreciation boards and post assets for first year, + verify the depreciation values and values in parent assets. """ + # + # first load demo assets and do some sanity checks + # + ict0 = self.browse_ref('account_asset_management.' + 'account_asset_asset_ict0') + self.assertEquals(ict0.state, 'draft') + self.assertEquals(ict0.purchase_value, 1500) + self.assertEquals(ict0.salvage_value, 0) + self.assertEquals(ict0.asset_value, 1500) + self.assertEquals(len(ict0.depreciation_line_ids), 1) + vehicle0 = self.browse_ref('account_asset_management.' + 'account_asset_asset_vehicle0') + self.assertEquals(vehicle0.state, 'draft') + self.assertEquals(vehicle0.purchase_value, 12000) + self.assertEquals(vehicle0.salvage_value, 2000) + self.assertEquals(vehicle0.asset_value, 10000) + self.assertEquals(len(vehicle0.depreciation_line_ids), 1) + ict = self.browse_ref('account_asset_management.' + 'account_asset_view_ict') + # self.assertEquals(ict.purchase_value, 1500) + # self.assertEquals(ict.salvage_value, 0) + self.assertEquals(ict.asset_value, 1500) + vehicle = self.browse_ref('account_asset_management.' + 'account_asset_view_vehicle') + # self.assertEquals(vehicle.purchase_value, 12000) + # self.assertEquals(vehicle.salvage_value, 2000) + self.assertEquals(vehicle.asset_value, 10000) + fa = self.browse_ref('account_asset_management.' + 'account_asset_view_fa') + # self.assertEquals(fa.purchase_value, 13500) + # self.assertEquals(fa.salvage_value, 2000) + self.assertEquals(fa.asset_value, 11500) + + # + # compute depreciation boards + # + self.asset_model.compute_depreciation_board( + self.cr, self.uid, [ict0.id, vehicle0.id]) + ict0.refresh() + self.assertEquals(len(ict0.depreciation_line_ids), 4) + self.assertEquals(ict0.depreciation_line_ids[1].amount, 500) + vehicle0.refresh() + self.assertEquals(len(vehicle0.depreciation_line_ids), 6) + self.assertEquals(vehicle0.depreciation_line_ids[1].amount, 2000) + + # + # post first depreciation line + # + ict0.validate() + ict0.depreciation_line_ids[1].create_move() + ict0.refresh() + self.assertEquals(ict0.state, 'open') + self.assertEquals(ict0.value_depreciated, 500) + self.assertEquals(ict0.value_residual, 1000) + ict.refresh() + self.assertEquals(ict.value_depreciated, 500) + self.assertEquals(ict.value_residual, 1000) + vehicle0.validate() + vehicle0.depreciation_line_ids[1].create_move() + vehicle0.refresh() + self.assertEquals(vehicle0.state, 'open') + self.assertEquals(vehicle0.value_depreciated, 2000) + self.assertEquals(vehicle0.value_residual, 8000) + vehicle.refresh() + self.assertEquals(vehicle.value_depreciated, 2000) + self.assertEquals(vehicle.value_residual, 8000) + fa.refresh() + self.assertEquals(fa.value_depreciated, 2500) + self.assertEquals(fa.value_residual, 9000) + + # + # change parent and check values + # + ict0.write({'parent_id': vehicle.id}) + ict.refresh() + vehicle.refresh() + fa.refresh() + self.assertEquals(ict.value_depreciated, 0) + self.assertEquals(ict.value_residual, 0) + self.assertEquals(vehicle.value_depreciated, 2500) + self.assertEquals(vehicle.value_residual, 9000) + self.assertEquals(fa.value_depreciated, 2500) + self.assertEquals(fa.value_residual, 9000) diff --git a/account_asset_management/wizard/account_asset_remove.py b/account_asset_management/wizard/account_asset_remove.py index e43250552..27ecf2af5 100644 --- a/account_asset_management/wizard/account_asset_remove.py +++ b/account_asset_management/wizard/account_asset_remove.py @@ -23,6 +23,8 @@ from openerp.osv import fields, orm from openerp.tools.translate import _ +from dateutil.relativedelta import relativedelta +from datetime import datetime import logging _logger = logging.getLogger(__name__) @@ -31,25 +33,263 @@ class account_asset_remove(orm.TransientModel): _name = 'account.asset.remove' _description = 'Remove Asset' + _residual_value_regime_countries = ['FR'] + + def _posting_regime(self, cr, uid, context=None): + return[ + ('residual_value', _('Residual Value')), + ('gain_loss_on_sale', _('Gain/Loss on Sale')), + ] + + def _get_posting_regime(self, cr, uid, context=None): + if not context: + context = {} + asset_obj = self.pool.get('account.asset.asset') + asset = asset_obj.browse(cr, uid, context.get('active_id')) + country = asset and asset.company_id.country_id.code or False + if country in self._residual_value_regime_countries: + return 'residual_value' + else: + return 'gain_loss_on_sale' + + def _get_sale(self, cr, uid, context=None): + if not context: + context = {} + inv_line_obj = self.pool.get('account.invoice.line') + currency_obj = self.pool.get('res.currency') + asset_id = context.get('active_id') + sale_value = 0.0 + account_sale_id = False + inv_line_ids = inv_line_obj.search( + cr, uid, [('asset_id', '=', asset_id)], context=context) + for line in inv_line_obj.browse(cr, uid, inv_line_ids): + inv = line.invoice_id + comp_curr = inv.company_id.currency_id + inv_curr = inv.currency_id + if line.invoice_id.state in ['open', 'paid']: + account_sale_id = line.account_id.id + amount = line.price_subtotal + if inv_curr != comp_curr: + amount = currency_obj.compute( + cr, uid, inv_curr.id, comp_curr.id, amount, + context=context) + sale_value += amount + return {'sale_value': sale_value, 'account_sale_id': account_sale_id} + + def _get_sale_value(self, cr, uid, context=None): + return self._get_sale(cr, uid, context=context)['sale_value'] + + def _get_sale_account(self, cr, uid, context=None): + return self._get_sale(cr, uid, context=context)['account_sale_id'] + + def _get_plus_account(self, cr, uid, context=None): + if not context: + context = {} + acc = False + asset_obj = self.pool.get('account.asset.asset') + asset = asset_obj.browse(cr, uid, context.get('active_id')) + if asset: + acc = asset.category_id.account_plus_value_id + return acc and acc.id or False + + def _get_min_account(self, cr, uid, context=None): + if not context: + context = {} + acc = False + asset_obj = self.pool.get('account.asset.asset') + asset = asset_obj.browse(cr, uid, context.get('active_id')) + if asset: + acc = asset.category_id.account_min_value_id + return acc and acc.id or False + + def _get_residual_account(self, cr, uid, context=None): + if not context: + context = {} + acc = False + asset_obj = self.pool.get('account.asset.asset') + asset = asset_obj.browse(cr, uid, context.get('active_id')) + if asset: + acc = asset.category_id.account_residual_value_id + return acc and acc.id or False + _columns = { - 'date_remove': fields.date('Asset Removal Date', required=True), + 'date_remove': fields.date( + 'Asset Removal Date', required=True, + help="Removal date must be after the last posted entry " + "in case of early removal"), 'period_id': fields.many2one( 'account.period', 'Force Period', domain=[('state', '<>', 'done')], help="Keep empty to use the period of the removal ate."), + 'sale_value': fields.float('Sale Value'), + 'account_sale_id': fields.many2one( + 'account.account', 'Asset Sale Account', + domain=[('type', '=', 'other')]), + 'account_plus_value_id': fields.many2one( + 'account.account', 'Plus-Value Account', + domain=[('type', '=', 'other')]), + 'account_min_value_id': fields.many2one( + 'account.account', 'Min-Value Account', + domain=[('type', '=', 'other')]), + 'account_residual_value_id': fields.many2one( + 'account.account', 'Residual Value Account', + domain=[('type', '=', 'other')]), + 'posting_regime': fields.selection( + _posting_regime, 'Removal Entry Policy', + required=True, + help="Removal Entry Policy \n" + " * Residual Value: The non-depreciated value will be " + "posted on the 'Residual Value Account' \n" + " * Gain/Loss on Sale: The Gain or Loss will be posted on " + "the 'Plus-Value Account' or 'Min-Value Account' "), 'note': fields.text('Notes'), } + _defaults = { + 'sale_value': _get_sale_value, + 'account_sale_id': _get_sale_account, + 'account_plus_value_id': _get_plus_account, + 'account_min_value_id': _get_min_account, + 'account_residual_value_id': _get_residual_account, + 'posting_regime': _get_posting_regime, + } + + _sql_constraints = [( + 'sale_value', 'CHECK (sale_value>=0)', + 'The Sale Value must be positive!')] + + def _prepare_early_removal(self, cr, uid, + asset, date_remove, context=None): + """ + Generate last depreciation entry on the day before the removal date. + """ + asset_line_obj = self.pool.get('account.asset.depreciation.line') + + digits = self.pool.get('decimal.precision').precision_get( + cr, uid, 'Account') + + dl_ids = asset_line_obj.search( + cr, uid, + [('asset_id', '=', asset.id), ('type', '=', 'depreciate'), + ('init_entry', '=', False), ('move_check', '=', False)], + order='line_date asc') + first_to_depreciate_dl = asset_line_obj.browse(cr, uid, dl_ids[0]) + + first_date = first_to_depreciate_dl.line_date + if date_remove > first_date: + raise orm.except_orm( + _('Error!'), + _("You can't make an early removal if all the depreciation " + "lines for previous periods are not posted.")) + + last_depr_date = first_to_depreciate_dl.previous_id.line_date + period_number_days = ( + datetime.strptime(first_date, '%Y-%m-%d') - + datetime.strptime(last_depr_date, '%Y-%m-%d')).days + date_remove = datetime.strptime(date_remove, '%Y-%m-%d') + new_line_date = date_remove + relativedelta(days=-1) + to_depreciate_days = ( + new_line_date - + datetime.strptime(last_depr_date, '%Y-%m-%d')).days + to_depreciate_amount = round( + float(to_depreciate_days) / float(period_number_days) * + first_to_depreciate_dl.amount, digits) + residual_value = asset.value_residual - to_depreciate_amount + if to_depreciate_amount: + update_vals = { + 'amount': to_depreciate_amount, + 'line_date': new_line_date + } + first_to_depreciate_dl.write(update_vals) + asset_line_obj.create_move( + cr, uid, [dl_ids[0]], context=context) + dl_ids.pop(0) + asset_line_obj.unlink(cr, uid, dl_ids, context=context) + return residual_value + + def _get_removal_data(self, cr, uid, wiz_data, asset, residual_value, + context=None): + move_lines = [] + partner_id = asset.partner_id and asset.partner_id.id or False + categ = asset.category_id + + # asset and asset depreciation account reversal + depr_amount = asset.asset_value - residual_value + move_line_vals = { + 'name': asset.name, + 'account_id': categ.account_depreciation_id.id, + 'debit': depr_amount > 0 and depr_amount or 0.0, + 'credit': depr_amount < 0 and -depr_amount or 0.0, + 'partner_id': partner_id, + 'asset_id': asset.id + } + move_lines.append((0, 0, move_line_vals)) + move_line_vals = { + 'name': asset.name, + 'account_id': categ.account_asset_id.id, + 'debit': asset.asset_value < 0 and -asset.asset_value or 0.0, + 'credit': asset.asset_value > 0 and asset.asset_value or 0.0, + 'partner_id': partner_id, + 'asset_id': asset.id + } + move_lines.append((0, 0, move_line_vals)) + + if residual_value: + if wiz_data.posting_regime == 'residual_value': + move_line_vals = { + 'name': asset.name, + 'account_id': wiz_data.account_residual_value_id.id, + 'debit': residual_value, + 'credit': 0.0, + 'partner_id': partner_id, + 'asset_id': asset.id + } + move_lines.append((0, 0, move_line_vals)) + elif wiz_data.posting_regime == 'gain_loss_on_sale': + if wiz_data.sale_value: + sale_value = wiz_data.sale_value + move_line_vals = { + 'name': asset.name, + 'account_id': wiz_data.account_sale_id.id, + 'debit': sale_value, + 'credit': 0.0, + 'partner_id': partner_id, + 'asset_id': asset.id + } + move_lines.append((0, 0, move_line_vals)) + balance = wiz_data.sale_value - residual_value + account_id = balance > 0 and wiz_data.account_plus_value_id.id \ + or wiz_data.account_min_value_id.id + move_line_vals = { + 'name': asset.name, + 'account_id': account_id, + 'debit': balance < 0 and -balance or 0.0, + 'credit': balance > 0 and balance or 0.0, + 'partner_id': partner_id, + 'asset_id': asset.id + } + move_lines.append((0, 0, move_line_vals)) + + return move_lines + def remove(self, cr, uid, ids, context=None): asset_obj = self.pool.get('account.asset.asset') asset_line_obj = self.pool.get('account.asset.depreciation.line') - move_line_obj = self.pool.get('account.move.line') move_obj = self.pool.get('account.move') period_obj = self.pool.get('account.period') - wiz_data = self.browse(cr, uid, ids[0], context=context) asset_id = context['active_id'] asset = asset_obj.browse(cr, uid, asset_id, context=context) + asset_ref = asset.code and '%s (ref: %s)' \ + % (asset.name, asset.code) or asset.name + wiz_data = self.browse(cr, uid, ids[0], context=context) + + if context.get('early_removal'): + residual_value = self._prepare_early_removal( + cr, uid, asset, wiz_data.date_remove, context=context) + else: + residual_value = asset.residual_value + ctx = dict(context, company_id=asset.company_id.id) period_id = wiz_data.period_id and wiz_data.period_id.id or False if not period_id: @@ -82,33 +322,6 @@ class account_asset_remove(orm.TransientModel): 'narration': wiz_data.note, } move_id = move_obj.create(cr, uid, move_vals, context=context) - partner_id = asset.partner_id and asset.partner_id.id or False - move_line_obj.create(cr, uid, { - 'name': asset.name, - 'ref': line_name, - 'move_id': move_id, - 'account_id': asset.category_id.account_depreciation_id.id, - 'debit': asset.asset_value > 0 and asset.asset_value or 0.0, - 'credit': asset.asset_value < 0 and -asset.asset_value or 0.0, - 'period_id': period_id, - 'journal_id': journal_id, - 'partner_id': partner_id, - 'date': wiz_data.date_remove, - 'asset_id': asset.id - }, context={'allow_asset': True}) - move_line_obj.create(cr, uid, { - 'name': asset.name, - 'ref': line_name, - 'move_id': move_id, - 'account_id': asset.category_id.account_asset_id.id, - 'debit': asset.asset_value < 0 and -asset.asset_value or 0.0, - 'credit': asset.asset_value > 0 and asset.asset_value or 0.0, - 'period_id': period_id, - 'journal_id': journal_id, - 'partner_id': partner_id, - 'date': wiz_data.date_remove, - 'asset_id': asset.id - }, context={'allow_asset': True}) # create asset line asset_line_vals = { @@ -122,6 +335,20 @@ class account_asset_remove(orm.TransientModel): asset_line_obj.create(cr, uid, asset_line_vals, context=context) asset.write({'state': 'removed', 'date_remove': wiz_data.date_remove}) - return {'type': 'ir.actions.act_window_close'} + # create move lines + move_lines = self._get_removal_data( + cr, uid, wiz_data, asset, residual_value, context=context) + move_obj.write(cr, uid, [move_id], {'line_id': move_lines}, + context=dict(context, allow_asset=True)) -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + return { + 'name': _("Asset '%s' Removal Journal Entry") % asset_ref, + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'account.move', + 'view_id': False, + 'type': 'ir.actions.act_window', + 'context': context, + 'nodestroy': True, + 'domain': [('id', '=', move_id)], + } diff --git a/account_asset_management/wizard/account_asset_remove_view.xml b/account_asset_management/wizard/account_asset_remove_view.xml index c8889bcf3..7b1ab6e53 100644 --- a/account_asset_management/wizard/account_asset_remove_view.xml +++ b/account_asset_management/wizard/account_asset_remove_view.xml @@ -7,12 +7,19 @@ account.asset.remove
- - + - - + + + + + + + + + +