banking-addons-team team mailing list archive
-
banking-addons-team team
-
Mailing list archive
-
Message #00185
lp:~c2c/banking-addons/bank-statement-reconcile_add_account-easy-reconcile into lp:banking-addons/bank-statement-reconcile-61
Alexandre Fayolle @ camptocamp has proposed merging lp:~c2c/banking-addons/bank-statement-reconcile_add_account-easy-reconcile into lp:banking-addons/bank-statement-reconcile-61.
Requested reviews:
Sébastien BEAU - http://www.akretion.com (sebastien.beau)
Banking Addons Team (banking-addons-team)
For more details, see:
https://code.launchpad.net/~c2c/banking-addons/bank-statement-reconcile_add_account-easy-reconcile/+merge/136663
added account_easy_reconcile from account_extra_addons
--
https://code.launchpad.net/~c2c/banking-addons/bank-statement-reconcile_add_account-easy-reconcile/+merge/136663
Your team Banking Addons Team is requested to review the proposed merge of lp:~c2c/banking-addons/bank-statement-reconcile_add_account-easy-reconcile into lp:banking-addons/bank-statement-reconcile-61.
=== modified file 'account_advanced_reconcile/__openerp__.py'
--- account_advanced_reconcile/__openerp__.py 2012-09-20 12:18:14 +0000
+++ account_advanced_reconcile/__openerp__.py 2012-11-28 13:35:41 +0000
@@ -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.
=== added directory 'account_easy_reconcile'
=== added file 'account_easy_reconcile/__init__.py'
--- account_easy_reconcile/__init__.py 1970-01-01 00:00:00 +0000
+++ account_easy_reconcile/__init__.py 2012-11-28 13:35:41 +0000
@@ -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
=== added file 'account_easy_reconcile/__openerp__.py'
--- account_easy_reconcile/__openerp__.py 1970-01-01 00:00:00 +0000
+++ account_easy_reconcile/__openerp__.py 2012-11-28 13:35:41 +0000
@@ -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.0",
+ "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,
+
+}
=== added file 'account_easy_reconcile/base_reconciliation.py'
--- account_easy_reconcile/base_reconciliation.py 1970-01-01 00:00:00 +0000
+++ account_easy_reconcile/base_reconciliation.py 2012-11-28 13:35:41 +0000
@@ -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
+
=== added file 'account_easy_reconcile/easy_reconcile.py'
--- account_easy_reconcile/easy_reconcile.py 1970-01-01 00:00:00 +0000
+++ account_easy_reconcile/easy_reconcile.py 2012-11-28 13:35:41 +0000
@@ -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
+
=== added file 'account_easy_reconcile/easy_reconcile.xml'
--- account_easy_reconcile/easy_reconcile.xml 1970-01-01 00:00:00 +0000
+++ account_easy_reconcile/easy_reconcile.xml 2012-11-28 13:35:41 +0000
@@ -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>
=== added directory 'account_easy_reconcile/i18n'
=== added file 'account_easy_reconcile/i18n/fr.po'
--- account_easy_reconcile/i18n/fr.po 1970-01-01 00:00:00 +0000
+++ account_easy_reconcile/i18n/fr.po 2012-11-28 13:35:41 +0000
@@ -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é"
+
=== added file 'account_easy_reconcile/simple_reconciliation.py'
--- account_easy_reconcile/simple_reconciliation.py 1970-01-01 00:00:00 +0000
+++ account_easy_reconcile/simple_reconciliation.py 2012-11-28 13:35:41 +0000
@@ -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'
Follow ups