forked from Mapan/odoo17e
560 lines
26 KiB
Python
560 lines
26 KiB
Python
from collections import defaultdict
|
|
from datetime import timedelta
|
|
|
|
from odoo import api, Command, fields, models, _
|
|
from odoo.exceptions import UserError
|
|
from odoo.tools import groupby
|
|
from odoo.tools.misc import formatLang
|
|
|
|
|
|
class AccountReconcileWizard(models.TransientModel):
|
|
""" This wizard is used to reconcile selected account.move.line. """
|
|
_name = 'account.reconcile.wizard'
|
|
_description = 'Account reconciliation wizard'
|
|
_check_company_auto = True
|
|
|
|
@api.model
|
|
def default_get(self, fields_list):
|
|
res = super().default_get(fields_list)
|
|
if 'move_line_ids' not in fields_list:
|
|
return res
|
|
if self.env.context.get('active_model') != 'account.move.line' or not self.env.context.get('active_ids'):
|
|
raise UserError(_('This can only be used on journal items'))
|
|
move_line_ids = self.env['account.move.line'].browse(self.env.context['active_ids'])
|
|
accounts = move_line_ids.account_id
|
|
if len(accounts) > 2:
|
|
raise UserError(_(
|
|
'You can only reconcile entries with up to two different accounts: %s',
|
|
', '.join(accounts.mapped('display_name')),
|
|
))
|
|
shadowed_aml_values = None
|
|
if len(accounts) == 2:
|
|
shadowed_aml_values = {
|
|
aml: {'account_id': move_line_ids[0].account_id}
|
|
for aml in move_line_ids.filtered(lambda line: line.account_id != move_line_ids[0].account_id)
|
|
}
|
|
move_line_ids._check_amls_exigibility_for_reconciliation(shadowed_aml_values=shadowed_aml_values)
|
|
res['move_line_ids'] = [Command.set(move_line_ids.ids)]
|
|
return res
|
|
|
|
company_id = fields.Many2one(comodel_name='res.company', required=True, readonly=True, compute='_compute_company_id')
|
|
move_line_ids = fields.Many2many(
|
|
comodel_name='account.move.line',
|
|
string='Move lines to reconcile',
|
|
required=True)
|
|
reco_account_id = fields.Many2one(
|
|
comodel_name='account.account',
|
|
string='Reconcile Account',
|
|
compute='_compute_reco_wizard_data')
|
|
amount = fields.Monetary(
|
|
string='Amount in company currency',
|
|
currency_field='company_currency_id',
|
|
compute='_compute_reco_wizard_data')
|
|
company_currency_id = fields.Many2one(comodel_name='res.currency', string='Company currency', related='company_id.currency_id')
|
|
amount_currency = fields.Monetary(
|
|
string='Amount',
|
|
currency_field='reco_currency_id',
|
|
compute='_compute_reco_wizard_data')
|
|
reco_currency_id = fields.Many2one(
|
|
comodel_name='res.currency',
|
|
string='Currency to use for reconciliation',
|
|
compute='_compute_reco_wizard_data')
|
|
single_currency_mode = fields.Boolean(compute='_compute_single_currency_mode')
|
|
allow_partials = fields.Boolean(string="Allow partials", compute='_compute_allow_partials', store=True, readonly=False)
|
|
force_partials = fields.Boolean(compute='_compute_reco_wizard_data')
|
|
display_allow_partials = fields.Boolean(compute='_compute_display_allow_partials')
|
|
date = fields.Date(string='Date', compute='_compute_date', store=True, readonly=False)
|
|
journal_id = fields.Many2one(
|
|
comodel_name='account.journal',
|
|
string='Journal',
|
|
check_company=True,
|
|
domain="[('type', '=', 'general')]",
|
|
compute='_compute_journal_id',
|
|
store=True,
|
|
readonly=False,
|
|
required=True,
|
|
precompute=True)
|
|
account_id = fields.Many2one(
|
|
comodel_name='account.account',
|
|
string='Account',
|
|
check_company=True,
|
|
domain="[('deprecated', '=', False), ('internal_group', '!=', 'off_balance')]")
|
|
label = fields.Char(string='Label', default='Write-Off')
|
|
tax_id = fields.Many2one(
|
|
comodel_name='account.tax',
|
|
string='Tax',
|
|
default=False,
|
|
check_company=True)
|
|
to_check = fields.Boolean(
|
|
string='To Check',
|
|
default=False,
|
|
help='Check if you are not certain of all the information of the counterpart.')
|
|
is_write_off_required = fields.Boolean(
|
|
string='Is a write-off move required to reconcile',
|
|
compute='_compute_is_write_off_required')
|
|
is_transfer_required = fields.Boolean(
|
|
string='Is an account transfer required',
|
|
compute='_compute_reco_wizard_data')
|
|
transfer_warning_message = fields.Char(
|
|
string='Is an account transfer required to reconcile',
|
|
compute='_compute_reco_wizard_data')
|
|
transfer_from_account_id = fields.Many2one(
|
|
comodel_name='account.account',
|
|
string='Account Transfer From',
|
|
compute='_compute_reco_wizard_data')
|
|
lock_date_violated_warning_message = fields.Char(
|
|
string='Is the date violating the lock date of moves',
|
|
compute='_compute_lock_date_violated_warning_message')
|
|
reco_model_id = fields.Many2one(
|
|
comodel_name='account.reconcile.model',
|
|
string='Reconciliation model',
|
|
store=False,
|
|
check_company=True)
|
|
reco_model_autocomplete_ids = fields.Many2many(
|
|
comodel_name='account.reconcile.model',
|
|
string='All reconciliation models',
|
|
compute='_compute_reco_model_autocomplete_ids')
|
|
|
|
# ==== Compute methods ====
|
|
@api.depends('move_line_ids.company_id')
|
|
def _compute_company_id(self):
|
|
for wizard in self:
|
|
wizard.company_id = wizard.move_line_ids[0].company_id
|
|
|
|
@api.depends('reco_currency_id', 'company_currency_id')
|
|
def _compute_single_currency_mode(self):
|
|
for wizard in self:
|
|
wizard.single_currency_mode = wizard.reco_currency_id == wizard.company_currency_id
|
|
|
|
@api.depends('force_partials')
|
|
def _compute_allow_partials(self):
|
|
for wizard in self:
|
|
wizard.allow_partials = wizard.display_allow_partials and wizard.force_partials
|
|
|
|
@api.depends('move_line_ids')
|
|
def _compute_display_allow_partials(self):
|
|
for wizard in self:
|
|
wizard.display_allow_partials = has_debit_line = has_credit_line = False
|
|
for aml in wizard.move_line_ids:
|
|
if aml.balance > 0.0 or aml.amount_currency > 0.0:
|
|
has_debit_line = True
|
|
elif aml.balance < 0.0 or aml.amount_currency < 0.0:
|
|
has_credit_line = True
|
|
if has_debit_line and has_credit_line:
|
|
wizard.display_allow_partials = True
|
|
break
|
|
|
|
@api.depends('move_line_ids', 'journal_id', 'tax_id')
|
|
def _compute_date(self):
|
|
for wizard in self:
|
|
highest_date = max(aml.date for aml in wizard.move_line_ids)
|
|
temp_move = self.env['account.move'].new({'journal_id': wizard.journal_id.id})
|
|
wizard.date = temp_move._get_accounting_date(highest_date, bool(wizard.tax_id))
|
|
|
|
@api.depends('company_id')
|
|
def _compute_journal_id(self):
|
|
for wizard in self:
|
|
wizard.journal_id = self.env['account.journal'].search([
|
|
*self.env['account.journal']._check_company_domain(wizard.company_id),
|
|
('type', '=', 'general')
|
|
], limit=1)
|
|
|
|
@api.depends('amount', 'amount_currency')
|
|
def _compute_is_write_off_required(self):
|
|
""" We need a write-off if the balance is not 0 and if we don't allow partial reconciliation."""
|
|
for wizard in self:
|
|
wizard.is_write_off_required = not wizard.company_currency_id.is_zero(wizard.amount) \
|
|
or (wizard.reco_currency_id and not wizard.reco_currency_id.is_zero(wizard.amount_currency))
|
|
|
|
@api.depends('move_line_ids')
|
|
def _compute_reco_wizard_data(self):
|
|
""" Compute various data needed for the reco wizard.
|
|
1. The currency to use for the reconciliation:
|
|
- if only one foreign currency is present in move lines we use it, unless the reco_account is not
|
|
payable nor receivable,
|
|
- if no foreign currency or more than 1 are used we use the company's default currency.
|
|
2. The account the reconciliation will happen on.
|
|
3. Transfer data.
|
|
4. Write-off amounts.
|
|
"""
|
|
|
|
def get_transfer_data(move_lines):
|
|
amounts_per_account = defaultdict(float)
|
|
for line in move_lines:
|
|
amounts_per_account[line.account_id] += line.amount_residual
|
|
if abs(amounts_per_account[accounts[0]]) < abs(amounts_per_account[accounts[1]]):
|
|
transfer_from_account, transfer_to_account = accounts[0], accounts[1]
|
|
else:
|
|
transfer_from_account, transfer_to_account = accounts[1], accounts[0]
|
|
|
|
amls_to_transfer = amls.filtered(lambda aml: aml.account_id == transfer_from_account)
|
|
transfer_foreign_curr = amls.currency_id - amls.company_currency_id
|
|
if len(transfer_foreign_curr) == 1:
|
|
transfer_currency = transfer_foreign_curr
|
|
transfer_amount_currency = sum(aml.amount_currency for aml in amls_to_transfer)
|
|
else:
|
|
transfer_currency = amls.company_currency_id
|
|
transfer_amount_currency = sum(aml.balance for aml in amls_to_transfer)
|
|
|
|
if transfer_amount_currency == 0.0 and transfer_currency != amls.company_currency_id:
|
|
# handle the transfer of exchange diff
|
|
transfer_currency = amls.company_currency_id
|
|
transfer_amount_currency = sum(aml.balance for aml in amls_to_transfer)
|
|
|
|
amount_formatted = formatLang(self.env, abs(transfer_amount_currency), currency_obj=transfer_currency)
|
|
transfer_warning_message = _(
|
|
'An entry will transfer %(amount)s from %(from_account)s to %(to_account)s.',
|
|
amount=amount_formatted,
|
|
from_account=transfer_from_account.display_name if transfer_amount_currency < 0 else transfer_to_account.display_name,
|
|
to_account=transfer_to_account.display_name if transfer_amount_currency < 0 else transfer_from_account.display_name,
|
|
)
|
|
return {
|
|
'transfer_from_account_id': transfer_from_account,
|
|
'reco_account_id': transfer_to_account,
|
|
'transfer_warning_message': transfer_warning_message,
|
|
}
|
|
|
|
def get_reco_currency(amls, aml_values_map):
|
|
company_currency = amls.company_currency_id
|
|
foreign_currencies = amls.currency_id - company_currency
|
|
if len(foreign_currencies) == 0:
|
|
return company_currency
|
|
elif len(foreign_currencies) == 1:
|
|
return foreign_currencies
|
|
else:
|
|
lines_with_residuals = self.env['account.move.line']
|
|
for residual, residual_values in aml_values_map.items():
|
|
if residual_values['amount_residual'] or residual_values['amount_residual_currency']:
|
|
lines_with_residuals += residual
|
|
if lines_with_residuals and len(lines_with_residuals.currency_id - company_currency) > 1:
|
|
# there is more than one residual and more than one currency in them
|
|
return False
|
|
return (lines_with_residuals.currency_id - company_currency) or company_currency
|
|
|
|
for wizard in self:
|
|
amls = wizard.move_line_ids._origin
|
|
accounts = amls.account_id # there is only 1 or 2 possible accounts
|
|
|
|
wizard.reco_currency_id = False
|
|
wizard.amount_currency = wizard.amount = 0.0
|
|
wizard.force_partials = True
|
|
wizard.transfer_from_account_id = wizard.transfer_warning_message = False
|
|
wizard.is_transfer_required = len(accounts) == 2
|
|
if wizard.is_transfer_required:
|
|
wizard.update(get_transfer_data(amls))
|
|
else:
|
|
wizard.reco_account_id = accounts
|
|
|
|
# Compute the residual amounts for each account.
|
|
shadowed_aml_values = {
|
|
aml: {'account_id': wizard.reco_account_id}
|
|
for aml in amls
|
|
}
|
|
|
|
# Batch the amls all together to know what should be reconciled and when.
|
|
plan_list, all_amls = amls._optimize_reconciliation_plan([amls], shadowed_aml_values=shadowed_aml_values)
|
|
|
|
# Prefetch data
|
|
all_amls.move_id
|
|
all_amls.matched_debit_ids
|
|
all_amls.matched_credit_ids
|
|
|
|
# All residual amounts are collected and updated until the creation of partials in batch.
|
|
# This is done that way to minimize the orm time for fields invalidation/mark as recompute and
|
|
# re-computation.
|
|
aml_values_map = {
|
|
aml: {
|
|
'aml': aml,
|
|
'amount_residual': aml.amount_residual,
|
|
'amount_residual_currency': aml.amount_residual_currency,
|
|
}
|
|
for aml in all_amls
|
|
}
|
|
|
|
disable_partial_exchange_diff = bool(self.env['ir.config_parameter'].sudo().get_param('account.disable_partial_exchange_diff'))
|
|
plan = plan_list[0]
|
|
# residuals are subtracted from aml_values_map
|
|
amls\
|
|
.with_context(no_exchange_difference=self._context.get('no_exchange_difference') or disable_partial_exchange_diff) \
|
|
._prepare_reconciliation_plan(plan, aml_values_map, shadowed_aml_values=shadowed_aml_values)
|
|
|
|
reco_currency = get_reco_currency(amls, aml_values_map)
|
|
if not reco_currency:
|
|
continue
|
|
|
|
residual_amounts = {
|
|
aml: aml._prepare_move_line_residual_amounts(aml_values, reco_currency, shadowed_aml_values=shadowed_aml_values)
|
|
for aml, aml_values in aml_values_map.items()
|
|
}
|
|
|
|
if all(reco_currency in residual_values for residual_values in residual_amounts.values() if residual_values):
|
|
wizard.reco_currency_id = reco_currency
|
|
elif all(amls.company_currency_id in residual_values for residual_values in residual_amounts.values() if residual_values):
|
|
wizard.reco_currency_id = amls.company_currency_id
|
|
reco_currency = wizard.reco_currency_id
|
|
else:
|
|
continue
|
|
|
|
# Compute write-off amounts
|
|
most_recent_line = max(amls, key=lambda aml: aml.date)
|
|
if most_recent_line.currency_id == reco_currency:
|
|
rate = abs(most_recent_line.amount_currency / most_recent_line.balance) if most_recent_line.balance else 0.0
|
|
else:
|
|
rate = wizard.reco_currency_id._get_conversion_rate(amls.company_currency_id, reco_currency, amls.company_id, most_recent_line.date)
|
|
|
|
wizard.amount_currency = sum(
|
|
residual_values[wizard.reco_currency_id]['residual']
|
|
for residual_values in residual_amounts.values()
|
|
if residual_values
|
|
)
|
|
wizard.amount = amls.company_currency_id.round(wizard.amount_currency / rate) if rate else 0.0
|
|
wizard.force_partials = False
|
|
|
|
@api.depends('move_line_ids.move_id', 'date')
|
|
def _compute_lock_date_violated_warning_message(self):
|
|
for wizard in self:
|
|
date_after_lock = wizard._get_date_after_lock_date()
|
|
lock_date_violated_warning_message = None
|
|
if date_after_lock:
|
|
lock_date_violated_warning_message = _(
|
|
'The date you set violates the lock date of one of your entry. It will be overriden by the following date : %(replacement_date)s',
|
|
replacement_date=date_after_lock,
|
|
)
|
|
wizard.lock_date_violated_warning_message = lock_date_violated_warning_message
|
|
|
|
@api.depends('company_id')
|
|
def _compute_reco_model_autocomplete_ids(self):
|
|
""" Computes available reconcile models, we only take models that are of type 'writeoff_button'
|
|
and that have one (and only one) line.
|
|
"""
|
|
for wizard in self:
|
|
domain = [
|
|
('rule_type', '=', 'writeoff_button'),
|
|
('company_id', '=', wizard.company_id.id),
|
|
]
|
|
query = self.env['account.reconcile.model']._where_calc(domain)
|
|
tables, where_clause, where_params = query.get_sql()
|
|
query_str = f"""
|
|
SELECT account_reconcile_model.id
|
|
FROM {tables}
|
|
JOIN account_reconcile_model_line line ON line.model_id = account_reconcile_model.id
|
|
WHERE {where_clause}
|
|
GROUP BY account_reconcile_model.id
|
|
HAVING COUNT(account_reconcile_model.id) = 1
|
|
"""
|
|
self._cr.execute(query_str, where_params)
|
|
reco_model_ids = [r[0] for r in self._cr.fetchall()]
|
|
wizard.reco_model_autocomplete_ids = self.env['account.reconcile.model'].browse(reco_model_ids)
|
|
|
|
# ==== Onchange methods ====
|
|
@api.onchange('reco_model_id')
|
|
def _onchange_reco_model_id(self):
|
|
""" We prefill the write-off data with the reconcile model selected. """
|
|
if self.reco_model_id:
|
|
self.to_check = self.reco_model_id.to_check
|
|
self.label = self.reco_model_id.line_ids.label
|
|
self.tax_id = self.reco_model_id.line_ids.tax_ids[0] if self.reco_model_id.line_ids[0].tax_ids else None
|
|
self.journal_id = self.reco_model_id.line_ids.journal_id # we limited models to those with one and only one line
|
|
self.account_id = self.reco_model_id.line_ids.account_id
|
|
|
|
# ==== Actions methods ====
|
|
def _action_open_wizard(self):
|
|
self.ensure_one()
|
|
return {
|
|
'name': _('Write-Off Entry'),
|
|
'type': 'ir.actions.act_window',
|
|
'view_type': 'form',
|
|
'view_mode': 'form',
|
|
'res_model': 'account.reconcile.wizard',
|
|
'target': 'new',
|
|
}
|
|
|
|
# ==== Business methods ====
|
|
|
|
def _get_date_after_lock_date(self):
|
|
self.ensure_one()
|
|
lock_dates = self.company_id._get_violated_lock_dates(self.date, bool(self.tax_id))
|
|
if lock_dates:
|
|
return lock_dates[-1][0] + timedelta(days=1)
|
|
|
|
def _compute_write_off_taxes_data(self, partner_id):
|
|
""" Computes the data needed to fill the write-off lines related to taxes.
|
|
:return: a dict of the form {
|
|
'base_amount': 100.0,
|
|
'base_amount_currency': 200.0,
|
|
'tax_lines_data': [{
|
|
'tax_amount': 21.0,
|
|
'tax_amount_currency': 42.0,
|
|
'tax_tag_ids': [tax_tags],
|
|
'tax_account_id': id_of_account,
|
|
} * nr of repartition lines of the self.tax_id ],
|
|
}
|
|
"""
|
|
rate = abs(self.amount_currency / self.amount)
|
|
tax_type = self.tax_id.type_tax_use if self.tax_id else None
|
|
is_refund = (tax_type == 'sale' and self.amount_currency > 0.0) or (tax_type == 'purchase' and self.amount_currency < 0.0)
|
|
tax_data = self.env['account.tax']._convert_to_tax_base_line_dict(
|
|
self,
|
|
partner=partner_id,
|
|
currency=self.reco_currency_id,
|
|
taxes=self.tax_id,
|
|
price_unit=self.amount_currency,
|
|
quantity=1.0,
|
|
account=self.account_id,
|
|
is_refund=is_refund,
|
|
rate=rate,
|
|
handle_price_include=True,
|
|
extra_context={'force_price_include': True},
|
|
)
|
|
tax_results = self.env['account.tax']._compute_taxes(
|
|
[tax_data],
|
|
include_caba_tags=True,
|
|
)
|
|
|
|
_tax_data, base_to_update = tax_results['base_lines_to_update'][0] # we can only have one baseline
|
|
tax_lines_data = []
|
|
for tax_line_vals in tax_results['tax_lines_to_add']:
|
|
tax_lines_data.append({
|
|
'tax_amount': tax_line_vals['tax_amount'],
|
|
'tax_amount_currency': tax_line_vals['tax_amount_currency'],
|
|
'tax_tag_ids': tax_line_vals['tax_tag_ids'],
|
|
'tax_account_id': tax_line_vals['account_id'],
|
|
})
|
|
base_amount_currency = base_to_update['price_subtotal']
|
|
base_amount = self.amount - sum(entry['tax_amount'] for entry in tax_lines_data)
|
|
|
|
return {
|
|
'base_amount': base_amount,
|
|
'base_amount_currency': base_amount_currency,
|
|
'base_tax_tag_ids': base_to_update['tax_tag_ids'],
|
|
'tax_lines_data': tax_lines_data,
|
|
}
|
|
|
|
def _create_write_off_lines(self, partner=None):
|
|
tax_data = self._compute_write_off_taxes_data(partner) if self.tax_id else None
|
|
partner_id = partner.id if partner else None
|
|
line_ids_commands = [
|
|
Command.create({
|
|
'name': self.label or _('Write-Off'),
|
|
'account_id': self.reco_account_id.id,
|
|
'partner_id': partner_id,
|
|
'currency_id': self.reco_currency_id.id,
|
|
'amount_currency': -self.amount_currency,
|
|
'balance': -self.amount,
|
|
}),
|
|
Command.create({
|
|
'name': self.label,
|
|
'account_id': self.account_id.id,
|
|
'partner_id': partner_id,
|
|
'currency_id': self.reco_currency_id.id,
|
|
'tax_ids': self.tax_id.ids,
|
|
'tax_tag_ids': None if not tax_data else tax_data['base_tax_tag_ids'],
|
|
'amount_currency': self.amount_currency if not tax_data else tax_data['base_amount_currency'],
|
|
'balance': self.amount if not tax_data else tax_data['base_amount'],
|
|
}),
|
|
]
|
|
# Add taxes lines to the write-off lines, one per repartition line
|
|
if tax_data:
|
|
for tax_datum in tax_data['tax_lines_data']:
|
|
line_ids_commands.append(Command.create({
|
|
'name': self.tax_id.name,
|
|
'account_id': tax_datum['tax_account_id'],
|
|
'partner_id': partner_id,
|
|
'currency_id': self.reco_currency_id.id,
|
|
'tax_tag_ids': tax_datum['tax_tag_ids'],
|
|
'amount_currency': tax_datum['tax_amount_currency'],
|
|
'balance': tax_datum['tax_amount'],
|
|
}))
|
|
return line_ids_commands
|
|
|
|
def create_write_off(self):
|
|
""" Create write-off move lines with the data provided in the wizard. """
|
|
self.ensure_one()
|
|
partners = self.move_line_ids.partner_id
|
|
partner = partners if len(partners) == 1 else None
|
|
write_off_vals = {
|
|
'journal_id': self.journal_id.id,
|
|
'company_id': self.company_id.id,
|
|
'date': self._get_date_after_lock_date() or self.date,
|
|
'to_check': self.to_check,
|
|
'line_ids': self._create_write_off_lines(partner=partner)
|
|
}
|
|
write_off_move = self.env['account.move'].with_context(
|
|
skip_invoice_sync=True,
|
|
skip_invoice_line_sync=True,
|
|
).create(write_off_vals)
|
|
write_off_move.action_post()
|
|
return write_off_move
|
|
|
|
def create_transfer(self):
|
|
""" Create transfer move.
|
|
We transfer lines squashed by partner and by currency to keep the partner ledger correct.
|
|
"""
|
|
self.ensure_one()
|
|
# we create one transfer per partner to keep
|
|
line_ids = []
|
|
lines_to_transfer = self.move_line_ids.filtered(lambda line: line.account_id == self.transfer_from_account_id)
|
|
for (partner, currency), lines_to_transfer_partner in groupby(lines_to_transfer, lambda l: (l.partner_id, l.currency_id)):
|
|
amount = sum(line.amount_residual for line in lines_to_transfer_partner)
|
|
amount_currency = sum(line.amount_residual_currency for line in lines_to_transfer_partner)
|
|
line_ids += [
|
|
Command.create({
|
|
'name': _('Transfer from %s', self.transfer_from_account_id.display_name),
|
|
'account_id': self.reco_account_id.id,
|
|
'partner_id': partner.id,
|
|
'currency_id': currency.id,
|
|
'amount_currency': amount_currency,
|
|
'balance': amount,
|
|
}),
|
|
Command.create({
|
|
'name': _('Transfer to %s', self.reco_account_id.display_name),
|
|
'account_id': self.transfer_from_account_id.id,
|
|
'partner_id': partner.id,
|
|
'currency_id': currency.id,
|
|
'amount_currency': -amount_currency,
|
|
'balance': -amount,
|
|
}),
|
|
]
|
|
transfer_vals = {
|
|
'journal_id': self.journal_id.id,
|
|
'company_id': self.company_id.id,
|
|
'date': self._get_date_after_lock_date() or self.date,
|
|
'line_ids': line_ids,
|
|
}
|
|
transfer_move = self.env['account.move'].create(transfer_vals)
|
|
transfer_move.action_post()
|
|
return transfer_move
|
|
|
|
def reconcile(self):
|
|
""" Reconcile selected moves, with a transfer and/or write-off move if necessary."""
|
|
self.ensure_one()
|
|
move_lines_to_reconcile = self.move_line_ids._origin
|
|
do_transfer = self.is_transfer_required
|
|
do_write_off = not self.allow_partials and self.is_write_off_required
|
|
if do_transfer:
|
|
transfer_move = self.create_transfer()
|
|
lines_to_transfer = move_lines_to_reconcile \
|
|
.filtered(lambda line: line.account_id == self.transfer_from_account_id)
|
|
transfer_line_from = transfer_move.line_ids \
|
|
.filtered(lambda line: line.account_id == self.transfer_from_account_id)
|
|
transfer_line_to = transfer_move.line_ids \
|
|
.filtered(lambda line: line.account_id == self.reco_account_id)
|
|
(lines_to_transfer + transfer_line_from).reconcile()
|
|
move_lines_to_reconcile = move_lines_to_reconcile - lines_to_transfer + transfer_line_to
|
|
|
|
if do_write_off:
|
|
write_off_move = self.create_write_off()
|
|
write_off_line_to_reconcile = write_off_move.line_ids[0]
|
|
move_lines_to_reconcile += write_off_line_to_reconcile
|
|
amls_plan = [[move_lines_to_reconcile, write_off_line_to_reconcile]]
|
|
else:
|
|
amls_plan = [move_lines_to_reconcile]
|
|
|
|
self.env['account.move.line']._reconcile_plan(amls_plan)
|
|
return move_lines_to_reconcile if not do_transfer else (move_lines_to_reconcile + transfer_move.line_ids)
|
|
|
|
def reconcile_open(self):
|
|
""" Reconcile selected move lines and open them in dedicated view. """
|
|
self.ensure_one()
|
|
return self.reconcile().open_reconcile_view()
|