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