From 374df2cebd3022debde321849d4c5367d6ea75c1 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 3 Jan 2013 16:05:46 +0100 Subject: [PATCH 01/15] [IMP] account_easy_reconcile: formatting --- account_easy_reconcile/__openerp__.py | 18 ++-- account_easy_reconcile/base_reconciliation.py | 43 ++++----- account_easy_reconcile/easy_reconcile.py | 94 +++++++++++-------- .../simple_reconciliation.py | 3 +- 4 files changed, 88 insertions(+), 70 deletions(-) diff --git a/account_easy_reconcile/__openerp__.py b/account_easy_reconcile/__openerp__.py index 6131accf..24707d70 100755 --- a/account_easy_reconcile/__openerp__.py +++ b/account_easy_reconcile/__openerp__.py @@ -20,11 +20,11 @@ ############################################################################## { - "name" : "Easy Reconcile", - "version" : "1.1", - "depends" : ["account", + "name": "Easy Reconcile", + "version": "1.1", + "depends": ["account", ], - "author" : "Akretion,Camptocamp", + "author": "Akretion,Camptocamp", "description": """ Easy Reconcile ============== @@ -53,11 +53,11 @@ This latter add more complex reconciliations, allows multiple lines and partial. """, - "website" : "http://www.akretion.com/", - "category" : "Finance", - "init_xml" : [], - "demo_xml" : [], - "update_xml" : [ + "website": "http://www.akretion.com/", + "category": "Finance", + "init_xml": [], + "demo_xml": [], + "update_xml": [ "easy_reconcile.xml", "easy_reconcile_history_view.xml", ], diff --git a/account_easy_reconcile/base_reconciliation.py b/account_easy_reconcile/base_reconciliation.py index b1134c55..6707ff3a 100644 --- a/account_easy_reconcile/base_reconciliation.py +++ b/account_easy_reconcile/base_reconciliation.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2012 Camptocamp SA (Guewen Baconnier) +# Copyright 2012-2013 Camptocamp SA (Guewen Baconnier) # Copyright (C) 2010 Sébastien Beau # # This program is free software: you can redistribute it and/or modify @@ -19,28 +19,28 @@ # ############################################################################## -from openerp.osv.orm import AbstractModel -from openerp.osv import fields, osv +from openerp.osv import fields, orm from operator import itemgetter, attrgetter -class easy_reconcile_base(AbstractModel): +class easy_reconcile_base(orm.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"), + '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): - """ + """ Reconciliation method called from the view. + :return: list of reconciled ids, list of partially reconciled entries """ if isinstance(ids, (int, long)): @@ -50,14 +50,15 @@ class easy_reconcile_base(AbstractModel): 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 + """ 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 + """ Mandatory columns for move lines queries + An extra column aliased as ``key`` should be defined in each query.""" aml_cols = ( 'id', @@ -104,7 +105,7 @@ class easy_reconcile_base(AbstractModel): return where, params def _below_writeoff_limit(self, cr, uid, rec, lines, - writeoff_limit, context=None): + writeoff_limit, context=None): precision = self.pool.get('decimal.precision').precision_get( cr, uid, 'Account') keys = ('debit', 'credit') @@ -119,7 +120,8 @@ class easy_reconcile_base(AbstractModel): 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): + 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): @@ -155,12 +157,14 @@ class easy_reconcile_base(AbstractModel): """ Try to reconcile given lines :param list lines: list of dict of move lines, they must at least - contain values for : id, debit, credit + contain values for : id, debit, credit :param boolean allow_partial: if True, partial reconciliation will be - created, otherwise only Full reconciliation will be created + 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) + have been reconciled or not, + the second is wether the reconciliation is full (True) + or partial (False) """ if context is None: context = {} @@ -168,8 +172,6 @@ class easy_reconcile_base(AbstractModel): 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) @@ -204,4 +206,3 @@ class easy_reconcile_base(AbstractModel): return True, False return False, False - diff --git a/account_easy_reconcile/easy_reconcile.py b/account_easy_reconcile/easy_reconcile.py index e45e4876..3a295f4b 100644 --- a/account_easy_reconcile/easy_reconcile.py +++ b/account_easy_reconcile/easy_reconcile.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2012 Camptocamp SA (Guewen Baconnier) +# Copyright 2012-2013 Camptocamp SA (Guewen Baconnier) # Copyright (C) 2010 Sébastien Beau # # This program is free software: you can redistribute it and/or modify @@ -19,19 +19,18 @@ # ############################################################################## -import time -from openerp.osv.orm import Model, AbstractModel -from openerp.osv import fields, osv +from openerp.osv import fields, osv, orm 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 +class easy_reconcile_options(orm.AbstractModel): + """Options of a reconciliation profile + + Columns shared by the configuration of methods + and by the reconciliation wizards. + This allows decoupling of the methods and the + wizards and allows to launch the wizards alone """ _name = 'easy.reconcile.options' @@ -46,10 +45,14 @@ class easy_reconcile_options(AbstractModel): _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, + '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), @@ -61,13 +64,12 @@ class easy_reconcile_options(AbstractModel): } -class account_easy_reconcile_method(Model): +class account_easy_reconcile_method(orm.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' @@ -82,11 +84,18 @@ class account_easy_reconcile_method(Model): 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'), + 'name': fields.selection( + _get_rec_method, 'Type', 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', + string='Task', + required=True, + ondelete='cascade'), } _defaults = { @@ -94,8 +103,11 @@ class account_easy_reconcile_method(Model): } def init(self, cr): - """ Migration stuff, name is not anymore methods names - but models name""" + """ Migration stuff + + Name is not anymore methods names but the name + of the model which does the reconciliation + """ cr.execute(""" UPDATE account_easy_reconcile_method SET name = 'easy.reconcile.simple.partner' @@ -108,7 +120,7 @@ class account_easy_reconcile_method(Model): """) -class account_easy_reconcile(Model): +class account_easy_reconcile(orm.Model): _name = 'account.easy.reconcile' _description = 'account easy reconcile' @@ -145,13 +157,17 @@ class account_easy_reconcile(Model): return result _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'), - 'unreconciled_count': fields.function(_get_total_unrec, - type='integer', string='Unreconciled Entries'), - 'reconciled_partial_count': fields.function(_get_partial_rec, - type='integer', string='Partially Reconciled Entries'), + 'name': fields.char('Name', required=True), + 'account': fields.many2one( + 'account.account', 'Account', required=True), + 'reconcile_method': fields.one2many( + 'account.easy.reconcile.method', 'task_id', 'Method'), + 'unreconciled_count': fields.function( + _get_total_unrec, type='integer', string='Unreconciled Entries'), + 'reconciled_partial_count': fields.function( + _get_partial_rec, + type='integer', + string='Partially Reconciled Entries'), 'history_ids': fields.one2many( 'easy.reconcile.history', 'easy_reconcile_id', @@ -168,11 +184,12 @@ class account_easy_reconcile(Model): 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, + '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} @@ -188,8 +205,6 @@ class account_easy_reconcile(Model): res = cr.fetchall() return [row[0] for row in res] - if context is None: - context = {} for rec in self.browse(cr, uid, ids, context=context): all_ml_rec_ids = [] all_ml_partial_ids = [] @@ -198,7 +213,8 @@ class account_easy_reconcile(Model): rec_model = self.pool.get(method.name) auto_rec_id = rec_model.create( cr, uid, - self._prepare_run_transient(cr, uid, method, context=context), + self._prepare_run_transient( + cr, uid, method, context=context), context=context) ml_rec_ids, ml_partial_ids = rec_model.automatic_reconcile( diff --git a/account_easy_reconcile/simple_reconciliation.py b/account_easy_reconcile/simple_reconciliation.py index 37786551..16fcb983 100644 --- a/account_easy_reconcile/simple_reconciliation.py +++ b/account_easy_reconcile/simple_reconciliation.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2012 Camptocamp SA (Guewen Baconnier) +# Copyright 2012-2013 Camptocamp SA (Guewen Baconnier) # Copyright (C) 2010 Sébastien Beau # # This program is free software: you can redistribute it and/or modify @@ -111,6 +111,7 @@ class easy_reconcile_simple_partner(TransientModel): # 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' From 9943a685aed7f6149df9d73dcbb03ce3e3829e43 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 3 Jan 2013 16:06:19 +0100 Subject: [PATCH 02/15] [IMP] replace range by xrange to avoid to build the full list when an anticipated break may stop the iteration --- account_easy_reconcile/simple_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_easy_reconcile/simple_reconciliation.py b/account_easy_reconcile/simple_reconciliation.py index 16fcb983..14013cd5 100644 --- a/account_easy_reconcile/simple_reconciliation.py +++ b/account_easy_reconcile/simple_reconciliation.py @@ -41,7 +41,7 @@ class easy_reconcile_simple(AbstractModel): count = 0 res = [] while (count < len(lines)): - for i in range(count+1, len(lines)): + for i in xrange(count+1, len(lines)): writeoff_account_id = False if lines[count][self._key_field] != lines[i][self._key_field]: break From e2ef0c21756631ddf9c3090774a3de7b10799a16 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 3 Jan 2013 16:22:44 +0100 Subject: [PATCH 03/15] [FIX] account_easy_reconcile: error when there is no history --- account_easy_reconcile/easy_reconcile.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/account_easy_reconcile/easy_reconcile.py b/account_easy_reconcile/easy_reconcile.py index 3a295f4b..1ed04c90 100644 --- a/account_easy_reconcile/easy_reconcile.py +++ b/account_easy_reconcile/easy_reconcile.py @@ -152,8 +152,10 @@ class account_easy_reconcile(orm.Model): def _last_history(self, cr, uid, ids, name, args, context=None): result = {} for history in self.browse(cr, uid, ids, context=context): - # history is sorted by date desc - result[history.id] = history.history_ids[0].id + result[history.id] = False + if history.history_ids: + # history is sorted by date desc + result[history.id] = history.history_ids[0].id return result _columns = { @@ -238,6 +240,16 @@ class account_easy_reconcile(orm.Model): context=context) return True + def _no_history(self, cr, uid, rec, context=None): + """ Raise an `osv.except_osv` error, supposed to + be called when there is no history on the reconciliation + task. + """ + raise osv.except_osv( + _('Error'), + _('There is no history of reconciled ' + 'entries on the task: %s.') % rec.name) + def last_history_reconcile(self, cr, uid, rec_id, context=None): """ Get the last history record for this reconciliation profile and return the action which opens move lines reconciled @@ -247,6 +259,8 @@ class account_easy_reconcile(orm.Model): "Only 1 id expected" rec_id = rec_id[0] rec = self.browse(cr, uid, rec_id, context=context) + if not rec.last_history: + self._no_history(cr, uid, rec, context=context) return rec.last_history.open_reconcile() def last_history_partial(self, cr, uid, rec_id, context=None): @@ -258,4 +272,6 @@ class account_easy_reconcile(orm.Model): "Only 1 id expected" rec_id = rec_id[0] rec = self.browse(cr, uid, rec_id, context=context) + if not rec.last_history: + self._no_history(cr, uid, rec, context=context) return rec.last_history.open_partial() From ab5e6a037ffc2fb4eeb8e26ce5663b85ce95ea79 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 3 Jan 2013 16:40:00 +0100 Subject: [PATCH 04/15] [FIX] typo reconcilation -> reconciliation --- account_easy_reconcile/easy_reconcile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_easy_reconcile/easy_reconcile.py b/account_easy_reconcile/easy_reconcile.py index 1ed04c90..b9a265e7 100644 --- a/account_easy_reconcile/easy_reconcile.py +++ b/account_easy_reconcile/easy_reconcile.py @@ -54,7 +54,7 @@ class easy_reconcile_options(orm.AbstractModel): 'date_base_on': fields.selection( _get_rec_base_date, required=True, - string='Date of reconcilation'), + string='Date of reconciliation'), 'filter': fields.char('Filter', size=128), } From 714a0f02c026591eb9dd049dabb2d68fb170ab70 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 3 Jan 2013 16:49:29 +0100 Subject: [PATCH 05/15] [IMP] account_easy_reconcile views --- account_easy_reconcile/easy_reconcile.xml | 112 ++++++++++-------- .../easy_reconcile_history_view.xml | 43 +++---- 2 files changed, 86 insertions(+), 69 deletions(-) diff --git a/account_easy_reconcile/easy_reconcile.xml b/account_easy_reconcile/easy_reconcile.xml index 6c93e70d..28388fcc 100644 --- a/account_easy_reconcile/easy_reconcile.xml +++ b/account_easy_reconcile/easy_reconcile.xml @@ -1,4 +1,4 @@ - + @@ -7,52 +7,67 @@ account.easy.reconcile.form 20 account.easy.reconcile - form -
- - - - - - - - - -