performance improvement + early removal

This commit is contained in:
luc-demeyer
2014-12-15 15:19:29 +01:00
parent ac56590e04
commit d02a73afbf
12 changed files with 516 additions and 118 deletions

View File

@@ -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',

View File

@@ -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:

View File

@@ -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:

View File

@@ -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,

View File

@@ -24,5 +24,16 @@
</field>
</record>
<record model="ir.ui.view" id="view_customer_invoice_asset">
<field name="name">account.invoice.customer.form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='invoice_line']/tree/field[@name='quantity']" position="before">
<field name="asset_id" groups="account.group_account_manager"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -20,6 +20,9 @@
<field name="account_asset_id"/>
<field name="account_depreciation_id"/>
<field name="account_expense_depreciation_id"/>
<field name="account_plus_value_id"/>
<field name="account_min_value_id"/>
<field name="account_residual_value_id"/>
</group>
<group string="Depreciation Dates">
<field name="method_time" on_change="onchange_method_time(method_time)"/>
@@ -76,10 +79,9 @@
<header>
<button name="validate" states="draft" string="Confirm Asset" type="object" class="oe_highlight"/>
<button name="set_to_draft" states="open,close" string="Set to Draft" type="object" groups="account.group_account_manager"/>
<!-- <button name="set_to_close" states="open" string="Set to Close" type="object" class="oe_highlight"/> -->
<button name="remove" string="Set to Removed" type="object" groups="account.group_account_manager"
attrs="{'invisible':['|',('method_time','!=','year'),('state','!=','close')]}"
help="Generate the removal entries for a fully depreciated asset."/>
<button name="remove" string="Remove" type="object" groups="account.group_account_manager"
attrs="{'invisible':['|', ('method_time', '!=', 'year'),('state', 'not in', ['open', 'close'])]}"
help="Asset removal."/>
<field name="state" widget="statusbar" statusbar_visible="draft,open,close,removed"/>
</header>
<sheet>
@@ -194,6 +196,7 @@
<page string="History">
<field name="account_move_line_ids" readonly="1">
<tree colors="red:state == 'draft';black:state == 'valid'" string="Journal Items">
<field name="move_id"/>
<field name="journal_id"/>
<field name="period_id"/>
<field name="date"/>
@@ -209,7 +212,7 @@
<field name="move_id" options='{"no_open":True}'/>
<newline/>
<field name="name"/>
<field name="ref"/>
<field name="ref"/>
<newline/>
<field name="date"/>
<field name="period_id" options='{"no_open":True}'/>

View File

@@ -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!'),

View File

@@ -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.
- 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.

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_account_asset_management
checks = [
test_account_asset_management,
]

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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)

View File

@@ -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)],
}

View File

@@ -7,12 +7,19 @@
<field name="model">account.asset.remove</field>
<field name="arch" type="xml">
<form string="Remove Asset" version="7.0">
<group colspan="4" col="2">
<separator string="Specify the asset removal date" colspan="2"/>
<group colspan="4" col="4">
<field name="date_remove"/>
<field name="period_id"/>
<separator string="Notes" colspan="2"/>
<field name="note" nolabel="1" colspan="2"/>
<field name="sale_value"/>
<field name="account_sale_id" attrs="{'invisible': [('sale_value', '=', 0.0)], 'required': [('sale_value', '>', 0.0)]}"/>
<newline/>
<field name="posting_regime"/>
<newline/>
<field name="account_plus_value_id" attrs="{'invisible': [('posting_regime', '=', 'residual_value')], 'required': [('posting_regime', '!=', 'residual_value')]}"/>
<field name="account_min_value_id" attrs="{'invisible': [('posting_regime', '=', 'residual_value')], 'required': [('posting_regime', '!=', 'residual_value')]}"/>
<field name="account_residual_value_id" attrs="{'invisible': [('posting_regime', '!=', 'residual_value')], 'required': [('posting_regime', '=', 'residual_value')]}"/>
<separator string="Notes" colspan="4"/>
<field name="note" nolabel="1" colspan="4"/>
</group>
<newline/>
<separator colspan="4"/>