mirror of
https://github.com/OCA/account-reconcile.git
synced 2025-01-20 12:27:39 +02:00
[ADD] added account_easy_reconcile from account_extra_addons
This commit is contained in:
committed by
Guewen Baconnier @ Camptocamp
commit
89aef1de9a
@@ -25,7 +25,7 @@
|
||||
'maintainer': 'Camptocamp',
|
||||
'category': 'Finance',
|
||||
'complexity': 'normal',
|
||||
'depends': ['account_easy_reconcile', # this comes from lp:account-extra-addons
|
||||
'depends': ['account_easy_reconcile',
|
||||
],
|
||||
'description': """
|
||||
Advanced reconciliation methods for the module account_easy_reconcile.
|
||||
|
||||
24
account_easy_reconcile/__init__.py
Executable file
24
account_easy_reconcile/__init__.py
Executable file
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright 2012 Camptocamp SA (Guewen Baconnier)
|
||||
# Copyright (C) 2010 Sébastien Beau
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import easy_reconcile
|
||||
import base_reconciliation
|
||||
import simple_reconciliation
|
||||
56
account_easy_reconcile/__openerp__.py
Executable file
56
account_easy_reconcile/__openerp__.py
Executable file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright 2012 Camptocamp SA (Guewen Baconnier)
|
||||
# Copyright (C) 2010 Sébastien Beau
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
"name" : "Easy Reconcile",
|
||||
"version" : "1.1",
|
||||
"depends" : ["account", "base_scheduler_creator"
|
||||
],
|
||||
"author" : "Akretion,Camptocamp",
|
||||
"description": """
|
||||
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 reconcilation runs with a few logs
|
||||
|
||||
2 simple reconciliation methods are integrated in this module, the simple
|
||||
reconciliations works on 2 lines (1 debit / 1 credit) and do not allows
|
||||
partial reconcilation, they also match on 1 key, partner or entry name.
|
||||
|
||||
You may be interested to install also the account_advanced_reconciliation
|
||||
module available at: https://code.launchpad.net/c2c-financial-addons
|
||||
This latter add more complex reconciliations, allows multiple lines and partial.
|
||||
|
||||
""",
|
||||
"website" : "http://www.akretion.com/",
|
||||
"category" : "Finance",
|
||||
"init_xml" : [],
|
||||
"demo_xml" : [],
|
||||
"update_xml" : ["easy_reconcile.xml"],
|
||||
'license': 'AGPL-3',
|
||||
"auto_install": False,
|
||||
"installable": True,
|
||||
|
||||
}
|
||||
207
account_easy_reconcile/base_reconciliation.py
Normal file
207
account_easy_reconcile/base_reconciliation.py
Normal file
@@ -0,0 +1,207 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright 2012 Camptocamp SA (Guewen Baconnier)
|
||||
# Copyright (C) 2010 Sébastien Beau
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv.orm import AbstractModel
|
||||
from openerp.osv import fields
|
||||
from operator import itemgetter, attrgetter
|
||||
|
||||
|
||||
class easy_reconcile_base(AbstractModel):
|
||||
"""Abstract Model for reconciliation methods"""
|
||||
|
||||
_name = 'easy.reconcile.base'
|
||||
|
||||
_inherit = 'easy.reconcile.options'
|
||||
_auto = True # restore property set to False by AbstractModel
|
||||
|
||||
_columns = {
|
||||
'account_id': fields.many2one('account.account', 'Account', required=True),
|
||||
'partner_ids': fields.many2many('res.partner',
|
||||
string="Restrict on partners"),
|
||||
# other columns are inherited from easy.reconcile.options
|
||||
}
|
||||
|
||||
def automatic_reconcile(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
:return: list of reconciled ids, list of partially reconciled entries
|
||||
"""
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
assert len(ids) == 1, "Has to be called on one id"
|
||||
rec = self.browse(cr, uid, ids[0], context=context)
|
||||
return self._action_rec(cr, uid, rec, context=context)
|
||||
|
||||
def _action_rec(self, cr, uid, rec, context=None):
|
||||
"""Must be inherited to implement the reconciliation
|
||||
:return: list of reconciled ids
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _base_columns(self, 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 _select(self, rec, *args, **kwargs):
|
||||
return "SELECT %s" % ', '.join(self._base_columns(rec))
|
||||
|
||||
def _from(self, rec, *args, **kwargs):
|
||||
return "FROM account_move_line"
|
||||
|
||||
def _where(self, 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 = [rec.account_id.id]
|
||||
|
||||
if rec.partner_ids:
|
||||
where += " AND account_move_line.partner_id IN %s"
|
||||
params.append(tuple([l.id for l in rec.partner_ids]))
|
||||
return where, params
|
||||
|
||||
def _get_filter(self, cr, uid, rec, context):
|
||||
ml_obj = self.pool.get('account.move.line')
|
||||
where = ''
|
||||
params = []
|
||||
if rec.filter:
|
||||
dummy, where, params = ml_obj._where_calc(
|
||||
cr, uid, eval(rec.filter), context=context).get_sql()
|
||||
if where:
|
||||
where = " AND %s" % where
|
||||
return where, params
|
||||
|
||||
def _below_writeoff_limit(self, cr, uid, rec, 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, rec, 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, rec, lines, allow_partial=False, context=None):
|
||||
""" Try to reconcile given lines
|
||||
|
||||
:param list lines: list of dict of move lines, they must at least
|
||||
contain values for : id, debit, credit
|
||||
:param boolean allow_partial: if True, partial reconciliation will be
|
||||
created, otherwise only Full reconciliation will be created
|
||||
:return: tuple of boolean values, first item is wether the the entries
|
||||
have been reconciled or not, the second is wether the reconciliation
|
||||
is full (True) or partial (False)
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
ml_obj = self.pool.get('account.move.line')
|
||||
writeoff = rec.write_off
|
||||
|
||||
keys = ('debit', 'credit')
|
||||
|
||||
line_ids = [l['id'] for l in lines]
|
||||
below_writeoff, sum_debit, sum_credit = self._below_writeoff_limit(
|
||||
cr, uid, rec, lines, writeoff, context=context)
|
||||
date = self._get_rec_date(
|
||||
cr, uid, rec, lines, rec.date_base_on, context=context)
|
||||
|
||||
rec_ctx = dict(context, date_p=date)
|
||||
if below_writeoff:
|
||||
if sum_credit < sum_debit:
|
||||
writeoff_account_id = rec.account_profit_id.id
|
||||
else:
|
||||
writeoff_account_id = rec.account_lost_id.id
|
||||
|
||||
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=rec.journal_id.id,
|
||||
context=rec_ctx)
|
||||
return True, True
|
||||
elif allow_partial:
|
||||
ml_obj.reconcile_partial(
|
||||
cr, uid,
|
||||
line_ids,
|
||||
type='manual',
|
||||
context=rec_ctx)
|
||||
return True, False
|
||||
|
||||
return False, False
|
||||
|
||||
206
account_easy_reconcile/easy_reconcile.py
Normal file
206
account_easy_reconcile/easy_reconcile.py
Normal file
@@ -0,0 +1,206 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright 2012 Camptocamp SA (Guewen Baconnier)
|
||||
# Copyright (C) 2010 Sébastien Beau
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import time
|
||||
from openerp.osv.orm import Model, AbstractModel
|
||||
from openerp.osv import fields
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
|
||||
class easy_reconcile_options(AbstractModel):
|
||||
"""Options of a reconciliation profile, columns
|
||||
shared by the configuration of methods and by the
|
||||
reconciliation wizards. This allows decoupling
|
||||
of the methods with the wizards and allows to
|
||||
launch the wizards alone
|
||||
"""
|
||||
|
||||
_name = 'easy.reconcile.options'
|
||||
|
||||
def _get_rec_base_date(self, cr, uid, context=None):
|
||||
return [('end_period_last_credit', 'End of period of most recent credit'),
|
||||
('newest', 'Most recent move line'),
|
||||
('actual', 'Today'),
|
||||
('end_period', 'End of period of most recent move line'),
|
||||
('newest_credit', 'Date of most recent credit'),
|
||||
('newest_debit', 'Date of most recent debit')]
|
||||
|
||||
_columns = {
|
||||
'write_off': fields.float('Write off allowed'),
|
||||
'account_lost_id': fields.many2one('account.account', 'Account Lost'),
|
||||
'account_profit_id': fields.many2one('account.account', 'Account Profit'),
|
||||
'journal_id': fields.many2one('account.journal', 'Journal'),
|
||||
'date_base_on': fields.selection(_get_rec_base_date,
|
||||
required=True,
|
||||
string='Date of reconcilation'),
|
||||
'filter': fields.char('Filter', size=128),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'write_off': 0.,
|
||||
'date_base_on': 'end_period_last_credit',
|
||||
}
|
||||
|
||||
|
||||
class account_easy_reconcile_method(Model):
|
||||
|
||||
_name = 'account.easy.reconcile.method'
|
||||
_description = 'reconcile method for account_easy_reconcile'
|
||||
|
||||
_inherit = 'easy.reconcile.options'
|
||||
_auto = True # restore property set to False by AbstractModel
|
||||
|
||||
_order = 'sequence'
|
||||
|
||||
def _get_all_rec_method(self, cr, uid, context=None):
|
||||
return [
|
||||
('easy.reconcile.simple.name', 'Simple. Amount and Name'),
|
||||
('easy.reconcile.simple.partner', 'Simple. Amount and Partner'),
|
||||
('easy.reconcile.simple.reference', 'Simple. Amount and Reference'),
|
||||
]
|
||||
|
||||
def _get_rec_method(self, cr, uid, context=None):
|
||||
return self._get_all_rec_method(cr, uid, context=None)
|
||||
|
||||
_columns = {
|
||||
'name': fields.selection(_get_rec_method, 'Type', size=128, required=True),
|
||||
'sequence': fields.integer('Sequence', required=True,
|
||||
help="The sequence field is used to order the reconcile method"),
|
||||
'task_id': fields.many2one('account.easy.reconcile', 'Task',
|
||||
required=True, ondelete='cascade'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'sequence': 1,
|
||||
}
|
||||
|
||||
def init(self, cr):
|
||||
""" Migration stuff, name is not anymore methods names
|
||||
but models name"""
|
||||
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 account_easy_reconcile(Model):
|
||||
|
||||
_name = 'account.easy.reconcile'
|
||||
_description = 'account easy reconcile'
|
||||
|
||||
def _get_total_unrec(self, cr, uid, ids, name, arg, context=None):
|
||||
obj_move_line = self.pool.get('account.move.line')
|
||||
res = {}
|
||||
for task in self.browse(cr, uid, ids, context=context):
|
||||
res[task.id] = len(obj_move_line.search(
|
||||
cr, uid,
|
||||
[('account_id', '=', task.account.id),
|
||||
('reconcile_id', '=', False),
|
||||
('reconcile_partial_id', '=', False)],
|
||||
context=context))
|
||||
return res
|
||||
|
||||
def _get_partial_rec(self, cr, uid, ids, name, arg, context=None):
|
||||
obj_move_line = self.pool.get('account.move.line')
|
||||
res = {}
|
||||
for task in self.browse(cr, uid, ids, context=context):
|
||||
res[task.id] = len(obj_move_line.search(
|
||||
cr, uid,
|
||||
[('account_id', '=', task.account.id),
|
||||
('reconcile_id', '=', False),
|
||||
('reconcile_partial_id', '!=', False)],
|
||||
context=context))
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True),
|
||||
'account': fields.many2one('account.account', 'Account', required=True),
|
||||
'reconcile_method': fields.one2many('account.easy.reconcile.method', 'task_id', 'Method'),
|
||||
'scheduler': fields.many2one('ir.cron', 'scheduler', readonly=True),
|
||||
'rec_log': fields.text('log', readonly=True),
|
||||
'unreconciled_count': fields.function(_get_total_unrec,
|
||||
type='integer', string='Fully Unreconciled Entries'),
|
||||
'reconciled_partial_count': fields.function(_get_partial_rec,
|
||||
type='integer', string='Partially Reconciled Entries'),
|
||||
}
|
||||
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default = dict(default, rec_log=False, scheduler=False)
|
||||
return super(account_easy_reconcile, self).copy_data(
|
||||
cr, uid, id, default=default, context=context)
|
||||
|
||||
def _prepare_run_transient(self, cr, uid, rec_method, context=None):
|
||||
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,
|
||||
'journal_id': rec_method.journal_id and rec_method.journal_id.id,
|
||||
'date_base_on': rec_method.date_base_on,
|
||||
'filter': rec_method.filter}
|
||||
|
||||
def run_reconcile(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
for rec_id in ids:
|
||||
rec = self.browse(cr, uid, rec_id, context=context)
|
||||
total_rec = 0
|
||||
total_partial_rec = 0
|
||||
details = []
|
||||
count = 0
|
||||
for method in rec.reconcile_method:
|
||||
count += 1
|
||||
|
||||
rec_model = self.pool.get(method.name)
|
||||
auto_rec_id = rec_model.create(
|
||||
cr, uid,
|
||||
self._prepare_run_transient(cr, uid, method, context=context),
|
||||
context=context)
|
||||
|
||||
rec_ids, partial_ids = rec_model.automatic_reconcile(
|
||||
cr, uid, auto_rec_id, context=context)
|
||||
|
||||
details.append(_('method %d : full: %d lines, partial: %d lines') % \
|
||||
(count, len(rec_ids), len(partial_ids)))
|
||||
|
||||
total_rec += len(rec_ids)
|
||||
total_partial_rec += len(partial_ids)
|
||||
|
||||
log = self.read(cr, uid, rec_id, ['rec_log'], context=context)['rec_log']
|
||||
log_lines = log and log.splitlines() or []
|
||||
log_lines[0:0] = [_("%s : %d lines have been fully reconciled" \
|
||||
" and %d lines have been partially reconciled (%s)") % \
|
||||
(time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), total_rec,
|
||||
total_partial_rec, ' | '.join(details))]
|
||||
log = "\n".join(log_lines)
|
||||
self.write(cr, uid, rec_id, {'rec_log': log}, context=context)
|
||||
return True
|
||||
|
||||
124
account_easy_reconcile/easy_reconcile.xml
Normal file
124
account_easy_reconcile/easy_reconcile.xml
Normal file
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- account.easy.reconcile view -->
|
||||
<record id="account_easy_reconcile_form" model="ir.ui.view">
|
||||
<field name="name">account.easy.reconcile.form</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="model">account.easy.reconcile</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Automatic Easy Reconcile">
|
||||
<separator colspan="4" string="Task Information" />
|
||||
<field name="name" select="1"/>
|
||||
<field name="account"/>
|
||||
<field name="unreconciled_count"/>
|
||||
<field name="reconciled_partial_count"/>
|
||||
<field name="scheduler"/>
|
||||
<separator colspan="4" string="Reconcile Method" />
|
||||
<notebook colspan="4">
|
||||
<page name="methods" string="Configuration">
|
||||
<field name="reconcile_method" colspan = "4" nolabel="1"/>
|
||||
</page>
|
||||
<page name="information" string="Information">
|
||||
<separator colspan="4" string="Simple. Amount and Name"/>
|
||||
<label string="Match one debit line vs one credit line. Do not allow partial reconcilation.
|
||||
The lines should have the same amount (with the write-off) and the same name to be reconciled." colspan="4"/>
|
||||
|
||||
<separator colspan="4" string="Simple. Amount and Name"/>
|
||||
<label string="Match one debit line vs one credit line. Do not allow partial reconcilation.
|
||||
The lines should have the same amount (with the write-off) and the same partner to be reconciled." colspan="4"/>
|
||||
|
||||
</page>
|
||||
</notebook>
|
||||
<button icon="gtk-ok" name="run_reconcile" colspan = "4" string="Start Auto Reconcilation" type="object"/>
|
||||
<separator colspan="4" string="Log" />
|
||||
<field name="rec_log" colspan = "4" nolabel="1"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_easy_reconcile_tree" model="ir.ui.view">
|
||||
<field name="name">account.easy.reconcile.tree</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="model">account.easy.reconcile</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Automatic Easy Reconcile">
|
||||
<field name="name"/>
|
||||
<field name="account"/>
|
||||
<field name="scheduler"/>
|
||||
<field name="unreconciled_count"/>
|
||||
<field name="reconciled_partial_count"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_account_easy_reconcile" model="ir.actions.act_window">
|
||||
<field name="name">Easy Automatic Reconcile</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">account.easy.reconcile</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'wizard_object' : 'account.easy.reconcile', 'function' : 'action_rec_auto', 'object_link' : 'account.easy.reconcile' }</field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- account.easy.reconcile.method view -->
|
||||
|
||||
<record id="account_easy_reconcile_method_form" model="ir.ui.view">
|
||||
<field name="name">account.easy.reconcile.method.form</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="model">account.easy.reconcile.method</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Automatic Easy Reconcile Method">
|
||||
<field name="sequence"/>
|
||||
<field name="name"/>
|
||||
<field name="write_off"/>
|
||||
<field name="account_lost_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="date_base_on"/>
|
||||
<field name="filter" groups="base.group_extended"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_easy_reconcile_method_tree" model="ir.ui.view">
|
||||
<field name="name">account.easy.reconcile.method.tree</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="model">account.easy.reconcile.method</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="top" string="Automatic Easy Reconcile Method">
|
||||
<field name="sequence"/>
|
||||
<field name="name"/>
|
||||
<field name="write_off"/>
|
||||
<field name="account_lost_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="date_base_on"/>
|
||||
<field name="filter"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- menu item -->
|
||||
|
||||
<menuitem action="action_account_easy_reconcile" id="menu_easy_reconcile" parent="account.periodical_processing_reconciliation"/>
|
||||
|
||||
|
||||
<!-- button on the left -->
|
||||
|
||||
<record id="ir_action_create_scheduler_in_easy_reconcile" model="ir.values">
|
||||
<field name="key2">client_action_multi</field>
|
||||
<field name="model">account.easy.reconcile</field>
|
||||
<field name="name">Create a Scheduler</field>
|
||||
<field eval="'ir.actions.act_window,%d'%ref('base_scheduler_creator.action_scheduler_creator_wizard')" name="value"/>
|
||||
<field eval="True" name="object"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
118
account_easy_reconcile/i18n/fr.po
Normal file
118
account_easy_reconcile/i18n/fr.po
Normal file
@@ -0,0 +1,118 @@
|
||||
# Translation of OpenERP Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * account_easy_reconcile
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 6.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-11-07 12:59+0000\n"
|
||||
"PO-Revision-Date: 2012-11-07 12:59+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Information"
|
||||
msgstr "Information"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile.method:0
|
||||
msgid "Automatic Easy Reconcile Method"
|
||||
msgstr "Méthode de léttrage automatisé"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Match one debit line vs one credit line. Do not allow partial reconcilation. The lines should have the same amount (with the write-off) and the same partner to be reconciled."
|
||||
msgstr "Lettre un débit avec un crédit ayant le même montant et le même partenaire. Le lettrage ne peut être partiel (écriture d'ajustement en cas d'écart)."
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Log"
|
||||
msgstr "Historique"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Match one debit line vs one credit line. Do not allow partial reconcilation. The lines should have the same amount (with the write-off) and the same name to be reconciled."
|
||||
msgstr "Lettre un débit avec un crédit ayant le même montant et la même description. Le lettrage ne peut être partiel (écriture d'ajustement en cas d'écart)."
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Automatic Easy Reconcile"
|
||||
msgstr "Léttrage automatisé"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile_method
|
||||
msgid "reconcile method for account_easy_reconcile"
|
||||
msgstr "Méthode de léttrage"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Start Auto Reconcilation"
|
||||
msgstr "Lancer le léttrage automatisé"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_name
|
||||
msgid "easy.reconcile.simple.name"
|
||||
msgstr "Léttrage automatisé.simple.Description"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_options
|
||||
msgid "easy.reconcile.options"
|
||||
msgstr "Léttrage automatisé.options"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Simple. Amount and Name"
|
||||
msgstr "Simple. Montant et description"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple
|
||||
msgid "easy.reconcile.simple"
|
||||
msgstr "Léttrage automatisé.simple"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.actions.act_window,name:account_easy_reconcile.action_account_easy_reconcile
|
||||
#: model:ir.ui.menu,name:account_easy_reconcile.menu_easy_reconcile
|
||||
msgid "Easy Automatic Reconcile"
|
||||
msgstr "Léttrage automatisé"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_reference
|
||||
msgid "easy.reconcile.simple.reference"
|
||||
msgstr "Léttrage automatisé.simple.réference"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Reconcile Method"
|
||||
msgstr "Méthode de léttrage"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_base
|
||||
msgid "easy.reconcile.base"
|
||||
msgstr "Léttrage automatisé.base"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Configuration"
|
||||
msgstr "Configuration"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_partner
|
||||
msgid "easy.reconcile.simple.partner"
|
||||
msgstr "Léttrage automatisé.simple.partenaire"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: view:account.easy.reconcile:0
|
||||
msgid "Task Information"
|
||||
msgstr "Information sur la tâche"
|
||||
|
||||
#. module: account_easy_reconcile
|
||||
#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile
|
||||
msgid "account easy reconcile"
|
||||
msgstr "Léttrage automatisé"
|
||||
|
||||
13
account_easy_reconcile/security/ir.model.access.csv
Normal file
13
account_easy_reconcile/security/ir.model.access.csv
Normal file
@@ -0,0 +1,13 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_easy_reconcile_options_acc_user,easy.reconcile.options,model_easy_reconcile_options,account.group_account_user,1,0,0,0
|
||||
access_account_easy_reconcile_method_acc_user,account.easy.reconcile.method,model_account_easy_reconcile_method,account.group_account_user,1,0,0,0
|
||||
access_account_easy_reconcile_acc_user,account.easy.reconcile,model_account_easy_reconcile,account.group_account_user,1,1,0,0
|
||||
access_easy_reconcile_simple_name_acc_user,easy.reconcile.simple.name,model_easy_reconcile_simple_name,account.group_account_user,1,0,0,0
|
||||
access_easy_reconcile_simple_partner_acc_user,easy.reconcile.simple.partner,model_easy_reconcile_simple_partner,account.group_account_user,1,0,0,0
|
||||
access_easy_reconcile_simple_reference_acc_user,easy.reconcile.simple.reference,model_easy_reconcile_simple_reference,account.group_account_user,1,0,0,0
|
||||
access_easy_reconcile_options_acc_mgr,easy.reconcile.options,model_easy_reconcile_options,account.group_account_user,1,0,0,0
|
||||
access_account_easy_reconcile_method_acc_mgr,account.easy.reconcile.method,model_account_easy_reconcile_method,account.group_account_user,1,1,1,1
|
||||
access_account_easy_reconcile_acc_mgr,account.easy.reconcile,model_account_easy_reconcile,account.group_account_user,1,1,1,1
|
||||
access_easy_reconcile_simple_name_acc_mgr,easy.reconcile.simple.name,model_easy_reconcile_simple_name,account.group_account_user,1,0,0,0
|
||||
access_easy_reconcile_simple_partner_acc_mgr,easy.reconcile.simple.partner,model_easy_reconcile_simple_partner,account.group_account_user,1,0,0,0
|
||||
access_easy_reconcile_simple_reference_acc_mgr,easy.reconcile.simple.reference,model_easy_reconcile_simple_reference,account.group_account_user,1,0,0,0
|
||||
|
122
account_easy_reconcile/simple_reconciliation.py
Normal file
122
account_easy_reconcile/simple_reconciliation.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright 2012 Camptocamp SA (Guewen Baconnier)
|
||||
# Copyright (C) 2010 Sébastien Beau
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv.orm import AbstractModel, TransientModel
|
||||
|
||||
|
||||
class easy_reconcile_simple(AbstractModel):
|
||||
|
||||
_name = 'easy.reconcile.simple'
|
||||
_inherit = 'easy.reconcile.base'
|
||||
|
||||
# has to be subclassed
|
||||
# field name used as key for matching the move lines
|
||||
_key_field = None
|
||||
|
||||
def rec_auto_lines_simple(self, cr, uid, rec, lines, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
if self._key_field is None:
|
||||
raise ValueError("_key_field has to be defined")
|
||||
|
||||
count = 0
|
||||
res = []
|
||||
while (count < len(lines)):
|
||||
for i in range(count+1, len(lines)):
|
||||
writeoff_account_id = False
|
||||
if lines[count][self._key_field] != lines[i][self._key_field]:
|
||||
break
|
||||
|
||||
check = False
|
||||
if lines[count]['credit'] > 0 and lines[i]['debit'] > 0:
|
||||
credit_line = lines[count]
|
||||
debit_line = lines[i]
|
||||
check = True
|
||||
elif lines[i]['credit'] > 0 and lines[count]['debit'] > 0:
|
||||
credit_line = lines[i]
|
||||
debit_line = lines[count]
|
||||
check = True
|
||||
if not check:
|
||||
continue
|
||||
|
||||
reconciled, dummy = self._reconcile_lines(
|
||||
cr, uid, rec, [credit_line, debit_line],
|
||||
allow_partial=False, context=context)
|
||||
if reconciled:
|
||||
res += [credit_line['id'], debit_line['id']]
|
||||
del lines[i]
|
||||
break
|
||||
count += 1
|
||||
return res, [] # empty list for partial, only full rec in "simple" rec
|
||||
|
||||
def _simple_order(self, rec, *args, **kwargs):
|
||||
return "ORDER BY account_move_line.%s" % self._key_field
|
||||
|
||||
def _action_rec(self, cr, uid, rec, context=None):
|
||||
"""Match only 2 move lines, do not allow partial reconcile"""
|
||||
select = self._select(rec)
|
||||
select += ", account_move_line.%s " % self._key_field
|
||||
where, params = self._where(rec)
|
||||
where += " AND account_move_line.%s IS NOT NULL " % self._key_field
|
||||
|
||||
where2, params2 = self._get_filter(cr, uid, rec, context=context)
|
||||
query = ' '.join((
|
||||
select,
|
||||
self._from(rec),
|
||||
where, where2,
|
||||
self._simple_order(rec)))
|
||||
|
||||
cr.execute(query, params + params2)
|
||||
lines = cr.dictfetchall()
|
||||
return self.rec_auto_lines_simple(cr, uid, rec, lines, context)
|
||||
|
||||
|
||||
class easy_reconcile_simple_name(TransientModel):
|
||||
|
||||
_name = 'easy.reconcile.simple.name'
|
||||
_inherit = 'easy.reconcile.simple'
|
||||
_auto = True # False when inherited from AbstractModel
|
||||
|
||||
# has to be subclassed
|
||||
# field name used as key for matching the move lines
|
||||
_key_field = 'name'
|
||||
|
||||
|
||||
class easy_reconcile_simple_partner(TransientModel):
|
||||
|
||||
_name = 'easy.reconcile.simple.partner'
|
||||
_inherit = 'easy.reconcile.simple'
|
||||
_auto = True # False when inherited from AbstractModel
|
||||
|
||||
# has to be subclassed
|
||||
# field name used as key for matching the move lines
|
||||
_key_field = 'partner_id'
|
||||
|
||||
class easy_reconcile_simple_reference(TransientModel):
|
||||
|
||||
_name = 'easy.reconcile.simple.reference'
|
||||
_inherit = 'easy.reconcile.simple'
|
||||
_auto = True # False when inherited from AbstractModel
|
||||
|
||||
# has to be subclassed
|
||||
# field name used as key for matching the move lines
|
||||
_key_field = 'ref'
|
||||
Reference in New Issue
Block a user