[FIX] account: several fixes on the new bank statement reconciliation widget
This commit is contained in:
parent
e833066b63
commit
f4e350ca36
|
@ -55,6 +55,19 @@ def check_cycle(self, cr, uid, ids, context=None):
|
|||
level -= 1
|
||||
return True
|
||||
|
||||
class res_company(osv.osv):
|
||||
_inherit = "res.company"
|
||||
_columns = {
|
||||
'income_currency_exchange_account_id': fields.many2one(
|
||||
'account.account',
|
||||
string="Gain Exchange Rate Account",
|
||||
domain="[('type', '=', 'other')]",),
|
||||
'expense_currency_exchange_account_id': fields.many2one(
|
||||
'account.account',
|
||||
string="Loss Exchange Rate Account",
|
||||
domain="[('type', '=', 'other')]",),
|
||||
}
|
||||
|
||||
class account_payment_term(osv.osv):
|
||||
_name = "account.payment.term"
|
||||
_description = "Payment Term"
|
||||
|
|
|
@ -405,31 +405,9 @@ class account_bank_statement(osv.osv):
|
|||
'context':ctx,
|
||||
}
|
||||
|
||||
def number_of_lines_reconciled(self, cr, uid, id, context=None):
|
||||
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', '=', id), ('journal_entry_id', '!=', False)], context=context)
|
||||
|
||||
def get_format_currency_js_function(self, cr, uid, id, context=None):
|
||||
""" Returns a string that can be used to instanciate a javascript function.
|
||||
That function formats a number according to the statement line's currency or the statement currency"""
|
||||
company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id
|
||||
st = id and self.browse(cr, uid, id, context=context)
|
||||
if not st:
|
||||
return
|
||||
statement_currency = st.journal_id.currency or company_currency
|
||||
digits = 2 # TODO : from currency_obj
|
||||
function = ""
|
||||
done_currencies = []
|
||||
for st_line in st.line_ids:
|
||||
st_line_currency = st_line.currency_id or statement_currency
|
||||
if st_line_currency.id not in done_currencies:
|
||||
if st_line_currency.position == 'after':
|
||||
return_str = "return amount.toFixed(" + str(digits) + ") + ' " + st_line_currency.symbol + "';"
|
||||
else:
|
||||
return_str = "return '" + st_line_currency.symbol + " ' + amount.toFixed(" + str(digits) + ");"
|
||||
function += "if (currency_id === " + str(st_line_currency.id) + "){ " + return_str + " }"
|
||||
done_currencies.append(st_line_currency.id)
|
||||
return function
|
||||
return bsl_obj.search_count(cr, uid, [('statement_id', 'in', ids), ('journal_entry_id', '!=', False)], context=context)
|
||||
|
||||
def link_bank_to_partner(self, cr, uid, ids, context=None):
|
||||
for statement in self.browse(cr, uid, ids, context=context):
|
||||
|
@ -465,98 +443,102 @@ class account_bank_statement_line(osv.osv):
|
|||
account_move_obj.button_cancel(cr, uid, move_ids, context=context)
|
||||
account_move_obj.unlink(cr, uid, move_ids, context)
|
||||
|
||||
def get_data_for_reconciliations(self, cr, uid, ids, context=None):
|
||||
""" Used to instanciate a batch of reconciliations in a single request """
|
||||
# Build a list of reconciliations data
|
||||
def get_data_for_reconciliations(self, cr, uid, ids, excluded_ids=None, search_reconciliation_proposition=True, context=None):
|
||||
""" Returns the data required to display a reconciliation, for each statement line id in ids """
|
||||
ret = []
|
||||
statement_line_done = {}
|
||||
mv_line_ids_selected = []
|
||||
for st_line in self.browse(cr, uid, ids, context=context):
|
||||
# look for structured communication first
|
||||
exact_match_id = self.search_structured_com(cr, uid, st_line, context=context)
|
||||
if exact_match_id:
|
||||
reconciliation_data = {
|
||||
'st_line': self.get_statement_line_for_reconciliation(cr, uid, st_line.id, context),
|
||||
'reconciliation_proposition': self.make_counter_part_lines(cr, uid, st_line, [exact_match_id], context=context)
|
||||
}
|
||||
for mv_line in reconciliation_data['reconciliation_proposition']:
|
||||
mv_line_ids_selected.append(mv_line['id'])
|
||||
statement_line_done[st_line.id] = reconciliation_data
|
||||
if excluded_ids is None:
|
||||
excluded_ids = []
|
||||
|
||||
for st_line_id in ids:
|
||||
if statement_line_done.get(st_line_id):
|
||||
ret.append(statement_line_done.get(st_line_id))
|
||||
for st_line in self.browse(cr, uid, ids, context=context):
|
||||
reconciliation_data = {}
|
||||
if search_reconciliation_proposition:
|
||||
reconciliation_proposition = self.get_reconciliation_proposition(cr, uid, st_line, excluded_ids=excluded_ids, context=context)
|
||||
for mv_line in reconciliation_proposition:
|
||||
excluded_ids.append(mv_line['id'])
|
||||
reconciliation_data['reconciliation_proposition'] = reconciliation_proposition
|
||||
else:
|
||||
reconciliation_data = {
|
||||
'st_line': self.get_statement_line_for_reconciliation(cr, uid, st_line_id, context),
|
||||
'reconciliation_proposition': self.get_reconciliation_proposition(cr, uid, st_line_id, mv_line_ids_selected, context)
|
||||
}
|
||||
for mv_line in reconciliation_data['reconciliation_proposition']:
|
||||
mv_line_ids_selected.append(mv_line['id'])
|
||||
reconciliation_data['reconciliation_proposition'] = []
|
||||
st_line = self.get_statement_line_for_reconciliation(cr, uid, st_line, context=context)
|
||||
reconciliation_data['st_line'] = st_line
|
||||
ret.append(reconciliation_data)
|
||||
|
||||
# Check if, now that 'candidate' move lines were selected, there are moves left for statement lines
|
||||
#for reconciliation_data in ret:
|
||||
# if not reconciliation_data['st_line']['has_no_partner']:
|
||||
# st_line = self.browse(cr, uid, reconciliation_data['st_line']['id'], context=context)
|
||||
# if not self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids=mv_line_ids_selected, count=True, context=context):
|
||||
# reconciliation_data['st_line']['no_match'] = True
|
||||
return ret
|
||||
|
||||
def get_statement_line_for_reconciliation(self, cr, uid, id, context=None):
|
||||
""" Returns the data required by the bank statement reconciliation use case """
|
||||
line = self.browse(cr, uid, id, context=context)
|
||||
statement_currency = line.journal_id.currency or line.journal_id.company_id.currency_id
|
||||
amount = line.amount
|
||||
rml_parser = report_sxw.rml_parse(cr, uid, 'statement_line_widget', context=context)
|
||||
amount_str = line.amount > 0 and line.amount or -line.amount
|
||||
amount_str = rml_parser.formatLang(amount_str, currency_obj=statement_currency)
|
||||
def get_statement_line_for_reconciliation(self, cr, uid, st_line, context=None):
|
||||
""" Returns the data required by the bank statement reconciliation widget to display a statement line """
|
||||
if context is None:
|
||||
context = {}
|
||||
statement_currency = st_line.journal_id.currency or st_line.journal_id.company_id.currency_id
|
||||
rml_parser = report_sxw.rml_parse(cr, uid, 'reconciliation_widget_asl', context=context)
|
||||
|
||||
if st_line.amount_currency and st_line.currency_id:
|
||||
amount = st_line.amount_currency
|
||||
amount_currency = st_line.amount
|
||||
amount_currency_str = amount_currency > 0 and amount_currency or -amount_currency
|
||||
amount_currency_str = rml_parser.formatLang(amount_currency_str, currency_obj=statement_currency)
|
||||
else:
|
||||
amount = st_line.amount
|
||||
amount_currency_str = ""
|
||||
if line.amount_currency and line.currency_id:
|
||||
amount_currency_str = amount_str
|
||||
amount_str = rml_parser.formatLang(line.amount_currency, currency_obj=line.currency_id)
|
||||
amount = line.amount_currency
|
||||
amount_str = amount > 0 and amount or -amount
|
||||
amount_str = rml_parser.formatLang(amount_str, currency_obj=st_line.currency_id or statement_currency)
|
||||
|
||||
data = {
|
||||
'id': line.id,
|
||||
'ref': line.ref,
|
||||
'note': line.note or "",
|
||||
'name': line.name,
|
||||
'date': line.date,
|
||||
'id': st_line.id,
|
||||
'ref': st_line.ref,
|
||||
'note': st_line.note or "",
|
||||
'name': st_line.name,
|
||||
'date': st_line.date,
|
||||
'amount': amount,
|
||||
'amount_str': amount_str,
|
||||
'currency_id': line.currency_id.id or statement_currency.id,
|
||||
'no_match': self.get_move_lines_counterparts(cr, uid, line, count=True, context=context) == 0,
|
||||
'partner_id': line.partner_id.id,
|
||||
'statement_id': line.statement_id.id,
|
||||
'account_code': line.journal_id.default_debit_account_id.code,
|
||||
'account_name': line.journal_id.default_debit_account_id.name,
|
||||
'partner_name': line.partner_id and line.partner_id.name or line.partner_name,
|
||||
'amount_currency_str': amount_currency_str,
|
||||
'has_no_partner': not line.partner_id.id,
|
||||
'amount_str': amount_str, # Amount in the statement line currency
|
||||
'currency_id': st_line.currency_id.id or statement_currency.id,
|
||||
'partner_id': st_line.partner_id.id,
|
||||
'statement_id': st_line.statement_id.id,
|
||||
'account_code': st_line.journal_id.default_debit_account_id.code,
|
||||
'account_name': st_line.journal_id.default_debit_account_id.name,
|
||||
'partner_name': st_line.partner_id.name,
|
||||
'amount_currency_str': amount_currency_str, # Amount in the statement currency
|
||||
'has_no_partner': not st_line.partner_id.id,
|
||||
}
|
||||
if line.partner_id.id:
|
||||
data['open_balance_account_id'] = line.partner_id.property_account_payable.id
|
||||
if st_line.partner_id.id:
|
||||
if amount > 0:
|
||||
data['open_balance_account_id'] = line.partner_id.property_account_receivable.id
|
||||
data['open_balance_account_id'] = st_line.partner_id.property_account_receivable.id
|
||||
else:
|
||||
data['open_balance_account_id'] = st_line.partner_id.property_account_payable.id
|
||||
|
||||
return data
|
||||
|
||||
def search_structured_com(self, cr, uid, st_line, context=None):
|
||||
domain = [('ref', '=', st_line.name.replace('/', ''))]
|
||||
if st_line.partner_id:
|
||||
domain += [('partner_id', '=', st_line.partner_id.id)]
|
||||
ids = self.pool.get('account.move.line').search(cr, uid, domain, limit=1, context=context)
|
||||
return ids and ids[0] or False
|
||||
|
||||
def get_reconciliation_proposition(self, cr, uid, id, excluded_ids=[], context=None):
|
||||
def get_reconciliation_proposition(self, cr, uid, st_line, excluded_ids=None, context=None):
|
||||
""" Returns move lines that constitute the best guess to reconcile a statement line. """
|
||||
st_line = self.browse(cr, uid, id, context=context)
|
||||
if excluded_ids is None:
|
||||
excluded_ids = []
|
||||
mv_line_pool = self.pool.get('account.move.line')
|
||||
|
||||
# Look for structured communication
|
||||
if st_line.name:
|
||||
structured_com_match_domain = [('ref', '=', st_line.name),('reconcile_id', '=', False),('state', '=', 'valid'),('account_id.reconcile', '=', True),('id', 'not in', excluded_ids)]
|
||||
match_id = mv_line_pool.search(cr, uid, structured_com_match_domain, offset=0, limit=1, context=context)
|
||||
if match_id:
|
||||
mv_line_br = mv_line_pool.browse(cr, uid, match_id, context=context)
|
||||
target_currency = st_line.currency_id or st_line.journal_id.currency or st_line.journal_id.company_id.currency_id
|
||||
mv_line = mv_line_pool.prepare_move_lines_for_reconciliation_widget(cr, uid, mv_line_br, target_currency=target_currency, target_date=st_line.date, context=context)[0]
|
||||
mv_line['has_no_partner'] = not bool(st_line.partner_id.id)
|
||||
# If the structured communication matches a move line that is associated with a partner, we can safely associate the statement line with the partner
|
||||
if (mv_line['partner_id']):
|
||||
self.write(cr, uid, st_line.id, {'partner_id': mv_line['partner_id']}, context=context)
|
||||
mv_line['has_no_partner'] = False
|
||||
return [mv_line]
|
||||
|
||||
# If there is no identified partner or structured communication, don't look further
|
||||
if not st_line.partner_id.id:
|
||||
return []
|
||||
|
||||
# Look for a move line whose amount matches the statement line's amount
|
||||
company_currency = st_line.journal_id.company_id.currency_id.id
|
||||
statement_currency = st_line.journal_id.currency.id or company_currency
|
||||
# either use the unsigned debit/credit fields or the signed amount_currency field
|
||||
sign = 1
|
||||
if statement_currency == company_currency:
|
||||
amount_field = 'credit'
|
||||
sign = -1
|
||||
if st_line.amount > 0:
|
||||
amount_field = 'debit'
|
||||
else:
|
||||
|
@ -564,124 +546,74 @@ class account_bank_statement_line(osv.osv):
|
|||
if st_line.amount < 0:
|
||||
sign = -1
|
||||
|
||||
#we don't propose anything if there is no partner detected
|
||||
if not st_line.partner_id.id:
|
||||
match_id = self.get_move_lines_for_reconciliation(cr, uid, st_line, excluded_ids=excluded_ids, offset=0, limit=1, additional_domain=[(amount_field, '=', (sign * st_line.amount))])
|
||||
if match_id:
|
||||
return [match_id[0]]
|
||||
|
||||
return []
|
||||
# look for exact match
|
||||
exact_match_id = self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids=excluded_ids, additional_domain=[(amount_field, '=', (sign * st_line.amount))])
|
||||
if exact_match_id:
|
||||
return exact_match_id
|
||||
|
||||
# select oldest move lines
|
||||
if sign == -1:
|
||||
mv_lines = self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids=excluded_ids, additional_domain=[(amount_field, '<', 0)])
|
||||
else:
|
||||
mv_lines = self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids=excluded_ids, additional_domain=[(amount_field, '>', 0)])
|
||||
ret = []
|
||||
total = 0
|
||||
# get_move_lines_counterparts inverts debit and credit
|
||||
amount_field = 'debit' if amount_field == 'credit' else 'credit'
|
||||
for line in mv_lines:
|
||||
if total + line[amount_field] <= abs(st_line.amount):
|
||||
ret.append(line)
|
||||
total += line[amount_field]
|
||||
if total >= abs(st_line.amount):
|
||||
break
|
||||
return ret
|
||||
|
||||
def get_move_lines_counterparts_id(self, cr, uid, st_line_id, excluded_ids=[], additional_domain=[], count=False, context=None):
|
||||
def get_move_lines_for_reconciliation_by_statement_line_id(self, cr, uid, st_line_id, excluded_ids=None, str=False, offset=0, limit=None, count=False, additional_domain=None, context=None):
|
||||
""" Bridge between the web client reconciliation widget and get_move_lines_for_reconciliation (which expects a browse record) """
|
||||
if excluded_ids is None:
|
||||
excluded_ids = []
|
||||
if additional_domain is None:
|
||||
additional_domain = []
|
||||
st_line = self.browse(cr, uid, st_line_id, context=context)
|
||||
return self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids, additional_domain, count, context=context)
|
||||
return self.get_move_lines_for_reconciliation(cr, uid, st_line, excluded_ids, str, offset, limit, count, additional_domain, context=context)
|
||||
|
||||
def get_move_lines_counterparts(self, cr, uid, st_line, excluded_ids=[], additional_domain=[], count=False, context=None):
|
||||
""" Find the move lines that could be used to reconcile a statement line and returns the counterpart that could be created to reconcile them
|
||||
If count is true, only returns the count.
|
||||
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.
|
||||
|
||||
:param st_line: the browse record of the statement line
|
||||
:param integers list excluded_ids: ids of move lines that should not be fetched
|
||||
:param string filter_str: string to filter lines
|
||||
:param integer offset: offset of the request
|
||||
:param integer limit: number of lines to fetch
|
||||
:param boolean count: just return the number of records
|
||||
:param tuples list domain: additional domain restrictions
|
||||
:param tuples list additional_domain: additional domain restrictions
|
||||
"""
|
||||
if excluded_ids is None:
|
||||
excluded_ids = []
|
||||
if additional_domain is None:
|
||||
additional_domain = []
|
||||
mv_line_pool = self.pool.get('account.move.line')
|
||||
|
||||
domain = additional_domain + [('reconcile_id', '=', False), ('state', '=', 'valid')]
|
||||
# Make domain
|
||||
domain = additional_domain + [('reconcile_id', '=', False),('state', '=', 'valid')]
|
||||
if st_line.partner_id.id:
|
||||
domain += [('partner_id', '=', st_line.partner_id.id),
|
||||
'|', ('account_id.type', '=', 'receivable'),
|
||||
('account_id.type', '=', 'payable')]
|
||||
else:
|
||||
domain += [('account_id.reconcile', '=', True), ('account_id.type', '=', 'other')]
|
||||
if str:
|
||||
domain += [('partner_id.name', 'ilike', str)]
|
||||
if excluded_ids:
|
||||
domain.append(('id', 'not in', excluded_ids))
|
||||
line_ids = mv_line_pool.search(cr, uid, domain, order="date_maturity asc, id asc", context=context)
|
||||
return self.make_counter_part_lines(cr, uid, st_line, line_ids, count=count, context=context)
|
||||
if str:
|
||||
domain += ['|', ('move_id.name', 'ilike', str), ('move_id.ref', 'ilike', str)]
|
||||
|
||||
def make_counter_part_lines(self, cr, uid, st_line, line_ids, count=False, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
mv_line_pool = self.pool.get('account.move.line')
|
||||
currency_obj = self.pool.get('res.currency')
|
||||
company_currency = st_line.journal_id.company_id.currency_id
|
||||
statement_currency = st_line.journal_id.currency or company_currency
|
||||
rml_parser = report_sxw.rml_parse(cr, uid, 'statement_line_counterpart_widget', context=context)
|
||||
#partially reconciled lines can be displayed only once
|
||||
reconcile_partial_ids = []
|
||||
# Get move lines
|
||||
line_ids = mv_line_pool.search(cr, uid, domain, offset=offset, limit=limit, order="date_maturity asc, id asc", context=context)
|
||||
lines = mv_line_pool.browse(cr, uid, line_ids, context=context)
|
||||
|
||||
# Either return number of lines
|
||||
if count:
|
||||
nb_lines = 0
|
||||
for line in mv_line_pool.browse(cr, uid, line_ids, context=context):
|
||||
reconcile_partial_ids = [] # for a partial reconciliation, take only one line
|
||||
for line in lines:
|
||||
if line.reconcile_partial_id and line.reconcile_partial_id.id in reconcile_partial_ids:
|
||||
continue
|
||||
nb_lines += 1
|
||||
if line.reconcile_partial_id:
|
||||
reconcile_partial_ids.append(line.reconcile_partial_id.id)
|
||||
return nb_lines
|
||||
|
||||
# Or return list of dicts representing the formatted move lines
|
||||
else:
|
||||
ret = []
|
||||
for line in mv_line_pool.browse(cr, uid, line_ids, context=context):
|
||||
if line.reconcile_partial_id and line.reconcile_partial_id.id in reconcile_partial_ids:
|
||||
continue
|
||||
amount_currency_str = ""
|
||||
if line.currency_id and line.amount_currency:
|
||||
amount_currency_str = rml_parser.formatLang(line.amount_currency, currency_obj=line.currency_id)
|
||||
ret_line = {
|
||||
'id': line.id,
|
||||
'name': line.move_id.name,
|
||||
'ref': line.move_id.ref,
|
||||
'account_code': line.account_id.code,
|
||||
'account_name': line.account_id.name,
|
||||
'account_type': line.account_id.type,
|
||||
'date_maturity': line.date_maturity,
|
||||
'date': line.date,
|
||||
'period_name': line.period_id.name,
|
||||
'journal_name': line.journal_id.name,
|
||||
'amount_currency_str': amount_currency_str,
|
||||
'partner_id': line.partner_id.id,
|
||||
'partner_name': line.partner_id.name,
|
||||
'has_no_partner': not bool(st_line.partner_id.id),
|
||||
}
|
||||
st_line_currency = st_line.currency_id or statement_currency
|
||||
line_currency = line.currency_id or company_currency
|
||||
if line_currency == st_line_currency and line_currency != company_currency:
|
||||
ret_line['debit'] = line.credit > 0 and -line.amount_residual_currency or 0.0
|
||||
ret_line['credit'] = line.debit > 0 and line.amount_residual_currency or 0.0
|
||||
ret_line['amount_currency_str'] = rml_parser.formatLang(line.amount_residual, currency_obj=company_currency)
|
||||
else:
|
||||
ret_line['debit'] = line.credit > 0 and -line.amount_residual or 0.0
|
||||
ret_line['credit'] = line.debit > 0 and line.amount_residual or 0.0
|
||||
if st_line_currency != company_currency:
|
||||
ctx = context.copy()
|
||||
ctx.update({'date': st_line.date})
|
||||
ret_line['debit'] = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, ret_line['debit'], context=ctx)
|
||||
ret_line['credit'] = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, ret_line['credit'], context=ctx)
|
||||
ret_line['debit_str'] = rml_parser.formatLang(ret_line['debit'], currency_obj=st_line_currency)
|
||||
ret_line['credit_str'] = rml_parser.formatLang(ret_line['credit'], currency_obj=st_line_currency)
|
||||
ret.append(ret_line)
|
||||
if line.reconcile_partial_id:
|
||||
reconcile_partial_ids.append(line.reconcile_partial_id.id)
|
||||
return ret
|
||||
target_currency = st_line.currency_id or st_line.journal_id.currency or st_line.journal_id.company_id.currency_id
|
||||
mv_lines = mv_line_pool.prepare_move_lines_for_reconciliation_widget(cr, uid, lines, target_currency=target_currency, target_date=st_line.date, context=context)
|
||||
has_no_partner = not bool(st_line.partner_id.id)
|
||||
for line in mv_lines:
|
||||
line['has_no_partner'] = has_no_partner
|
||||
return mv_lines
|
||||
|
||||
def get_currency_rate_line(self, cr, uid, st_line, currency_diff, move_id, context=None):
|
||||
if currency_diff < 0:
|
||||
|
@ -769,20 +701,21 @@ class account_bank_statement_line(osv.osv):
|
|||
mv_line = aml_obj.browse(cr, uid, mv_line_dict['counterpart_move_line_id'], context=context)
|
||||
mv_line_dict['account_id'] = mv_line.account_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=context)
|
||||
credit_at_current_rate = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['credit'] / st_line_currency_rate, context=context)
|
||||
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=context)
|
||||
credit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=context)
|
||||
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)
|
||||
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 = context.copy()
|
||||
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)
|
||||
|
@ -790,7 +723,7 @@ class account_bank_statement_line(osv.osv):
|
|||
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))
|
||||
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))
|
||||
|
@ -812,12 +745,9 @@ class account_bank_statement_line(osv.osv):
|
|||
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:
|
||||
# TODO : too slow
|
||||
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)
|
||||
|
||||
|
@ -826,7 +756,7 @@ class account_bank_statement_line(osv.osv):
|
|||
# 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_id', '=', False)]
|
||||
|
||||
_order = "statement_id desc, sequence"
|
||||
_name = "account.bank.statement.line"
|
||||
|
@ -864,13 +794,13 @@ class account_statement_operation_template(osv.osv):
|
|||
'label': fields.char('Label'),
|
||||
'amount_type': fields.selection([('fixed', 'Fixed'),('percentage_of_total','Percentage of total amount'),('percentage_of_balance', 'Percentage of open balance')],
|
||||
'Amount type', required=True),
|
||||
'amount': fields.float('Amount', digits_compute=dp.get_precision('Account'), help="Leave to 0 to ignore."),
|
||||
'amount': fields.float('Amount', digits_compute=dp.get_precision('Account'), help="The amount will count as a debit if it is negative, as a credit if it is positive (except if amount type is 'Percentage of open balance').", required=True),
|
||||
'tax_id': fields.many2one('account.tax', 'Tax', ondelete='cascade'),
|
||||
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', ondelete='cascade'),
|
||||
}
|
||||
_defaults = {
|
||||
'amount_type': 'fixed',
|
||||
'amount': 0.0
|
||||
'amount_type': 'percentage_of_balance',
|
||||
'amount': 100.0
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -645,10 +645,6 @@ class account_invoice(models.Model):
|
|||
invoice.check_total = invoice.amount_total
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _convert_ref(ref):
|
||||
return (ref or '').replace('/','')
|
||||
|
||||
@api.multi
|
||||
def _get_analytic_lines(self):
|
||||
""" Return a list of dict for creating analytic lines for self[0] """
|
||||
|
@ -661,7 +657,7 @@ class account_invoice(models.Model):
|
|||
if self.type in ('in_invoice', 'in_refund'):
|
||||
ref = self.reference
|
||||
else:
|
||||
ref = self._convert_ref(self.number)
|
||||
ref = self.number
|
||||
if not self.journal_id.analytic_journal_id:
|
||||
raise except_orm(_('No Analytic Journal!'),
|
||||
_("You have to define an analytic journal on the '%s' journal!") % (self.journal_id.name,))
|
||||
|
@ -827,7 +823,7 @@ class account_invoice(models.Model):
|
|||
if inv.type in ('in_invoice', 'in_refund'):
|
||||
ref = inv.reference
|
||||
else:
|
||||
ref = self._convert_ref(inv.number)
|
||||
ref = inv.number
|
||||
|
||||
diff_currency = inv.currency_id != company_currency
|
||||
# create one move line for the total and possibly adjust the other lines amount
|
||||
|
@ -955,11 +951,11 @@ class account_invoice(models.Model):
|
|||
|
||||
if inv.type in ('in_invoice', 'in_refund'):
|
||||
if not inv.reference:
|
||||
ref = self._convert_ref(inv.number)
|
||||
ref = inv.number
|
||||
else:
|
||||
ref = inv.reference
|
||||
else:
|
||||
ref = self._convert_ref(inv.number)
|
||||
ref = inv.number
|
||||
|
||||
self._cr.execute(""" UPDATE account_move SET ref=%s
|
||||
WHERE id=%s AND (ref IS NULL OR ref = '')""",
|
||||
|
@ -1131,7 +1127,7 @@ class account_invoice(models.Model):
|
|||
if self.type in ('in_invoice', 'in_refund'):
|
||||
ref = self.reference
|
||||
else:
|
||||
ref = self._convert_ref(self.number)
|
||||
ref = self.number
|
||||
partner = self.partner_id._find_accounting_partner(self.partner_id)
|
||||
name = name or self.invoice_line.name or self.number
|
||||
# Pay attention to the sign for both debit/credit AND amount_currency
|
||||
|
|
|
@ -22,12 +22,12 @@
|
|||
import time
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
from openerp import workflow
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from openerp import tools
|
||||
from openerp.report import report_sxw
|
||||
import openerp
|
||||
|
||||
class account_move_line(osv.osv):
|
||||
|
@ -752,6 +752,74 @@ class account_move_line(osv.osv):
|
|||
args.append(('partner_id', '=', partner[0]))
|
||||
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
|
||||
|
||||
:param target_currency: curreny you want the move line debit/credit converted into
|
||||
:param target_date: date to use for the monetary conversion
|
||||
"""
|
||||
if not lines:
|
||||
return []
|
||||
if context is None:
|
||||
context = {}
|
||||
ctx = context.copy()
|
||||
currency_obj = self.pool.get('res.currency')
|
||||
company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id
|
||||
rml_parser = report_sxw.rml_parse(cr, uid, 'reconciliation_widget_aml', context=context)
|
||||
reconcile_partial_ids = [] # for a partial reconciliation, take only one line
|
||||
ret = []
|
||||
|
||||
for line in lines:
|
||||
if line.reconcile_partial_id and line.reconcile_partial_id.id in reconcile_partial_ids:
|
||||
continue
|
||||
if line.reconcile_partial_id:
|
||||
reconcile_partial_ids.append(line.reconcile_partial_id.id)
|
||||
|
||||
ret_line = {
|
||||
'id': line.id,
|
||||
'name': line.move_id.name,
|
||||
'ref': line.move_id.ref,
|
||||
'account_code': line.account_id.code,
|
||||
'account_name': line.account_id.name,
|
||||
'account_type': line.account_id.type,
|
||||
'date_maturity': line.date_maturity,
|
||||
'date': line.date,
|
||||
'period_name': line.period_id.name,
|
||||
'journal_name': line.journal_id.name,
|
||||
'partner_id': line.partner_id.id,
|
||||
'partner_name': line.partner_id.name,
|
||||
}
|
||||
|
||||
# Get right debit / credit:
|
||||
line_currency = line.currency_id or company_currency
|
||||
amount_currency_str = ""
|
||||
if line.currency_id and line.amount_currency:
|
||||
amount_currency_str = rml_parser.formatLang(line.amount_currency, currency_obj=line.currency_id)
|
||||
if target_currency and line_currency == target_currency and target_currency != company_currency:
|
||||
debit = line.debit > 0 and line.amount_residual_currency or 0.0
|
||||
credit = line.credit > 0 and line.amount_residual_currency or 0.0
|
||||
amount_currency_str = rml_parser.formatLang(line.amount_residual, currency_obj=company_currency)
|
||||
amount_str = rml_parser.formatLang(debit or credit, currency_obj=target_currency)
|
||||
else:
|
||||
debit = line.debit > 0 and line.amount_residual or 0.0
|
||||
credit = line.credit > 0 and line.amount_residual or 0.0
|
||||
amount_str = rml_parser.formatLang(debit or credit, currency_obj=company_currency)
|
||||
if target_currency and target_currency != company_currency:
|
||||
amount_currency_str = rml_parser.formatLang(debit or credit, currency_obj=line_currency)
|
||||
ctx = context.copy()
|
||||
if target_date:
|
||||
ctx.update({'date': target_date})
|
||||
debit = currency_obj.compute(cr, uid, target_currency.id, company_currency.id, debit, context=ctx)
|
||||
credit = currency_obj.compute(cr, uid, target_currency.id, company_currency.id, credit, context=ctx)
|
||||
amount_str = rml_parser.formatLang(debit or credit, currency_obj=target_currency)
|
||||
|
||||
ret_line['credit'] = credit
|
||||
ret_line['debit'] = debit
|
||||
ret_line['amount_str'] = amount_str
|
||||
ret_line['amount_currency_str'] = amount_currency_str
|
||||
ret.append(ret_line)
|
||||
return ret
|
||||
|
||||
def list_partners_to_reconcile(self, cr, uid, context=None):
|
||||
cr.execute(
|
||||
"""SELECT partner_id FROM (
|
||||
|
|
|
@ -493,7 +493,7 @@
|
|||
<record id="action_bank_reconcile_bank_statements" model="ir.actions.client">
|
||||
<field name="name">Reconciliation on Bank Statements</field>
|
||||
<field name="tag">bank_statement_reconciliation_view</field>
|
||||
<field name="context">{'statement_id': active_id}</field>
|
||||
<field name="context">{'statement_ids': [active_id]}</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_bank_statement_filter" model="ir.ui.view">
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<field name="statement_id" ref="demo_bank_statement_1"/>
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="name">SAJ2014002</field>
|
||||
<field name="name">SAJ/2014/002</field>
|
||||
<field name="journal_id" ref="account.bank_journal"/>
|
||||
<field name="amount" eval="650.0"/>
|
||||
<field name="date" eval="time.strftime('%Y')+'-01-01'"/>
|
||||
|
@ -70,15 +70,15 @@
|
|||
<field name="name">Bank Fees</field>
|
||||
<field name="account_id" ref="a_expense"></field>
|
||||
<field name="label">Bank Fees</field>
|
||||
<field name="amount_type">fixed</field>
|
||||
<field name="amount"></field>
|
||||
<field name="amount_type">percentage_of_balance</field>
|
||||
<field name="amount">100.0</field>
|
||||
</record>
|
||||
<record id="statement_operation_template_3" model="account.statement.operation.template">
|
||||
<field name="name">Profit / Loss</field>
|
||||
<field name="account_id" ref="a_sale"></field>
|
||||
<field name="label">Profit / Loss</field>
|
||||
<field name="amount_type">fixed</field>
|
||||
<field name="amount"></field>
|
||||
<field name="amount_type">percentage_of_balance</field>
|
||||
<field name="amount">100.0</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -125,7 +125,29 @@ class account_config_settings(osv.osv_memory):
|
|||
help="Allows you to use the analytic accounting."),
|
||||
'group_check_supplier_invoice_total': fields.boolean('Check the total of supplier invoices',
|
||||
implied_group="account.group_supplier_inv_check_total"),
|
||||
'income_currency_exchange_account_id': fields.related(
|
||||
'company_id', 'income_currency_exchange_account_id',
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string="Gain Exchange Rate Account",
|
||||
domain="[('type', '=', 'other')]"),
|
||||
'expense_currency_exchange_account_id': fields.related(
|
||||
'company_id', 'expense_currency_exchange_account_id',
|
||||
type="many2one",
|
||||
relation='account.account',
|
||||
string="Loss Exchange Rate Account",
|
||||
domain="[('type', '=', 'other')]"),
|
||||
}
|
||||
def onchange_company_id(self, cr, uid, ids, company_id, context=None):
|
||||
res = super(account_config_settings, self).onchange_company_id(cr, uid, ids, company_id, context=context)
|
||||
if company_id:
|
||||
company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
|
||||
res['value'].update({'income_currency_exchange_account_id': company.income_currency_exchange_account_id and company.income_currency_exchange_account_id.id or False,
|
||||
'expense_currency_exchange_account_id': company.expense_currency_exchange_account_id and company.expense_currency_exchange_account_id.id or False})
|
||||
else:
|
||||
res['value'].update({'income_currency_exchange_account_id': False,
|
||||
'expense_currency_exchange_account_id': False})
|
||||
return res
|
||||
|
||||
def _default_company(self, cr, uid, context=None):
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
|
|
|
@ -124,6 +124,14 @@
|
|||
<div name="group_multi_currency">
|
||||
<field name="group_multi_currency" class="oe_inline" on_change="onchange_multi_currency(group_multi_currency)"/>
|
||||
<label for="group_multi_currency"/>
|
||||
<group attrs="{'invisible': [('group_multi_currency', '<>', True)]}" col="2">
|
||||
<group>
|
||||
<field name="income_currency_exchange_account_id"/>
|
||||
<field name="expense_currency_exchange_account_id"/>
|
||||
</group>
|
||||
<group>
|
||||
</group>
|
||||
</group>
|
||||
</div>
|
||||
<div>
|
||||
<field name="module_account_accountant" class="oe_inline"/>
|
||||
|
|
|
@ -6,18 +6,29 @@
|
|||
-o-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
height: 100%;
|
||||
/* icons */ }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_form_sheetbg {
|
||||
border-bottom: 0;
|
||||
padding: 0;
|
||||
height: 100%; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_form_sheetbg .oe_form_sheet {
|
||||
position: relative;
|
||||
padding: 20px 15px 30px 15px;
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
height: 100%; }
|
||||
.openerp .oe_bank_statement_reconciliation h1 {
|
||||
width: 48%;
|
||||
padding: 0 0 0 15px;
|
||||
margin: 0 0 35px 0;
|
||||
margin: 0 0 25px 0;
|
||||
float: left;
|
||||
font-size: 2.3em; }
|
||||
font-size: 2em; }
|
||||
.openerp .oe_bank_statement_reconciliation h2 {
|
||||
font-size: 1.8em; }
|
||||
.openerp .oe_bank_statement_reconciliation .progress {
|
||||
width: 49%;
|
||||
margin: 6px 15px 0 0;
|
||||
margin: 4px 15px 0 0;
|
||||
float: right;
|
||||
position: relative;
|
||||
display: inline-block; }
|
||||
|
@ -29,9 +40,6 @@
|
|||
top: 2px;
|
||||
z-index: 10;
|
||||
text-shadow: -1px -1px 0 #f5f5f5, 1px -1px 0 #f5f5f5, -1px 1px 0 #f5f5f5, 1px 1px 0 #f5f5f5; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_form_sheet {
|
||||
position: relative;
|
||||
padding-bottom: 30px; }
|
||||
.openerp .oe_bank_statement_reconciliation .protip {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
|
@ -100,7 +108,12 @@
|
|||
-moz-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg); }
|
||||
transform: rotate(0deg);
|
||||
-webkit-transition-duration: 300ms;
|
||||
-moz-transition-duration: 300ms;
|
||||
-ms-transition-duration: 300ms;
|
||||
-o-transition-duration: 300ms;
|
||||
transition-duration: 300ms; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .visible_toggle, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line[data-mode="match"] .toggle_match, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line[data-mode="create"] .toggle_create {
|
||||
visibility: visible !important;
|
||||
-webkit-transform: rotate(90deg);
|
||||
|
@ -136,24 +149,24 @@
|
|||
.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; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(1), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(7), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(1), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(7) {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_action, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_info_popover, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_action, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_info_popover {
|
||||
width: 15px;
|
||||
padding: 0; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(1), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(1) {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_action, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_action {
|
||||
text-align: left; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(2), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(2) {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_account_code, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_account_code {
|
||||
width: 80px;
|
||||
padding-left: 3px; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(3), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(3) {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_due_date, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_due_date {
|
||||
width: 100px; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(5), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(5) {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_debit, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_debit {
|
||||
text-align: right;
|
||||
width: 15%; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(6), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(6) {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_credit, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_credit {
|
||||
width: 15%;
|
||||
text-align: right;
|
||||
padding-right: 3px; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(7), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(7) {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_info_popover, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_info_popover {
|
||||
text-align: right; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr.line_open_balance, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr.line_open_balance {
|
||||
color: #bbb; }
|
||||
|
@ -176,8 +189,13 @@
|
|||
-webkit-transition-property: background-color;
|
||||
-moz-transition-property: background-color;
|
||||
-ms-transition-property: background-color;
|
||||
transition-property: background-color; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view .initial_line > td:nth-child(1), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view .initial_line > td:nth-child(7) {
|
||||
transition-property: background-color;
|
||||
-webkit-transition-duration: 300ms;
|
||||
-moz-transition-duration: 300ms;
|
||||
-ms-transition-duration: 300ms;
|
||||
-o-transition-duration: 300ms;
|
||||
transition-duration: 300ms; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view .initial_line > td.cell_action, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view .initial_line > td.cell_info_popover {
|
||||
border-top: none;
|
||||
background: white !important;
|
||||
padding-top: 6px;
|
||||
|
@ -188,9 +206,9 @@
|
|||
font-weight: bold;
|
||||
height: 26px;
|
||||
margin: 0 15px 4px 15px; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption .button_ok {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption button {
|
||||
float: right; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption .button_ok:disabled {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption button:disabled {
|
||||
opacity: 0.5; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption > span, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption > input {
|
||||
position: relative;
|
||||
|
@ -198,7 +216,7 @@
|
|||
/* meh */
|
||||
font-weight: bold;
|
||||
cursor: pointer; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(6) {
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_credit {
|
||||
border-left: 1px solid black; }
|
||||
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match .match_controls {
|
||||
padding: 0 0 5px 18px; }
|
||||
|
|
|
@ -3,6 +3,8 @@ $mainTableBordersPadding: 3px;
|
|||
$lightBorder: 1px solid #bbb;
|
||||
$accountingBorder: 1px solid #000;
|
||||
$initialLineBackground: #f0f0f0;
|
||||
// Warning, this value is also specified in the instance.web.account.abstractReconciliation widget
|
||||
$aestetic_animation_speed: 300ms;
|
||||
|
||||
|
||||
.openerp .oe_bank_statement_reconciliation {
|
||||
|
@ -13,13 +15,28 @@ $initialLineBackground: #f0f0f0;
|
|||
-o-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
height: 100%;
|
||||
|
||||
.oe_form_sheetbg {
|
||||
border-bottom: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
|
||||
.oe_form_sheet {
|
||||
position: relative;
|
||||
padding: 20px 15px 30px 15px;
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
width: 48%;
|
||||
padding: 0 0 0 $actionColWidth;
|
||||
margin: 0 0 35px 0;
|
||||
margin: 0 0 25px 0;
|
||||
float: left;
|
||||
font-size: 2.3em;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
@ -28,7 +45,7 @@ $initialLineBackground: #f0f0f0;
|
|||
|
||||
.progress {
|
||||
width: 49%;
|
||||
margin: 6px $actionColWidth 0 0;
|
||||
margin: 4px $actionColWidth 0 0;
|
||||
float: right;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
@ -48,11 +65,6 @@ $initialLineBackground: #f0f0f0;
|
|||
}
|
||||
}
|
||||
|
||||
.oe_form_sheet {
|
||||
position: relative;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.protip {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
|
@ -145,6 +157,11 @@ $initialLineBackground: #f0f0f0;
|
|||
-ms-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
-webkit-transition-duration: $aestetic_animation_speed;
|
||||
-moz-transition-duration: $aestetic_animation_speed;
|
||||
-ms-transition-duration: $aestetic_animation_speed;
|
||||
-o-transition-duration: $aestetic_animation_speed;
|
||||
transition-duration: $aestetic_animation_speed;
|
||||
}
|
||||
|
||||
.visible_toggle {
|
||||
|
@ -228,40 +245,40 @@ $initialLineBackground: #f0f0f0;
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
td:nth-child(1), td:nth-child(7) {
|
||||
td.cell_action, td.cell_info_popover {
|
||||
width: $actionColWidth;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
td:nth-child(1) {
|
||||
td.cell_action {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td:nth-child(2) {
|
||||
td.cell_account_code {
|
||||
width: 80px;
|
||||
padding-left: $mainTableBordersPadding;
|
||||
}
|
||||
|
||||
td:nth-child(3) {
|
||||
td.cell_due_date {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
td:nth-child(4) {
|
||||
td.cell_label {
|
||||
|
||||
}
|
||||
|
||||
td:nth-child(5) {
|
||||
td.cell_debit {
|
||||
text-align: right;
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
td:nth-child(6) {
|
||||
td.cell_credit {
|
||||
width: 15%;
|
||||
text-align: right;
|
||||
padding-right: $mainTableBordersPadding;
|
||||
}
|
||||
|
||||
td:nth-child(7) {
|
||||
td.cell_info_popover {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
@ -301,8 +318,13 @@ $initialLineBackground: #f0f0f0;
|
|||
-moz-transition-property: background-color;
|
||||
-ms-transition-property: background-color;
|
||||
transition-property: background-color;
|
||||
-webkit-transition-duration: $aestetic_animation_speed;
|
||||
-moz-transition-duration: $aestetic_animation_speed;
|
||||
-ms-transition-duration: $aestetic_animation_speed;
|
||||
-o-transition-duration: $aestetic_animation_speed;
|
||||
transition-duration: $aestetic_animation_speed;
|
||||
|
||||
&:nth-child(1), &:nth-child(7) {
|
||||
&.cell_action, &.cell_info_popover {
|
||||
border-top: none;
|
||||
background: white !important;
|
||||
// Hack pour l'alignement au px près
|
||||
|
@ -318,7 +340,7 @@ $initialLineBackground: #f0f0f0;
|
|||
height: 26px;
|
||||
margin: 0 $actionColWidth 4px $actionColWidth;
|
||||
|
||||
.button_ok {
|
||||
button {
|
||||
float: right;
|
||||
|
||||
&:disabled {
|
||||
|
@ -334,7 +356,7 @@ $initialLineBackground: #f0f0f0;
|
|||
}
|
||||
|
||||
// accounting "T"
|
||||
td:nth-child(6) { border-left: $accountingBorder; }
|
||||
td.cell_credit { border-left: $accountingBorder; }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var _t = openerp._t;
|
||||
|
||||
openerp.Tour.register({
|
||||
id: 'bank_statement_reconciliation',
|
||||
name: _t("Reconcile the demo bank statement"),
|
||||
path: '/web',
|
||||
mode: 'test',
|
||||
// TODO : identify menu by data-menu attr or text node ?
|
||||
steps: [
|
||||
// Go to the first statement reconciliation
|
||||
{
|
||||
title: "go to accounting",
|
||||
element: '.oe_menu_toggler:contains("Accounting"):visible',
|
||||
},
|
||||
{
|
||||
title: "go to bank statements",
|
||||
element: '.oe_menu_leaf:contains("Bank Statement"):visible',
|
||||
},
|
||||
{
|
||||
title: "select first bank statement",
|
||||
element: '.oe_list_content tbody tr:contains("BNK/2014/001")',
|
||||
},
|
||||
{
|
||||
title: "click the reconcile button",
|
||||
element: '.oe_form_container header button:contains("Reconcile")',
|
||||
},
|
||||
|
||||
|
||||
// Check mutual exclusion of move lines
|
||||
{
|
||||
title: "set second reconciliation in match mode",
|
||||
element: '.oe_bank_statement_reconciliation_line:nth-child(2) .initial_line'
|
||||
},
|
||||
{
|
||||
title: "deselect SAJ/2014/002 from second reconciliation",
|
||||
element: '.oe_bank_statement_reconciliation_line:nth-child(2) .accounting_view .mv_line:contains("SAJ/2014/002")'
|
||||
},
|
||||
{
|
||||
title: "check it appeared in first reconciliation's matches list and select SAJ/2014/002 in second reconciliation",
|
||||
waitNot: '.oe_bank_statement_reconciliation_line:nth-child(2) .accounting_view .mv_line:contains("SAJ/2014/002")',
|
||||
waitFor: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/002")',
|
||||
element: '.oe_bank_statement_reconciliation_line:nth-child(2) .mv_line:contains("SAJ/2014/002")'
|
||||
},
|
||||
|
||||
|
||||
// Make a partial reconciliation
|
||||
{
|
||||
title: "select SAJ/2014/001",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/001")'
|
||||
},
|
||||
{
|
||||
title: "click on the partial reconciliation button",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/001") .do_partial_reconcile_button'
|
||||
},
|
||||
{
|
||||
title: "click on the OK button",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight'
|
||||
},
|
||||
|
||||
|
||||
// Test changing the partner
|
||||
{
|
||||
title: "change the partner (1)",
|
||||
waitNot: '.oe_bank_statement_reconciliation_line:nth-child(4)', // wait for the reconciliation to be processed
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .partner_name'
|
||||
},
|
||||
{
|
||||
title: "change the partner (2)",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .change_partner_container input',
|
||||
sampleText: 'Vauxoo',
|
||||
},
|
||||
{
|
||||
title: "change the partner (3)",
|
||||
element: '.ui-autocomplete .ui-menu-item:contains("Vauxoo")'
|
||||
},
|
||||
{
|
||||
title: "check the reconciliation is reloaded and has no match",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child.no_match',
|
||||
},
|
||||
{
|
||||
title: "change the partner back (1)",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .partner_name'
|
||||
},
|
||||
{
|
||||
title: "change the partner back (2)",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .change_partner_container input',
|
||||
sampleText: 'Best Designers',
|
||||
},
|
||||
{
|
||||
title: "change the partner back (3)",
|
||||
element: '.ui-autocomplete .ui-menu-item:contains("Best Designers")'
|
||||
},
|
||||
{
|
||||
title: "select SAJ/2014/002",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/002")'
|
||||
},
|
||||
{
|
||||
title: "click on the OK button",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight'
|
||||
},
|
||||
|
||||
|
||||
// Create a new move line in first reconciliation and validate it
|
||||
{
|
||||
title: "check following reconciliation passes in mode create",
|
||||
waitNot: '.oe_bank_statement_reconciliation_line:nth-child(3)', // wait for the reconciliation to be processed
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child[data-mode="create"]'
|
||||
},
|
||||
{
|
||||
title: "click the Profit/Loss preset",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child button:contains("Profit / Loss")'
|
||||
},
|
||||
{
|
||||
title: "click on the OK button",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight'
|
||||
},
|
||||
|
||||
|
||||
// Leave an open balance
|
||||
{
|
||||
title: "select SAJ/2014/003",
|
||||
waitNot: '.oe_bank_statement_reconciliation_line:nth-child(2)', // wait for the reconciliation to be processed
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/003")'
|
||||
},
|
||||
{
|
||||
title: "click on the Keep Open button",
|
||||
element: '.oe_bank_statement_reconciliation_line:first-child .button_ok:not(.oe_highlight)'
|
||||
},
|
||||
|
||||
|
||||
// Be done
|
||||
{
|
||||
title: "check 'finish screen' and close the statement",
|
||||
waitFor: '.done_message',
|
||||
element: '.button_close_statement'
|
||||
},
|
||||
{
|
||||
title: "check the statement is closed",
|
||||
element: '.oe_form_container header .label:contains("Closed")'
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
}());
|
|
@ -13,7 +13,8 @@ openerp.account = function (instance) {
|
|||
init: function(parent, context) {
|
||||
this._super(parent);
|
||||
this.max_reconciliations_displayed = 10;
|
||||
this.statement_id = context.context.statement_id;
|
||||
if (context.context.statement_id) this.statement_ids = [context.context.statement_id];
|
||||
if (context.context.statement_ids) this.statement_ids = context.context.statement_ids;
|
||||
this.title = context.context.title || _t("Reconciliation");
|
||||
this.st_lines = [];
|
||||
this.last_displayed_reconciliation_index = undefined; // Flow control
|
||||
|
@ -37,7 +38,7 @@ openerp.account = function (instance) {
|
|||
// We'll need to get the code of an account selected in a many2one (whose value is the id)
|
||||
this.map_account_id_code = {};
|
||||
// The same move line cannot be selected for multiple resolutions
|
||||
this.excluded_move_lines_ids = [];
|
||||
this.excluded_move_lines_ids = {};
|
||||
// Description of the fields to initialize in the "create new line" form
|
||||
// NB : for presets to work correctly, a field id must be the same string as a preset field
|
||||
this.create_form_fields = {
|
||||
|
@ -118,48 +119,35 @@ openerp.account = function (instance) {
|
|||
start: function() {
|
||||
this._super();
|
||||
var self = this;
|
||||
|
||||
// Inject variable styles
|
||||
var style = document.createElement("style");
|
||||
style.appendChild(document.createTextNode(""));
|
||||
document.head.appendChild(style);
|
||||
var css_selector = ".oe_bank_statement_reconciliation_line .toggle_match, .oe_bank_statement_reconciliation_line .toggle_create, .oe_bank_statement_reconciliation_line .initial_line > td";
|
||||
if(style.sheet.insertRule) {
|
||||
style.sheet.insertRule(css_selector + " { -webkit-transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
|
||||
style.sheet.insertRule(css_selector + " { -moz-transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
|
||||
style.sheet.insertRule(css_selector + " { -ms-transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
|
||||
style.sheet.insertRule(css_selector + " { -o-transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
|
||||
style.sheet.insertRule(css_selector + " { transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
|
||||
} else {
|
||||
style.sheet.addRule(css_selector, "-webkit-transition-duration: "+self.aestetic_animation_speed+"ms;");
|
||||
style.sheet.addRule(css_selector, "-moz-transition-duration: "+self.aestetic_animation_speed+"ms;");
|
||||
style.sheet.addRule(css_selector, "-ms-transition-duration: "+self.aestetic_animation_speed+"ms;");
|
||||
style.sheet.addRule(css_selector, "-o-transition-duration: "+self.aestetic_animation_speed+"ms;");
|
||||
style.sheet.addRule(css_selector, "-webkit-transition-duration: "+self.aestetic_animation_speed+"ms;");
|
||||
}
|
||||
|
||||
// Retreive statement infos and reconciliation data from the model
|
||||
var lines_filter = [['journal_entry_id', '=', false], ['account_id', '=', false]];
|
||||
var deferred_promises = [];
|
||||
|
||||
if (self.statement_id) {
|
||||
lines_filter.push(['statement_id', '=', self.statement_id]);
|
||||
// Working on specified statement(s)
|
||||
if (self.statement_ids && self.statement_ids.length > 0) {
|
||||
lines_filter.push(['statement_id', 'in', self.statement_ids]);
|
||||
|
||||
// If only one statement, retreive its name
|
||||
if (self.statement_ids.length === 1) {
|
||||
deferred_promises.push(self.model_bank_statement
|
||||
.query(["name"])
|
||||
.filter([['id', '=', self.statement_id]])
|
||||
.filter([['id', '=', self.statement_ids[0]]])
|
||||
.first()
|
||||
.then(function(title){
|
||||
self.title = title.name;
|
||||
})
|
||||
);
|
||||
}
|
||||
// Anyway, find out how many statement lines are reconciled (for the progressbar)
|
||||
deferred_promises.push(self.model_bank_statement
|
||||
.call("number_of_lines_reconciled", [self.statement_id])
|
||||
.call("number_of_lines_reconciled", [self.statement_ids])
|
||||
.then(function(num) {
|
||||
self.already_reconciled_lines = num;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Get operation templates
|
||||
deferred_promises.push(new instance.web.Model("account.statement.operation.template")
|
||||
.query(['id','name','account_id','label','amount_type','amount','tax_id','analytic_account_id'])
|
||||
.all().then(function (data) {
|
||||
|
@ -169,17 +157,19 @@ openerp.account = function (instance) {
|
|||
})
|
||||
);
|
||||
|
||||
deferred_promises.push(self.model_bank_statement
|
||||
.call("get_format_currency_js_function", [self.statement_id])
|
||||
.then(function(data){
|
||||
// Get the function to format currencies
|
||||
deferred_promises.push(new instance.web.Model("res.currency")
|
||||
.call("get_format_currencies_js_function")
|
||||
.then(function(data) {
|
||||
self.formatCurrency = new Function("amount, currency_id", data);
|
||||
})
|
||||
);
|
||||
|
||||
// Get statement lines
|
||||
deferred_promises.push(self.model_bank_statement_line
|
||||
.query(['id'])
|
||||
.filter(lines_filter)
|
||||
.order_by('id')
|
||||
.order_by('statement_id, id')
|
||||
.all().then(function (data) {
|
||||
self.st_lines = _(data).map(function(o){ return o.id });
|
||||
})
|
||||
|
@ -255,33 +245,58 @@ openerp.account = function (instance) {
|
|||
}
|
||||
},
|
||||
|
||||
excludeMoveLines: function(line_ids) {
|
||||
// Adds move line ids to the list of move lines not to fetch for a given partner
|
||||
// This is required because the same move line cannot be selected for multiple reconciliation
|
||||
excludeMoveLines: function(source_child, partner_id, line_ids) {
|
||||
var self = this;
|
||||
|
||||
var excluded_ids = this.excluded_move_lines_ids[partner_id];
|
||||
var excluded_move_lines_changed = false;
|
||||
_.each(line_ids, function(line_id){
|
||||
line_id = parseInt(line_id);
|
||||
if (self.excluded_move_lines_ids.indexOf(line_id) === -1) {
|
||||
self.excluded_move_lines_ids.push(line_id);
|
||||
if (excluded_ids.indexOf(line_id) === -1) {
|
||||
excluded_ids.push(line_id);
|
||||
excluded_move_lines_changed = true;
|
||||
}
|
||||
});
|
||||
//update all children view
|
||||
if (! excluded_move_lines_changed)
|
||||
return;
|
||||
|
||||
// Function that finds if an array of line objects contains at least a line identified by its id
|
||||
var contains_lines = function(lines_array, line_ids) {
|
||||
for (var i = 0; i < lines_array.length; i++)
|
||||
for (var j = 0; j < line_ids.length; j++)
|
||||
if (lines_array[i].id === line_ids[j])
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
// Update children if needed
|
||||
_.each(self.getChildren(), function(child){
|
||||
child.render();
|
||||
if (child.partner_id === partner_id && child !== source_child) {
|
||||
if (contains_lines(child.get("mv_lines_selected"), line_ids)) {
|
||||
child.set("mv_lines_selected", _.filter(child.get("mv_lines_selected"), function(o){ return line_ids.indexOf(o.id) === -1 }));
|
||||
} else if (contains_lines(child.mv_lines_deselected, line_ids)) {
|
||||
child.mv_lines_deselected = _.filter(child.mv_lines_deselected, function(o){ return line_ids.indexOf(o.id) === -1 });
|
||||
child.updateMatches();
|
||||
} else if (contains_lines(child.get("mv_lines"), line_ids)) {
|
||||
child.updateMatches();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
unexcludeMoveLines: function(line_ids) {
|
||||
unexcludeMoveLines: function(source_child, partner_id, line_ids) {
|
||||
var self = this;
|
||||
var index = -1;
|
||||
_.each(line_ids, function(line_id){
|
||||
line_id = parseInt(line_id);
|
||||
index = self.excluded_move_lines_ids.indexOf(line_id);
|
||||
if (index > -1) {
|
||||
self.excluded_move_lines_ids.splice(index,1);
|
||||
}
|
||||
});
|
||||
//update all children view
|
||||
|
||||
var initial_excluded_lines_num = this.excluded_move_lines_ids[partner_id].length;
|
||||
this.excluded_move_lines_ids[partner_id] = _.difference(this.excluded_move_lines_ids[partner_id], line_ids);
|
||||
if (this.excluded_move_lines_ids[partner_id].length === initial_excluded_lines_num)
|
||||
return;
|
||||
|
||||
// Update children if needed
|
||||
_.each(self.getChildren(), function(child){
|
||||
child.render();
|
||||
if (child.partner_id === partner_id && child !== source_child && (child.get("mode") === "match" || child.$el.hasClass("no_match")))
|
||||
child.updateMatches();
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -313,6 +328,11 @@ openerp.account = function (instance) {
|
|||
if (self.last_displayed_reconciliation_index < self.st_lines.length) {
|
||||
self.displayReconciliation(self.st_lines[self.last_displayed_reconciliation_index++], 'inactive');
|
||||
}
|
||||
// Congratulate the user if the work is done
|
||||
if (self.reconciled_lines === self.st_lines.length) {
|
||||
self.displayDoneMessage();
|
||||
}
|
||||
|
||||
// Put the first line in match mode
|
||||
if (self.reconciled_lines !== self.st_lines.length) {
|
||||
var first_child = self.getChildren()[0];
|
||||
|
@ -320,15 +340,12 @@ openerp.account = function (instance) {
|
|||
first_child.set("mode", "match");
|
||||
}
|
||||
}
|
||||
// Congratulate the user if the work is done
|
||||
if (self.reconciled_lines === self.st_lines.length) {
|
||||
self.displayDoneMessage();
|
||||
}
|
||||
},
|
||||
|
||||
displayDoneMessage: function() {
|
||||
var self = this;
|
||||
|
||||
var is_single_statement = self.statement_ids !== undefined && self.statement_ids.length === 1;
|
||||
var sec_taken = Math.round((Date.now()-self.time_widget_loaded)/1000);
|
||||
var sec_per_item = Math.round(sec_taken/self.reconciled_lines);
|
||||
var achievements = [];
|
||||
|
@ -364,7 +381,7 @@ openerp.account = function (instance) {
|
|||
transactions_done: self.reconciled_lines,
|
||||
done_with_ctrl_enter: self.lines_reconciled_with_ctrl_enter,
|
||||
achievements: achievements,
|
||||
has_statement_id: self.statement_id !== undefined,
|
||||
has_statement_id: is_single_statement,
|
||||
}));
|
||||
|
||||
// Animate it
|
||||
|
@ -383,11 +400,11 @@ openerp.account = function (instance) {
|
|||
});
|
||||
});
|
||||
|
||||
if (self.$(".button_close_statement").length !== 0) {
|
||||
if (is_single_statement && self.$(".button_close_statement").length !== 0) {
|
||||
self.$(".button_close_statement").hide();
|
||||
self.model_bank_statement
|
||||
.query(["balance_end_real", "balance_end"])
|
||||
.filter([['id', '=', self.statement_id]])
|
||||
.filter([['id', '=', self.statement_ids[0]]])
|
||||
.first()
|
||||
.then(function(data){
|
||||
if (data.balance_end_real === data.balance_end) {
|
||||
|
@ -395,7 +412,7 @@ openerp.account = function (instance) {
|
|||
self.$(".button_close_statement").click(function() {
|
||||
self.$(".button_close_statement").attr("disabled", "disabled");
|
||||
self.model_bank_statement
|
||||
.call("button_confirm_bank", [[self.statement_id]])
|
||||
.call("button_confirm_bank", [[self.statement_ids[0]]])
|
||||
.then(function () {
|
||||
self.do_action({
|
||||
type: 'ir.actions.client',
|
||||
|
@ -456,9 +473,12 @@ openerp.account = function (instance) {
|
|||
init: function(parent, context) {
|
||||
this._super(parent);
|
||||
|
||||
this.formatCurrency = this.getParent().formatCurrency;
|
||||
if (context.initial_data_provided) {
|
||||
// Process data
|
||||
_(context.reconciliation_proposition).each(this.decorateMoveLine.bind(this));
|
||||
_.each(context.reconciliation_proposition, function(line) {
|
||||
this.decorateMoveLine(line, context.st_line.currency_id);
|
||||
}, this);
|
||||
this.set("mv_lines_selected", context.reconciliation_proposition);
|
||||
this.st_line = context.st_line;
|
||||
this.partner_id = context.st_line.partner_id;
|
||||
|
@ -466,7 +486,9 @@ openerp.account = function (instance) {
|
|||
|
||||
// Exclude selected move lines
|
||||
var selected_line_ids = _(context.reconciliation_proposition).map(function(o){ return o.id });
|
||||
this.getParent().excludeMoveLines(selected_line_ids);
|
||||
if (this.getParent().excluded_move_lines_ids[this.partner_id] === undefined)
|
||||
this.getParent().excluded_move_lines_ids[this.partner_id] = [];
|
||||
this.getParent().excludeMoveLines(this, this.partner_id, selected_line_ids);
|
||||
} else {
|
||||
this.set("mv_lines_selected", []);
|
||||
this.st_line = undefined;
|
||||
|
@ -483,29 +505,31 @@ openerp.account = function (instance) {
|
|||
this.model_tax = new instance.web.Model("account.tax");
|
||||
this.map_account_id_code = this.getParent().map_account_id_code;
|
||||
this.map_tax_id_amount = this.getParent().map_tax_id_amount;
|
||||
this.formatCurrency = this.getParent().formatCurrency;
|
||||
this.presets = this.getParent().presets;
|
||||
this.is_valid = true;
|
||||
this.is_consistent = true; // Used to prevent bad server requests
|
||||
this.total_move_lines_num = undefined; // Used for pagers
|
||||
this.filter = "";
|
||||
// In rare cases like when deleting a statement line's partner we don't want the server to
|
||||
// look for a reconciliation proposition (in this particular case it might find a move line
|
||||
// matching the statement line and decide to set the statement line's partner accordingly)
|
||||
this.do_load_reconciliation_proposition = true;
|
||||
|
||||
this.set("balance", undefined); // Debit is +, credit is -
|
||||
this.on("change:balance", this, this.balanceChanged);
|
||||
this.set("mode", undefined);
|
||||
this.on("change:mode", this, this.modeChanged);
|
||||
this.set("balance", undefined); // Debit is +, credit is -
|
||||
this.on("change:balance", this, this.balanceChanged);
|
||||
this.set("pager_index", 0);
|
||||
this.on("change:pager_index", this, this.pagerChanged);
|
||||
// NB : mv_lines represent the counterpart that will be created to reconcile existing move lines, so debit and credit are inverted
|
||||
this.set("mv_lines", []);
|
||||
this.on("change:mv_lines", this, this.mvLinesChanged);
|
||||
this.mv_lines_deselected = []; // deselected lines are displayed on top of the match table
|
||||
this.on("change:mv_lines_selected", this, this.mvLinesSelectedChanged);
|
||||
this.set("lines_created", []);
|
||||
this.set("line_created_being_edited", [{'id': 0}]);
|
||||
this.on("change:lines_created", this, this.createdLinesChanged);
|
||||
this.on("change:line_created_being_edited", this, this.createdLinesChanged);
|
||||
|
||||
//all lines associated to current reconciliation
|
||||
this.propositions_lines = undefined;
|
||||
},
|
||||
|
||||
start: function() {
|
||||
|
@ -516,49 +540,70 @@ openerp.account = function (instance) {
|
|||
self.aestetic_animation_speed = 0;
|
||||
|
||||
self.is_consistent = false;
|
||||
if (self.context.animate_entrance) self.$el.css("opacity", "0");
|
||||
|
||||
// Fetch data
|
||||
var deferred_fetch_data = new $.Deferred();
|
||||
if (! self.context.initial_data_provided) {
|
||||
// Load statement line
|
||||
self.model_bank_statement_line
|
||||
.call("get_statement_line_for_reconciliation", [self.st_line_id])
|
||||
.then(function (data) {
|
||||
self.st_line = data;
|
||||
self.decorateStatementLine(self.st_line);
|
||||
self.partner_id = data.partner_id;
|
||||
$.when(self.loadReconciliationProposition()).then(function(){
|
||||
deferred_fetch_data.resolve();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
deferred_fetch_data.resolve();
|
||||
if (self.context.animate_entrance) {
|
||||
self.$el.fadeOut(0);
|
||||
self.$el.slideUp(0);
|
||||
}
|
||||
return $.when(self.loadData()).then(function(){
|
||||
return $.when(self.render()).then(function(){
|
||||
self.is_consistent = true;
|
||||
// Make an entrance
|
||||
self.animation_speed = self.getParent().animation_speed;
|
||||
self.aestetic_animation_speed = self.getParent().aestetic_animation_speed;
|
||||
if (self.context.animate_entrance) {
|
||||
return self.$el.stop(true, true).fadeIn({ duration: self.aestetic_animation_speed, queue: false }).css('display', 'none').slideDown(self.aestetic_animation_speed);
|
||||
}
|
||||
|
||||
// Display the widget
|
||||
return $.when(deferred_fetch_data).then(function(){
|
||||
//load all lines that can be usefull for counterparts
|
||||
var deferred_total_move_lines_num = self.model_bank_statement_line
|
||||
.call("get_move_lines_counterparts_id", [self.st_line.id, []])
|
||||
.then(function(lines){
|
||||
_(lines).each(self.decorateMoveLine.bind(self));
|
||||
self.propositions_lines = lines;
|
||||
});
|
||||
return deferred_total_move_lines_num;
|
||||
}).then(function(){
|
||||
// Render template
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
loadData: function() {
|
||||
var self = this;
|
||||
if (self.context.initial_data_provided)
|
||||
return;
|
||||
|
||||
// Get ids of selected move lines (to exclude them from reconciliation proposition)
|
||||
var excluded_move_lines_ids = [];
|
||||
if (self.do_load_reconciliation_proposition) {
|
||||
_.each(self.getParent().excluded_move_lines_ids, function(o){
|
||||
excluded_move_lines_ids = excluded_move_lines_ids.concat(o);
|
||||
});
|
||||
}
|
||||
// Load statement line
|
||||
return self.model_bank_statement_line
|
||||
.call("get_data_for_reconciliations", [[self.st_line_id], excluded_move_lines_ids, self.do_load_reconciliation_proposition])
|
||||
.then(function (data) {
|
||||
self.st_line = data[0].st_line;
|
||||
self.decorateStatementLine(self.st_line);
|
||||
self.partner_id = data[0].st_line.partner_id;
|
||||
if (self.getParent().excluded_move_lines_ids[self.partner_id] === undefined)
|
||||
self.getParent().excluded_move_lines_ids[self.partner_id] = [];
|
||||
var mv_lines = [];
|
||||
_.each(data[0].reconciliation_proposition, function(line) {
|
||||
self.decorateMoveLine(line, self.st_line.currency_id);
|
||||
mv_lines.push(line);
|
||||
}, self);
|
||||
self.set("mv_lines_selected", self.get("mv_lines_selected").concat(mv_lines));
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
var presets_array = [];
|
||||
for (var id in self.presets)
|
||||
if (self.presets.hasOwnProperty(id))
|
||||
presets_array.push(self.presets[id]);
|
||||
self.$el.prepend(QWeb.render("bank_statement_reconciliation_line", {line: self.st_line, mode: self.context.mode, presets: presets_array}));
|
||||
self.$el.prepend(QWeb.render("bank_statement_reconciliation_line", {
|
||||
line: self.st_line,
|
||||
mode: self.context.mode,
|
||||
presets: presets_array
|
||||
}));
|
||||
|
||||
// Stuff that require the template to be rendered
|
||||
self.$(".match").slideUp(0);
|
||||
self.$(".create").slideUp(0);
|
||||
if (self.st_line.no_match) self.$el.addClass("no_match");
|
||||
if (self.context.mode !== "match") self.render();
|
||||
self.bindPopoverTo(self.$(".line_info_button"));
|
||||
self.createFormWidgets();
|
||||
// Special case hack : no identified partner
|
||||
|
@ -566,32 +611,23 @@ openerp.account = function (instance) {
|
|||
self.$el.css("opacity", "0");
|
||||
self.updateBalance();
|
||||
self.$(".change_partner_container").show(0);
|
||||
self.change_partner_field.$el.find("input").attr("placeholder", _t("Select Partner"));
|
||||
self.$(".match").slideUp(0);
|
||||
self.$el.addClass("no_partner");
|
||||
self.set("mode", self.context.mode);
|
||||
self.balanceChanged();
|
||||
self.updateAccountingViewMatchedLines();
|
||||
self.animation_speed = self.getParent().animation_speed;
|
||||
self.aestetic_animation_speed = self.getParent().aestetic_animation_speed;
|
||||
self.$el.animate({opacity: 1}, self.aestetic_animation_speed);
|
||||
self.is_consistent = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO : the .on handler's returned deferred is lost
|
||||
return $.when(self.set("mode", self.context.mode)).then(function(){
|
||||
self.is_consistent = true;
|
||||
|
||||
// Make sure the display is OK
|
||||
self.balanceChanged();
|
||||
self.createdLinesChanged();
|
||||
self.updateAccountingViewMatchedLines();
|
||||
|
||||
// Make an entrance
|
||||
self.animation_speed = self.getParent().animation_speed;
|
||||
self.aestetic_animation_speed = self.getParent().aestetic_animation_speed;
|
||||
if (self.context.animate_entrance) return self.$el.animate({opacity: 1}, self.aestetic_animation_speed);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -603,7 +639,7 @@ openerp.account = function (instance) {
|
|||
_.each(self.getChildren(), function(o){ o.destroy() });
|
||||
self.is_consistent = false;
|
||||
return $.when(self.$el.animate({opacity: 0}, self.animation_speed)).then(function() {
|
||||
self.getParent().unexcludeMoveLines(_.map(self.get("mv_lines_selected"), function(o){ return o.id }));
|
||||
self.getParent().unexcludeMoveLines(self, self.partner_id, _.map(self.get("mv_lines_selected"), function(o){ return o.id }));
|
||||
$.each(self.$(".bootstrap_popover"), function(){ $(this).popover('destroy') });
|
||||
self.$el.empty();
|
||||
self.$el.removeClass("no_partner");
|
||||
|
@ -617,6 +653,7 @@ openerp.account = function (instance) {
|
|||
self.set("pager_index", 0, {silent: true});
|
||||
self.set("mv_lines", [], {silent: true});
|
||||
self.set("mv_lines_selected", [], {silent: true});
|
||||
self.mv_lines_deselected = [];
|
||||
self.set("lines_created", [], {silent: true});
|
||||
self.set("line_created_being_edited", [{'id': 0}], {silent: true});
|
||||
// Rebirth
|
||||
|
@ -749,6 +786,7 @@ openerp.account = function (instance) {
|
|||
self.change_partner_field.on("change:value", self.change_partner_field, function() {
|
||||
self.changePartner(this.get_value());
|
||||
});
|
||||
self.change_partner_field.$el.find("input").attr("placeholder", _t("Select Partner"));
|
||||
|
||||
field_manager.do_show();
|
||||
},
|
||||
|
@ -761,20 +799,19 @@ openerp.account = function (instance) {
|
|||
},
|
||||
|
||||
// adds fields, prefixed with q_, to the move line for qweb rendering
|
||||
decorateMoveLine: function(line){
|
||||
decorateMoveLine: function(line, currency_id) {
|
||||
line.partial_reconcile = false;
|
||||
line.propose_partial_reconcile = false;
|
||||
line['credit'] = [line['debit'], line['debit'] = line['credit']][0];
|
||||
line.q_due_date = (line.date_maturity === false ? line.date : line.date_maturity);
|
||||
line.q_amount = (line.debit !== 0 ? "- "+line.q_debit : "") + (line.credit !== 0 ? line.q_credit : "");
|
||||
line.q_popover = QWeb.render("bank_statement_reconciliation_move_line_details", {line: line});
|
||||
line.q_label = line.name;
|
||||
|
||||
if (line.has_no_partner){
|
||||
line.q_label = line.partner_name + ': ' +line.q_label;
|
||||
}
|
||||
// WARNING : pretty much of a ugly hack
|
||||
// The value of account_move.ref is either the move's communication or it's name without the slashes
|
||||
if (line.ref && line.ref !== line.name.replace(/\//g,''))
|
||||
line.debit_str = this.formatCurrency(line.debit, currency_id);
|
||||
line.credit_str = this.formatCurrency(line.credit, currency_id);
|
||||
line.q_popover = QWeb.render("bank_statement_reconciliation_move_line_details", {line: line});
|
||||
if (line.has_no_partner)
|
||||
line.q_label = line.partner_name + ': ' + line.q_label;
|
||||
if (line.ref && line.ref !== line.name)
|
||||
line.q_label += " : " + line.ref;
|
||||
},
|
||||
|
||||
|
@ -819,31 +856,77 @@ openerp.account = function (instance) {
|
|||
selectMoveLine: function(mv_line) {
|
||||
var self = this;
|
||||
var line_id = mv_line.dataset.lineid;
|
||||
var line = _.find(self.propositions_lines, function(o){ return o.id == line_id});
|
||||
$(mv_line).attr('data-selected','true');
|
||||
self.getParent().excludeMoveLines([line_id]);
|
||||
|
||||
// find the line in mv_lines or mv_lines_deselected
|
||||
var line = _.find(self.get("mv_lines"), function(o){ return o.id == line_id});
|
||||
if (! line) {
|
||||
line = _.find(self.mv_lines_deselected, function(o){ return o.id == line_id });
|
||||
self.mv_lines_deselected = _.filter(self.mv_lines_deselected, function(o) { return o.id != line_id });
|
||||
}
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
// If statement line has no partner, give it the partner of the selected move line
|
||||
if (!this.st_line.partner_id && line.partner_id) {
|
||||
self.changePartner(line.partner_id, function() {
|
||||
self.selectMoveLine(mv_line);
|
||||
});
|
||||
} else {
|
||||
self.set("mv_lines_selected", self.get("mv_lines_selected").concat(line));
|
||||
// $(mv_line).attr('data-selected','true');
|
||||
// self.set("mv_lines_selected", self.get("mv_lines_selected").concat(line));
|
||||
// this.set("mv_lines", _.reject(this.get("mv_lines"), function(o){return o.id == line_id}));
|
||||
// this.getParent().excludeMoveLines([line_id]);
|
||||
}
|
||||
},
|
||||
|
||||
deselectMoveLine: function(mv_line) {
|
||||
var self = this;
|
||||
var line_id = mv_line.dataset.lineid;
|
||||
var line = _.find(self.propositions_lines, function(o){ return o.id == line_id});
|
||||
$(mv_line).attr('data-selected','false');
|
||||
self.getParent().unexcludeMoveLines([line_id]);
|
||||
self.set("mv_lines_selected",_.filter(self.get("mv_lines_selected"), function(o) { return o.id != line_id }));
|
||||
var line = _.find(self.get("mv_lines_selected"), function(o){ return o.id == line_id});
|
||||
if (! line) return; // If no line found, we've got a syncing problem (let's turn a deaf ear)
|
||||
|
||||
// add the line to mv_lines_deselected and remove it from mv_lines_selected
|
||||
self.mv_lines_deselected.unshift(line);
|
||||
var mv_lines_selected = _.filter(self.get("mv_lines_selected"), function(o) { return o.id != line_id });
|
||||
|
||||
// remove partial reconciliation stuff if necessary
|
||||
if (line.partial_reconcile === true) self.unpartialReconcileLine(line);
|
||||
if (line.propose_partial_reconcile === true) line.propose_partial_reconcile = false;
|
||||
|
||||
self.$el.removeClass("no_match");
|
||||
self.set("mode", "match");
|
||||
self.set("mv_lines_selected", mv_lines_selected);
|
||||
|
||||
|
||||
// $(mv_line).attr('data-selected','false');
|
||||
// this.set("mv_lines", this.get("mv_lines").concat(line));
|
||||
// this.getParent().unexcludeMoveLines([line_id]);
|
||||
},
|
||||
|
||||
/** Matches pagination */
|
||||
|
||||
pagerControlLeftHandler: function() {
|
||||
var self = this;
|
||||
if (self.$(".pager_control_left").hasClass("disabled")) { return; /* shouldn't happen, anyway*/ }
|
||||
if (self.total_move_lines_num < 0) { return; }
|
||||
self.set("pager_index", self.get("pager_index")-1 );
|
||||
},
|
||||
|
||||
pagerControlRightHandler: function() {
|
||||
var self = this;
|
||||
var new_index = self.get("pager_index")+1;
|
||||
if (self.$(".pager_control_right").hasClass("disabled")) { return; /* shouldn't happen, anyway*/ }
|
||||
if ((new_index * self.max_move_lines_displayed) >= self.total_move_lines_num) { return; }
|
||||
self.set("pager_index", new_index );
|
||||
},
|
||||
|
||||
|
@ -851,9 +934,11 @@ openerp.account = function (instance) {
|
|||
var self = this;
|
||||
self.set("pager_index", 0);
|
||||
self.filter = self.$(".filter").val();
|
||||
self.render();
|
||||
window.clearTimeout(self.apply_filter_timeout);
|
||||
self.apply_filter_timeout = window.setTimeout(self.proxy('updateMatches'), 200);
|
||||
},
|
||||
|
||||
|
||||
/** Creating */
|
||||
|
||||
initializeCreateForm: function() {
|
||||
|
@ -862,6 +947,7 @@ openerp.account = function (instance) {
|
|||
_.each(self.create_form, function(field) {
|
||||
field.set("value", false);
|
||||
});
|
||||
self.label_field.set("value", self.st_line.name);
|
||||
self.amount_field.set("value", -1*self.get("balance"));
|
||||
self.account_id_field.focus();
|
||||
},
|
||||
|
@ -894,21 +980,26 @@ openerp.account = function (instance) {
|
|||
var self = this;
|
||||
self.initializeCreateForm();
|
||||
var preset = self.presets[e.currentTarget.dataset.presetid];
|
||||
// Hack : set_value of a field calls a handler that returns a deferred because it could make a RPC call
|
||||
// to compute the tax before it updates the line being edited. Unfortunately this deferred is lost.
|
||||
// Hence this ugly hack to avoid concurrency problem that arose when setting amount (in initializeCreateForm), then tax, then another amount
|
||||
if (preset.tax && self.tax_field) self.tax_field.set_value(false);
|
||||
if (preset.amount && self.amount_field) self.amount_field.set_value(false);
|
||||
|
||||
for (var key in preset) {
|
||||
if (! preset.hasOwnProperty(key) || key === "amount") continue;
|
||||
if (self.hasOwnProperty(key+"_field"))
|
||||
if (preset[key] && self.hasOwnProperty(key+"_field"))
|
||||
self[key+"_field"].set_value(preset[key]);
|
||||
}
|
||||
var sign = self.amount_field.get_value() < 0 ? -1 : 1;
|
||||
if (preset.amount && self.amount_field) {
|
||||
if (preset.amount_type === "fixed")
|
||||
self.amount_field.set_value(sign * preset.amount);
|
||||
self.amount_field.set_value(preset.amount);
|
||||
else if (preset.amount_type === "percentage_of_total")
|
||||
self.amount_field.set_value(sign * self.st_line.amount * preset.amount / 100);
|
||||
self.amount_field.set_value(self.st_line.amount * preset.amount / 100);
|
||||
else if (preset.amount_type === "percentage_of_balance") {
|
||||
self.amount_field.set_value(0);
|
||||
self.updateBalance();
|
||||
self.amount_field.set_value(sign * Math.abs(self.get("balance")) * preset.amount / 100);
|
||||
self.amount_field.set_value(-1 * self.get("balance") * preset.amount / 100);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -937,9 +1028,11 @@ openerp.account = function (instance) {
|
|||
|
||||
partnerNameClickHandler: function() {
|
||||
var self = this;
|
||||
// Delete statement line's partner
|
||||
return self.changePartner('', function() {
|
||||
self.$(".partner_name").hide();
|
||||
self.change_partner_field.$el.find("input").attr("placeholder", self.st_line.partner_name);
|
||||
self.$(".change_partner_container").show();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
|
@ -985,34 +1078,37 @@ openerp.account = function (instance) {
|
|||
table.empty();
|
||||
var slice_start = self.get("pager_index") * self.max_move_lines_displayed;
|
||||
var slice_end = (self.get("pager_index")+1) * self.max_move_lines_displayed;
|
||||
|
||||
var visible = 0
|
||||
_(self.get("mv_lines")).each(function(line){
|
||||
if (visible >= slice_start && visible < slice_end) {
|
||||
_( _.filter(self.mv_lines_deselected, function(o){
|
||||
return o.name.indexOf(self.filter) !== -1 || o.ref.indexOf(self.filter) !== -1 })
|
||||
.slice(slice_start, slice_end)).each(function(line){
|
||||
var $line = $(QWeb.render("bank_statement_reconciliation_move_line", {line: line, selected: false}));
|
||||
self.bindPopoverTo($line.find(".line_info_button"));
|
||||
table.append($line);
|
||||
nothing_displayed = false;
|
||||
}
|
||||
visible = visible + 1;
|
||||
});
|
||||
if (nothing_displayed)
|
||||
_(self.get("mv_lines")).each(function(line){
|
||||
var $line = $(QWeb.render("bank_statement_reconciliation_move_line", {line: line, selected: false}));
|
||||
self.bindPopoverTo($line.find(".line_info_button"));
|
||||
table.append($line);
|
||||
nothing_displayed = false;
|
||||
});
|
||||
if (nothing_displayed && this.filter !== "")
|
||||
table.append(QWeb.render("filter_no_match", {filter_str: self.filter}));
|
||||
},
|
||||
|
||||
updatePagerControls: function() {
|
||||
var self = this;
|
||||
|
||||
if (self.get("pager_index") === 0)
|
||||
self.$(".pager_control_left").addClass("disabled");
|
||||
else
|
||||
self.$(".pager_control_left").removeClass("disabled");
|
||||
if (self.get('mv_lines').length <= ((self.get("pager_index")+1) * self.max_move_lines_displayed))
|
||||
if (self.total_move_lines_num <= ((self.get("pager_index")+1) * self.max_move_lines_displayed))
|
||||
self.$(".pager_control_right").addClass("disabled");
|
||||
else
|
||||
self.$(".pager_control_right").removeClass("disabled");
|
||||
},
|
||||
|
||||
|
||||
/** Properties changed */
|
||||
|
||||
// Updates the validation button and the "open balance" line
|
||||
|
@ -1034,8 +1130,12 @@ openerp.account = function (instance) {
|
|||
self.is_valid = false;
|
||||
var debit = (balance > 0 ? self.formatCurrency(balance, self.st_line.currency_id) : "");
|
||||
var credit = (balance < 0 ? self.formatCurrency(-1*balance, self.st_line.currency_id) : "");
|
||||
var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {debit: debit, credit: credit, account_code: self.map_account_id_code[self.st_line.open_balance_account_id]}));
|
||||
$line.find('.js_open_balance')[0].innerHTML = "Choose counterpart";
|
||||
var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {
|
||||
debit: debit,
|
||||
credit: credit,
|
||||
account_code: self.map_account_id_code[self.st_line.open_balance_account_id]
|
||||
}));
|
||||
$line.find('.js_open_balance')[0].innerHTML = _t("Choose counterpart");
|
||||
self.$(".tbody_open_balance").append($line);
|
||||
}
|
||||
return;
|
||||
|
@ -1049,7 +1149,11 @@ openerp.account = function (instance) {
|
|||
self.$(".button_ok").text("Keep open");
|
||||
var debit = (balance > 0 ? self.formatCurrency(balance, self.st_line.currency_id) : "");
|
||||
var credit = (balance < 0 ? self.formatCurrency(-1*balance, self.st_line.currency_id) : "");
|
||||
var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {debit: debit, credit: credit, account_code: self.map_account_id_code[self.st_line.open_balance_account_id]}));
|
||||
var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {
|
||||
debit: debit,
|
||||
credit: credit,
|
||||
account_code: self.map_account_id_code[self.st_line.open_balance_account_id]
|
||||
}));
|
||||
self.$(".tbody_open_balance").append($line);
|
||||
}
|
||||
},
|
||||
|
@ -1059,15 +1163,21 @@ openerp.account = function (instance) {
|
|||
|
||||
self.$(".action_pane.active").removeClass("active");
|
||||
|
||||
// Special case hack : if no_partner and mode == inactive
|
||||
// Special case hack : if no_partner, either inactive or create
|
||||
if (self.st_line.has_no_partner) {
|
||||
if (self.get("mode") === "inactive") {
|
||||
self.$(".match").slideUp(self.animation_speed);
|
||||
self.$(".create").slideUp(self.animation_speed);
|
||||
self.$(".toggle_match").removeClass("visible_toggle");
|
||||
self.el.dataset.mode = "inactive";
|
||||
return;
|
||||
} else {
|
||||
self.initializeCreateForm();
|
||||
self.$(".match").slideUp(self.animation_speed);
|
||||
self.$(".create").slideDown(self.animation_speed);
|
||||
self.$(".toggle_match").addClass("visible_toggle");
|
||||
self.el.dataset.mode = "create";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.get("mode") === "inactive") {
|
||||
|
@ -1076,7 +1186,7 @@ openerp.account = function (instance) {
|
|||
self.el.dataset.mode = "inactive";
|
||||
|
||||
} else if (self.get("mode") === "match") {
|
||||
return $.when(self.render()).then(function() {
|
||||
return $.when(self.updateMatches()).then(function() {
|
||||
if (self.$el.hasClass("no_match")) {
|
||||
self.set("mode", "inactive");
|
||||
return;
|
||||
|
@ -1095,15 +1205,18 @@ openerp.account = function (instance) {
|
|||
},
|
||||
|
||||
pagerChanged: function() {
|
||||
var self = this;
|
||||
self.render();
|
||||
this.updateMatches();
|
||||
},
|
||||
|
||||
mvLinesChanged: function() {
|
||||
var self = this;
|
||||
// If pager_index is out of range, set it to display the last page
|
||||
if (self.get("pager_index") !== 0 && self.total_move_lines_num <= (self.get("pager_index") * self.max_move_lines_displayed)) {
|
||||
self.set("pager_index", Math.ceil(self.total_move_lines_num/self.max_move_lines_displayed)-1);
|
||||
}
|
||||
|
||||
// If there is no match to display, disable match view and pass in mode inactive
|
||||
if (self.get("mv_lines").length === 0 && self.filter === "") {
|
||||
if (self.total_move_lines_num + self.mv_lines_deselected.length === 0 && self.filter === "") {
|
||||
self.$el.addClass("no_match");
|
||||
if (self.get("mode") === "match") {
|
||||
self.set("mode", "inactive");
|
||||
|
@ -1122,11 +1235,13 @@ openerp.account = function (instance) {
|
|||
var added_lines_ids = _.map(_.difference(val.newValue, val.oldValue), function(o){ return o.id });
|
||||
var removed_lines_ids = _.map(_.difference(val.oldValue, val.newValue), function(o){ return o.id });
|
||||
|
||||
self.getParent().excludeMoveLines(added_lines_ids);
|
||||
self.getParent().unexcludeMoveLines(removed_lines_ids);
|
||||
self.getParent().excludeMoveLines(self, self.partner_id, added_lines_ids);
|
||||
self.getParent().unexcludeMoveLines(self, self.partner_id, removed_lines_ids);
|
||||
|
||||
$.when(self.updateMatches()).then(function(){
|
||||
self.updateAccountingViewMatchedLines();
|
||||
self.updateBalance();
|
||||
});
|
||||
},
|
||||
|
||||
// Generic function for updating the line_created_being_edited
|
||||
|
@ -1134,7 +1249,6 @@ openerp.account = function (instance) {
|
|||
var self = this;
|
||||
var line_created_being_edited = self.get("line_created_being_edited");
|
||||
line_created_being_edited[0][elt.corresponding_property] = val.newValue;
|
||||
|
||||
line_created_being_edited[0].currency_id = self.st_line.currency_id;
|
||||
|
||||
// Specific cases
|
||||
|
@ -1155,17 +1269,26 @@ openerp.account = function (instance) {
|
|||
var current_line_cursor = 1;
|
||||
$.each(data.taxes, function(index, tax){
|
||||
if (tax.amount !== 0.0) {
|
||||
var tax_account_id = (amount > 0 ? tax.account_collected_id : tax.account_paid_id)
|
||||
tax_account_id = tax_account_id !== false ? tax_account_id: line_created_being_edited[0].account_id
|
||||
line_created_being_edited[current_line_cursor] = {id: line_created_being_edited[0].id, account_id: tax_account_id, account_num: self.map_account_id_code[tax_account_id], label: tax.name, amount: tax.amount, no_remove_action: true, currency_id: self.st_line.currency_id, is_tax_line: true};
|
||||
current_line_cursor = current_line_cursor + 1;
|
||||
var tax_account_id = (amount > 0 ? tax.account_collected_id : tax.account_paid_id);
|
||||
tax_account_id = tax_account_id !== false ? tax_account_id: line_created_being_edited[0].account_id;
|
||||
line_created_being_edited[current_line_cursor] = {
|
||||
id: line_created_being_edited[0].id,
|
||||
account_id: tax_account_id,
|
||||
account_num: self.map_account_id_code[tax_account_id],
|
||||
label: tax.name,
|
||||
amount: tax.amount,
|
||||
no_remove_action: true,
|
||||
currency_id: self.st_line.currency_id,
|
||||
is_tax_line: true
|
||||
};
|
||||
current_line_cursor = current_line_cursor + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
line_created_being_edited[0].amount = amount;
|
||||
delete line_created_being_edited[1];
|
||||
line_created_being_edited.length = 1;
|
||||
deferred_tax.resolve();
|
||||
}
|
||||
} else { deferred_tax.resolve(); }
|
||||
|
@ -1211,7 +1334,7 @@ openerp.account = function (instance) {
|
|||
var balance = self.get("balance");
|
||||
line.initial_amount = line.debit !== 0 ? line.debit : -1 * line.credit;
|
||||
if (balance < 0) {
|
||||
line.debit -= balance;
|
||||
line.debit += balance;
|
||||
line.debit_str = self.formatCurrency(line.debit, self.st_line.currency_id);
|
||||
} else {
|
||||
line.credit -= balance;
|
||||
|
@ -1250,6 +1373,19 @@ openerp.account = function (instance) {
|
|||
updateBalance: function() {
|
||||
var self = this;
|
||||
var mv_lines_selected = self.get("mv_lines_selected");
|
||||
var lines_selected_num = mv_lines_selected.length;
|
||||
var lines_created_num = self.getCreatedLines().length;
|
||||
|
||||
// Undo partial reconciliation if necessary
|
||||
if (lines_selected_num !== 1 || lines_created_num !== 0) {
|
||||
_.each(mv_lines_selected, function(line) {
|
||||
if (line.partial_reconcile === true) self.unpartialReconcileLine(line);
|
||||
if (line.propose_partial_reconcile === true) line.propose_partial_reconcile = false;
|
||||
});
|
||||
self.updateAccountingViewMatchedLines();
|
||||
}
|
||||
|
||||
// Compute balance
|
||||
var balance = 0;
|
||||
balance -= self.st_line.amount;
|
||||
_.each(mv_lines_selected, function(o) {
|
||||
|
@ -1258,57 +1394,71 @@ openerp.account = function (instance) {
|
|||
_.each(self.getCreatedLines(), function(o) {
|
||||
balance += o.amount;
|
||||
});
|
||||
// Should work as long as currency's rounding factor is > 0.001 (ie: don't use gold kilos as a currency)
|
||||
balance = Math.round(balance*1000)/1000;
|
||||
self.set("balance", balance);
|
||||
|
||||
// Propose partial reconciliation if necessary
|
||||
var lines_selected_num = mv_lines_selected.length;
|
||||
var lines_created_num = self.getCreatedLines().length;
|
||||
if (lines_selected_num === 1 && lines_created_num === 0 && self.st_line.amount * balance > 0) {
|
||||
mv_lines_selected[0].propose_partial_reconcile = true;
|
||||
self.updateAccountingViewMatchedLines();
|
||||
}
|
||||
if (lines_selected_num !== 1 || lines_created_num !== 0) {
|
||||
// remove partial reconciliation stuff if necessary
|
||||
_.each(mv_lines_selected, function(line) {
|
||||
if (line.partial_reconcile === true) self.unpartialReconcileLine(line);
|
||||
if (line.propose_partial_reconcile === true) line.propose_partial_reconcile = false;
|
||||
});
|
||||
self.updateAccountingViewMatchedLines();
|
||||
}
|
||||
},
|
||||
|
||||
loadReconciliationProposition: function() {
|
||||
// Loads move lines according to the widget's state
|
||||
updateMatches: function() {
|
||||
if (this.st_line.has_no_partner) return;
|
||||
var self = this;
|
||||
return self.model_bank_statement_line
|
||||
.call("get_reconciliation_proposition", [self.st_line.id, self.getParent().excluded_move_lines_ids])
|
||||
var deselected_lines_num = self.mv_lines_deselected.length;
|
||||
var move_lines_num = 0;
|
||||
var offset = self.get("pager_index") * self.max_move_lines_displayed - deselected_lines_num;
|
||||
if (offset < 0) offset = 0;
|
||||
var limit = (self.get("pager_index")+1) * self.max_move_lines_displayed - deselected_lines_num;
|
||||
if (limit > self.max_move_lines_displayed) limit = self.max_move_lines_displayed;
|
||||
var excluded_ids = _.collect(self.get("mv_lines_selected").concat(self.mv_lines_deselected), function(o){ return o.id });
|
||||
excluded_ids = excluded_ids.concat(self.getParent().excluded_move_lines_ids[self.partner_id]);
|
||||
|
||||
var deferred_move_lines;
|
||||
var move_lines = [];
|
||||
if (limit > 0) {
|
||||
// Load move lines
|
||||
deferred_move_lines = self.model_bank_statement_line
|
||||
.call("get_move_lines_for_reconciliation_by_statement_line_id", [self.st_line.id, excluded_ids, self.filter, offset, limit])
|
||||
.then(function (lines) {
|
||||
_(lines).each(self.decorateMoveLine.bind(self));
|
||||
self.set("mv_lines_selected", self.get("mv_lines_selected").concat(lines));
|
||||
_.each(lines, function(line) {
|
||||
self.decorateMoveLine(line, self.st_line.currency_id);
|
||||
move_lines.push(line);
|
||||
}, self);
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
var lines_to_show = [];
|
||||
_.each(self.propositions_lines, function(line){
|
||||
var filter = (line.q_label.toLowerCase().indexOf(self.filter.toLowerCase()) > -1 || line.account_code.toLowerCase().indexOf(self.filter.toLowerCase()) > -1);
|
||||
if (self.getParent().excluded_move_lines_ids.indexOf(line.id) === -1 && filter) {
|
||||
lines_to_show.push(line);
|
||||
}
|
||||
|
||||
// Fetch the number of move lines corresponding to this statement line and this filter
|
||||
var deferred_total_move_lines_num = self.model_bank_statement_line
|
||||
.call("get_move_lines_for_reconciliation_by_statement_line_id", [self.st_line.id, excluded_ids, self.filter, 0, undefined, true])
|
||||
.then(function(num){
|
||||
move_lines_num = num;
|
||||
});
|
||||
|
||||
return $.when(deferred_move_lines, deferred_total_move_lines_num).then(function(){
|
||||
self.total_move_lines_num = move_lines_num + deselected_lines_num;
|
||||
self.set("mv_lines", move_lines);
|
||||
});
|
||||
self.set("mv_lines", lines_to_show);
|
||||
},
|
||||
|
||||
// Changes the partner_id of the statement_line in the DB and reloads the widget
|
||||
changePartner: function(partner_id) {
|
||||
changePartner: function(partner_id, callback) {
|
||||
var self = this;
|
||||
self.is_consistent = false;
|
||||
return self.model_bank_statement_line
|
||||
// Update model
|
||||
.call("write", [[self.st_line_id], {'partner_id': partner_id}])
|
||||
.then(function () {
|
||||
self.do_load_reconciliation_proposition = false; // of the server might set the statement line's partner
|
||||
return $.when(self.restart(self.get("mode"))).then(function(){
|
||||
self.do_load_reconciliation_proposition = true;
|
||||
self.is_consistent = true;
|
||||
self.set("mode", "match");
|
||||
if (callback) callback();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -1353,15 +1503,12 @@ openerp.account = function (instance) {
|
|||
},
|
||||
|
||||
// Persist data, notify parent view and terminate widget
|
||||
persistAndDestroy: function() {
|
||||
persistAndDestroy: function(speed) {
|
||||
var self = this;
|
||||
speed = (isNaN(speed) ? self.animation_speed : speed);
|
||||
if (! self.is_consistent) return;
|
||||
|
||||
// Prepare data
|
||||
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)) });
|
||||
if (Math.abs(self.get("balance")).toFixed(3) !== "0.000") mv_line_dicts.push(self.prepareOpenBalanceForPersisting());
|
||||
self.getParent().unexcludeMoveLines(self, self.partner_id, _.map(self.get("mv_lines_selected"), function(o){ return o.id }));
|
||||
|
||||
// Sliding animation
|
||||
var height = self.$el.outerHeight();
|
||||
|
@ -1370,11 +1517,10 @@ openerp.account = function (instance) {
|
|||
.css("marginTop", self.$el.css("marginTop"))
|
||||
.css("marginBottom", self.$el.css("marginBottom"));
|
||||
self.$el.wrap(container);
|
||||
var deferred_animation = self.$el.parent().slideUp(self.animation_speed*height/150);
|
||||
var deferred_animation = self.$el.parent().slideUp(speed*height/150);
|
||||
|
||||
// RPC
|
||||
return self.model_bank_statement_line
|
||||
.call("process_reconciliation", [self.st_line_id, mv_line_dicts])
|
||||
return $.when(self.makeRPCForPersisting())
|
||||
.then(function () {
|
||||
$.each(self.$(".bootstrap_popover"), function(){ $(this).popover('destroy') });
|
||||
return $.when(deferred_animation).then(function(){
|
||||
|
@ -1385,11 +1531,20 @@ openerp.account = function (instance) {
|
|||
});
|
||||
});
|
||||
}, function(){
|
||||
self.$el.parent().slideDown(self.animation_speed*height/150, function(){
|
||||
self.$el.parent().slideDown(speed*height/150, function(){
|
||||
self.$el.unwrap();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
makeRPCForPersisting: 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)) });
|
||||
if (Math.abs(self.get("balance")).toFixed(3) !== "0.000") mv_line_dicts.push(self.prepareOpenBalanceForPersisting());
|
||||
return self.model_bank_statement_line
|
||||
.call("process_reconciliation", [self.st_line_id, mv_line_dicts]);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -53,22 +53,21 @@
|
|||
<table class="accounting_view">
|
||||
<caption>
|
||||
<button class="button_ok"></button>
|
||||
<span class="partner_name"><t t-if="line.partner_id"><t t-esc="line.partner_name"/></t></span>
|
||||
<span t-if="! line.has_no_partner" class="partner_name"><t t-esc="line.partner_name"/></span>
|
||||
<div class="change_partner_container oe_form"></div>
|
||||
</caption>
|
||||
<tbody class="tbody_initial_line">
|
||||
<tr class="initial_line">
|
||||
<td><span class="toggle_match glyphicon glyphicon-cog"></span></td>
|
||||
<td><t t-esc="line.account_code"/></td>
|
||||
<td><t t-esc="line.date"/></td>
|
||||
<td><t t-if="!line.partner_id"><t t-if="line.partner_name"><t t-esc="line.partner_name"/>: </t></t>
|
||||
<t t-esc="line.name"/>
|
||||
<td class="cell_action"><span class="toggle_match glyphicon glyphicon-cog"></span></td>
|
||||
<td class="cell_account_code"><t t-esc="line.account_code"/></td>
|
||||
<td class="cell_due_date"><t t-esc="line.date"/></td>
|
||||
<td class="cell_label"><t t-if="line.name" t-esc="line.name"/>
|
||||
<t t-if="line.amount_currency_str"> (<t t-esc="line.amount_currency_str"/>)</t></td>
|
||||
<td><t t-if="line.amount > 0">
|
||||
<td class="cell_debit"><t t-if="line.amount > 0">
|
||||
<t t-esc="line.amount_str"/></t></td>
|
||||
<td><t t-if="line.amount < 0">
|
||||
<td class="cell_credit"><t t-if="line.amount < 0">
|
||||
<t t-esc="line.amount_str"/></t></td>
|
||||
<td><span class="line_info_button glyphicon glyphicon-info-sign" t-att-data-content="line.q_popover"></span></td>
|
||||
<td class="cell_info_popover"><span class="line_info_button glyphicon glyphicon-info-sign" t-att-data-content="line.q_popover"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody class="tbody_matched_lines"></tbody>
|
||||
|
@ -115,7 +114,7 @@
|
|||
<t t-name="bank_statement_reconciliation_line_details">
|
||||
<table class='details'>
|
||||
<tr><td>Date</td><td><t t-esc="line.date"/></td></tr>
|
||||
<tr><td>Partner</td><td><t t-esc="line.partner_name"/></td></tr>
|
||||
<tr t-if="line.partner_name"><td>Partner</td><td><t t-esc="line.partner_name"/></td></tr>
|
||||
<tr t-if="line.ref"><td>Transaction</td><td><t t-esc="line.ref"/></td></tr>
|
||||
<tr><td>Description</td><td><t t-esc="line.name"/></td></tr>
|
||||
<tr><td>Amount</td><td><t t-esc="line.amount_str"/><t t-if="line.amount_currency_str"> (<t t-esc="line.amount_currency_str"/>)</t></td></tr>
|
||||
|
@ -123,40 +122,26 @@
|
|||
</table>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-name="bank_statement_reconciliation_move_line">
|
||||
<tr class="mv_line" t-att-data-lineid="line.id" t-att-data-selected="selected">
|
||||
<td><span class="glyphicon glyphicon-add-remove"></span></td>
|
||||
<td><t t-esc="line.account_code"/></td>
|
||||
<td><t t-esc="line.q_due_date"/></td>
|
||||
<td class="js_qlabel"><t t-esc="line.q_label"/></td>
|
||||
|
||||
<td><t t-if="line.debit !== 0">
|
||||
<t t-if="line.propose_partial_reconcile" t-call="icon_do_partial_reconciliation"></t>
|
||||
<t t-if="line.partial_reconcile" t-call="icon_undo_partial_reconciliation"></t>
|
||||
<t t-esc="line.debit_str"/>
|
||||
</t></td>
|
||||
<td><t t-if="line.credit !== 0">
|
||||
<t t-if="line.propose_partial_reconcile"><t t-call="icon_do_partial_reconciliation" /></t>
|
||||
<t t-if="line.partial_reconcile"><t t-call="icon_undo_partial_reconciliation" /></t>
|
||||
<t t-esc="line.credit_str"/>
|
||||
</t></td>
|
||||
<td><span class="line_info_button glyphicon glyphicon-info-sign" t-att-data-content="line.q_popover"></span></td>
|
||||
<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>
|
||||
<td class="cell_label js_qlabel"><t t-esc="line.q_label"/></td>
|
||||
<td class="cell_debit"><t t-if="line.debit !== 0"><t t-if="line.propose_partial_reconcile" t-call="icon_do_partial_reconciliation"></t><t t-if="line.partial_reconcile" t-call="icon_undo_partial_reconciliation"></t><t t-esc="line.debit_str"/></t></td>
|
||||
<td class="cell_credit"><t t-if="line.credit !== 0"><t t-if="line.propose_partial_reconcile"><t t-call="icon_do_partial_reconciliation" /></t><t t-if="line.partial_reconcile"><t t-call="icon_undo_partial_reconciliation" /></t><t t-esc="line.credit_str"/></t></td>
|
||||
<td class="cell_info_popover"><span class="line_info_button glyphicon glyphicon-info-sign" t-att-data-content="line.q_popover"></span></td>
|
||||
</tr>
|
||||
</t>
|
||||
|
||||
<t t-name="icon_do_partial_reconciliation">
|
||||
<i class="do_partial_reconcile_button fa fa-exclamation-triangle" data-content="This move's amount is higher than the transaction's amount. Click to do a partial reconciliation"></i>
|
||||
</t>
|
||||
<t t-name="icon_do_partial_reconciliation"><i class="do_partial_reconcile_button fa fa-exclamation-triangle" data-content="This move's amount is higher than the transaction's amount. Click to do a partial reconciliation"></i></t>
|
||||
|
||||
<t t-name="icon_undo_partial_reconciliation">
|
||||
<i class="undo_partial_reconcile_button fa fa-exclamation-triangle" data-content="Undo the partial reconciliation."></i>
|
||||
</t>
|
||||
<t t-name="icon_undo_partial_reconciliation"><i class="undo_partial_reconcile_button fa fa-exclamation-triangle" data-content="Undo the partial reconciliation."></i></t>
|
||||
|
||||
<t t-name="bank_statement_reconciliation_move_line_details">
|
||||
<table class='details'>
|
||||
<tr><td>ID</td><td><t t-esc="line.id"/></td></tr>
|
||||
<tr><td>Account</td><td><t t-esc="line.account_code"/> <t t-esc="line.account_name"/></td></tr>
|
||||
<tr><td>id</td><td><t t-esc="line.id" /></td></tr>
|
||||
<tr t-if="line.account_code"><td>Account</td><td><t t-esc="line.account_code"/> <t t-esc="line.account_name"/></td></tr>
|
||||
<tr><td>Journal</td><td><t t-esc="line.journal_name"/></td></tr>
|
||||
<tr><td>Period</td><td><t t-esc="line.period_name"/></td></tr>
|
||||
<tr><td>Date</td><td><t t-esc="line.date"/></td></tr>
|
||||
|
@ -168,28 +153,27 @@
|
|||
</table>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-name="bank_statement_reconciliation_created_line">
|
||||
<tr class="created_line" t-att-data-lineid="line.id">
|
||||
<td><t t-if="! line.no_remove_action"><span class="line_remove_button glyphicon glyphicon-remove"></span></t></td>
|
||||
<td><t t-esc="line.account_num"/></td>
|
||||
<td></td>
|
||||
<td><t t-esc="line.label"/></td>
|
||||
<td><t t-if="line.amount < 0"><t t-esc="line.amount_str"/></t></td>
|
||||
<td><t t-if="line.amount > 0"><t t-esc="line.amount_str"/></t></td>
|
||||
<td></td>
|
||||
<td class="cell_action"><t t-if="! line.no_remove_action"><span class="line_remove_button glyphicon glyphicon-remove"></span></t></td>
|
||||
<td class="cell_account_code"><t t-esc="line.account_num"/></td>
|
||||
<td class="cell_due_date"></td>
|
||||
<td class="cell_label"><t t-esc="line.label"/></td>
|
||||
<td class="cell_debit"><t t-if="line.amount < 0"><t t-esc="line.amount_str"/></t></td>
|
||||
<td class="cell_credit"><t t-if="line.amount > 0"><t t-esc="line.amount_str"/></t></td>
|
||||
<td class="cell_info_popover"></td>
|
||||
</tr>
|
||||
</t>
|
||||
|
||||
<t t-name="bank_statement_reconciliation_line_open_balance">
|
||||
<tr class="line_open_balance">
|
||||
<td><span class="toggle_create glyphicon glyphicon-play"></span></td>
|
||||
<td><t t-esc="account_code"/></td>
|
||||
<td></td>
|
||||
<td class="js_open_balance">Open balance</td>
|
||||
<td><t t-esc="debit"/></td>
|
||||
<td><t t-esc="credit"/></td>
|
||||
<td></td>
|
||||
<td class="cell_action"><span class="toggle_create glyphicon glyphicon-play"></span></td>
|
||||
<td class="cell_account_code"><t t-esc="account_code"/></td>
|
||||
<td class="cell_due_date"></td>
|
||||
<td class="cell_label js_open_balance">Open balance</td>
|
||||
<td class="cell_debit"><t t-esc="debit"/></td>
|
||||
<td class="cell_credit"><t t-esc="credit"/></td>
|
||||
<td class="cell_info_popover"></td>
|
||||
</tr>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from . import test_tax
|
||||
from . import test_search
|
||||
from . import test_reconciliation
|
||||
|
||||
fast_suite = [
|
||||
test_tax,
|
||||
test_search,
|
||||
test_reconciliation,
|
||||
]
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import openerp.tests
|
||||
|
||||
@openerp.tests.common.at_install(False)
|
||||
@openerp.tests.common.post_install(True)
|
||||
|
||||
class TestUi(openerp.tests.HttpCase):
|
||||
def test_01_admin_bank_statement_reconciliation(self):
|
||||
self.phantom_js("/", "openerp.Tour.run('bank_statement_reconciliation', 'test')", "openerp.Tour.tours.bank_statement_reconciliation", login="admin")
|
|
@ -0,0 +1,162 @@
|
|||
from openerp.tests.common import TransactionCase
|
||||
|
||||
class TestReconciliation(TransactionCase):
|
||||
"""Tests for reconciliation (account.tax)
|
||||
|
||||
Test used to check that when doing a sale or purchase invoice in a different currency,
|
||||
the result will be balanced.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestReconciliation, self).setUp()
|
||||
self.account_invoice_model = self.registry('account.invoice')
|
||||
self.account_invoice_line_model = self.registry('account.invoice.line')
|
||||
self.acc_bank_stmt_model = self.registry('account.bank.statement')
|
||||
self.acc_bank_stmt_line_model = self.registry('account.bank.statement.line')
|
||||
|
||||
self.partner_agrolait_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "base", "res_partner_2")[1]
|
||||
self.currency_swiss_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "base", "CHF")[1]
|
||||
self.currency_usd_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "base", "USD")[1]
|
||||
self.account_rcv_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "a_recv")[1]
|
||||
self.account_rsa_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "rsa")[1]
|
||||
self.product_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "product", "product_product_4")[1]
|
||||
|
||||
self.bank_journal_usd_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "bank_journal_usd")[1]
|
||||
self.account_usd_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "usd_bnk")[1]
|
||||
|
||||
self.company_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "base", "main_company")[1]
|
||||
#set expense_currency_exchange_account_id and income_currency_exchange_account_id to a random account
|
||||
self.registry("res.company").write(self.cr, self.uid, [self.company_id], {'expense_currency_exchange_account_id': self.account_rsa_id, 'income_currency_exchange_account_id':self.account_rsa_id})
|
||||
|
||||
def test_balanced_customer_invoice(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
#we create an invoice in CHF
|
||||
invoice_id = self.account_invoice_model.create(cr, uid, {'partner_id': self.partner_agrolait_id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': self.currency_swiss_id,
|
||||
'name': 'invoice to client',
|
||||
'account_id': self.account_rcv_id,
|
||||
'type': 'out_invoice'
|
||||
})
|
||||
self.account_invoice_line_model.create(cr, uid, {'product_id': self.product_id,
|
||||
'quantity': 1,
|
||||
'price_unit': 100,
|
||||
'invoice_id': invoice_id,
|
||||
'name': 'product that cost 100',})
|
||||
|
||||
#validate purchase
|
||||
self.registry('account.invoice').signal_workflow(cr, uid, [invoice_id], 'invoice_open')
|
||||
invoice_record = self.account_invoice_model.browse(cr, uid, [invoice_id])
|
||||
|
||||
#we pay half of it on a journal with currency in dollar (bank statement)
|
||||
bank_stmt_id = self.acc_bank_stmt_model.create(cr, uid, {'journal_id': self.bank_journal_usd_id,})
|
||||
|
||||
bank_stmt_line_id = self.acc_bank_stmt_line_model.create(cr, uid, {'name': 'half payment',
|
||||
'statement_id': bank_stmt_id,
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'amount': 42,
|
||||
'amount_currency': 50,
|
||||
'currency_id': self.currency_swiss_id,})
|
||||
|
||||
#reconcile the payment with the invoice
|
||||
for l in invoice_record.move_id.line_id:
|
||||
if l.account_id.id == self.account_rcv_id:
|
||||
line_id = l
|
||||
break
|
||||
self.acc_bank_stmt_line_model.process_reconciliation(cr, uid, bank_stmt_line_id, [
|
||||
{'counterpart_move_line_id': line_id.id, 'credit':50, 'debit':0, 'name': line_id.name,}])
|
||||
|
||||
#we check that the line is balanced (bank statement line)
|
||||
move_line_ids = self.acc_bank_stmt_model.browse(cr,uid,bank_stmt_id).move_line_ids
|
||||
|
||||
self.assertEquals(len(move_line_ids), 3)
|
||||
checked_line = 0
|
||||
for move_line in move_line_ids:
|
||||
if move_line.account_id.id == self.account_usd_id:
|
||||
self.assertEquals(move_line.debit, 27.47)
|
||||
self.assertEquals(move_line.credit, 0.0)
|
||||
self.assertEquals(move_line.amount_currency, 42)
|
||||
self.assertEquals(move_line.currency_id.id, self.currency_usd_id)
|
||||
checked_line += 1
|
||||
continue
|
||||
if move_line.account_id.id == self.account_rcv_id:
|
||||
self.assertEquals(move_line.debit, 0.0)
|
||||
self.assertEquals(move_line.credit, 38.21)
|
||||
self.assertEquals(move_line.amount_currency, -50)
|
||||
self.assertEquals(move_line.currency_id.id, self.currency_swiss_id)
|
||||
checked_line += 1
|
||||
continue
|
||||
if move_line.account_id.id == self.account_rsa_id:
|
||||
self.assertEquals(move_line.debit, 10.74)
|
||||
self.assertEquals(move_line.credit, 0.0)
|
||||
checked_line += 1
|
||||
continue
|
||||
self.assertEquals(checked_line, 3)
|
||||
|
||||
|
||||
|
||||
def test_balanced_supplier_invoice(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
#we create a supplier invoice in CHF
|
||||
invoice_id = self.account_invoice_model.create(cr, uid, {'partner_id': self.partner_agrolait_id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': self.currency_swiss_id,
|
||||
'name': 'invoice to client',
|
||||
'account_id': self.account_rcv_id,
|
||||
'type': 'in_invoice'
|
||||
})
|
||||
self.account_invoice_line_model.create(cr, uid, {'product_id': self.product_id,
|
||||
'quantity': 1,
|
||||
'price_unit': 100,
|
||||
'invoice_id': invoice_id,
|
||||
'name': 'product that cost 100',})
|
||||
|
||||
#validate purchase
|
||||
self.registry('account.invoice').signal_workflow(cr, uid, [invoice_id], 'invoice_open')
|
||||
invoice_record = self.account_invoice_model.browse(cr, uid, [invoice_id])
|
||||
|
||||
#we pay half of it on a journal with currency in dollar (bank statement)
|
||||
bank_stmt_id = self.acc_bank_stmt_model.create(cr, uid, {'journal_id': self.bank_journal_usd_id,})
|
||||
|
||||
bank_stmt_line_id = self.acc_bank_stmt_line_model.create(cr, uid, {'name': 'half payment',
|
||||
'statement_id': bank_stmt_id,
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'amount': -42,
|
||||
'amount_currency': -50,
|
||||
'currency_id': self.currency_swiss_id,})
|
||||
|
||||
#reconcile the payment with the invoice
|
||||
for l in invoice_record.move_id.line_id:
|
||||
if l.account_id.id == self.account_rcv_id:
|
||||
line_id = l
|
||||
break
|
||||
self.acc_bank_stmt_line_model.process_reconciliation(cr, uid, bank_stmt_line_id, [
|
||||
{'counterpart_move_line_id': line_id.id, 'credit':0, 'debit':50, 'name': line_id.name,}])
|
||||
|
||||
#we check that the line is balanced (bank statement line)
|
||||
move_line_ids = self.acc_bank_stmt_model.browse(cr,uid,bank_stmt_id).move_line_ids
|
||||
|
||||
self.assertEquals(len(move_line_ids), 3)
|
||||
checked_line = 0
|
||||
for move_line in move_line_ids:
|
||||
if move_line.account_id.id == self.account_usd_id:
|
||||
self.assertEquals(move_line.debit, 0.0)
|
||||
self.assertEquals(move_line.credit, 27.47)
|
||||
self.assertEquals(move_line.amount_currency, -42)
|
||||
self.assertEquals(move_line.currency_id.id, self.currency_usd_id)
|
||||
checked_line += 1
|
||||
continue
|
||||
if move_line.account_id.id == self.account_rcv_id:
|
||||
self.assertEquals(move_line.debit, 38.21)
|
||||
self.assertEquals(move_line.credit, 0.0)
|
||||
self.assertEquals(move_line.amount_currency, 50)
|
||||
self.assertEquals(move_line.currency_id.id, self.currency_swiss_id)
|
||||
checked_line += 1
|
||||
continue
|
||||
if move_line.account_id.id == self.account_rsa_id:
|
||||
self.assertEquals(move_line.debit, 0.0)
|
||||
self.assertEquals(move_line.credit, 10.74)
|
||||
checked_line += 1
|
||||
continue
|
||||
self.assertEquals(checked_line, 3)
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
<link rel="stylesheet" href="/account/static/src/css/account_bank_statement_reconciliation.css"/>
|
||||
<script type="text/javascript" src="/account/static/src/js/account_widgets.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/account_move_line_quickadd.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/account_tour_bank_statement_reconciliation.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
|
|
|
@ -41,47 +41,6 @@ class res_currency(osv.osv):
|
|||
return res
|
||||
|
||||
|
||||
class res_company(osv.osv):
|
||||
_inherit = "res.company"
|
||||
_columns = {
|
||||
'income_currency_exchange_account_id': fields.many2one(
|
||||
'account.account',
|
||||
string="Gain Exchange Rate Account",
|
||||
domain="[('type', '=', 'other')]",),
|
||||
'expense_currency_exchange_account_id': fields.many2one(
|
||||
'account.account',
|
||||
string="Loss Exchange Rate Account",
|
||||
domain="[('type', '=', 'other')]",),
|
||||
}
|
||||
|
||||
|
||||
class account_config_settings(osv.osv_memory):
|
||||
_inherit = 'account.config.settings'
|
||||
_columns = {
|
||||
'income_currency_exchange_account_id': fields.related(
|
||||
'company_id', 'income_currency_exchange_account_id',
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string="Gain Exchange Rate Account",
|
||||
domain="[('type', '=', 'other')]"),
|
||||
'expense_currency_exchange_account_id': fields.related(
|
||||
'company_id', 'expense_currency_exchange_account_id',
|
||||
type="many2one",
|
||||
relation='account.account',
|
||||
string="Loss Exchange Rate Account",
|
||||
domain="[('type', '=', 'other')]"),
|
||||
}
|
||||
def onchange_company_id(self, cr, uid, ids, company_id, context=None):
|
||||
res = super(account_config_settings, self).onchange_company_id(cr, uid, ids, company_id, context=context)
|
||||
if company_id:
|
||||
company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
|
||||
res['value'].update({'income_currency_exchange_account_id': company.income_currency_exchange_account_id and company.income_currency_exchange_account_id.id or False,
|
||||
'expense_currency_exchange_account_id': company.expense_currency_exchange_account_id and company.expense_currency_exchange_account_id.id or False})
|
||||
else:
|
||||
res['value'].update({'income_currency_exchange_account_id': False,
|
||||
'expense_currency_exchange_account_id': False})
|
||||
return res
|
||||
|
||||
class account_voucher(osv.osv):
|
||||
def _check_paid(self, cr, uid, ids, name, args, context=None):
|
||||
res = {}
|
||||
|
|
|
@ -194,26 +194,6 @@
|
|||
<field name="search_view_id" ref="view_voucher_filter"/>
|
||||
</record>
|
||||
|
||||
<!-- res.config form view -->
|
||||
<record model="ir.ui.view" id="view_account_settings_currency_xchange_form">
|
||||
<field name="name">account.config.settings.inherit</field>
|
||||
<field name="inherit_id" ref="account.view_account_config_settings"/>
|
||||
<field name="model">account.config.settings</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='group_multi_currency']" position="after">
|
||||
<group attrs="{'invisible': [('group_multi_currency', '<>', True)]}" col="2">
|
||||
<group>
|
||||
<field name="income_currency_exchange_account_id"/>
|
||||
<field name="expense_currency_exchange_account_id"/>
|
||||
</group>
|
||||
<group>
|
||||
</group>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
<data noupdate="1">
|
||||
|
||||
|
|
|
@ -286,7 +286,7 @@ class account_coda_import(osv.osv_memory):
|
|||
if 'counterpartyAddress' in line and line['counterpartyAddress'] != '':
|
||||
note.append(_('Counter Party Address') + ': ' + line['counterpartyAddress'])
|
||||
partner_id = None
|
||||
structured_com = ""
|
||||
structured_com = False
|
||||
bank_account_id = False
|
||||
if line['communication_struct'] and 'communication_type' in line and line['communication_type'] == '101':
|
||||
structured_com = line['communication']
|
||||
|
@ -322,21 +322,16 @@ class account_coda_import(osv.osv_memory):
|
|||
self.pool.get('account.bank.statement.line').create(cr, uid, data, context=context)
|
||||
if statement['coda_note'] != '':
|
||||
self.pool.get('account.bank.statement').write(cr, uid, [statement['id']], {'coda_note': statement['coda_note']}, context=context)
|
||||
model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_statement_tree')
|
||||
model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_reconcile_bank_statements')
|
||||
action = self.pool[model].browse(cr, uid, action_id, context=context)
|
||||
statements_ids = [statement['id'] for statement in statements]
|
||||
return {
|
||||
'name': action.name,
|
||||
'view_type': action.view_type,
|
||||
'view_mode': action.view_mode,
|
||||
'res_model': action.res_model,
|
||||
'domain': action.domain,
|
||||
'context': action.context,
|
||||
'type': 'ir.actions.act_window',
|
||||
'search_view_id': action.search_view_id.id,
|
||||
'views': [(v.view_id.id, v.view_mode) for v in action.view_ids]
|
||||
'tag': action.tag,
|
||||
'context': {'statement_ids': statements_ids},
|
||||
'type': 'ir.actions.client',
|
||||
}
|
||||
|
||||
|
||||
def rmspaces(s):
|
||||
return " ".join(s.split())
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
import re
|
||||
import time
|
||||
import math
|
||||
|
||||
from openerp import api, fields as fields2
|
||||
from openerp import tools
|
||||
|
@ -270,6 +271,22 @@ class res_currency(osv.osv):
|
|||
# apply rounding
|
||||
return to_currency.round(to_amount) if round else to_amount
|
||||
|
||||
def get_format_currencies_js_function(self, cr, uid, context=None):
|
||||
""" Returns a string that can be used to instanciate a javascript function that formats numbers as currencies.
|
||||
That function expects the number as first parameter and the currency id as second parameter. In case of failure it returns undefined."""
|
||||
function = ""
|
||||
for row in self.search_read(cr, uid, domain=[], fields=['id', 'name', 'symbol', 'rounding', 'position'], context=context):
|
||||
digits = int(math.log10(1 / row['rounding']))
|
||||
symbol = row['symbol'] or row['name']
|
||||
|
||||
format_number_str = "openerp.web.format_value(arguments[0], {type: 'float', digits: [69," + str(digits) + "]}, 0.00)"
|
||||
if row['position'] == 'after':
|
||||
return_str = "return " + format_number_str + " + '\\xA0" + symbol + "';"
|
||||
else:
|
||||
return_str = "return '" + symbol + "\\xA0' + " + format_number_str + ";"
|
||||
function += "if (arguments[1] === " + str(row['id']) + ") { " + return_str + " }"
|
||||
return function
|
||||
|
||||
class res_currency_rate(osv.osv):
|
||||
_name = "res.currency.rate"
|
||||
_description = "Currency Rate"
|
||||
|
@ -285,4 +302,3 @@ class res_currency_rate(osv.osv):
|
|||
_order = "name desc"
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
Loading…
Reference in New Issue