diff --git a/account_advanced_reconcile/__init__.py b/account_advanced_reconcile/__init__.py deleted file mode 100644 index 1c643cae..00000000 --- a/account_advanced_reconcile/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# 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 . -# -############################################################################## - -import easy_reconcile -import base_advanced_reconciliation -import advanced_reconciliation diff --git a/account_advanced_reconcile/__openerp__.py b/account_advanced_reconcile/__openerp__.py deleted file mode 100644 index 5ca17767..00000000 --- a/account_advanced_reconcile/__openerp__.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# 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 . -# -############################################################################## - -{'name': "Advanced Reconcile", - 'version': '1.0', - 'author': 'Camptocamp', - 'maintainer': 'Camptocamp', - 'category': 'Finance', - 'complexity': 'normal', - 'depends': ['account_easy_reconcile'], - 'description': """ -Advanced reconciliation methods for the module account_easy_reconcile. - -account_easy_reconcile, which is a dependency, is available in the branch: -lp:~openerp-community-committers/+junk/account-extra-addons -This branch is temporary and will soon be merged with the Akretion master -branch, but the master branch does not already exist. Sorry for the -inconvenience. - -In addition to the features implemented in account_easy_reconcile, which are: - - 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 - -It implements a basis to created advanced reconciliation methods in a few lines -of code. - -Typically, such a method can be: - - Reconcile entries if the partner and the ref are equal - - Reconcile entries if the partner is equal and the ref is the same than ref - or name - - Reconcile entries if the partner is equal and the ref match with a pattern - -And they allows: - - Reconciliations with multiple credit / multiple debit lines - - Partial reconciliations - - Write-off amount as well - -A method is already implemented in this module, it matches on entries: - * Partner - * Ref on credit move lines should be case insensitive equals to the ref or - the name of the debit move line - -The base class to find the reconciliations is built to be as efficient as -possible. - - -So basically, if you have an invoice with 3 payments (one per month), the first -month, it will partial reconcile the debit move line with the first payment, the second -month, it will partial reconcile the debit move line with 2 first payments, -the third month, it will make the full reconciliation. - -This module is perfectly adapted for E-Commerce business where a big volume of -move lines and so, reconciliations, are involved and payments often come from -many offices. - - """, - 'website': 'http://www.camptocamp.com', - 'init_xml': [], - 'update_xml': ['easy_reconcile_view.xml'], - 'demo_xml': [], - 'test': [], - 'images': [], - 'installable': True, - 'auto_install': False, - 'license': 'AGPL-3', - 'application': True, -} diff --git a/account_advanced_reconcile/advanced_reconciliation.py b/account_advanced_reconcile/advanced_reconciliation.py deleted file mode 100644 index dfdb8883..00000000 --- a/account_advanced_reconcile/advanced_reconciliation.py +++ /dev/null @@ -1,120 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# 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 . -# -############################################################################## - -from openerp.osv.orm import TransientModel - - -class easy_reconcile_advanced_ref(TransientModel): - - _name = 'easy.reconcile.advanced.ref' - _inherit = 'easy.reconcile.advanced' - _auto = True # False when inherited from AbstractModel - - def _skip_line(self, cr, uid, rec, move_line, context=None): - """ - When True is returned on some conditions, the credit move line - will be skipped for reconciliation. Can be inherited to - skip on some conditions. ie: ref or partner_id is empty. - """ - return not (move_line.get('ref') and move_line.get('partner_id')) - - def _matchers(self, cr, uid, rec, move_line, context=None): - """ - Return the values used as matchers to found the opposite lines - - All the matcher keys in the dict must have their equivalent in - the `_opposite_matchers`. - - The values of each matcher key will be searched in the - one returned by the `_opposite_matchers` - - Must be inherited to implement the matchers for one method - - As instance, it can returns: - return ('ref', move_line['rec']) - - or - return (('partner_id', move_line['partner_id']), - ('ref', "prefix_%s" % move_line['rec'])) - - All the matchers have to be found in the opposite lines - to consider them as "opposite" - - The matchers will be evaluated in the same order than declared - vs the the opposite matchers, so you can gain performance by - declaring first the partners with the less computation. - - All matchers should match with their opposite to be considered - as "matching". - So with the previous example, partner_id and ref have to be - equals on the opposite line matchers. - - :return: tuple of tuples (key, value) where the keys are - the matchers keys - (must be the same than `_opposite_matchers` returns, - and their values to match in the opposite lines. - A matching key can have multiples values. - """ - return (('partner_id', move_line['partner_id']), - ('ref', move_line['ref'].lower().strip())) - - def _opposite_matchers(self, cr, uid, rec, move_line, context=None): - """ - Return the values of the opposite line used as matchers - so the line is matched - - Must be inherited to implement the matchers for one method - It can be inherited to apply some formatting of fields - (strip(), lower() and so on) - - This method is the counterpart of the `_matchers()` method. - - Each matcher have to yield its value respecting the orders - of the `_matchers()`. - - When a matcher does not correspond, the next matchers won't - be evaluated so the ones which need the less computation - have to be executed first. - - If the `_matchers()` returns: - (('partner_id', move_line['partner_id']), - ('ref', move_line['ref'])) - - Here, you should yield : - yield ('partner_id', move_line['partner_id']) - yield ('ref', move_line['ref']) - - Note that a matcher can contain multiple values, as instance, - if for a move line, you want to search from its `ref` in the - `ref` or `name` fields of the opposite move lines, you have to - yield ('partner_id', move_line['partner_id']) - yield ('ref', (move_line['ref'], move_line['name']) - - An OR is used between the values for the same key. - An AND is used between the differents keys. - - :param dict move_line: values of the move_line - :yield: matchers as tuple ('matcher key', value(s)) - """ - yield ('partner_id', move_line['partner_id']) - yield ('ref', (move_line['ref'].lower().strip(), - move_line['name'].lower().strip())) - diff --git a/account_advanced_reconcile/base_advanced_reconciliation.py b/account_advanced_reconcile/base_advanced_reconciliation.py deleted file mode 100644 index df26708c..00000000 --- a/account_advanced_reconcile/base_advanced_reconciliation.py +++ /dev/null @@ -1,274 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# 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 . -# -############################################################################## - -from itertools import groupby, product -from operator import itemgetter -from openerp.osv.orm import Model, AbstractModel, TransientModel -from openerp.osv import fields - - -class easy_reconcile_advanced(AbstractModel): - - _name = 'easy.reconcile.advanced' - _inherit = 'easy.reconcile.base' - - def _query_debit(self, cr, uid, rec, context=None): - """Select all move (debit>0) as candidate. Optional choice on invoice - will filter with an inner join on the related moves. - """ - select = self._select(rec) - sql_from = self._from(rec) - where, params = self._where(rec) - where += " AND account_move_line.debit > 0 " - - where2, params2 = self._get_filter(cr, uid, rec, context=context) - - query = ' '.join((select, sql_from, where, where2)) - - cr.execute(query, params + params2) - return cr.dictfetchall() - - def _query_credit(self, cr, uid, rec, context=None): - """Select all move (credit>0) as candidate. Optional choice on invoice - will filter with an inner join on the related moves. - """ - select = self._select(rec) - sql_from = self._from(rec) - where, params = self._where(rec) - where += " AND account_move_line.credit > 0 " - - where2, params2 = self._get_filter(cr, uid, rec, context=context) - - query = ' '.join((select, sql_from, where, where2)) - - cr.execute(query, params + params2) - return cr.dictfetchall() - - def _matchers(self, cr, uid, rec, move_line, context=None): - """ - Return the values used as matchers to found the opposite lines - - All the matcher keys in the dict must have their equivalent in - the `_opposite_matchers`. - - The values of each matcher key will be searched in the - one returned by the `_opposite_matchers` - - Must be inherited to implement the matchers for one method - - As instance, it can returns: - return ('ref', move_line['rec']) - - or - return (('partner_id', move_line['partner_id']), - ('ref', "prefix_%s" % move_line['rec'])) - - All the matchers have to be found in the opposite lines - to consider them as "opposite" - - The matchers will be evaluated in the same order than declared - vs the the opposite matchers, so you can gain performance by - declaring first the partners with the less computation. - - All matchers should match with their opposite to be considered - as "matching". - So with the previous example, partner_id and ref have to be - equals on the opposite line matchers. - - :return: tuple of tuples (key, value) where the keys are - the matchers keys - (must be the same than `_opposite_matchers` returns, - and their values to match in the opposite lines. - A matching key can have multiples values. - """ - raise NotImplementedError - - def _opposite_matchers(self, cr, uid, rec, move_line, context=None): - """ - Return the values of the opposite line used as matchers - so the line is matched - - Must be inherited to implement the matchers for one method - It can be inherited to apply some formatting of fields - (strip(), lower() and so on) - - This method is the counterpart of the `_matchers()` method. - - Each matcher have to yield its value respecting the orders - of the `_matchers()`. - - When a matcher does not correspond, the next matchers won't - be evaluated so the ones which need the less computation - have to be executed first. - - If the `_matchers()` returns: - (('partner_id', move_line['partner_id']), - ('ref', move_line['ref'])) - - Here, you should yield : - yield ('partner_id', move_line['partner_id']) - yield ('ref', move_line['ref']) - - Note that a matcher can contain multiple values, as instance, - if for a move line, you want to search from its `ref` in the - `ref` or `name` fields of the opposite move lines, you have to - yield ('partner_id', move_line['partner_id']) - yield ('ref', (move_line['ref'], move_line['name']) - - An OR is used between the values for the same key. - An AND is used between the differents keys. - - :param dict move_line: values of the move_line - :yield: matchers as tuple ('matcher key', value(s)) - """ - raise NotImplementedError - - @staticmethod - def _compare_values(key, value, opposite_value): - """Can be inherited to modify the equality condition - specifically according to the matcher key (maybe using - a like operator instead of equality on 'ref' as instance) - """ - # consider that empty vals are not valid matchers - # it can still be inherited for some special cases - # where it would be allowed - if not (value and opposite_value): - return False - - if value == opposite_value: - return True - return False - - @staticmethod - def _compare_matcher_values(key, values, opposite_values): - """ Compare every values from a matcher vs an opposite matcher - and return True if it matches - """ - for value, ovalue in product(values, opposite_values): - # we do not need to compare all values, if one matches - # we are done - if easy_reconcile_advanced._compare_values(key, value, ovalue): - return True - return False - - @staticmethod - def _compare_matchers(matcher, opposite_matcher): - """ - Prepare and check the matchers to compare - """ - mkey, mvalue = matcher - omkey, omvalue = opposite_matcher - assert mkey == omkey, "A matcher %s is compared with a matcher %s, " \ - " the _matchers and _opposite_matchers are probably wrong" % \ - (mkey, omkey) - if not isinstance(mvalue, (list, tuple)): - mvalue = mvalue, - if not isinstance(omvalue, (list, tuple)): - omvalue = omvalue, - return easy_reconcile_advanced._compare_matcher_values(mkey, mvalue, omvalue) - - def _compare_opposite(self, cr, uid, rec, move_line, opposite_move_line, - matchers, context=None): - opp_matchers = self._opposite_matchers(cr, uid, rec, opposite_move_line, - context=context) - for matcher in matchers: - try: - opp_matcher = opp_matchers.next() - except StopIteration: - # if you fall here, you probably missed to put a `yield` - # in `_opposite_matchers()` - raise ValueError("Missing _opposite_matcher: %s" % matcher[0]) - - if not self._compare_matchers(matcher, opp_matcher): - # if any of the matcher fails, the opposite line - # is not a valid counterpart - # directly returns so the next yield of _opposite_matchers - # are not evaluated - return False - - return True - - def _search_opposites(self, cr, uid, rec, move_line, opposite_move_lines, context=None): - """ - Search the opposite move lines for a move line - - :param dict move_line: the move line for which we search opposites - :param list opposite_move_lines: list of dict of move lines values, the move - lines we want to search for - :return: list of matching lines - """ - matchers = self._matchers(cr, uid, rec, move_line, context=context) - return [op for op in opposite_move_lines if \ - self._compare_opposite(cr, uid, rec, move_line, op, matchers, context=context)] - - def _action_rec(self, cr, uid, rec, context=None): - credit_lines = self._query_credit(cr, uid, rec, context=context) - debit_lines = self._query_debit(cr, uid, rec, context=context) - return self._rec_auto_lines_advanced( - cr, uid, rec, credit_lines, debit_lines, context=context) - - def _skip_line(self, cr, uid, rec, move_line, context=None): - """ - When True is returned on some conditions, the credit move line - will be skipped for reconciliation. Can be inherited to - skip on some conditions. ie: ref or partner_id is empty. - """ - return False - - def _rec_auto_lines_advanced(self, cr, uid, rec, credit_lines, debit_lines, context=None): - if context is None: - context = {} - - reconciled_ids = [] - partial_reconciled_ids = [] - reconcile_groups = [] - - for credit_line in credit_lines: - if self._skip_line(cr, uid, rec, credit_line, context=context): - continue - - opposite_lines = self._search_opposites( - cr, uid, rec, credit_line, debit_lines, context=context) - - if not opposite_lines: - continue - - opposite_ids = [l['id'] for l in opposite_lines] - line_ids = opposite_ids + [credit_line['id']] - for group in reconcile_groups: - if any([lid in group for lid in opposite_ids]): - group.update(line_ids) - break - else: - reconcile_groups.append(set(line_ids)) - - lines_by_id = dict([(l['id'], l) for l in credit_lines + debit_lines]) - for reconcile_group_ids in reconcile_groups: - group_lines = [lines_by_id[lid] for lid in reconcile_group_ids] - reconciled, full = self._reconcile_lines( - cr, uid, rec, group_lines, allow_partial=True, context=context) - if reconciled and full: - reconciled_ids += reconcile_group_ids - elif reconciled: - partial_reconciled_ids += reconcile_group_ids - - return reconciled_ids, partial_reconciled_ids - diff --git a/account_advanced_reconcile/easy_reconcile.py b/account_advanced_reconcile/easy_reconcile.py deleted file mode 100644 index 747a2e3c..00000000 --- a/account_advanced_reconcile/easy_reconcile.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# 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 . -# -############################################################################## - -from openerp.osv.orm import Model - - -class account_easy_reconcile_method(Model): - - _inherit = 'account.easy.reconcile.method' - - def _get_all_rec_method(self, cr, uid, context=None): - methods = super(account_easy_reconcile_method, self).\ - _get_all_rec_method(cr, uid, context=context) - methods += [ - ('easy.reconcile.advanced.ref', - 'Advanced. Partner and Ref.'), - ] - return methods - diff --git a/account_advanced_reconcile/easy_reconcile_view.xml b/account_advanced_reconcile/easy_reconcile_view.xml deleted file mode 100644 index 961add68..00000000 --- a/account_advanced_reconcile/easy_reconcile_view.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - account.easy.reconcile.form - account.easy.reconcile - form - - - - - - - - - - -