account-closing/account_multicurrency_revaluation/wizard/wizard_currency_revaluation.py
2021-03-23 20:28:14 +01:00

355 lines
13 KiB
Python

# Copyright 2012-2018 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from flectra import models, fields, api, _
from flectra.exceptions import Warning as UserError
class WizardCurrencyRevaluation(models.TransientModel):
_name = 'wizard.currency.revaluation'
@api.model
def _get_default_revaluation_date(self):
"""
Get today's date
"""
return fields.date.today()
@api.model
def _get_default_journal_id(self):
"""
Get default journal if one is defined in company settings
"""
return self.env.user.company_id.default_currency_reval_journal_id
@api.model
def _get_default_label(self):
"""
Get label
"""
return "%(currency)s %(account)s %(rate)s currency revaluation"
revaluation_date = fields.Date(
string='Revaluation Date',
required=True,
default=lambda self: self._get_default_revaluation_date(),
)
journal_id = fields.Many2one(
comodel_name='account.journal',
string='Journal',
domain="[('type','=','general')]",
help="You can set the default journal in company settings.",
required=True,
default=lambda self: self._get_default_journal_id()
)
label = fields.Char(
string='Entry description',
size=100,
help="This label will be inserted in entries description. "
"You can use %(account)s, %(currency)s and %(rate)s keywords.",
required=True,
default=lambda self: self._get_default_label(),
)
def _create_move_and_lines(
self, amount, debit_account_id, credit_account_id,
sums, label, form, partner_id, currency_id,
analytic_debit_acc_id=False, analytic_credit_acc_id=False):
reversable = form.journal_id.company_id.reversable_revaluations
base_move = {
'name': label,
'journal_id': form.journal_id.id,
'date': form.revaluation_date,
'to_be_reversed': reversable
}
base_line = {
'name': label,
'partner_id': partner_id,
'currency_id': currency_id,
'amount_currency': 0.0,
'date': form.revaluation_date
}
base_line['gl_foreign_balance'] = sums.get('foreign_balance', 0.0)
base_line['gl_balance'] = sums.get('balance', 0.0)
base_line['gl_revaluated_balance'] = sums.get(
'revaluated_balance', 0.0)
base_line['gl_currency_rate'] = sums.get('currency_rate', 0.0)
debit_line = base_line.copy()
credit_line = base_line.copy()
debit_line.update({
'debit': amount,
'credit': 0.0,
'account_id': debit_account_id,
})
if analytic_debit_acc_id:
credit_line.update({
'analytic_account_id': analytic_debit_acc_id,
})
credit_line.update({
'debit': 0.0,
'credit': amount,
'account_id': credit_account_id,
})
if analytic_credit_acc_id:
credit_line.update({
'analytic_account_id': analytic_credit_acc_id,
})
base_move['line_ids'] = [(0, 0, debit_line), (0, 0, credit_line)]
created_move = self.env['account.move'].create(base_move)
created_move.post()
return [x.id for x in created_move.line_ids]
@api.multi
def _compute_unrealized_currency_gl(self, currency_id, balances):
"""
Update data dict with the unrealized currency gain and loss
plus add 'currency_rate' which is the value used for rate in
computation
@param int currency_id: currency to revaluate
@param dict balances: contains foreign balance and balance
@return: updated data for foreign balance plus rate value used
"""
context = self.env.context
currency_obj = self.env['res.currency']
# Compute unrealized gain loss
ctx_rate = context.copy()
ctx_rate['date'] = self.revaluation_date
cp_currency = self.journal_id.company_id.currency_id
currency = currency_obj.browse(currency_id).with_context(ctx_rate)
foreign_balance = adjusted_balance = balances.get(
'foreign_balance', 0.0)
balance = balances.get('balance', 0.0)
unrealized_gain_loss = 0.0
if foreign_balance:
adjusted_balance = currency.compute(foreign_balance, cp_currency)
unrealized_gain_loss = adjusted_balance - balance
else:
if balance:
if currency_id != cp_currency.id:
unrealized_gain_loss = 0.0 - balance
return {'unrealized_gain_loss': unrealized_gain_loss,
'currency_rate': currency.rate,
'revaluated_balance': adjusted_balance}
@api.model
def _format_label(self, text, account_id, currency_id, rate):
"""
Return a text with replaced keywords by values
@param str text: label template, can use
%(account)s, %(currency)s, %(rate)s
@param int account_id: id of the account to display in label
@param int currency_id: id of the currency to display
@param float rate: rate to display
"""
account_obj = self.env['account.account']
currency_obj = self.env['res.currency']
account = account_obj.browse(account_id)
currency = currency_obj.browse(currency_id)
data = {'account': account.code or False,
'currency': currency.name or False,
'rate': rate or False}
return text % data
@api.multi
def _write_adjust_balance(self, account_id, currency_id,
partner_id, amount, label, form, sums):
"""
Generate entries to adjust balance in the revaluation accounts
@param account_id: ID of account to be reevaluated
@param amount: Amount to be written to adjust the balance
@param label: Label to be written on each entry
@param form: Wizard browse record containing data
@return: ids of created move_lines
"""
if partner_id is None:
partner_id = False
company = form.journal_id.company_id or self.env.user.company_id
created_ids = []
# over revaluation
if amount >= 0.01:
reval_gain_account = company.revaluation_gain_account_id
if reval_gain_account:
analytic_acc_id = (company.revaluation_analytic_account_id.id
if company.revaluation_analytic_account_id
else False)
line_ids = self._create_move_and_lines(
amount, account_id, reval_gain_account.id, sums, label,
form, partner_id, currency_id,
analytic_credit_acc_id=analytic_acc_id)
created_ids.extend(line_ids)
if company.provision_bs_gain_account_id and \
company.provision_pl_gain_account_id:
analytic_acc_id = (
company.provision_pl_analytic_account_id and
company.provision_pl_analytic_account_id.id or
False)
line_ids = self._create_move_and_lines(
amount, company.provision_bs_gain_account_id.id,
company.provision_pl_gain_account_id.id, sums, label,
form, partner_id, currency_id,
analytic_credit_acc_id=analytic_acc_id)
created_ids.extend(line_ids)
# under revaluation
elif amount <= -0.01:
amount = -amount
reval_loss_account = company.revaluation_loss_account_id
if reval_loss_account:
analytic_acc_id = (company.revaluation_analytic_account_id.id
if company.revaluation_analytic_account_id
else False)
line_ids = self._create_move_and_lines(
amount, reval_loss_account.id, account_id, sums, label,
form, partner_id, currency_id,
analytic_debit_acc_id=analytic_acc_id)
created_ids.extend(line_ids)
if company.provision_bs_loss_account_id and \
company.provision_pl_loss_account_id:
analytic_acc_id = (
company.provision_pl_analytic_account_id and
company.provision_pl_analytic_account_id.id or
False)
line_ids = self._create_move_and_lines(
amount, company.provision_pl_loss_account_id.id,
company.provision_bs_loss_account_id.id, sums, label,
form, partner_id, currency_id,
analytic_debit_acc_id=analytic_acc_id)
created_ids.extend(line_ids)
return created_ids
@staticmethod
def _check_company(company):
return (not company.revaluation_loss_account_id and
not company.revaluation_gain_account_id and
not (company.provision_bs_loss_account_id and
company.provision_pl_loss_account_id) and
not (company.provision_bs_gain_account_id and
company.provision_pl_gain_account_id))
@api.multi
def revaluate_currency(self):
"""
Compute unrealized currency gain and loss and add entries to
adjust balances
@return: dict to open an Entries view filtered on generated move lines
"""
account_obj = self.env['account.account']
company = self.journal_id.company_id or self.env.user.company_id
if self._check_company(company):
raise UserError(
_("No revaluation or provision account are defined"
" for your company.\n"
"You must specify at least one provision account or"
" a couple of provision account in the accounting settings.")
)
created_ids = []
# Search for accounts Balance Sheet to be revaluated
# on those criteria
# - deferral method of account type is not None
account_ids = account_obj.search([
('user_type_id.include_initial_balance', '=', 'True'),
('currency_revaluation', '=', True)])
if not account_ids:
raise UserError(
_("No account to be revaluated found. "
"Please check 'Allow Currency Revaluation' "
"for at least one account in account form.")
)
# Get balance sums
account_sums = account_ids.compute_revaluations(self.revaluation_date)
for account_id, account_tree in account_sums.items():
for currency_id, currency_tree in account_tree.items():
for partner_id, sums in currency_tree.items():
if not sums['balance']:
continue
# Update sums with compute amount currency balance
diff_balances = self._compute_unrealized_currency_gl(
currency_id,
sums
)
account_sums[account_id][currency_id][partner_id].\
update(diff_balances)
# Create entries only after all computation have been done
for account_id, account_tree in account_sums.items():
for currency_id, currency_tree in account_tree.items():
for partner_id, sums in currency_tree.items():
adj_balance = sums.get('unrealized_gain_loss', 0.0)
if not adj_balance:
continue
account_type = account_obj.browse(account_id).internal_type
if account_type not in ['receivable', 'payable']:
partner_id = None
rate = sums.get('currency_rate', 0.0)
label = self._format_label(
self.label, account_id, currency_id, rate
)
# Write an entry to adjust balance
new_ids = self._write_adjust_balance(
account_id,
currency_id,
partner_id,
adj_balance,
label,
self,
sums
)
created_ids.extend(new_ids)
if created_ids:
return {'domain': "[('id', 'in', %s)]" % created_ids,
'name': _("Created revaluation lines"),
'view_type': 'form',
'view_mode': 'tree,form',
'auto_search': True,
'res_model': 'account.move.line',
'view_id': False,
'search_view_id': False,
'type': 'ir.actions.act_window'}
else:
raise UserError(
_("No accounting entry has been posted.")
)