[MERGE] merged the branch with the improvement of the reconciliation on bank statement (allow to reconcile with existing entries in the bank journal not yet linked to a statement)

This commit is contained in:
qdp-odoo
2015-01-16 14:20:54 +01:00
parent 90869f1de4
commit 0802ebdb13
11 changed files with 271 additions and 200 deletions
+8 -7
View File
@@ -681,7 +681,7 @@ class account_account(osv.osv):
return True
# For legal reason (forbiden to modify journal entries which belongs to a closed fy or period), Forbid to modify
# the code of an account if journal entries have been already posted on this account. This cannot be simply
# the code of an account if journal entries have been already posted on this account. This cannot be simply
# 'configurable' since it can lead to a lack of confidence in Odoo and this is what we want to change.
def _check_allow_code_change(self, cr, uid, ids, context=None):
line_obj = self.pool.get('account.move.line')
@@ -1273,6 +1273,7 @@ class account_move(osv.osv):
'narration':fields.text('Internal Note'),
'company_id': fields.related('journal_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),
'balance': fields.float('balance', digits_compute=dp.get_precision('Account'), help="This is a field only used for internal purpose and shouldn't be displayed"),
'statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line reconciled with this entry', copy=False, readonly=True)
}
_defaults = {
@@ -1635,7 +1636,7 @@ class account_move_reconcile(osv.osv):
_defaults = {
'name': lambda self,cr,uid,ctx=None: self.pool.get('ir.sequence').next_by_code(cr, uid, 'account.reconcile', context=ctx) or '/',
}
# You cannot unlink a reconciliation if it is a opening_reconciliation one,
# you should use the generate opening entries wizard for that
def unlink(self, cr, uid, ids, context=None):
@@ -1644,7 +1645,7 @@ class account_move_reconcile(osv.osv):
raise osv.except_osv(_('Error!'), _('You cannot unreconcile journal items if they has been generated by the \
opening/closing fiscal year process.'))
return super(account_move_reconcile, self).unlink(cr, uid, ids, context=context)
# Look in the line_id and line_partial_ids to ensure the partner is the same or empty
# on all lines. We allow that only for opening/closing period
def _check_same_partner(self, cr, uid, ids, context=None):
@@ -1664,7 +1665,7 @@ class account_move_reconcile(osv.osv):
_constraints = [
(_check_same_partner, 'You can only reconcile journal items with the same partner.', ['line_id', 'line_partial_ids']),
]
def reconcile_partial_check(self, cr, uid, ids, type='auto', context=None):
total = 0.0
for rec in self.browse(cr, uid, ids, context=context):
@@ -3027,7 +3028,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
def _get_chart_parent_ids(self, cr, uid, chart_template, context=None):
""" Returns the IDs of all ancestor charts, including the chart itself.
(inverse of child_of operator)
:param browse_record chart_template: the account.chart.template record
:return: the IDS of all ancestor charts, including the chart itself.
"""
@@ -3091,7 +3092,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
if model_data:
chart_id = model_data[0]['res_id']
chart = account_chart_template.browse(cr, uid, chart_id, context=context)
chart_hierarchy_ids = self._get_chart_parent_ids(cr, uid, chart, context=context)
chart_hierarchy_ids = self._get_chart_parent_ids(cr, uid, chart, context=context)
if 'chart_template_id' in fields:
res.update({'only_one_chart_template': len(ids) == 1,
'chart_template_id': chart_id})
@@ -3405,7 +3406,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
for ref in (('product','list_price'),('product','standard_price'),('product','list0'),('purchase','list0')):
try:
tmp2 = obj_data.get_object_reference(cr, uid, *ref)
if tmp2:
if tmp2:
self.pool[tmp2[0]].write(cr, uid, tmp2[1], {
'currency_id': obj_wizard.currency_id.id
})
+185 -154
View File
@@ -19,12 +19,13 @@
#
##############################################################################
from openerp.osv import fields, osv
from openerp.osv import fields, osv, expression
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
from openerp.report import report_sxw
from openerp.tools import float_compare, float_round
from operator import add
import time
class account_bank_statement(osv.osv):
@@ -108,7 +109,7 @@ class account_bank_statement(osv.osv):
def _all_lines_reconciled(self, cr, uid, ids, name, args, context=None):
res = {}
for statement in self.browse(cr, uid, ids, context=context):
res[statement.id] = all([line.journal_entry_id.id for line in statement.line_ids])
res[statement.id] = all([line.journal_entry_ids.ids for line in statement.line_ids])
return res
_order = "date desc, id desc"
@@ -161,7 +162,7 @@ class account_bank_statement(osv.osv):
}
_defaults = {
'name': '/',
'name': '/',
'date': fields.date.context_today,
'state': 'draft',
'journal_id': _default_journal_id,
@@ -215,6 +216,7 @@ class account_bank_statement(osv.osv):
:return: dict of value to create() the account.move
"""
return {
'statement_line_id': st_line.id,
'journal_id': st_line.statement_id.journal_id.id,
'period_id': st_line.statement_id.period_id.id,
'date': st_line.date,
@@ -240,28 +242,40 @@ class account_bank_statement(osv.osv):
"""
return st_line.partner_id and st_line.partner_id.id or False
def _prepare_bank_move_line(self, cr, uid, st_line, move_id, amount, company_currency_id, context=None):
def _prepare_bank_move_line(self, cr, uid, st_line, move_id, amount, company_currency, context=None):
"""Compute the args to build the dict of values to create the counter part move line from a
statement line by calling the _prepare_move_line_vals.
statement line by calling the _prepare_move_line_vals.
:param browse_record st_line: account.bank.statement.line record to create the move from.
:param int/long move_id: ID of the account.move to link the move line
:param float amount: amount of the move line
:param int/long company_currency_id: ID of currency of the concerned company
:param browse_record company_currency: res.currency record of the concerned company
:return: dict of value to create() the bank account.move.line
"""
currency_obj = self.pool.get('res.currency')
if context is None:
context = {}
ctx = context.copy()
ctx['date'] = st_line.date
account_id = self._get_counter_part_account(cr, uid, st_line, context=context)
partner_id = self._get_counter_part_partner(cr, uid, st_line, context=context)
debit = ((amount > 0) and amount) or 0.0
credit = ((amount < 0) and -amount) or 0.0
cur_id = False
amt_cur = False
if st_line.statement_id.currency.id != company_currency_id:
amt_cur = st_line.amount
cur_id = st_line.statement_id.currency.id
elif st_line.currency_id and st_line.amount_currency:
amt_cur = st_line.amount_currency
cur_id = st_line.currency_id.id
if st_line.statement_id.currency.id != company_currency.id or (st_line.currency_id and st_line.amount_currency):
#the amount given is in foreign currency and needs to be converted at the ratio of the transaction
st_line_currency_rate = st_line.currency_id and (st_line.amount_currency / st_line.amount) or 1
if st_line.currency_id != company_currency:
debit = currency_obj.round(cr, uid, company_currency, debit / st_line_currency_rate)
credit = currency_obj.round(cr, uid, company_currency, credit / st_line_currency_rate)
amt_cur = amount
if st_line.statement_id.currency != company_currency:
amt_cur = amount / st_line_currency_rate
cur_id = st_line.statement_id.currency.id != company_currency.id and st_line.statement_id.currency.id or st_line.currency_id.id
if st_line.statement_id.currency.id != company_currency.id and st_line.currency_id != company_currency:
debit = currency_obj.compute(cr, uid, st_line.statement_id.currency.id, company_currency.id, debit, context=ctx)
credit = currency_obj.compute(cr, uid, st_line.statement_id.currency.id, company_currency.id, credit, context=ctx)
return self._prepare_move_line_vals(cr, uid, st_line, move_id, debit, credit,
amount_currency=amt_cur, currency_id=cur_id, account_id=account_id,
partner_id=partner_id, context=context)
@@ -335,7 +349,7 @@ class account_bank_statement(osv.osv):
for st_line in st.line_ids:
if not st_line.amount:
continue
if st_line.account_id and not st_line.journal_entry_id.id:
if st_line.account_id and not st_line.journal_entry_ids.ids:
#make an account move as before
vals = {
'debit': st_line.amount < 0 and -st_line.amount or 0.0,
@@ -344,9 +358,9 @@ class account_bank_statement(osv.osv):
'name': st_line.name
}
self.pool.get('account.bank.statement.line').process_reconciliation(cr, uid, st_line.id, [vals], context=context)
elif not st_line.journal_entry_id.id:
elif not st_line.journal_entry_ids.ids:
raise osv.except_osv(_('Error!'), _('All the account entries lines must be processed in order to close the statement.'))
move_ids.append(st_line.journal_entry_id.id)
move_ids += st_line.journal_entry_ids.ids
if move_ids:
self.pool.get('account.move').post(cr, uid, move_ids, context=context)
self.message_post(cr, uid, [st.id], body=_('Statement %s confirmed, journal items were created.') % (st.name,), context=context)
@@ -388,7 +402,7 @@ class account_bank_statement(osv.osv):
for item in self.browse(cr, uid, ids, context=context):
if item.state != 'draft':
raise osv.except_osv(
_('Invalid Action!'),
_('Invalid Action!'),
_('In order to delete a bank statement, you must first cancel it to delete related journal items.')
)
# Explicitly unlink bank statement lines
@@ -413,7 +427,7 @@ class account_bank_statement(osv.osv):
def number_of_lines_reconciled(self, cr, uid, ids, context=None):
bsl_obj = self.pool.get('account.bank.statement.line')
return bsl_obj.search_count(cr, uid, [('statement_id', 'in', ids), ('journal_entry_id', '!=', False)], context=context)
return bsl_obj.search_count(cr, uid, [('statement_id', 'in', ids), ('journal_entry_ids', '!=', [])], context=context)
def link_bank_to_partner(self, cr, uid, ids, context=None):
for statement in self.browse(cr, uid, ids, context=context):
@@ -430,9 +444,9 @@ class account_bank_statement_line(osv.osv):
def unlink(self, cr, uid, ids, context=None):
for item in self.browse(cr, uid, ids, context=context):
if item.journal_entry_id:
if item.journal_entry_ids:
raise osv.except_osv(
_('Invalid Action!'),
_('Invalid Action!'),
_('In order to delete a bank statement line, you must first cancel it to delete related journal items.')
)
return super(account_bank_statement_line, self).unlink(cr, uid, ids, context=context)
@@ -441,15 +455,17 @@ class account_bank_statement_line(osv.osv):
account_move_obj = self.pool.get('account.move')
move_ids = []
for line in self.browse(cr, uid, ids, context=context):
if line.journal_entry_id:
move_ids.append(line.journal_entry_id.id)
for aml in line.journal_entry_id.line_id:
if aml.reconcile_id:
move_lines = [l.id for l in aml.reconcile_id.line_id]
move_lines.remove(aml.id)
self.pool.get('account.move.reconcile').unlink(cr, uid, [aml.reconcile_id.id], context=context)
if len(move_lines) >= 2:
self.pool.get('account.move.line').reconcile_partial(cr, uid, move_lines, 'auto', context=context)
if line.journal_entry_ids.ids :
move_ids += line.journal_entry_ids.ids
account_move_obj.write(cr, uid, move_ids, {'statement_line_id': False}, context=context)
for move in account_move_obj.browse(cr, uid, move_ids, context=context):
for aml in move.line_id:
if aml.reconcile_id:
move_lines = [l.id for l in aml.reconcile_id.line_id]
move_lines.remove(aml.id)
self.pool.get('account.move.reconcile').unlink(cr, uid, [aml.reconcile_id.id], context=context)
if len(move_lines) >= 2:
self.pool.get('account.move.line').reconcile_partial(cr, uid, move_lines, 'auto', context=context)
if move_ids:
account_move_obj.button_cancel(cr, uid, move_ids, context=context)
account_move_obj.unlink(cr, uid, move_ids, context)
@@ -575,10 +591,8 @@ class account_bank_statement_line(osv.osv):
return []
# Look for a set of move line whose amount is <= to the line's amount
if amount > 0: # Make sure we can't mix receivable and payable
domain += [('account_id.type', '=', 'receivable')]
else:
domain += [('account_id.type', '=', 'payable')]
domain += [('reconcile_id', '=', False)]
domain += [('account_id.type', '=', (amount > 0 and 'receivable' or 'payable'))] # Make sure we can't mix receivable and payable
if amount_field == 'amount_currency' and amount < 0:
domain += [(amount_field, '<', 0), (amount_field, '>', (sign * amount))]
else:
@@ -608,29 +622,38 @@ class account_bank_statement_line(osv.osv):
excluded_ids = []
if additional_domain is None:
additional_domain = []
# Make domain
domain = additional_domain + [
('reconcile_id', '=', False),
('state', '=', 'valid'),
('account_id.reconcile', '=', True)
]
else:
additional_domain = expression.normalize_domain(additional_domain)
# Domain to fetch reconciled move lines (use case where you register a payment before you get the bank statement)
domain_rapprochement = ['&', ('statement_id', '=', False), ('account_id', 'in', [st_line.journal_id.default_credit_account_id.id, st_line.journal_id.default_debit_account_id.id])]
# Domain to fetch unreconciled move lines (the bank reconciliation process will create a payment journal entry to reconcile with)
domain_reconciliation = [('reconcile_id', '=', False)]
if st_line.partner_id.id:
domain += [('partner_id', '=', st_line.partner_id.id)]
domain_reconciliation = expression.AND([domain_reconciliation, [('account_id.type', 'in', ['payable', 'receivable'])]])
else:
domain_reconciliation = expression.AND([domain_reconciliation, [('account_id.reconcile', '=', True)]])
# Let's add what applies to both
domain = expression.OR([domain_rapprochement, domain_reconciliation])
domain = expression.AND([domain, [('state', '=', 'valid')]])
if st_line.partner_id.id:
domain = expression.AND([domain, [('partner_id', '=', st_line.partner_id.id)]])
if excluded_ids:
domain.append(('id', 'not in', excluded_ids))
domain = expression.AND([domain, [('id', 'not in', excluded_ids)]])
if str:
domain += [
str_domain = [
'|', ('move_id.name', 'ilike', str),
'|', ('move_id.ref', 'ilike', str),
('date_maturity', 'like', str),
'|', ('date_maturity', 'like', str),
'&', ('name', '!=', '/'), ('name', 'ilike', str)
]
if not st_line.partner_id.id:
domain.insert(-1, '|', )
domain.append(('partner_id.name', 'ilike', str))
if str != '/':
domain.insert(-1, '|', )
domain.append(('name', 'ilike', str))
return domain
str_domain = expression.OR([str_domain, [('partner_id.name', 'ilike', str)]])
domain = expression.AND([domain, str_domain])
return expression.AND([additional_domain, domain])
def get_move_lines_for_reconciliation(self, cr, uid, st_line, excluded_ids=None, str=False, offset=0, limit=None, count=False, additional_domain=None, context=None):
""" Find the move lines that could be used to reconcile a statement line. If count is true, only returns the count.
@@ -642,7 +665,7 @@ class account_bank_statement_line(osv.osv):
"""
mv_line_pool = self.pool.get('account.move.line')
domain = self._domain_move_lines_for_reconciliation(cr, uid, st_line, excluded_ids=excluded_ids, str=str, additional_domain=additional_domain, context=context)
# Get move lines ; in case of a partial reconciliation, only consider one line
filtered_lines = []
reconcile_partial_ids = []
@@ -708,7 +731,8 @@ class account_bank_statement_line(osv.osv):
self.process_reconciliation(cr, uid, datum[0], datum[1], context=context)
def process_reconciliation(self, cr, uid, id, mv_line_dicts, context=None):
""" Creates a move line for each item of mv_line_dicts and for the statement line. Reconcile a new move line with its counterpart_move_line_id if specified. Finally, mark the statement line as reconciled by putting the newly created move id in the column journal_entry_id.
""" Creates a move line for each item of mv_line_dicts and for the statement line. Reconcile a new move line with its counterpart_move_line_id if specified.
Finally, mark the statement line as reconciled by putting the reconciled moves ids in the column journal_entry_ids.
:param int id: id of the bank statement line
:param list of dicts mv_line_dicts: move lines to create. If counterpart_move_line_id is specified, reconcile with it
@@ -724,7 +748,7 @@ class account_bank_statement_line(osv.osv):
currency_obj = self.pool.get('res.currency')
# Checks
if st_line.journal_entry_id.id:
if st_line.journal_entry_ids.ids:
raise osv.except_osv(_('Error!'), _('The bank statement line was already reconciled.'))
for mv_line_dict in mv_line_dicts:
for field in ['debit', 'credit', 'amount_currency']:
@@ -735,118 +759,125 @@ class account_bank_statement_line(osv.osv):
if mv_line.reconcile_id:
raise osv.except_osv(_('Error!'), _('A selected move line was already reconciled.'))
# Create the move
move_name = (st_line.statement_id.name or st_line.name) + "/" + str(st_line.sequence)
move_vals = bs_obj._prepare_move(cr, uid, st_line, move_name, context=context)
move_id = am_obj.create(cr, uid, move_vals, context=context)
# When the move line is already paid (fully reconciled move), we just link them to the bank statement
to_reconcile_mv_line_ids = [x['counterpart_move_line_id'] for x in mv_line_dicts if x.get('already_paid', False)]
if to_reconcile_mv_line_ids:
aml_obj.write(cr, uid, to_reconcile_mv_line_ids, {'statement_id': st_line.statement_id.id}, context=context)
for aml in aml_obj.browse(cr, uid, to_reconcile_mv_line_ids, context=context):
am_obj.write(cr, uid, aml.move_id.id, {'statement_line_id': st_line.id}, context=context)
# Create the move line for the statement line
if st_line.statement_id.currency.id != company_currency.id:
if st_line.currency_id == company_currency:
amount = st_line.amount_currency
else:
ctx = context.copy()
ctx['date'] = st_line.date
amount = currency_obj.compute(cr, uid, st_line.statement_id.currency.id, company_currency.id, st_line.amount, context=ctx)
else:
amount = st_line.amount
bank_st_move_vals = bs_obj._prepare_bank_move_line(cr, uid, st_line, move_id, amount, company_currency.id, context=context)
aml_obj.create(cr, uid, bank_st_move_vals, context=context)
# Complete the dicts
st_line_currency = st_line.currency_id or statement_currency
st_line_currency_rate = st_line.currency_id and (st_line.amount_currency / st_line.amount) or False
to_create = []
for mv_line_dict in mv_line_dicts:
if mv_line_dict.get('is_tax_line'):
continue
mv_line_dict['ref'] = move_name
mv_line_dict['move_id'] = move_id
mv_line_dict['period_id'] = st_line.statement_id.period_id.id
mv_line_dict['journal_id'] = st_line.journal_id.id
mv_line_dict['company_id'] = st_line.company_id.id
mv_line_dict['statement_id'] = st_line.statement_id.id
if mv_line_dict.get('counterpart_move_line_id'):
mv_line = aml_obj.browse(cr, uid, mv_line_dict['counterpart_move_line_id'], context=context)
mv_line_dict['partner_id'] = mv_line.partner_id.id or st_line.partner_id.id
mv_line_dict['account_id'] = mv_line.account_id.id
if mv_line.currency_id:
ctx = context.copy()
ctx['date'] = mv_line.date
mv_line_dict['amount_currency'] = currency_obj.compute(cr, uid, company_currency.id, mv_line.currency_id.id, mv_line_dict['debit'] - mv_line_dict['credit'], context=ctx)
mv_line_dict['currency_id'] = mv_line.currency_id.id
if st_line_currency.id != company_currency.id:
ctx = context.copy()
ctx['date'] = st_line.date
mv_line_dict['amount_currency'] = mv_line_dict['debit'] - mv_line_dict['credit']
mv_line_dict['currency_id'] = st_line_currency.id
if st_line.currency_id and statement_currency.id == company_currency.id and st_line_currency_rate:
debit_at_current_rate = self.pool.get('res.currency').round(cr, uid, company_currency, mv_line_dict['debit'] / st_line_currency_rate)
credit_at_current_rate = self.pool.get('res.currency').round(cr, uid, company_currency, mv_line_dict['credit'] / st_line_currency_rate)
elif st_line.currency_id and st_line_currency_rate:
debit_at_current_rate = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['debit'] / st_line_currency_rate, context=ctx)
credit_at_current_rate = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['credit'] / st_line_currency_rate, context=ctx)
else:
debit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx)
credit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx)
# Otherwise, we create a move line. Either matching an existing journal entry (eg. invoice), in which
# case we reconcile the existing and the new move lines together, or being a write-off.
to_create_mv_line_dicts = [x for x in mv_line_dicts if not x.get('already_paid', False)]
if to_create_mv_line_dicts:
# Create the move
move_name = (st_line.statement_id.name or st_line.name) + "/" + str(st_line.sequence)
move_vals = bs_obj._prepare_move(cr, uid, st_line, move_name, context=context)
move_id = am_obj.create(cr, uid, move_vals, context=context)
# Create the move line for the statement line
st_line_amount = reduce(add, [x['credit'] - x['debit'] for x in to_create_mv_line_dicts])
bank_st_move_vals = bs_obj._prepare_bank_move_line(cr, uid, st_line, move_id, st_line_amount, company_currency, context=context)
aml_obj.create(cr, uid, bank_st_move_vals, context=context)
# Complete the dicts
st_line_currency = st_line.currency_id or statement_currency
st_line_currency_rate = st_line.currency_id and (st_line.amount_currency / st_line.amount) or 1
to_create = []
for mv_line_dict in to_create_mv_line_dicts:
mv_line_dict.pop('already_paid', None)
mv_line_dict['ref'] = move_name
mv_line_dict['move_id'] = move_id
mv_line_dict['period_id'] = st_line.statement_id.period_id.id
mv_line_dict['journal_id'] = st_line.journal_id.id
mv_line_dict['company_id'] = st_line.company_id.id
mv_line_dict['statement_id'] = st_line.statement_id.id
if mv_line_dict.get('counterpart_move_line_id'):
#post an account line that use the same currency rate than the counterpart (to balance the account) and post the difference in another line
ctx['date'] = mv_line.date
debit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx)
credit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx)
mv_line_dict['credit'] = credit_at_old_rate
mv_line_dict['debit'] = debit_at_old_rate
if debit_at_old_rate - debit_at_current_rate:
currency_diff = debit_at_current_rate - debit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, -currency_diff, move_id, context=context))
if credit_at_old_rate - credit_at_current_rate:
currency_diff = credit_at_current_rate - credit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, currency_diff, move_id, context=context))
else:
mv_line_dict['debit'] = debit_at_current_rate
mv_line_dict['credit'] = credit_at_current_rate
elif statement_currency.id != company_currency.id:
#statement is in foreign currency but the transaction is in company currency
prorata_factor = (mv_line_dict['debit'] - mv_line_dict['credit']) / st_line.amount_currency
mv_line_dict['amount_currency'] = prorata_factor * st_line.amount
if mv_line_dict.get('counterpart_move_line_id'):
counterpart_line = aml_obj.browse(cr, uid, mv_line_dict['counterpart_move_line_id'], context=context)
if counterpart_line.currency_id and counterpart_line.currency_id.id == statement_currency.id:
#post an account line that use the same currency rate than the counterpart (to balance the account) and post the difference in another line
mv_line = aml_obj.browse(cr, uid, mv_line_dict['counterpart_move_line_id'], context=context)
mv_line_dict['partner_id'] = mv_line.partner_id.id or st_line.partner_id.id
mv_line_dict['account_id'] = mv_line.account_id.id
if mv_line.currency_id:
ctx = context.copy()
ctx['date'] = mv_line.date
debit_at_old_rate = mv_line_dict['amount_currency'] > 0 and currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['amount_currency'], context=ctx) or 0.0
credit_at_old_rate = mv_line_dict['amount_currency'] < 0 and currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, -mv_line_dict['amount_currency'], context=ctx) or 0.0
if debit_at_old_rate - mv_line_dict['debit']:
currency_diff = mv_line_dict['debit'] - debit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, -currency_diff, move_id, context=context))
if credit_at_old_rate - mv_line_dict['credit']:
currency_diff = mv_line_dict['credit'] - credit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, currency_diff, move_id, context=context))
mv_line_dict['debit'] = debit_at_old_rate
mv_line_dict['amount_currency'] = currency_obj.compute(cr, uid, company_currency.id, mv_line.currency_id.id, mv_line_dict['debit'] - mv_line_dict['credit'], context=ctx)
mv_line_dict['currency_id'] = mv_line.currency_id.id
if st_line_currency.id != company_currency.id:
ctx = context.copy()
ctx['date'] = st_line.date
mv_line_dict['amount_currency'] = mv_line_dict['debit'] - mv_line_dict['credit']
mv_line_dict['currency_id'] = st_line_currency.id
if st_line.currency_id and statement_currency.id == company_currency.id:
#statement is in company currency but the transaction is in foreign currency
debit_at_current_rate = self.pool.get('res.currency').round(cr, uid, company_currency, mv_line_dict['debit'] / st_line_currency_rate)
credit_at_current_rate = self.pool.get('res.currency').round(cr, uid, company_currency, mv_line_dict['credit'] / st_line_currency_rate)
elif st_line.currency_id:
#statement is in foreign currency and the transaction is in another one
debit_at_current_rate = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['debit'] / st_line_currency_rate, context=ctx)
credit_at_current_rate = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['credit'] / st_line_currency_rate, context=ctx)
else:
#statement is in foreign currency and no extra currency is given for the transaction
debit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'] / st_line_currency_rate, context=ctx)
credit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'] / st_line_currency_rate, context=ctx)
if mv_line_dict.get('counterpart_move_line_id'):
#post an account line that use the same currency rate than the counterpart (to balance the account) and post the difference in another line
ctx['date'] = mv_line.date
debit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx)
credit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx)
mv_line_dict['credit'] = credit_at_old_rate
to_create.append(mv_line_dict)
# Create move lines
move_line_pairs_to_reconcile = []
for mv_line_dict in to_create:
counterpart_move_line_id = None # NB : this attribute is irrelevant for aml_obj.create() and needs to be removed from the dict
if mv_line_dict.get('counterpart_move_line_id'):
counterpart_move_line_id = mv_line_dict['counterpart_move_line_id']
del mv_line_dict['counterpart_move_line_id']
new_aml_id = aml_obj.create(cr, uid, mv_line_dict, context=context)
if counterpart_move_line_id != None:
move_line_pairs_to_reconcile.append([new_aml_id, counterpart_move_line_id])
# Reconcile
for pair in move_line_pairs_to_reconcile:
aml_obj.reconcile_partial(cr, uid, pair, context=context)
# Mark the statement line as reconciled
self.write(cr, uid, id, {'journal_entry_id': move_id}, context=context)
mv_line_dict['debit'] = debit_at_old_rate
if debit_at_old_rate - debit_at_current_rate:
currency_diff = debit_at_current_rate - debit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, -currency_diff, move_id, context=context))
if credit_at_old_rate - credit_at_current_rate:
currency_diff = credit_at_current_rate - credit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, currency_diff, move_id, context=context))
else:
mv_line_dict['debit'] = debit_at_current_rate
mv_line_dict['credit'] = credit_at_current_rate
elif statement_currency.id != company_currency.id:
#statement is in foreign currency but the transaction is in company currency
prorata_factor = (mv_line_dict['debit'] - mv_line_dict['credit']) / st_line.amount_currency
mv_line_dict['amount_currency'] = prorata_factor * st_line.amount
if mv_line_dict.get('counterpart_move_line_id'):
counterpart_line = aml_obj.browse(cr, uid, mv_line_dict['counterpart_move_line_id'], context=context)
if counterpart_line.currency_id and counterpart_line.currency_id.id == statement_currency.id:
#post an account line that use the same currency rate than the counterpart (to balance the account) and post the difference in another line
ctx = context.copy()
ctx['date'] = mv_line.date
debit_at_old_rate = mv_line_dict['amount_currency'] > 0 and currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['amount_currency'], context=ctx) or 0.0
credit_at_old_rate = mv_line_dict['amount_currency'] < 0 and currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, -mv_line_dict['amount_currency'], context=ctx) or 0.0
if debit_at_old_rate - mv_line_dict['debit']:
currency_diff = mv_line_dict['debit'] - debit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, -currency_diff, move_id, context=context))
if credit_at_old_rate - mv_line_dict['credit']:
currency_diff = mv_line_dict['credit'] - credit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, currency_diff, move_id, context=context))
mv_line_dict['debit'] = debit_at_old_rate
mv_line_dict['credit'] = credit_at_old_rate
to_create.append(mv_line_dict)
# Create move lines
move_line_pairs_to_reconcile = []
for mv_line_dict in to_create:
counterpart_move_line_id = None # NB : this attribute is irrelevant for aml_obj.create() and needs to be removed from the dict
if mv_line_dict.get('counterpart_move_line_id'):
counterpart_move_line_id = mv_line_dict['counterpart_move_line_id']
del mv_line_dict['counterpart_move_line_id']
new_aml_id = aml_obj.create(cr, uid, mv_line_dict, context=context)
if counterpart_move_line_id != None:
move_line_pairs_to_reconcile.append([new_aml_id, counterpart_move_line_id])
# Reconcile
for pair in move_line_pairs_to_reconcile:
aml_obj.reconcile_partial(cr, uid, pair, context=context)
# FIXME : if it wasn't for the multicompany security settings in account_security.xml, the method would just
# return [('journal_entry_id', '=', False)]
# return [('journal_entry_ids', '!=', True)]
# Unfortunately, that spawns a "no access rights" error ; it shouldn't.
def _needaction_domain_get(self, cr, uid, context=None):
user = self.pool.get("res.users").browse(cr, uid, uid)
return ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id]), ('journal_entry_id', '=', False)]
return ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id]), ('journal_entry_ids', '=', False)]
_order = "statement_id desc, sequence"
_name = "account.bank.statement.line"
@@ -866,7 +897,7 @@ class account_bank_statement_line(osv.osv):
'note': fields.text('Notes'),
'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of bank statement lines."),
'company_id': fields.related('statement_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
'journal_entry_id': fields.many2one('account.move', 'Journal Entry', copy=False),
'journal_entry_ids': fields.one2many('account.move', 'statement_line_id', 'Journal Entries', copy=False, readonly=True),
'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optional other currency if it is a multi-currency entry.", digits_compute=dp.get_precision('Account')),
'currency_id': fields.many2one('res.currency', 'Currency', help="The optional other currency if it is a multi-currency entry."),
}
+8 -1
View File
@@ -758,7 +758,7 @@ class account_move_line(osv.osv):
return super(account_move_line, self).search(cr, uid, args, offset, limit, order, context, count)
def prepare_move_lines_for_reconciliation_widget(self, cr, uid, lines, target_currency=False, target_date=False, context=None):
""" Returns move lines formatted for the manual/bank reconciliation widget
""" Returns move lines formatted for the bank reconciliation widget
:param target_currency: curreny you want the move line debit/credit converted into
:param target_date: date to use for the monetary conversion
@@ -783,6 +783,8 @@ class account_move_line(osv.osv):
'id': line.id,
'name': line.name != '/' and line.move_id.name + ': ' + line.name or line.move_id.name,
'ref': line.move_id.ref,
# For reconciliation between statement transactions and already registered payments (eg. checks)
'already_paid': line.account_id.type == 'liquidity',
'account_code': line.account_id.code,
'account_name': line.account_id.name,
'account_type': line.account_id.type,
@@ -806,6 +808,11 @@ class account_move_line(osv.osv):
amount = -amount
amount_currency = -amount_currency
# For already reconciled lines, don't use amount_residual(_currency)
if line.account_id.type == 'liquidity':
amount = abs(debit - credit)
amount_currency = line.amount_currency
# Get right debit / credit:
target_currency = target_currency or company_currency
line_currency = line.currency_id or company_currency
+9 -9
View File
@@ -600,21 +600,21 @@
<notebook>
<page string="Transactions" name="statement_line_ids">
<field name="line_ids" context="{'date':date}">
<tree editable="bottom" string="Statement lines" colors="grey:journal_entry_id!=False">
<tree editable="bottom" string="Statement lines">
<field name="sequence" readonly="1" invisible="1"/>
<field name="journal_entry_id" invisible="1"/>
<field name="date" attrs="{'readonly' : [('journal_entry_id', '!=', False)] }"/>
<field name="name" attrs="{'readonly' : [('journal_entry_id', '!=', False)] }"/>
<field name="ref" attrs="{'readonly' : [('journal_entry_id', '!=', False)] }"/>
<field name="journal_entry_ids" invisible="1"/>
<field name="date" attrs="{'readonly' : [('journal_entry_ids', '!=', [])] }"/>
<field name="name" attrs="{'readonly' : [('journal_entry_ids', '!=', [])] }"/>
<field name="ref" attrs="{'readonly' : [('journal_entry_ids', '!=', [])] }"/>
<field name="partner_id" domain="[
'&amp;',
'|',('parent_id','=',False),('is_company','=',True),
'|',('customer','=',True),('supplier','=',True)]"
context="{'default_supplier': 1}"
attrs="{'readonly' : [('journal_entry_id', '!=', False)] }"/>
<field name="amount" attrs="{'readonly' : [('journal_entry_id', '!=', False)] }"/>
<field name="amount_currency" groups="base.group_multi_currency" attrs="{'readonly' : [('journal_entry_id', '!=', False)] }"/>
<field name="currency_id" groups="base.group_multi_currency" attrs="{'readonly' : [('journal_entry_id', '!=', False)] }"/>
attrs="{'readonly' : [('journal_entry_ids', '!=', [])] }"/>
<field name="amount" attrs="{'readonly' : [('journal_entry_ids', '!=', [])] }"/>
<field name="amount_currency" groups="base.group_multi_currency" attrs="{'readonly' : [('journal_entry_ids', '!=', [])] }"/>
<field name="currency_id" groups="base.group_multi_currency" attrs="{'readonly' : [('journal_entry_ids', '!=', [])] }"/>
<field name="bank_account_id" groups="base.group_no_one"/>
</tree>
</field>
@@ -167,10 +167,20 @@
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line table.details td:first-child {
padding-right: 10px;
font-weight: bold; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line table.details tr.one_line_info td {
padding-top: 10px;
text-align: center;
color: #44c; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table {
width: 100%; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr {
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tbody tr, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tbody tr {
cursor: pointer; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tbody tr.created_line, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tbody tr.created_line {
cursor: default !important; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tbody tr.created_line .line_remove_button, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tbody tr.created_line .line_remove_button {
cursor: pointer; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr.already_reconciled, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr.already_reconciled {
color: #44c; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td {
padding: 1px 8px;
vertical-align: middle; }
@@ -198,7 +208,7 @@
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr .glyphicon:not(.line_info_button), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr .glyphicon:not(.line_info_button) {
visibility: hidden;
color: #555; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr:hover .glyphicon, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr.active .glyphicon, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr:hover .glyphicon, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr.active .glyphicon {
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr:hover .glyphicon, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr:focus .glyphicon, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr.active .glyphicon, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr:hover .glyphicon, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr:focus .glyphicon, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr.active .glyphicon {
visibility: visible; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr .do_partial_reconcile_button, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr .do_partial_reconcile_button {
color: #f0ad4e;
@@ -3,6 +3,7 @@ $mainTableBordersPadding: 3px;
$lightBorder: 1px solid #bbb;
$accountingBorder: 1px solid #000;
$initialLineBackground: #f0f0f0;
$infoColor: #44c;
// Warning, this value is also specified in the instance.web.account.abstractReconciliation widget
$aestetic_animation_speed: 300ms;
@@ -273,16 +274,28 @@ $aestetic_animation_speed: 300ms;
padding-right: 10px;
font-weight: bold;
}
tr.one_line_info {
td {
padding-top: 10px;
text-align: center;
color: $infoColor;
}
}
}
/* arrays of move lines */
.accounting_view, .match table {
width: 100%;
tr {
tbody tr {
cursor: pointer;
}
tr.already_reconciled {
color: $infoColor;
}
td {
padding: 1px 8px;
vertical-align: middle;
@@ -334,7 +347,7 @@ $aestetic_animation_speed: 300ms;
color: #555;
}
tr:hover .glyphicon, tr.active .glyphicon {
tr:hover .glyphicon, tr:focus .glyphicon, tr.active .glyphicon {
visibility: visible;
}
+21 -12
View File
@@ -133,7 +133,7 @@ openerp.account = function (instance) {
this._super();
var self = this;
// Retreive statement infos and reconciliation data from the model
var lines_filter = [['journal_entry_id', '=', false], ['account_id', '=', false]];
var lines_filter = [['journal_entry_ids', '=', false], ['account_id', '=', false]];
var deferred_promises = [];
// Working on specified statement(s)
@@ -688,6 +688,7 @@ openerp.account = function (instance) {
this.presets = this.getParent().presets;
this.is_valid = true;
this.is_consistent = true; // Used to prevent bad server requests
this.is_rapprochement; // matching a statement line with an already reconciled journal item
this.can_fetch_more_move_lines; // Tell if we can show more move lines
this.filter = "";
// In rare cases like when deleting a statement line's partner we don't want the server to
@@ -1047,12 +1048,12 @@ openerp.account = function (instance) {
if (! line) return; // If no line found, we've got a syncing problem (let's turn a deaf ear)
// Warn the user if he's selecting lines from both a payable and a receivable account
var last_selected_line = _.last(self.get("mv_lines_selected"));
if (last_selected_line && last_selected_line.account_type != line.account_type) {
new instance.web.Dialog(this, {
title: _t("Warning"),
size: 'medium',
}, $("<div />").text(_.str.sprintf(_t("You are selecting transactions from both a payable and a receivable account.\n\nIn order to proceed, you first need to deselect the %s transactions."), last_selected_line.account_type))).open();
selected_lines_account_types = _.collect(self.get("mv_lines_selected").concat(line), function(l) { return l.account_type });
if (selected_lines_account_types.indexOf("payable") != -1 && selected_lines_account_types.indexOf("receivable") != -1) {
new instance.web.CrashManager().show_warning({data: {
exception_type: "Incorrect Operation",
message: _t("You cannot mix items from receivable and payable accounts.")
}});
return;
}
@@ -1305,7 +1306,11 @@ openerp.account = function (instance) {
// Show or hide partial reconciliation
if (self.get("mv_lines_selected").length > 0) {
var propose_partial = self.getCreatedLines().length === 0 && self.get("mv_lines_selected").length === 1 && balance_type === "greater" && ! self.get("mv_lines_selected")[0].partial_reconcile;
var propose_partial = self.getCreatedLines().length === 0
&& self.get("mv_lines_selected").length === 1
&& balance_type === "greater"
&& ! self.get("mv_lines_selected")[0].partial_reconcile
&& ! self.is_rapprochement;
self.get("mv_lines_selected")[0].propose_partial_reconcile = propose_partial;
self.updateAccountingViewMatchedLines();
}
@@ -1411,7 +1416,9 @@ openerp.account = function (instance) {
self.getParent().excludeMoveLines(self, self.partner_id, added_lines);
self.getParent().unexcludeMoveLines(self, self.partner_id, removed_lines);
self.is_rapprochement = added_lines.length > 0 && added_lines[0].already_paid;
$.when(self.updateMatches()).then(function(){
self.updateAccountingViewMatchedLines();
self.updateBalance();
@@ -1575,6 +1582,7 @@ openerp.account = function (instance) {
balance = Math.round(balance*1000)/1000;
self.set("balance", balance);
},
// Loads move lines according to the widget's state
@@ -1636,6 +1644,7 @@ openerp.account = function (instance) {
debit: line.debit,
credit: line.credit,
counterpart_move_line_id: line.id,
already_paid: line.already_paid,
};
},
@@ -1649,7 +1658,6 @@ openerp.account = function (instance) {
if (amount > 0) dict['credit'] = amount;
if (amount < 0) dict['debit'] = -1 * amount;
if (line.tax_id) dict['account_tax_id'] = line.tax_id;
if (line.is_tax_line) dict['is_tax_line'] = line.is_tax_line;
if (line.analytic_account_id) dict['analytic_account_id'] = line.analytic_account_id;
return dict;
@@ -1671,8 +1679,9 @@ openerp.account = function (instance) {
makeMoveLineDicts: function() {
var self = this;
var mv_line_dicts = [];
_.each(self.get("mv_lines_selected"), function(o) { mv_line_dicts.push(self.prepareSelectedMoveLineForPersisting(o)) });
_.each(self.getCreatedLines(), function(o) { mv_line_dicts.push(self.prepareCreatedMoveLineForPersisting(o)) });
_.each(self.get("mv_lines_selected"), function(l) { mv_line_dicts.push(self.prepareSelectedMoveLineForPersisting(l)) });
created_lines = _.filter(self.getCreatedLines(), function(l) { return ! l.is_tax_line; }); // Tax lines are created via account_tax_id
_.each(self.getCreatedLines(), function(l) { mv_line_dicts.push(self.prepareCreatedMoveLineForPersisting(l)) });
if (Math.abs(self.get("balance")).toFixed(3) !== "0.000") mv_line_dicts.push(self.prepareOpenBalanceForPersisting());
return mv_line_dicts;
},
@@ -155,7 +155,7 @@
</t>
<t t-name="bank_statement_reconciliation_move_line">
<tr class="mv_line" t-att-data-lineid="line.id" t-att-data-selected="selected">
<tr t-att-class="'mv_line'+(line.already_paid ? ' already_reconciled' : '')" t-att-data-lineid="line.id" t-att-data-selected="selected">
<td class="cell_action"><span class="glyphicon glyphicon-add-remove"></span></td>
<td class="cell_account_code" t-if="line.account_code"><t t-esc="line.account_code"/></td>
<td class="cell_due_date"><t t-esc="line.q_due_date"/></td>
@@ -186,6 +186,9 @@
<t t-if="line.debit !== 0" t-esc="line.debit_str"/><t t-if="line.credit !== 0" t-esc="line.credit_str"/>
<t t-if="line.amount_currency_str"> (<t t-esc="line.amount_currency_str"/>)</t>
</td></tr>
<tr class="one_line_info" t-if='line.already_paid'>
<td colspan="2">This payment is registered but not reconciled.</td>
</tr>
</table>
</t>
@@ -40,7 +40,7 @@
<field name="inherit_id" ref="account.view_bank_statement_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='bank_account_id']" position="after">
<button name="cancel" attrs="{'invisible': [('journal_entry_id', '=', False)]}" string="Cancel" type="object" icon="gtk-undo"/>
<button name="cancel" attrs="{'invisible': [('journal_entry_ids', '=', [])]}" string="Cancel" type="object" icon="gtk-undo"/>
</xpath>
</field>
</record>
+1 -4
View File
@@ -1062,10 +1062,7 @@ class account_voucher(osv.osv):
else:
raise osv.except_osv(_('Error!'),
_('Please define a sequence on the journal.'))
if not voucher.reference:
ref = name.replace('/','')
else:
ref = voucher.reference
ref = voucher.reference or name
move = {
'name': name,
+7 -7
View File
@@ -1066,15 +1066,15 @@ class expression(object):
subselect = """WITH temp_irt_current (id, name) as (
SELECT ct.id, coalesce(it.value,ct.{quote_left})
FROM {current_table} ct
LEFT JOIN ir_translation it ON (it.name = %s and
it.lang = %s and
it.type = %s and
it.res_id = ct.id and
FROM {current_table} ct
LEFT JOIN ir_translation it ON (it.name = %s and
it.lang = %s and
it.type = %s and
it.res_id = ct.id and
it.value != '')
)
)
SELECT id FROM temp_irt_current WHERE {name} {operator} {right} order by name
""".format(current_table=model._table, quote_left=_quote(left), name=unaccent('name'),
""".format(current_table=model._table, quote_left=_quote(left), name=unaccent('name'),
operator=sql_operator, right=instr)
params = (