[FIX] account_easy_reconcile: code review

This commit is contained in:
Damien Crier
2015-06-26 09:48:32 +02:00
committed by mpanarin
parent 1bc8b6e020
commit b9ab2f74b6
8 changed files with 91 additions and 137 deletions

View File

@@ -42,9 +42,22 @@ Credits
Contributors
------------
* Damien Crier <damien.crier@camptocamp.com>
* Sébastien Beau <sebastien.beau@akretion.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* Vincent Renaville <vincent.renaville@camptocamp.com>
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
* Joël Grand-Guillaume <joel.grandguillaume@camptocamp.com>
* Nicolas Bessis <nicolas.bessi@camptocamp.com>
* Pedro M.Baeza <pedro.baeza@gmail.com>
* Matthieu Dietrich <matthieu.dietrich@camptocamp.com>
* Leonardo Pistone <leonardo.pistone@camptocamp.com>
* Ecino <ecino@compassion.ch>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Rudolf Schnapka <rs@techno-flex.de>
* Florian Dacosta <florian.dacosta@akretion.com>
* Laetitia Gangloff <laetitia.gangloff@acsone.eu>
* Frédéric Clémenti <frederic.clementi@camptocamp.com>
* Damien Crier <damien.crier@camptocamp.com>
Maintainer
----------

View File

@@ -25,34 +25,6 @@
"version": "1.3.1",
"depends": ["account"],
"author": "Akretion,Camptocamp,Odoo Community Association (OCA)",
"description": """
Easy Reconcile
==============
This is a shared work between Akretion and Camptocamp
in order to provide:
- reconciliation facilities for big volume of transactions
- setup different profiles of reconciliation by account
- each profile can use many methods of reconciliation
- this module is also a base to create others
reconciliation methods which can plug in the profiles
- a profile a reconciliation can be run manually
or by a cron
- monitoring of reconciliation runs with an history
which keep track of the reconciled Journal items
2 simple reconciliation methods are integrated
in this module, the simple reconciliations works
on 2 lines (1 debit / 1 credit) and do not allow
partial reconcilation, they also match on 1 key,
partner or Journal item name.
You may be interested to install also the
``account_advanced_reconciliation`` module.
This latter add more complex reconciliations,
allows multiple lines and partial.
""",
"website": "http://www.akretion.com/",
"category": "Finance",
"data": ["easy_reconcile.xml",

View File

@@ -78,9 +78,11 @@ class EasyReconcileBase(models.AbstractModel):
'move_id')
return ["account_move_line.%s" % col for col in aml_cols]
@api.multi
def _select(self, *args, **kwargs):
return "SELECT %s" % ', '.join(self._base_columns())
@api.multi
def _from(self, *args, **kwargs):
return ("FROM account_move_line "
"LEFT OUTER JOIN account_move_reconcile ON "
@@ -88,6 +90,7 @@ class EasyReconcileBase(models.AbstractModel):
"= account_move_reconcile.id)"
)
@api.multi
def _where(self, *args, **kwargs):
where = ("WHERE account_move_line.account_id = %s "
"AND COALESCE(account_move_reconcile.type,'') <> 'manual' "
@@ -102,8 +105,9 @@ class EasyReconcileBase(models.AbstractModel):
params.append(tuple([l.id for l in self.partner_ids]))
return where, params
@api.multi
def _get_filter(self):
ml_obj = self.pool.get('account.move.line')
ml_obj = self.env['account.move.line']
where = ''
params = []
if self.filter:
@@ -116,7 +120,7 @@ class EasyReconcileBase(models.AbstractModel):
@api.multi
def _below_writeoff_limit(self, lines, writeoff_limit):
self.ensure_one()
precision = self.pool.get('decimal.precision').precision_get('Account')
precision = self.env['decimal.precision'].precision_get('Account')
keys = ('debit', 'credit')
sums = reduce(
lambda line, memo:
@@ -134,7 +138,8 @@ class EasyReconcileBase(models.AbstractModel):
def last_period(mlines):
period_ids = [ml['period_id'] for ml in mlines]
return max(period_ids, key=attrgetter('date_stop'))
periods = self.env['account.period'].browse(period_ids)
return max(periods, key=attrgetter('date_stop'))
def last_date(mlines):
return max(mlines, key=itemgetter('date'))
@@ -175,13 +180,12 @@ class EasyReconcileBase(models.AbstractModel):
"""
self.ensure_one()
ml_obj = self.env['account.move.line']
writeoff = self.write_off
line_ids = [l['id'] for l in lines]
below_writeoff, sum_debit, sum_credit = self._below_writeoff_limit(
lines, writeoff
lines, self.write_off
)
date = self._get_rec_date(lines, self.date_base_on)
rec_ctx = dict(self.env.context or {}, date_p=date)
rec_ctx = dict(self.env.context, date_p=date)
if below_writeoff:
if sum_credit > sum_debit:
writeoff_account_id = self.account_profit_id.id
@@ -190,11 +194,11 @@ class EasyReconcileBase(models.AbstractModel):
period_id = self.env['account.period'].find(dt=date)[0]
if self.analytic_account_id:
rec_ctx['analytic_id'] = self.analytic_account_id.id
ml_obj.with_context(rec_ctx).reconcile(
line_ids,
line_rs = ml_obj.browse(line_ids)
line_rs.with_context(rec_ctx).reconcile(
type='auto',
writeoff_acc_id=writeoff_account_id,
writeoff_period_id=period_id,
writeoff_period_id=period_id.id,
writeoff_journal_id=self.journal_id.id
)
return True, True
@@ -226,12 +230,12 @@ class EasyReconcileBase(models.AbstractModel):
period_id = self.env['account.period'].find(dt=date)[0]
if self.analytic_account_id:
rec_ctx['analytic_id'] = self.analytic_account_id.id
ml_obj.with_context(rec_ctx).reconcile_partial(
line_ids,
line_rs = ml_obj.browse(line_ids)
line_rs.with_context(rec_ctx).reconcile(
type='manual',
writeoff_acc_id=writeoff_account_id,
writeoff_period_id=period_id,
writeoff_journal_id=self.journal_id.id,
writeoff_period_id=period_id.id,
writeoff_journal_id=self.journal_id.id
)
return True, False
return False, False

View File

@@ -22,8 +22,7 @@
from datetime import datetime
from openerp import models, api, fields, _
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.exceptions import except_orm
from openerp.exceptions import Warning
from openerp import sql_db
# from openerp import pooler
@@ -65,16 +64,16 @@ class EasyReconcileOptions(models.AbstractModel):
required=True,
string='Date of reconciliation',
default='end_period_last_credit')
filter = fields.Char(string='Filter', size=128)
filter = fields.Char(string='Filter')
analytic_account_id = fields.Many2one('account.analytic.account',
string='Analytic_account',
help="Analytic account"
help="Analytic account "
"for the write-off")
income_exchange_account_id = fields.Many2one('account.account',
string='Gain Exchange'
string='Gain Exchange '
'Rate Account')
expense_exchange_account_id = fields.Many2one('account.account',
string='Loss Exchange'
string='Loss Exchange '
'Rate Account')
@@ -116,23 +115,6 @@ class AccountEasyReconcileMethod(models.Model):
readonly=True
)
# def init(self, cr):
# """ Migration stuff
#
# Name is not anymore methods names but the name
# of the model which does the reconciliation
# """
# cr.execute("""
# UPDATE account_easy_reconcile_method
# SET name = 'easy.reconcile.simple.partner'
# WHERE name = 'action_rec_auto_partner'
# """)
# cr.execute("""
# UPDATE account_easy_reconcile_method
# SET name = 'easy.reconcile.simple.name'
# WHERE name = 'action_rec_auto_name'
# """)
class AccountEasyReconcile(models.Model):
@@ -144,21 +126,21 @@ class AccountEasyReconcile(models.Model):
@api.depends('account')
def _get_total_unrec(self):
obj_move_line = self.env['account.move.line']
self.unreconciled_count = len(obj_move_line.search(
self.unreconciled_count = obj_move_line.search_count(
[('account_id', '=', self.account.id),
('reconcile_id', '=', False),
('reconcile_partial_id', '=', False)],
))
)
@api.one
@api.depends('account')
def _get_partial_rec(self):
obj_move_line = self.env['account.move.line']
self.reconciled_partial_count = len(obj_move_line.search(
self.reconciled_partial_count = obj_move_line.search_count(
[('account_id', '=', self.account.id),
('reconcile_id', '=', False),
('reconcile_partial_id', '!=', False)],
))
)
@api.one
@api.depends('history_ids')
@@ -171,9 +153,9 @@ class AccountEasyReconcile(models.Model):
[('easy_reconcile_id', '=', self.id)],
limit=1, order='date desc'
)
self.last_history = last_history_rs[0] if last_history_rs else False
self.last_history = last_history_rs or False
name = fields.Char(string='Name', size=32, required=True)
name = fields.Char(string='Name', required=True)
account = fields.Many2one('account.account',
string='Account',
required=True,
@@ -197,7 +179,6 @@ class AccountEasyReconcile(models.Model):
last_history = fields.Many2one('easy.reconcile.history',
string='readonly=True',
compute='_last_history',
readonly=True
)
company_id = fields.Many2one('res.company', string='Company')
@@ -205,20 +186,14 @@ class AccountEasyReconcile(models.Model):
def _prepare_run_transient(self, rec_method):
return {'account_id': rec_method.task_id.account.id,
'write_off': rec_method.write_off,
'account_lost_id': (rec_method.account_lost_id and
rec_method.account_lost_id.id),
'account_profit_id': (rec_method.account_profit_id and
rec_method.account_profit_id.id),
'analytic_account_id': (rec_method.analytic_account_id and
rec_method.analytic_account_id.id),
'account_lost_id': (rec_method.account_lost_id.id),
'account_profit_id': (rec_method.account_profit_id.id),
'analytic_account_id': (rec_method.analytic_account_id.id),
'income_exchange_account_id':
(rec_method.income_exchange_account_id and
rec_method.income_exchange_account_id.id),
(rec_method.income_exchange_account_id.id),
'expense_exchange_account_id':
(rec_method.income_exchange_account_id and
rec_method.income_exchange_account_id.id),
'journal_id': (rec_method.journal_id and
rec_method.journal_id.id),
(rec_method.income_exchange_account_id.id),
'journal_id': (rec_method.journal_id.id),
'date_base_on': rec_method.date_base_on,
'filter': rec_method.filter}
@@ -305,7 +280,7 @@ class AccountEasyReconcile(models.Model):
self.env['easy.reconcile.history'].create(
{
'easy_reconcile_id': rec.id,
'date': fields.datetime.now(),
'date': fields.Datetime.now(),
'reconcile_ids': [],
'reconcile_partial_ids': [],
}
@@ -315,8 +290,6 @@ class AccountEasyReconcile(models.Model):
new_cr.commit()
new_cr.close()
# self.env.cr.close()
return True
@api.model
@@ -325,10 +298,10 @@ class AccountEasyReconcile(models.Model):
be called when there is no history on the reconciliation
task.
"""
raise except_orm(
_('Error'),
raise Warning(
_('There is no history of reconciled '
'items on the task: %s.') % rec.name)
'items on the task: %s.') % rec.name
)
@api.model
def _open_move_line_list(self, move_line_ids, name):
@@ -349,52 +322,42 @@ class AccountEasyReconcile(models.Model):
""" Open the view of move line with the unreconciled move lines"""
self.ensure_one()
obj_move_line = self.env['account.move.line']
line_ids = obj_move_line.search(
lines = obj_move_line.search(
[('account_id', '=', self.account.id),
('reconcile_id', '=', False),
('reconcile_partial_id', '=', False)])
name = _('Unreconciled items')
return self._open_move_line_list(line_ids and line_ids.ids or [], name)
return self._open_move_line_list(lines.ids or [], name)
@api.multi
def open_partial_reconcile(self):
""" Open the view of move line with the unreconciled move lines"""
self.ensure_one()
obj_move_line = self.env['account.move.line']
line_ids = obj_move_line.search(
lines = obj_move_line.search(
[('account_id', '=', self.account.id),
('reconcile_id', '=', False),
('reconcile_partial_id', '!=', False)])
name = _('Partial reconciled items')
return self._open_move_line_list(line_ids and line_ids.ids or [], name)
return self._open_move_line_list(lines.ids or [], name)
@api.model
def last_history_reconcile(self, rec_id):
@api.multi
def last_history_reconcile(self):
""" Get the last history record for this reconciliation profile
and return the action which opens move lines reconciled
"""
if isinstance(rec_id, (tuple, list)):
assert len(rec_id) == 1, \
"Only 1 id expected"
rec_id = rec_id[0]
rec = self.browse(rec_id)
if not rec.last_history:
self._no_history(rec)
return rec.last_history.open_reconcile()
if not self.last_history:
self._no_history()
return self.last_history.open_reconcile()
@api.model
def last_history_partial(self, rec_id):
@api.multi
def last_history_partial(self):
""" Get the last history record for this reconciliation profile
and return the action which opens move lines reconciled
"""
if isinstance(rec_id, (tuple, list)):
assert len(rec_id) == 1, \
"Only 1 id expected"
rec_id = rec_id[0]
rec = self.browse(rec_id)
if not rec.last_history:
self._no_history(rec)
return rec.last_history.open_partial()
if not self.last_history:
self._no_history()
return self.last_history.open_partial()
@api.model
def run_scheduler(self, run_all=None):
@@ -408,8 +371,7 @@ class AccountEasyReconcile(models.Model):
"""
def _get_date(reconcile):
if reconcile.last_history.date:
return datetime.strptime(reconcile.last_history.date,
DEFAULT_SERVER_DATETIME_FORMAT)
return fields.Datetime.from_string(reconcile.last_history.date)
else:
return datetime.min

View File

@@ -136,7 +136,7 @@ The lines should have the same amount (with the write-off) and the same referenc
<field name="income_exchange_account_id" groups="base.group_multi_currency"/>
<field name="expense_exchange_account_id" groups="base.group_multi_currency"/>
<field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_account_id" domain="[('type', '!=', 'view')]" groups="analytic.group_analytic_accounting"/>
<field name="date_base_on"/>
</form>
</field>
@@ -156,7 +156,7 @@ The lines should have the same amount (with the write-off) and the same referenc
<field name="income_exchange_account_id" groups="base.group_multi_currency"/>
<field name="expense_exchange_account_id" groups="base.group_multi_currency"/>
<field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_account_id" domain="[('type', '!=', 'view')]" groups="analytic.group_analytic_accounting"/>
<field name="date_base_on"/>
</tree>
</field>

View File

@@ -36,15 +36,14 @@ class EasyReconcileHistory(models.Model):
def _reconcile_line_ids(self):
move_line_ids = []
for reconcile in self.reconcile_ids:
move_line_ids += [line.id
for line
in reconcile.line_id]
move_lines = reconcile.mapped('line_id')
move_line_ids.extend(move_lines.ids)
self.reconcile_line_ids = move_line_ids
move_line_ids = []
for reconcile in self.reconcile_partial_ids:
move_line_ids += [line.id
for line
in reconcile.line_partial_ids]
move_lines = reconcile.mapped('line_partial_ids')
move_line_ids.extend(move_lines.ids)
self.partial_line_ids = move_line_ids
easy_reconcile_id = fields.Many2one(
@@ -69,15 +68,13 @@ class EasyReconcileHistory(models.Model):
comodel_name='account.move.line',
relation='account_move_line_history_rel',
string='Reconciled Items',
readonly=True,
multi='lines',
_compute='_reconcile_line_ids'
)
partial_line_ids = fields.Many2many(
comodel_name='account.move.line',
relation='account_move_line_history_rel',
relation='account_move_line_history_partial_rel',
string='Partially Reconciled Items',
readonly=True,
multi='lines',
_compute='_reconcile_line_ids'
)
@@ -99,14 +96,19 @@ class EasyReconcileHistory(models.Model):
"""
assert rec_type in ('full', 'partial'), \
"rec_type must be 'full' or 'partial'"
history = self
move_line_ids = []
if rec_type == 'full':
field = 'reconcile_line_ids'
move_line_ids = []
for reconcile in self.reconcile_ids:
move_lines = reconcile.mapped('line_id')
move_line_ids.extend(move_lines.ids)
name = _('Reconciliations')
else:
field = 'partial_line_ids'
move_line_ids = []
for reconcile in self.reconcile_partial_ids:
move_lines = reconcile.mapped('line_partial_ids')
move_line_ids.extend(move_lines.ids)
name = _('Partial Reconciliations')
move_line_ids = [line.id for line in getattr(history, field)]
return {
'name': name,
'view_mode': 'tree,form',
@@ -119,6 +121,7 @@ class EasyReconcileHistory(models.Model):
'domain': unicode([('id', 'in', move_line_ids)]),
}
@api.multi
def open_reconcile(self):
""" For an history record, open the view of move line
with the reconciled move lines
@@ -130,7 +133,7 @@ class EasyReconcileHistory(models.Model):
self.ensure_one()
return self._open_move_lines(rec_type='full')
@api.model
@api.multi
def open_partial(self):
""" For an history record, open the view of move line
with the partially reconciled move lines

View File

@@ -28,7 +28,7 @@ class AccountConfigSettings(models.TransientModel):
reconciliation_commit_every = fields.Integer(
related="company_id.reconciliation_commit_every",
string="How often to commit when performing automatic"
string="How often to commit when performing automatic "
"reconciliation.",
help="""Leave zero to commit only at the end of the process."""
)
@@ -52,7 +52,7 @@ class Company(models.Model):
_inherit = "res.company"
reconciliation_commit_every = fields.Integer(
string="How often to commit when performing automatic"
string="How often to commit when performing automatic "
"reconciliation.",
help="""Leave zero to commit only at the end of the process."""
help="Leave zero to commit only at the end of the process."
)

View File

@@ -54,7 +54,6 @@ class EasyReconcileSimple(models.AbstractModel):
if not check:
continue
reconciled, dummy = self._reconcile_lines(
self,
[credit_line, debit_line],
allow_partial=False
)
@@ -65,6 +64,7 @@ class EasyReconcileSimple(models.AbstractModel):
count += 1
return res, [] # empty list for partial, only full rec in "simple" rec
@api.multi
def _simple_order(self, *args, **kwargs):
return "ORDER BY account_move_line.%s" % self._key_field