mirror of
https://github.com/OCA/account-reconcile.git
synced 2025-01-20 12:27:39 +02:00
[REF] account_easy_reconcile: refactoring of easy_reconcile
(lp:account-extra-addons rev 26.1.8)
This commit is contained in:
@@ -22,6 +22,7 @@ import string
|
||||
from osv import fields, osv
|
||||
from tools.translate import _
|
||||
from tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from operator import itemgetter, attrgetter
|
||||
|
||||
|
||||
class account_easy_reconcile_method(osv.osv):
|
||||
@@ -62,7 +63,14 @@ class account_easy_reconcile_method(osv.osv):
|
||||
'require_write_off': fields.boolean('Require Write-off'),
|
||||
'require_account_id': fields.boolean('Require Account'),
|
||||
'require_journal_id': fields.boolean('Require Journal'),
|
||||
'date_base_on': fields.selection([('newest', 'the most recent'), ('actual', 'today'), ('credit_line', 'credit line date'), ('debit_line', 'debit line date')], 'Date Base On'),
|
||||
'date_base_on': fields.selection(
|
||||
[('newest', 'Most recent move line'),
|
||||
('actual', 'Today'),
|
||||
('end_period_last_credit', 'End of period of most recent credit'),
|
||||
('end_period', 'End of period of most recent move line'),
|
||||
('newest_credit', 'Date of most recent credit'),
|
||||
('newest_debit', 'Date of most recent debit')],
|
||||
string='Date of reconcilation'),
|
||||
'filter': fields.char('Filter', size=128),
|
||||
}
|
||||
|
||||
@@ -95,15 +103,104 @@ class account_easy_reconcile(osv.osv):
|
||||
'unreconcile_entry_number': fields.function(_get_unrec_number, method=True, type='integer', string='Unreconcile Entries'),
|
||||
}
|
||||
|
||||
def _below_writeoff_limit(self, cr, uid, lines,
|
||||
writeoff_limit, context=None):
|
||||
precision = self.pool.get('decimal.precision').precision_get(
|
||||
cr, uid, 'Account')
|
||||
keys = ('debit', 'credit')
|
||||
sums = reduce(
|
||||
lambda line, memo:
|
||||
dict((key, value + memo[key])
|
||||
for key, value
|
||||
in line.iteritems()
|
||||
if key in keys), lines)
|
||||
|
||||
debit, credit = sums['debit'], sums['credit']
|
||||
writeoff_amount = round(debit - credit, precision)
|
||||
return bool(writeoff_limit >= abs(writeoff_amount)), debit, credit
|
||||
|
||||
def _get_rec_date(self, cr, uid, lines, based_on='end_period_last_credit', context=None):
|
||||
period_obj = self.pool.get('account.period')
|
||||
|
||||
def last_period(mlines):
|
||||
period_ids = [ml['period_id'] for ml in mlines]
|
||||
periods = period_obj.browse(
|
||||
cr, uid, period_ids, context=context)
|
||||
return max(periods, key=attrgetter('date_stop'))
|
||||
|
||||
def last_date(mlines):
|
||||
return max(mlines, key=itemgetter('date'))
|
||||
|
||||
def credit(mlines):
|
||||
return [l for l in mlines if l['credit'] > 0]
|
||||
|
||||
def debit(mlines):
|
||||
return [l for l in mlines if l['debit'] > 0]
|
||||
|
||||
if based_on == 'end_period_last_credit':
|
||||
return last_period(credit(lines)).date_stop
|
||||
if based_on == 'end_period':
|
||||
return last_period(lines).date_stop
|
||||
elif based_on == 'newest':
|
||||
return last_date(lines)['date']
|
||||
elif based_on == 'newest_credit':
|
||||
return last_date(credit(lines))['date']
|
||||
elif based_on == 'newest_debit':
|
||||
return last_date(debit(lines))['date']
|
||||
# reconcilation date will be today
|
||||
# when date is None
|
||||
return None
|
||||
|
||||
def _reconcile_lines(self, cr, uid, lines, allow_partial=False, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
ml_obj = self.pool.get('account.move.line')
|
||||
writeoff = context.get('write_off', 0.)
|
||||
|
||||
keys = ('debit', 'credit')
|
||||
|
||||
line_ids = [l['id'] for l in lines]
|
||||
below_writeoff, sum_debit, sum_credit = self._below_writeoff_limit(
|
||||
cr, uid, lines, writeoff, context=context)
|
||||
date = self._get_rec_date(
|
||||
cr, uid, lines, context.get('date_base_on'), context=context)
|
||||
|
||||
rec_ctx = dict(context, date_p=date)
|
||||
if below_writeoff:
|
||||
if sum_credit < sum_debit:
|
||||
writeoff_account_id = context.get('account_profit_id', False)
|
||||
else:
|
||||
writeoff_account_id = context.get('account_lost_id', False)
|
||||
|
||||
period_id = self.pool.get('account.period').find(
|
||||
cr, uid, dt=date, context=context)[0]
|
||||
|
||||
ml_obj.reconcile(
|
||||
cr, uid,
|
||||
line_ids,
|
||||
type='auto',
|
||||
writeoff_acc_id=writeoff_account_id,
|
||||
writeoff_period_id=period_id,
|
||||
writeoff_journal_id=context.get('journal_id'),
|
||||
context=rec_ctx)
|
||||
return True
|
||||
elif allow_partial:
|
||||
ml_obj.reconcile_partial(
|
||||
cr, uid,
|
||||
line_ids,
|
||||
type='manual',
|
||||
context=rec_ctx)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def rec_auto_lines_simple(self, cr, uid, lines, context=None):
|
||||
if not context:
|
||||
context={}
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
count = 0
|
||||
res = 0
|
||||
precision = self.pool.get('decimal.precision').precision_get(
|
||||
cr, uid, 'Account')
|
||||
max_diff = context.get('write_off', 0.)
|
||||
while (count < len(lines)):
|
||||
for i in range(count+1, len(lines)):
|
||||
writeoff_account_id = False
|
||||
@@ -122,69 +219,86 @@ class account_easy_reconcile(osv.osv):
|
||||
if not check:
|
||||
continue
|
||||
|
||||
diff = round(abs(credit_line['credit'] - debit_line['debit']), precision)
|
||||
if diff <= max_diff:
|
||||
if context.get('write_off', 0) > 0 and diff:
|
||||
if credit_line['credit'] < debit_line['debit']:
|
||||
writeoff_account_id = context.get('account_profit_id', False)
|
||||
else:
|
||||
writeoff_account_id = context.get('account_lost_id', False)
|
||||
|
||||
context['comment'] = _('Write-Off %s') % credit_line['key']
|
||||
|
||||
if context.get('date_base_on') == 'credit_line':
|
||||
date = credit_line['date']
|
||||
elif context.get('date_base_on') == 'debit_line':
|
||||
date = debit_line['date']
|
||||
elif context.get('date_base_on') == 'newest':
|
||||
date = (credit_line['date'] > debit_line['date']) and credit_line['date'] or debit_line['date']
|
||||
else:
|
||||
date = None
|
||||
|
||||
context['date_p'] = date
|
||||
period_id = self.pool.get('account.period').find(cr, uid, dt=date, context=context)[0]
|
||||
|
||||
self.pool.get('account.move.line').reconcile(cr, uid, [lines[count]['id'], lines[i]['id']], writeoff_acc_id=writeoff_account_id, writeoff_period_id=period_id, writeoff_journal_id=context.get('journal_id'), context=context)
|
||||
del lines[i]
|
||||
if self._reconcile_lines(cr, uid, [credit_line, debit_line],
|
||||
allow_partial=False, context=context):
|
||||
res += 2
|
||||
del lines[i]
|
||||
break
|
||||
count += 1
|
||||
return res
|
||||
|
||||
def get_params(self, cr, uid, account_id, context):
|
||||
def _get_filter(self, cr, uid, account_id, context):
|
||||
ml_obj = self.pool.get('account.move.line')
|
||||
where = ''
|
||||
params = []
|
||||
if context.get('filter'):
|
||||
(from_clause, where_clause, where_clause_params) = self.pool.get('account.move.line')._where_calc(cr, uid, context['filter'], context=context).get_sql()
|
||||
if where_clause:
|
||||
where_clause = " AND %s" % where_clause
|
||||
where_clause_params = account_id, + tuple(where_clause_params)
|
||||
else:
|
||||
where_clause = ''
|
||||
where_clause_params = account_id,
|
||||
return where_clause, where_clause_params
|
||||
dummy, where, params = ml_obj._where_calc(
|
||||
cr, uid, context['filter'], context=context).get_sql()
|
||||
if where:
|
||||
where = " AND %s" % where
|
||||
return where, params
|
||||
|
||||
def action_rec_auto_name(self, cr, uid, account_id, context):
|
||||
(qu1, qu2) = self.get_params(cr, uid, account_id, context)
|
||||
cr.execute("""
|
||||
SELECT name as key, credit, debit, id, date
|
||||
FROM account_move_line
|
||||
WHERE account_id=%s
|
||||
AND reconcile_id IS NULL
|
||||
AND name IS NOT NULL""" + qu1 + " ORDER BY name",
|
||||
qu2)
|
||||
def _base_columns(self, easy_rec):
|
||||
"""Mandatory columns for move lines queries
|
||||
An extra column aliased as `key` should be defined
|
||||
in each query."""
|
||||
aml_cols = (
|
||||
'id',
|
||||
'debit',
|
||||
'credit',
|
||||
'date',
|
||||
'period_id',
|
||||
'ref',
|
||||
'name',
|
||||
'partner_id',
|
||||
'account_id',
|
||||
'move_id')
|
||||
return ["account_move_line.%s" % col for col in aml_cols]
|
||||
|
||||
def _base_select(self, easy_rec, *args, **kwargs):
|
||||
return "SELECT %s" % ', '.join(self._base_columns(easy_rec))
|
||||
|
||||
def _base_from(self, easy_rec, *args, **kwargs):
|
||||
return "FROM account_move_line"
|
||||
|
||||
def _base_where(self, easy_rec, *args, **kwargs):
|
||||
where = ("WHERE account_move_line.account_id = %s "
|
||||
"AND account_move_line.reconcile_id IS NULL ")
|
||||
# it would be great to use dict for params
|
||||
# but as we use _where_calc in _get_filter
|
||||
# which returns a list, we have to
|
||||
# accomodate with that
|
||||
params = [easy_rec.account.id]
|
||||
return where, params
|
||||
|
||||
def _simple_order(self, easy_rec, key, *args, **kwargs):
|
||||
return "ORDER BY account_move_line.%s" % key
|
||||
|
||||
def _action_rec_simple(self, cr, uid, easy_rec, key, context=None):
|
||||
"""Match only 2 move lines, do not allow partial reconcile"""
|
||||
select = self._base_select(easy_rec)
|
||||
select += ", account_move_line.%s as key " % key
|
||||
where, params = self._base_where(easy_rec)
|
||||
where += " AND account_move_line.%s IS NOT NULL " % key
|
||||
|
||||
where2, params2 = self._get_filter(cr, uid, easy_rec, context=context)
|
||||
query = ' '.join((
|
||||
select,
|
||||
self._base_from(easy_rec),
|
||||
where, where2,
|
||||
self._simple_order(easy_rec, key)))
|
||||
|
||||
cr.execute(query, params + params2)
|
||||
lines = cr.dictfetchall()
|
||||
return self.rec_auto_lines_simple(cr, uid, lines, context)
|
||||
|
||||
def action_rec_auto_partner(self, cr, uid, account_id, context):
|
||||
(qu1, qu2) = self.get_params(cr, uid, account_id, context)
|
||||
cr.execute("""
|
||||
SELECT partner_id as key, credit, debit, id, date
|
||||
FROM account_move_line
|
||||
WHERE account_id=%s
|
||||
AND reconcile_id IS NULL
|
||||
AND partner_id IS NOT NULL""" + qu1 + " ORDER BY partner_id",
|
||||
qu2)
|
||||
lines = cr.dictfetchall()
|
||||
return self.rec_auto_lines_simple(cr, uid, lines, context)
|
||||
def _action_rec_auto_name(self, cr, uid, easy_rec, context):
|
||||
return self._action_rec_simple(
|
||||
cr, uid, easy_rec, 'name', context=context)
|
||||
|
||||
def _action_rec_auto_partner(self, cr, uid, easy_rec, context):
|
||||
return self._action_rec_simple(
|
||||
cr, uid, easy_rec, 'partner_id', context=context)
|
||||
|
||||
def action_rec_auto(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
@@ -205,11 +319,11 @@ class account_easy_reconcile(osv.osv):
|
||||
account_profit_id=method.account_profit_id.id,
|
||||
journal_id=method.journal_id.id)
|
||||
|
||||
py_meth = getattr(self, method.name)
|
||||
res = py_meth(cr, uid, rec.account.id, context=context)
|
||||
py_meth = getattr(self, "_%s" % method.name)
|
||||
res = py_meth(cr, uid, rec, context=ctx)
|
||||
details += _(' method %d : %d lines |') % (count, res)
|
||||
log = self.read(cr, uid, rec_id, ['rec_log'], context=context)['rec_log']
|
||||
log_lines = log and log.splitlines or []
|
||||
log_lines = log and log.splitlines() or []
|
||||
log_lines[0:0] = [_('%s : %d lines have been reconciled (%s)') %
|
||||
(time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), total_rec, details[0:-2])]
|
||||
log = "\n".join(log_lines)
|
||||
|
||||
Reference in New Issue
Block a user