2
0
account-financial-tools/account_asset_management/models/account_asset_line.py
2022-12-24 00:42:21 +01:00

281 lines
11 KiB
Python

# Copyright 2009-2018 Noviat
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models, _
import odoo.addons.decimal_precision as dp
from odoo.exceptions import UserError
class AccountAssetLine(models.Model):
_name = 'account.asset.line'
_description = 'Asset depreciation table line'
_order = 'type, line_date'
name = fields.Char(string='Depreciation Name', size=64, readonly=True)
asset_id = fields.Many2one(
comodel_name='account.asset', string='Asset',
required=True, ondelete='cascade')
previous_id = fields.Many2one(
comodel_name='account.asset.line',
string='Previous Depreciation Line',
readonly=True)
parent_state = fields.Selection(
related='asset_id.state',
string='State of Asset',
readonly=True,
)
depreciation_base = fields.Float(
related='asset_id.depreciation_base',
string='Depreciation Base',
readonly=True,
)
amount = fields.Float(
string='Amount', digits=dp.get_precision('Account'),
required=True)
remaining_value = fields.Float(
compute='_compute_values',
digits=dp.get_precision('Account'),
string='Next Period Depreciation',
store=True)
depreciated_value = fields.Float(
compute='_compute_values',
digits=dp.get_precision('Account'),
string='Amount Already Depreciated',
store=True)
line_date = fields.Date(string='Date', required=True)
line_days = fields.Integer(
string='Days',
readonly=True,
)
move_id = fields.Many2one(
comodel_name='account.move',
string='Depreciation Entry', readonly=True)
move_check = fields.Boolean(
compute='_compute_move_check',
string='Posted',
store=True)
type = fields.Selection(
selection=[
('create', 'Depreciation Base'),
('depreciate', 'Depreciation'),
('remove', 'Asset Removal')],
readonly=True, default='depreciate')
init_entry = fields.Boolean(
string='Initial Balance Entry',
help="Set this flag for entries of previous fiscal years "
"for which Odoo has not generated accounting entries.")
@api.depends('amount', 'previous_id', 'type')
@api.multi
def _compute_values(self):
dlines = self
if self.env.context.get('no_compute_asset_line_ids'):
# skip compute for lines in unlink
exclude_ids = self.env.context['no_compute_asset_line_ids']
dlines = self.filtered(lambda l: l.id not in exclude_ids)
dlines = dlines.filtered(lambda l: l.type == 'depreciate')
dlines = dlines.sorted(key=lambda l: l.line_date)
# Group depreciation lines per asset
asset_ids = dlines.mapped('asset_id')
grouped_dlines = []
for asset in asset_ids:
grouped_dlines.append(
dlines.filtered(lambda l: l.asset_id.id == asset.id))
for dlines in grouped_dlines:
for i, dl in enumerate(dlines):
if i == 0:
depreciation_base = dl.depreciation_base
tmp = depreciation_base - dl.previous_id.remaining_value
depreciated_value = dl.previous_id and tmp or 0.0
remaining_value = \
depreciation_base - depreciated_value - dl.amount
else:
depreciated_value += dl.previous_id.amount
remaining_value -= dl.amount
dl.depreciated_value = depreciated_value
dl.remaining_value = remaining_value
@api.depends('move_id')
@api.multi
def _compute_move_check(self):
for line in self:
line.move_check = bool(line.move_id)
@api.onchange('amount')
def _onchange_amount(self):
if self.type == 'depreciate':
self.remaining_value = self.depreciation_base - \
self.depreciated_value - self.amount
@api.multi
def write(self, vals):
for dl in self:
line_date = vals.get('line_date') or dl.line_date
asset_lines = dl.asset_id.depreciation_line_ids
if list(vals.keys()) == ['move_id'] and not vals['move_id']:
# allow to remove an accounting entry via the
# 'Delete Move' button on the depreciation lines.
if not self.env.context.get('unlink_from_asset'):
raise UserError(_(
"You are not allowed to remove an accounting entry "
"linked to an asset."
"\nYou should remove such entries from the asset."))
elif list(vals.keys()) == ['asset_id']:
continue
elif dl.move_id and not self.env.context.get(
'allow_asset_line_update'):
raise UserError(_(
"You cannot change a depreciation line "
"with an associated accounting entry."))
elif vals.get('init_entry'):
check = asset_lines.filtered(
lambda l: l.move_check and l.type == 'depreciate' and
l.line_date <= line_date)
if check:
raise UserError(_(
"You cannot set the 'Initial Balance Entry' flag "
"on a depreciation line "
"with prior posted entries."))
elif vals.get('line_date'):
if dl.type == 'create':
check = asset_lines.filtered(
lambda l: l.type != 'create' and
(l.init_entry or l.move_check) and
l.line_date < fields.Date.to_date(vals['line_date']))
if check:
raise UserError(
_("You cannot set the Asset Start Date "
"after already posted entries."))
else:
check = asset_lines.filtered(
lambda l: l != dl and
(l.init_entry or l.move_check) and
l.line_date > fields.Date.to_date(vals['line_date']))
if check:
raise UserError(_(
"You cannot set the date on a depreciation line "
"prior to already posted entries."))
return super().write(vals)
@api.multi
def unlink(self):
for dl in self:
if dl.type == 'create' and dl.amount:
raise UserError(_(
"You cannot remove an asset line "
"of type 'Depreciation Base'."))
elif dl.move_id:
raise UserError(_(
"You cannot delete a depreciation line with "
"an associated accounting entry."))
previous = dl.previous_id
next_line = dl.asset_id.depreciation_line_ids.filtered(
lambda l: l.previous_id == dl and l not in self)
if next_line:
next_line.previous_id = previous
return super(AccountAssetLine, self.with_context(
no_compute_asset_line_ids=self.ids)).unlink()
def _setup_move_data(self, depreciation_date):
asset = self.asset_id
move_data = {
'name': asset.name,
'date': depreciation_date,
'ref': self.name,
'journal_id': asset.profile_id.journal_id.id,
}
return move_data
def _setup_move_line_data(self, depreciation_date, account, ml_type, move):
asset = self.asset_id
amount = self.amount
analytic_id = False
if ml_type == 'depreciation':
debit = amount < 0 and -amount or 0.0
credit = amount > 0 and amount or 0.0
elif ml_type == 'expense':
debit = amount > 0 and amount or 0.0
credit = amount < 0 and -amount or 0.0
analytic_id = asset.account_analytic_id.id
move_line_data = {
'name': asset.name,
'ref': self.name,
'move_id': move.id,
'account_id': account.id,
'credit': credit,
'debit': debit,
'journal_id': asset.profile_id.journal_id.id,
'partner_id': asset.partner_id.id,
'analytic_account_id': analytic_id,
'date': depreciation_date,
'asset_id': asset.id,
}
return move_line_data
@api.multi
def create_move(self):
created_move_ids = []
asset_ids = set()
ctx = dict(self.env.context,
allow_asset=True, check_move_validity=False)
for line in self:
asset = line.asset_id
depreciation_date = line.line_date
am_vals = line._setup_move_data(depreciation_date)
move = self.env['account.move'].with_context(ctx).create(am_vals)
depr_acc = asset.profile_id.account_depreciation_id
exp_acc = asset.profile_id.account_expense_depreciation_id
aml_d_vals = line._setup_move_line_data(
depreciation_date, depr_acc, 'depreciation', move)
self.env['account.move.line'].with_context(ctx).create(aml_d_vals)
aml_e_vals = line._setup_move_line_data(
depreciation_date, exp_acc, 'expense', move)
self.env['account.move.line'].with_context(ctx).create(aml_e_vals)
move.post()
line.with_context(allow_asset_line_update=True).write({
'move_id': move.id
})
created_move_ids.append(move.id)
asset_ids.add(asset.id)
# we re-evaluate the assets to determine if we can close them
for asset in self.env['account.asset'].browse(list(asset_ids)):
if asset.company_currency_id.is_zero(asset.value_residual):
asset.state = 'close'
return created_move_ids
@api.multi
def open_move(self):
self.ensure_one()
return {
'name': _("Journal Entry"),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.move',
'view_id': False,
'type': 'ir.actions.act_window',
'context': self.env.context,
'domain': [('id', '=', self.move_id.id)],
}
@api.multi
def unlink_move(self):
for line in self:
move = line.move_id
if move.state == 'posted':
move.button_cancel()
move.with_context(unlink_from_asset=True).unlink()
# trigger store function
line.with_context(unlink_from_asset=True).write(
{'move_id': False})
if line.parent_state == 'close':
line.asset_id.write({'state': 'open'})
elif line.parent_state == 'removed' and line.type == 'remove':
line.asset_id.write({
'state': 'close',
'date_remove': False,
})
line.unlink()
return True