mirror of
https://gitlab.com/flectra-community/account-closing.git
synced 2024-11-23 06:02:03 +00:00
355 lines
13 KiB
Python
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.")
|
||
|
)
|