diff --git a/__unported__/account_easy_reconcile/easy_reconcile.py b/__unported__/account_easy_reconcile/easy_reconcile.py deleted file mode 100644 index 89af1759..00000000 --- a/__unported__/account_easy_reconcile/easy_reconcile.py +++ /dev/null @@ -1,419 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# 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 -# 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 datetime import datetime -from openerp.osv import fields, orm -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.tools.translate import _ -from openerp import pooler - -import logging -_logger = logging.getLogger(__name__) - - -class EasyReconcileOptions(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' - - 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 reconciliation'), - 'filter': fields.char('Filter', size=128), - 'analytic_account_id': fields.many2one( - 'account.analytic.account', 'Analytic Account', - help="Analytic account for the write-off"), - 'income_exchange_account_id': fields.many2one( - 'account.account', 'Gain Exchange Rate Account'), - 'expense_exchange_account_id': fields.many2one( - 'account.account', 'Loss Exchange Rate Account'), - - } - - _defaults = { - 'write_off': 0., - 'date_base_on': 'end_period_last_credit', - } - - -class AccountEasyReconcileMethod(orm.Model): - _name = 'account.easy.reconcile.method' - _description = 'reconcile method for account_easy_reconcile' - _inherit = 'easy.reconcile.options' - _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', 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'), - 'company_id': fields.related('task_id', 'company_id', - relation='res.company', - type='many2one', - string='Company', - store=True, - readonly=True), - } - - _defaults = { - 'sequence': 1, - } - - def init(self, cr): - """ Migration stuff - - Name is not anymore methods names but the name - of the model which does the reconciliation - """ - cr.execute(""" - UPDATE account_easy_reconcile_method - SET name = 'easy.reconcile.simple.partner' - WHERE name = 'action_rec_auto_partner' - """) - cr.execute(""" - UPDATE account_easy_reconcile_method - SET name = 'easy.reconcile.simple.name' - WHERE name = 'action_rec_auto_name' - """) - - -class AccountEasyReconcile(orm.Model): - - _name = 'account.easy.reconcile' - _inherit = ['mail.thread'] - _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 - - def _last_history(self, cr, uid, ids, name, args, context=None): - result = {} - # do a search() for retrieving the latest history line, - # as a read() will badly split the list of ids with 'date desc' - # and return the wrong result. - history_obj = self.pool['easy.reconcile.history'] - for reconcile_id in ids: - last_history = history_obj.search( - cr, uid, [('easy_reconcile_id', '=', reconcile_id)], - limit=1, order='date desc', context=context - ) - result[reconcile_id] = last_history[0] if last_history else False - return result - - _columns = { - '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 Items'), - 'reconciled_partial_count': fields.function( - _get_partial_rec, - type='integer', - string='Partially Reconciled Items'), - 'history_ids': fields.one2many( - 'easy.reconcile.history', - 'easy_reconcile_id', - string='History', - readonly=True), - 'last_history': - fields.function( - _last_history, - string='Last History', - type='many2one', - relation='easy.reconcile.history', - readonly=True), - 'company_id': fields.many2one('res.company', 'Company'), - } - - 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), - 'analytic_account_id': (rec_method.analytic_account_id and - rec_method.analytic_account_id.id), - 'income_exchange_account_id': - (rec_method.income_exchange_account_id and - rec_method.income_exchange_account_id.id), - 'expense_exchange_account_id': - (rec_method.income_exchange_account_id and - rec_method.income_exchange_account_id.id), - 'journal_id': (rec_method.journal_id and - rec_method.journal_id.id), - 'date_base_on': rec_method.date_base_on, - 'filter': rec_method.filter} - - def run_reconcile(self, cr, uid, ids, context=None): - def find_reconcile_ids(cr, fieldname, move_line_ids): - if not move_line_ids: - return [] - sql = ("SELECT DISTINCT " + fieldname + - " FROM account_move_line " - " WHERE id in %s " - " AND " + fieldname + " IS NOT NULL") - cr.execute(sql, (tuple(move_line_ids),)) - res = cr.fetchall() - return [row[0] for row in res] - - # we use a new cursor to be able to commit the reconciliation - # often. We have to create it here and not later to avoid problems - # where the new cursor sees the lines as reconciles but the old one - # does not. - if context is None: - context = {} - - for rec in self.browse(cr, uid, ids, context=context): - ctx = context.copy() - ctx['commit_every'] = ( - rec.account.company_id.reconciliation_commit_every - ) - if ctx['commit_every']: - new_cr = pooler.get_db(cr.dbname).cursor() - else: - new_cr = cr - try: - all_ml_rec_ids = [] - all_ml_partial_ids = [] - - for method in rec.reconcile_method: - rec_model = self.pool.get(method.name) - auto_rec_id = rec_model.create( - new_cr, uid, - self._prepare_run_transient( - new_cr, uid, method, context=context), - context=context) - - ml_rec_ids, ml_partial_ids = rec_model.automatic_reconcile( - new_cr, uid, auto_rec_id, context=ctx) - - all_ml_rec_ids += ml_rec_ids - all_ml_partial_ids += ml_partial_ids - - reconcile_ids = find_reconcile_ids( - new_cr, 'reconcile_id', all_ml_rec_ids) - partial_ids = find_reconcile_ids( - new_cr, 'reconcile_partial_id', all_ml_partial_ids) - - self.pool.get('easy.reconcile.history').create(new_cr, uid, { - 'easy_reconcile_id': rec.id, - 'date': fields.datetime.now(), - 'reconcile_ids': [(4, rid) for rid in reconcile_ids], - 'reconcile_partial_ids': [(4, rid) for rid in partial_ids], - }, context=context) - except Exception as e: - # In case of error, we log it in the mail thread, log the - # stack trace and create an empty history line; otherwise, - # the cron will just loop on this reconcile task. - _logger.exception("The reconcile task %s had an exception: %s", - rec.name, e.value) - message = "There was an error during reconciliation : %s" \ - % e.value - self.message_post(cr, uid, rec.id, - body=message, context=context) - self.pool.get('easy.reconcile.history').create(new_cr, uid, { - 'easy_reconcile_id': rec.id, - 'date': fields.datetime.now(), - 'reconcile_ids': [], - 'reconcile_partial_ids': [], - }) - finally: - if ctx['commit_every']: - new_cr.commit() - new_cr.close() - return True - - def _no_history(self, cr, uid, rec, context=None): - """ Raise an `orm.except_orm` error, supposed to - be called when there is no history on the reconciliation - task. - """ - raise orm.except_orm( - _('Error'), - _('There is no history of reconciled ' - 'items on the task: %s.') % rec.name) - - def _open_move_line_list(sefl, cr, uid, move_line_ids, name, context=None): - return { - 'name': name, - 'view_mode': 'tree,form', - 'view_id': False, - 'view_type': 'form', - 'res_model': 'account.move.line', - 'type': 'ir.actions.act_window', - 'nodestroy': True, - 'target': 'current', - 'domain': unicode([('id', 'in', move_line_ids)]), - } - - def open_unreconcile(self, cr, uid, ids, context=None): - """ Open the view of move line with the unreconciled move lines""" - assert len(ids) == 1, \ - "You can only open entries from one profile at a time" - obj_move_line = self.pool.get('account.move.line') - for task in self.browse(cr, uid, ids, context=context): - line_ids = obj_move_line.search( - cr, uid, - [('account_id', '=', task.account.id), - ('reconcile_id', '=', False), - ('reconcile_partial_id', '=', False)], - context=context) - - name = _('Unreconciled items') - return self._open_move_line_list(cr, uid, line_ids, name, - context=context) - - def open_partial_reconcile(self, cr, uid, ids, context=None): - """ Open the view of move line with the unreconciled move lines""" - assert len(ids) == 1, \ - "You can only open entries from one profile at a time" - obj_move_line = self.pool.get('account.move.line') - for task in self.browse(cr, uid, ids, context=context): - line_ids = obj_move_line.search( - cr, uid, - [('account_id', '=', task.account.id), - ('reconcile_id', '=', False), - ('reconcile_partial_id', '!=', False)], - context=context) - name = _('Partial reconciled items') - return self._open_move_line_list(cr, uid, line_ids, name, - context=context) - - 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 - """ - if isinstance(rec_id, (tuple, list)): - assert len(rec_id) == 1, \ - "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): - """ Get the last history record for this reconciliation profile - and return the action which opens move lines reconciled - """ - if isinstance(rec_id, (tuple, list)): - assert len(rec_id) == 1, \ - "Only 1 id expected" - rec_id = rec_id[0] - rec = self.browse(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() - - def run_scheduler(self, cr, uid, run_all=None, context=None): - """ Launch the reconcile with the oldest run - This function is mostly here to be used with cron task - - :param run_all: if set it will ingore lookup and launch - all reconciliation - :returns: True in case of success or raises an exception - - """ - def _get_date(reconcile): - if reconcile.last_history.date: - return datetime.strptime(reconcile.last_history.date, - DEFAULT_SERVER_DATETIME_FORMAT) - else: - return datetime.min - - ids = self.search(cr, uid, [], context=context) - assert ids, "No easy reconcile available" - if run_all: - self.run_reconcile(cr, uid, ids, context=context) - return True - reconciles = self.browse(cr, uid, ids, context=context) - reconciles.sort(key=_get_date) - older = reconciles[0] - self.run_reconcile(cr, uid, [older.id], context=context) - return True diff --git a/__unported__/account_easy_reconcile/easy_reconcile_history.py b/__unported__/account_easy_reconcile/easy_reconcile_history.py deleted file mode 100644 index d73c0033..00000000 --- a/__unported__/account_easy_reconcile/easy_reconcile_history.py +++ /dev/null @@ -1,146 +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 import orm, fields -from openerp.tools.translate import _ - - -class EasyReconcileHistory(orm.Model): - """ Store an history of the runs per profile - Each history stores the list of reconciliations done""" - - _name = 'easy.reconcile.history' - _rec_name = 'easy_reconcile_id' - _order = 'date DESC' - - def _reconcile_line_ids(self, cr, uid, ids, name, args, context=None): - result = {} - for history in self.browse(cr, uid, ids, context=context): - result[history.id] = {} - move_line_ids = [] - for reconcile in history.reconcile_ids: - move_line_ids += [line.id - for line - in reconcile.line_id] - result[history.id]['reconcile_line_ids'] = move_line_ids - - move_line_ids = [] - for reconcile in history.reconcile_partial_ids: - move_line_ids += [line.id - for line - in reconcile.line_partial_ids] - result[history.id]['partial_line_ids'] = move_line_ids - return result - - _columns = { - 'easy_reconcile_id': fields.many2one( - 'account.easy.reconcile', 'Reconcile Profile', readonly=True), - 'date': fields.datetime('Run date', readonly=True, required=True), - 'reconcile_ids': fields.many2many( - 'account.move.reconcile', - 'account_move_reconcile_history_rel', - string='Reconciliations', readonly=True), - 'reconcile_partial_ids': fields.many2many( - 'account.move.reconcile', - 'account_move_reconcile_history_partial_rel', - string='Partial Reconciliations', readonly=True), - 'reconcile_line_ids': fields.function( - _reconcile_line_ids, - string='Reconciled Items', - type='many2many', - relation='account.move.line', - readonly=True, - multi='lines'), - 'partial_line_ids': fields.function( - _reconcile_line_ids, - string='Partially Reconciled Items', - type='many2many', - relation='account.move.line', - readonly=True, - multi='lines'), - 'company_id': fields.related( - 'easy_reconcile_id', 'company_id', - relation='res.company', - type='many2one', - string='Company', - store=True, - readonly=True), - - } - - def _open_move_lines(self, cr, uid, history_id, rec_type='full', - context=None): - """ For an history record, open the view of move line with - the reconciled or partially reconciled move lines - - :param history_id: id of the history - :param rec_type: 'full' or 'partial' - :return: action to open the move lines - """ - assert rec_type in ('full', 'partial'), \ - "rec_type must be 'full' or 'partial'" - history = self.browse(cr, uid, history_id, context=context) - if rec_type == 'full': - field = 'reconcile_line_ids' - name = _('Reconciliations') - else: - field = 'partial_line_ids' - name = _('Partial Reconciliations') - move_line_ids = [line.id for line in getattr(history, field)] - return { - 'name': name, - 'view_mode': 'tree,form', - 'view_id': False, - 'view_type': 'form', - 'res_model': 'account.move.line', - 'type': 'ir.actions.act_window', - 'nodestroy': True, - 'target': 'current', - 'domain': unicode([('id', 'in', move_line_ids)]), - } - - def open_reconcile(self, cr, uid, history_ids, context=None): - """ For an history record, open the view of move line - with the reconciled move lines - - :param history_ids: id of the record as int or long - Accept a list with 1 id too to be - used from the client. - """ - if isinstance(history_ids, (tuple, list)): - assert len(history_ids) == 1, "only 1 ID is accepted" - history_ids = history_ids[0] - return self._open_move_lines( - cr, uid, history_ids, rec_type='full', context=None) - - def open_partial(self, cr, uid, history_ids, context=None): - """ For an history record, open the view of move line - with the partially reconciled move lines - - :param history_ids: id of the record as int or long - Accept a list with 1 id too to be - used from the client. - """ - if isinstance(history_ids, (tuple, list)): - assert len(history_ids) == 1, "only 1 ID is accepted" - history_ids = history_ids[0] - return self._open_move_lines( - cr, uid, history_ids, rec_type='partial', context=None) diff --git a/account_easy_reconcile/README.rst b/account_easy_reconcile/README.rst new file mode 100644 index 00000000..00a18535 --- /dev/null +++ b/account_easy_reconcile/README.rst @@ -0,0 +1,76 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +Easy Reconcile +============== + +This is a shared work between Akretion and Camptocamp +in order to provide: + - reconciliation facilities for big volume of transactions + - setup different profiles of reconciliation by account + - each profile can use many methods of reconciliation + - this module is also a base to create others + reconciliation methods which can plug in the profiles + - a profile a reconciliation can be run manually + or by a cron + - monitoring of reconciliation runs with an history + which keep track of the reconciled Journal items + +2 simple reconciliation methods are integrated +in this module, the simple reconciliations works +on 2 lines (1 debit / 1 credit) and do not allow +partial reconcilation, they also match on 1 key, +partner or Journal item name. + +You may be interested to install also the +``account_advanced_reconciliation`` module. +This latter add more complex reconciliations, +allows multiple lines and partial. + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed feedback +`here `_. + + +Credits +======= + +Contributors +------------ +* Sébastien Beau +* Guewen Baconnier +* Vincent Renaville +* Alexandre Fayolle +* Joël Grand-Guillaume +* Nicolas Bessis +* Pedro M.Baeza +* Matthieu Dietrich +* Leonardo Pistone +* Ecino +* Yannick Vaucher +* Rudolf Schnapka +* Florian Dacosta +* Laetitia Gangloff +* Frédéric Clémenti +* Damien Crier + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. + diff --git a/__unported__/account_easy_reconcile/__init__.py b/account_easy_reconcile/__init__.py similarity index 93% rename from __unported__/account_easy_reconcile/__init__.py rename to account_easy_reconcile/__init__.py index 0b0a2b53..3ac78713 100755 --- a/__unported__/account_easy_reconcile/__init__.py +++ b/account_easy_reconcile/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2012 Camptocamp SA (Guewen Baconnier) +# Copyright 2012, 2015 Camptocamp SA (Guewen Baconnier, Damien Crier) # Copyright (C) 2010 Sébastien Beau # # This program is free software: you can redistribute it and/or modify diff --git a/__unported__/account_easy_reconcile/__openerp__.py b/account_easy_reconcile/__openerp__.py similarity index 56% rename from __unported__/account_easy_reconcile/__openerp__.py rename to account_easy_reconcile/__openerp__.py index abe1124b..36b54948 100755 --- a/__unported__/account_easy_reconcile/__openerp__.py +++ b/account_easy_reconcile/__openerp__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2012 Camptocamp SA (Guewen Baconnier) +# Copyright 2012, 2015 Camptocamp SA (Guewen Baconnier, Damien Crier) # Copyright (C) 2010 Sébastien Beau # # This program is free software: you can redistribute it and/or modify @@ -24,45 +24,18 @@ "version": "1.3.1", "depends": ["account"], "author": "Akretion,Camptocamp,Odoo Community Association (OCA)", - "description": """ -Easy Reconcile -============== - -This is a shared work between Akretion and Camptocamp -in order to provide: - - reconciliation facilities for big volume of transactions - - setup different profiles of reconciliation by account - - each profile can use many methods of reconciliation - - this module is also a base to create others - reconciliation methods which can plug in the profiles - - a profile a reconciliation can be run manually - or by a cron - - monitoring of reconciliation runs with an history - which keep track of the reconciled Journal items - -2 simple reconciliation methods are integrated -in this module, the simple reconciliations works -on 2 lines (1 debit / 1 credit) and do not allow -partial reconcilation, they also match on 1 key, -partner or Journal item name. - -You may be interested to install also the -``account_advanced_reconciliation`` module. -This latter add more complex reconciliations, -allows multiple lines and partial. - -""", "website": "http://www.akretion.com/", "category": "Finance", - "demo_xml": [], "data": ["easy_reconcile.xml", "easy_reconcile_history_view.xml", "security/ir_rule.xml", "security/ir.model.access.csv", "res_config_view.xml", ], + "test": ['test/easy_reconcile.yml', + ], 'license': 'AGPL-3', "auto_install": False, - 'installable': False, + 'installable': True, } diff --git a/__unported__/account_easy_reconcile/base_reconciliation.py b/account_easy_reconcile/base_reconciliation.py similarity index 64% rename from __unported__/account_easy_reconcile/base_reconciliation.py rename to account_easy_reconcile/base_reconciliation.py index 35ce4920..83048334 100644 --- a/__unported__/account_easy_reconcile/base_reconciliation.py +++ b/account_easy_reconcile/base_reconciliation.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2012-2013 Camptocamp SA (Guewen Baconnier) +# Copyright 2012-2013, 2015 Camptocamp SA (Guewen Baconnier, Damien Crier) # Copyright (C) 2010 Sébastien Beau # # This program is free software: you can redistribute it and/or modify @@ -19,11 +19,12 @@ # ############################################################################## -from openerp.osv import fields, orm +from openerp import models, api, fields +from openerp.tools.safe_eval import safe_eval from operator import itemgetter, attrgetter -class EasyReconcileBase(orm.AbstractModel): +class EasyReconcileBase(models.AbstractModel): """Abstract Model for reconciliation methods""" @@ -31,33 +32,35 @@ class EasyReconcileBase(orm.AbstractModel): _inherit = 'easy.reconcile.options' - _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 - } + account_id = fields.Many2one( + 'account.account', + string='Account', + required=True + ) + partner_ids = fields.Many2many( + comodel_name='res.partner', + string='Restrict on partners', + ) + # other fields are inherited from easy.reconcile.options - def automatic_reconcile(self, cr, uid, ids, context=None): + @api.multi + def automatic_reconcile(self): """ Reconciliation method called from the view. :return: list of reconciled ids, list of partially reconciled items """ - 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) + self.ensure_one() + return self._action_rec() - def _action_rec(self, cr, uid, rec, context=None): + @api.multi + def _action_rec(self): """ Must be inherited to implement the reconciliation :return: list of reconciled ids """ raise NotImplementedError - def _base_columns(self, rec): + def _base_columns(self): """ Mandatory columns for move lines queries An extra column aliased as ``key`` should be defined in each query.""" @@ -75,17 +78,20 @@ class EasyReconcileBase(orm.AbstractModel): '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)) + @api.multi + def _select(self, *args, **kwargs): + return "SELECT %s" % ', '.join(self._base_columns()) - def _from(self, rec, *args, **kwargs): + @api.multi + def _from(self, *args, **kwargs): return ("FROM account_move_line " "LEFT OUTER JOIN account_move_reconcile ON " "(account_move_line.reconcile_partial_id " "= account_move_reconcile.id)" ) - def _where(self, rec, *args, **kwargs): + @api.multi + def _where(self, *args, **kwargs): where = ("WHERE account_move_line.account_id = %s " "AND COALESCE(account_move_reconcile.type,'') <> 'manual' " "AND account_move_line.reconcile_id IS NULL ") @@ -93,27 +99,28 @@ class EasyReconcileBase(orm.AbstractModel): # 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: + params = [self.account_id.id] + if self.partner_ids: where += " AND account_move_line.partner_id IN %s" - params.append(tuple([l.id for l in rec.partner_ids])) + params.append(tuple([l.id for l in self.partner_ids])) return where, params - def _get_filter(self, cr, uid, rec, context): - ml_obj = self.pool.get('account.move.line') + @api.multi + def _get_filter(self): + ml_obj = self.env['account.move.line'] where = '' params = [] - if rec.filter: + if self.filter: dummy, where, params = ml_obj._where_calc( - cr, uid, eval(rec.filter), context=context).get_sql() + safe_eval(self.filter)).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') + @api.multi + def _below_writeoff_limit(self, lines, writeoff_limit): + self.ensure_one() + precision = self.env['decimal.precision'].precision_get('Account') keys = ('debit', 'credit') sums = reduce( lambda line, memo: @@ -125,14 +132,13 @@ class EasyReconcileBase(orm.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): - period_obj = self.pool['account.period'] + @api.multi + def _get_rec_date(self, lines, based_on='end_period_last_credit'): + self.ensure_one() def last_period(mlines): period_ids = [ml['period_id'] for ml in mlines] - periods = period_obj.browse( - cr, uid, period_ids, context=context) + periods = self.env['account.period'].browse(period_ids) return max(periods, key=attrgetter('date_stop')) def last_date(mlines): @@ -158,8 +164,8 @@ class EasyReconcileBase(orm.AbstractModel): # when date is None return None - def _reconcile_lines(self, cr, uid, rec, lines, allow_partial=False, - context=None): + @api.multi + def _reconcile_lines(self, lines, allow_partial=False): """ Try to reconcile given lines :param list lines: list of dict of move lines, they must at least @@ -172,33 +178,29 @@ class EasyReconcileBase(orm.AbstractModel): 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 + self.ensure_one() + ml_obj = self.env['account.move.line'] 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) + lines, self.write_off + ) + date = self._get_rec_date(lines, self.date_base_on) + rec_ctx = dict(self.env.context, date_p=date) if below_writeoff: if sum_credit > sum_debit: - writeoff_account_id = rec.account_profit_id.id + writeoff_account_id = self.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] - if rec.analytic_account_id: - rec_ctx['analytic_id'] = rec.analytic_account_id.id - ml_obj.reconcile( - cr, uid, - line_ids, + writeoff_account_id = self.account_lost_id.id + period_id = self.env['account.period'].find(dt=date)[0] + if self.analytic_account_id: + rec_ctx['analytic_id'] = self.analytic_account_id.id + line_rs = ml_obj.browse(line_ids) + line_rs.with_context(rec_ctx).reconcile( type='auto', writeoff_acc_id=writeoff_account_id, - writeoff_period_id=period_id, - writeoff_journal_id=rec.journal_id.id, - context=rec_ctx) + writeoff_period_id=period_id.id, + writeoff_journal_id=self.journal_id.id + ) return True, True elif allow_partial: # Check if the group of move lines was already partially @@ -209,9 +211,8 @@ class EasyReconcileBase(orm.AbstractModel): existing_partial_id = lines[0]['reconcile_partial_id'] if existing_partial_id: partial_line_ids = set(ml_obj.search( - cr, uid, [('reconcile_partial_id', '=', existing_partial_id)], - context=context)) + )) if set(line_ids) == partial_line_ids: return True, False @@ -223,20 +224,18 @@ class EasyReconcileBase(orm.AbstractModel): # it will do a full reconcile instead of a partial reconcile # and make a write-off for exchange if sum_credit > sum_debit: - writeoff_account_id = rec.income_exchange_account_id.id + writeoff_account_id = self.income_exchange_account_id.id else: - writeoff_account_id = rec.expense_exchange_account_id.id - period_id = self.pool['account.period'].find( - cr, uid, dt=date, context=context)[0] - if rec.analytic_account_id: - rec_ctx['analytic_id'] = rec.analytic_account_id.id - ml_obj.reconcile_partial( - cr, uid, - line_ids, + writeoff_account_id = self.expense_exchange_account_id.id + period_id = self.env['account.period'].find(dt=date)[0] + if self.analytic_account_id: + rec_ctx['analytic_id'] = self.analytic_account_id.id + line_rs = ml_obj.browse(line_ids) + line_rs.with_context(rec_ctx).reconcile( type='manual', writeoff_acc_id=writeoff_account_id, - writeoff_period_id=period_id, - writeoff_journal_id=rec.journal_id.id, - context=rec_ctx) + writeoff_period_id=period_id.id, + writeoff_journal_id=self.journal_id.id + ) return True, False return False, False diff --git a/account_easy_reconcile/easy_reconcile.py b/account_easy_reconcile/easy_reconcile.py new file mode 100644 index 00000000..7a7a1bf2 --- /dev/null +++ b/account_easy_reconcile/easy_reconcile.py @@ -0,0 +1,379 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2012-2013, 2015 Camptocamp SA (Guewen Baconnier, Damien Crier) +# 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 . +# +############################################################################## + +from datetime import datetime +from openerp import models, api, fields, _ +from openerp.exceptions import Warning +from openerp import sql_db + +import logging +_logger = logging.getLogger(__name__) + + +class EasyReconcileOptions(models.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' + + @api.model + def _get_rec_base_date(self): + 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') + ] + + write_off = fields.Float('Write off allowed', default=0.) + account_lost_id = fields.Many2one('account.account', + string="Account Lost") + account_profit_id = fields.Many2one('account.account', + string="Account Profit") + journal_id = fields.Many2one('account.journal', + string="Journal") + date_base_on = fields.Selection('_get_rec_base_date', + required=True, + string='Date of reconciliation', + default='end_period_last_credit') + filter = fields.Char(string='Filter') + analytic_account_id = fields.Many2one('account.analytic.account', + string='Analytic_account', + help="Analytic account " + "for the write-off") + income_exchange_account_id = fields.Many2one('account.account', + string='Gain Exchange ' + 'Rate Account') + expense_exchange_account_id = fields.Many2one('account.account', + string='Loss Exchange ' + 'Rate Account') + + +class AccountEasyReconcileMethod(models.Model): + _name = 'account.easy.reconcile.method' + _description = 'reconcile method for account_easy_reconcile' + _inherit = 'easy.reconcile.options' + _order = 'sequence' + + @api.model + def _get_all_rec_method(self): + 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'), + ] + + @api.model + def _get_rec_method(self): + return self._get_all_rec_method() + + name = fields.Selection('_get_rec_method', string='Type', required=True) + sequence = fields.Integer(string='Sequence', + default=1, + 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' + ) + company_id = fields.Many2one('res.company', + string='Company', + related="task_id.company_id", + store=True, + readonly=True + ) + + +class AccountEasyReconcile(models.Model): + + _name = 'account.easy.reconcile' + _inherit = ['mail.thread'] + _description = 'account easy reconcile' + + @api.one + @api.depends('account') + def _get_total_unrec(self): + obj_move_line = self.env['account.move.line'] + self.unreconciled_count = obj_move_line.search_count( + [('account_id', '=', self.account.id), + ('reconcile_id', '=', False), + ('reconcile_partial_id', '=', False)], + ) + + @api.one + @api.depends('account') + def _get_partial_rec(self): + obj_move_line = self.env['account.move.line'] + self.reconciled_partial_count = obj_move_line.search_count( + [('account_id', '=', self.account.id), + ('reconcile_id', '=', False), + ('reconcile_partial_id', '!=', False)], + ) + + @api.one + @api.depends('history_ids') + def _last_history(self): + # do a search() for retrieving the latest history line, + # as a read() will badly split the list of ids with 'date desc' + # and return the wrong result. + history_obj = self.env['easy.reconcile.history'] + last_history_rs = history_obj.search( + [('easy_reconcile_id', '=', self.id)], + limit=1, order='date desc' + ) + self.last_history = last_history_rs or False + + name = fields.Char(string='Name', required=True) + account = fields.Many2one('account.account', + string='Account', + required=True, + ) + reconcile_method = fields.One2many('account.easy.reconcile.method', + 'task_id', + string='Method' + ) + unreconciled_count = fields.Integer(string='Unreconciled Items', + compute='_get_total_unrec' + ) + reconciled_partial_count = fields.Integer( + string='Partially Reconciled Items', + compute='_get_partial_rec' + ) + history_ids = fields.One2many('easy.reconcile.history', + 'easy_reconcile_id', + string='History', + readonly=True + ) + last_history = fields.Many2one('easy.reconcile.history', + string='Last history', readonly=True, + compute='_last_history', + ) + company_id = fields.Many2one('res.company', string='Company') + + @api.model + def _prepare_run_transient(self, rec_method): + return {'account_id': rec_method.task_id.account.id, + 'write_off': rec_method.write_off, + 'account_lost_id': (rec_method.account_lost_id.id), + 'account_profit_id': (rec_method.account_profit_id.id), + 'analytic_account_id': (rec_method.analytic_account_id.id), + 'income_exchange_account_id': + (rec_method.income_exchange_account_id.id), + 'expense_exchange_account_id': + (rec_method.income_exchange_account_id.id), + 'journal_id': (rec_method.journal_id.id), + 'date_base_on': rec_method.date_base_on, + 'filter': rec_method.filter} + + @api.multi + def run_reconcile(self): + def find_reconcile_ids(fieldname, move_line_ids): + if not move_line_ids: + return [] + sql = ("SELECT DISTINCT " + fieldname + + " FROM account_move_line " + " WHERE id in %s " + " AND " + fieldname + " IS NOT NULL") + self.env.cr.execute(sql, (tuple(move_line_ids),)) + res = self.env.cr.fetchall() + return [row[0] for row in res] + + # we use a new cursor to be able to commit the reconciliation + # often. We have to create it here and not later to avoid problems + # where the new cursor sees the lines as reconciles but the old one + # does not. + + for rec in self: + ctx = self.env.context.copy() + ctx['commit_every'] = ( + rec.account.company_id.reconciliation_commit_every + ) + if ctx['commit_every']: + new_cr = sql_db.db_connect(self.env.cr.dbname).cursor() + else: + new_cr = self.env.cr + + try: + all_ml_rec_ids = [] + all_ml_partial_ids = [] + + for method in rec.reconcile_method: + rec_model = self.env[method.name] + auto_rec_id = rec_model.create( + self._prepare_run_transient(method) + ) + + ml_rec_ids, ml_partial_ids = ( + auto_rec_id.automatic_reconcile() + ) + + all_ml_rec_ids += ml_rec_ids + all_ml_partial_ids += ml_partial_ids + + reconcile_ids = find_reconcile_ids( + 'reconcile_id', + all_ml_rec_ids + ) + partial_ids = find_reconcile_ids( + 'reconcile_partial_id', + all_ml_partial_ids + ) + self.env['easy.reconcile.history'].create( + { + 'easy_reconcile_id': rec.id, + 'date': fields.Datetime.now(), + 'reconcile_ids': [ + (4, rid) for rid in reconcile_ids + ], + 'reconcile_partial_ids': [ + (4, rid) for rid in partial_ids + ], + }) + except Exception as e: + # In case of error, we log it in the mail thread, log the + # stack trace and create an empty history line; otherwise, + # the cron will just loop on this reconcile task. + _logger.exception( + "The reconcile task %s had an exception: %s", + rec.name, e.message + ) + message = _("There was an error during reconciliation : %s") \ + % e.message + rec.message_post(body=message) + self.env['easy.reconcile.history'].create( + { + 'easy_reconcile_id': rec.id, + 'date': fields.Datetime.now(), + 'reconcile_ids': [], + 'reconcile_partial_ids': [], + } + ) + finally: + if ctx['commit_every']: + new_cr.commit() + new_cr.close() + + return True + + @api.multi + def _no_history(self): + """ Raise an `orm.except_orm` error, supposed to + be called when there is no history on the reconciliation + task. + """ + raise Warning( + _('There is no history of reconciled ' + 'items on the task: %s.') % self.name + ) + + @api.model + def _open_move_line_list(self, move_line_ids, name): + return { + 'name': name, + 'view_mode': 'tree,form', + 'view_id': False, + 'view_type': 'form', + 'res_model': 'account.move.line', + 'type': 'ir.actions.act_window', + 'nodestroy': True, + 'target': 'current', + 'domain': unicode([('id', 'in', move_line_ids)]), + } + + @api.multi + def open_unreconcile(self): + """ Open the view of move line with the unreconciled move lines""" + self.ensure_one() + obj_move_line = self.env['account.move.line'] + lines = obj_move_line.search( + [('account_id', '=', self.account.id), + ('reconcile_id', '=', False), + ('reconcile_partial_id', '=', False)]) + name = _('Unreconciled items') + return self._open_move_line_list(lines.ids or [], name) + + @api.multi + def open_partial_reconcile(self): + """ Open the view of move line with the partially + reconciled move lines""" + self.ensure_one() + obj_move_line = self.env['account.move.line'] + lines = obj_move_line.search( + [('account_id', '=', self.account.id), + ('reconcile_id', '=', False), + ('reconcile_partial_id', '!=', False)]) + name = _('Partial reconciled items') + return self._open_move_line_list(lines.ids or [], name) + + @api.multi + def last_history_reconcile(self): + """ Get the last history record for this reconciliation profile + and return the action which opens move lines reconciled + """ + if not self.last_history: + self._no_history() + return self.last_history.open_reconcile() + + @api.multi + def last_history_partial(self): + """ Get the last history record for this reconciliation profile + and return the action which opens move lines reconciled + """ + if not self.last_history: + self._no_history() + return self.last_history.open_partial() + + @api.model + def run_scheduler(self, run_all=None): + """ Launch the reconcile with the oldest run + This function is mostly here to be used with cron task + + :param run_all: if set it will ingore lookup and launch + all reconciliation + :returns: True in case of success or raises an exception + + """ + def _get_date(reconcile): + if reconcile.last_history.date: + return fields.Datetime.from_string(reconcile.last_history.date) + else: + return datetime.min + + reconciles = self.search([]) + assert reconciles.ids, "No easy reconcile available" + if run_all: + reconciles.run_reconcile() + return True + reconciles.sorted(key=_get_date) + older = reconciles[0] + older.run_reconcile() + return True diff --git a/__unported__/account_easy_reconcile/easy_reconcile.xml b/account_easy_reconcile/easy_reconcile.xml similarity index 97% rename from __unported__/account_easy_reconcile/easy_reconcile.xml rename to account_easy_reconcile/easy_reconcile.xml index 076bd3b3..7de62cbc 100644 --- a/__unported__/account_easy_reconcile/easy_reconcile.xml +++ b/account_easy_reconcile/easy_reconcile.xml @@ -136,7 +136,7 @@ The lines should have the same amount (with the write-off) and the same referenc - + @@ -156,7 +156,7 @@ The lines should have the same amount (with the write-off) and the same referenc - + diff --git a/account_easy_reconcile/easy_reconcile_history.py b/account_easy_reconcile/easy_reconcile_history.py new file mode 100644 index 00000000..58904b3e --- /dev/null +++ b/account_easy_reconcile/easy_reconcile_history.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Guewen Baconnier, Damien Crier +# Copyright 2012, 2015 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 import models, api, fields, _ + + +class EasyReconcileHistory(models.Model): + """ Store an history of the runs per profile + Each history stores the list of reconciliations done""" + + _name = 'easy.reconcile.history' + _rec_name = 'easy_reconcile_id' + _order = 'date DESC' + + @api.one + @api.depends('reconcile_ids', 'reconcile_partial_ids') + def _get_reconcile_line_ids(self): + move_line_ids = [] + for reconcile in self.reconcile_ids: + move_lines = reconcile.mapped('line_id') + move_line_ids.extend(move_lines.ids) + self.reconcile_line_ids = move_line_ids + + move_line_ids2 = [] + for reconcile2 in self.reconcile_partial_ids: + move_lines2 = reconcile2.mapped('line_partial_ids') + move_line_ids2.extend(move_lines2.ids) + self.partial_line_ids = move_line_ids2 + + easy_reconcile_id = fields.Many2one( + 'account.easy.reconcile', + string='Reconcile Profile', + readonly=True + ) + date = fields.Datetime(string='Run date', readonly=True, required=True) + reconcile_ids = fields.Many2many( + comodel_name='account.move.reconcile', + relation='account_move_reconcile_history_rel', + string='Partial Reconciliations', + readonly=True + ) + reconcile_partial_ids = fields.Many2many( + comodel_name='account.move.reconcile', + relation='account_move_reconcile_history_partial_rel', + string='Partial Reconciliations', + readonly=True + ) + reconcile_line_ids = fields.Many2many( + comodel_name='account.move.line', + relation='account_move_line_history_rel', + string='Reconciled Items', + compute='_get_reconcile_line_ids' + ) + partial_line_ids = fields.Many2many( + comodel_name='account.move.line', + relation='account_move_line_history_partial_rel', + string='Partially Reconciled Items', + compute='_get_reconcile_line_ids' + ) + company_id = fields.Many2one( + 'res.company', + string='Company', + store=True, + readonly=True, + related='easy_reconcile_id.company_id' + ) + + @api.multi + def _open_move_lines(self, rec_type='full'): + """ For an history record, open the view of move line with + the reconciled or partially reconciled move lines + + :param history_id: id of the history + :param rec_type: 'full' or 'partial' + :return: action to open the move lines + """ + assert rec_type in ('full', 'partial'), \ + "rec_type must be 'full' or 'partial'" + move_line_ids = [] + if rec_type == 'full': + move_line_ids = self.mapped('reconcile_ids.line_id').ids + name = _('Reconciliations') + else: + move_line_ids = self.mapped( + 'reconcile_partial_ids.line_partial_ids').ids + name = _('Partial Reconciliations') + return { + 'name': name, + 'view_mode': 'tree,form', + 'view_id': False, + 'view_type': 'form', + 'res_model': 'account.move.line', + 'type': 'ir.actions.act_window', + 'nodestroy': True, + 'target': 'current', + 'domain': unicode([('id', 'in', move_line_ids)]), + } + + @api.multi + def open_reconcile(self): + """ For an history record, open the view of move line + with the reconciled move lines + + :param history_ids: id of the record as int or long + Accept a list with 1 id too to be + used from the client. + """ + self.ensure_one() + return self._open_move_lines(rec_type='full') + + @api.multi + def open_partial(self): + """ For an history record, open the view of move line + with the partially reconciled move lines + + :param history_ids: id of the record as int or long + Accept a list with 1 id too to be + used from the client. + """ + self.ensure_one() + return self._open_move_lines(rec_type='partial') diff --git a/__unported__/account_easy_reconcile/easy_reconcile_history_view.xml b/account_easy_reconcile/easy_reconcile_history_view.xml similarity index 100% rename from __unported__/account_easy_reconcile/easy_reconcile_history_view.xml rename to account_easy_reconcile/easy_reconcile_history_view.xml diff --git a/__unported__/account_easy_reconcile/i18n/account_easy_reconcile.pot b/account_easy_reconcile/i18n/account_easy_reconcile.pot similarity index 54% rename from __unported__/account_easy_reconcile/i18n/account_easy_reconcile.pot rename to account_easy_reconcile/i18n/account_easy_reconcile.pot index 598b81ea..76d49a52 100644 --- a/__unported__/account_easy_reconcile/i18n/account_easy_reconcile.pot +++ b/account_easy_reconcile/i18n/account_easy_reconcile.pot @@ -1,13 +1,13 @@ -# Translation of OpenERP Server. +# Translation of Odoo Server. # This file contains the translation of the following modules: # * account_easy_reconcile # msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 7.0\n" +"Project-Id-Version: Odoo Server 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-01-21 11:55+0000\n" -"PO-Revision-Date: 2014-01-21 11:55+0000\n" +"POT-Creation-Date: 2015-06-12 14:08+0000\n" +"PO-Revision-Date: 2015-06-12 14:08+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -16,38 +16,8 @@ msgstr "" "Plural-Forms: \n" #. module: account_easy_reconcile -#: code:addons/account_easy_reconcile/easy_reconcile_history.py:108 -#: view:easy.reconcile.history:0 -#: field:easy.reconcile.history,reconcile_ids:0 -#, python-format -msgid "Reconciliations" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -#: view:easy.reconcile.history:0 -msgid "Automatic Easy Reconcile History" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Information" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -#: view:easy.reconcile.history:0 -msgid "Go to partially reconciled items" -msgstr "" - -#. module: account_easy_reconcile -#: help:account.easy.reconcile.method,sequence:0 -msgid "The sequence field is used to order the reconcile method" -msgstr "" - -#. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_history -msgid "easy.reconcile.history" +#: view:easy.reconcile.history:account_easy_reconcile.view_easy_reconcile_history_search +msgid "7 Days" msgstr "" #. module: account_easy_reconcile @@ -63,161 +33,6 @@ msgid "

\n" " " msgstr "" -#. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_options -msgid "easy.reconcile.options" -msgstr "" - -#. module: account_easy_reconcile -#: view:easy.reconcile.history:0 -msgid "Group By..." -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile,unreconciled_count:0 -msgid "Unreconciled Items" -msgstr "" - -#. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_base -msgid "easy.reconcile.base" -msgstr "" - -#. module: account_easy_reconcile -#: field:easy.reconcile.history,reconcile_line_ids:0 -msgid "Reconciled Items" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile,reconcile_method:0 -msgid "Method" -msgstr "" - -#. module: account_easy_reconcile -#: view:easy.reconcile.history:0 -msgid "7 Days" -msgstr "" - -#. module: account_easy_reconcile -#: model:ir.actions.act_window,name:account_easy_reconcile.action_easy_reconcile_history -msgid "Easy Automatic Reconcile History" -msgstr "" - -#. module: account_easy_reconcile -#: field:easy.reconcile.history,date:0 -msgid "Run date" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same reference to be reconciled." -msgstr "" - -#. module: account_easy_reconcile -#: model:ir.actions.act_window,name:account_easy_reconcile.act_easy_reconcile_to_history -msgid "History Details" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Display items reconciled on the last run" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile.method,name:0 -msgid "Type" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile,company_id:0 -#: field:account.easy.reconcile.method,company_id:0 -#: field:easy.reconcile.history,company_id:0 -msgid "Company" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile.method,account_profit_id:0 -#: field:easy.reconcile.base,account_profit_id:0 -#: field:easy.reconcile.options,account_profit_id:0 -#: field:easy.reconcile.simple,account_profit_id:0 -#: field:easy.reconcile.simple.name,account_profit_id:0 -#: field:easy.reconcile.simple.partner,account_profit_id:0 -#: field:easy.reconcile.simple.reference,account_profit_id:0 -msgid "Account Profit" -msgstr "" - -#. module: account_easy_reconcile -#: view:easy.reconcile.history:0 -msgid "Todays' Reconcilations" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Simple. Amount and Name" -msgstr "" - -#. module: account_easy_reconcile -#: field:easy.reconcile.base,partner_ids:0 -#: field:easy.reconcile.simple,partner_ids:0 -#: field:easy.reconcile.simple.name,partner_ids:0 -#: field:easy.reconcile.simple.partner,partner_ids:0 -#: field:easy.reconcile.simple.reference,partner_ids:0 -msgid "Restrict on partners" -msgstr "" - -#. 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 "" - -#. module: account_easy_reconcile -#: view:easy.reconcile.history:0 -msgid "Today" -msgstr "" - -#. module: account_easy_reconcile -#: view:easy.reconcile.history:0 -msgid "Date" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile,last_history:0 -msgid "Last History" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Configuration" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile,reconciled_partial_count:0 -#: field:easy.reconcile.history,partial_line_ids:0 -msgid "Partially Reconciled Items" -msgstr "" - -#. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_partner -msgid "easy.reconcile.simple.partner" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile.method,write_off:0 -#: field:easy.reconcile.base,write_off:0 -#: field:easy.reconcile.options,write_off:0 -#: field:easy.reconcile.simple,write_off:0 -#: field:easy.reconcile.simple.name,write_off:0 -#: field:easy.reconcile.simple.partner,write_off:0 -#: field:easy.reconcile.simple.reference,write_off:0 -msgid "Write off allowed" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Automatic Easy Reconcile" -msgstr "" - #. module: account_easy_reconcile #: field:account.easy.reconcile,account:0 #: field:easy.reconcile.base,account_id:0 @@ -228,79 +43,6 @@ msgstr "" msgid "Account" msgstr "" -#. module: account_easy_reconcile -#: field:account.easy.reconcile.method,task_id:0 -msgid "Task" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile,name:0 -msgid "Name" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Simple. Amount and Partner" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Start Auto Reconcilation" -msgstr "" - -#. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_name -msgid "easy.reconcile.simple.name" -msgstr "" - -#. module: account_easy_reconcile -#: field:account.easy.reconcile.method,filter:0 -#: field:easy.reconcile.base,filter:0 -#: field:easy.reconcile.options,filter:0 -#: field:easy.reconcile.simple,filter:0 -#: field:easy.reconcile.simple.name,filter:0 -#: field:easy.reconcile.simple.partner,filter:0 -#: field:easy.reconcile.simple.reference,filter:0 -msgid "Filter" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same partner to be reconciled." -msgstr "" - -#. module: account_easy_reconcile -#: field:easy.reconcile.history,easy_reconcile_id:0 -msgid "Reconcile Profile" -msgstr "" - -#. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile_method -msgid "reconcile method for account_easy_reconcile" -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Start Auto Reconciliation" -msgstr "" - -#. module: account_easy_reconcile -#: code:addons/account_easy_reconcile/easy_reconcile.py:257 -#, python-format -msgid "Error" -msgstr "" - -#. module: account_easy_reconcile -#: code:addons/account_easy_reconcile/easy_reconcile.py:258 -#, python-format -msgid "There is no history of reconciled items on the task: %s." -msgstr "" - -#. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same name to be reconciled." -msgstr "" - #. module: account_easy_reconcile #: field:account.easy.reconcile.method,account_lost_id:0 #: field:easy.reconcile.base,account_lost_id:0 @@ -313,55 +55,98 @@ msgid "Account Lost" msgstr "" #. module: account_easy_reconcile -#: view:easy.reconcile.history:0 -msgid "Reconciliation Profile" +#: field:account.easy.reconcile.method,account_profit_id:0 +#: field:easy.reconcile.base,account_profit_id:0 +#: field:easy.reconcile.options,account_profit_id:0 +#: field:easy.reconcile.simple,account_profit_id:0 +#: field:easy.reconcile.simple.name,account_profit_id:0 +#: field:easy.reconcile.simple.partner,account_profit_id:0 +#: field:easy.reconcile.simple.reference,account_profit_id:0 +msgid "Account Profit" msgstr "" #. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -#: field:account.easy.reconcile,history_ids:0 -msgid "History" +#: help:account.easy.reconcile.method,analytic_account_id:0 +#: help:easy.reconcile.base,analytic_account_id:0 +#: help:easy.reconcile.options,analytic_account_id:0 +#: help:easy.reconcile.simple,analytic_account_id:0 +#: help:easy.reconcile.simple.name,analytic_account_id:0 +#: help:easy.reconcile.simple.partner,analytic_account_id:0 +#: help:easy.reconcile.simple.reference,analytic_account_id:0 +msgid "Analytic accountfor the write-off" msgstr "" #. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -#: view:easy.reconcile.history:0 -msgid "Go to reconciled items" +#: field:account.easy.reconcile.method,analytic_account_id:0 +#: field:easy.reconcile.base,analytic_account_id:0 +#: field:easy.reconcile.options,analytic_account_id:0 +#: field:easy.reconcile.simple,analytic_account_id:0 +#: field:easy.reconcile.simple.name,analytic_account_id:0 +#: field:easy.reconcile.simple.partner,analytic_account_id:0 +#: field:easy.reconcile.simple.reference,analytic_account_id:0 +msgid "Analytic_account" msgstr "" #. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Profile Information" +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_tree +msgid "Automatic Easy Reconcile" msgstr "" #. module: account_easy_reconcile -#: view:account.easy.reconcile.method:0 +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +#: view:easy.reconcile.history:account_easy_reconcile.easy_reconcile_history_form +#: view:easy.reconcile.history:account_easy_reconcile.easy_reconcile_history_tree +#: view:easy.reconcile.history:account_easy_reconcile.view_easy_reconcile_history_search +msgid "Automatic Easy Reconcile History" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile.method:account_easy_reconcile.account_easy_reconcile_method_form +#: view:account.easy.reconcile.method:account_easy_reconcile.account_easy_reconcile_method_tree msgid "Automatic Easy Reconcile Method" msgstr "" #. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Simple. Amount and Reference" +#: model:ir.model,name:account_easy_reconcile.model_res_company +msgid "Companies" msgstr "" #. module: account_easy_reconcile -#: view:account.easy.reconcile:0 -msgid "Display items partially reconciled on the last run" +#: field:account.easy.reconcile,company_id:0 +#: field:account.easy.reconcile.method,company_id:0 +#: field:easy.reconcile.history,company_id:0 +msgid "Company" msgstr "" #. module: account_easy_reconcile -#: field:account.easy.reconcile.method,sequence:0 -msgid "Sequence" +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Configuration" msgstr "" #. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple -msgid "easy.reconcile.simple" +#: field:account.easy.reconcile,create_uid:0 +#: field:account.easy.reconcile.method,create_uid:0 +#: field:easy.reconcile.history,create_uid:0 +#: field:easy.reconcile.simple.name,create_uid:0 +#: field:easy.reconcile.simple.partner,create_uid:0 +#: field:easy.reconcile.simple.reference,create_uid:0 +msgid "Created by" msgstr "" #. module: account_easy_reconcile -#: view:easy.reconcile.history:0 -msgid "Reconciliations of last 7 days" +#: field:account.easy.reconcile,create_date:0 +#: field:account.easy.reconcile.method,create_date:0 +#: field:easy.reconcile.history,create_date:0 +#: field:easy.reconcile.simple.name,create_date:0 +#: field:easy.reconcile.simple.partner,create_date:0 +#: field:easy.reconcile.simple.reference,create_date:0 +msgid "Created on" +msgstr "" + +#. module: account_easy_reconcile +#: view:easy.reconcile.history:account_easy_reconcile.view_easy_reconcile_history_search +msgid "Date" msgstr "" #. module: account_easy_reconcile @@ -376,11 +161,142 @@ msgid "Date of reconciliation" msgstr "" #. module: account_easy_reconcile -#: code:addons/account_easy_reconcile/easy_reconcile_history.py:111 -#: view:easy.reconcile.history:0 -#: field:easy.reconcile.history,reconcile_partial_ids:0 +#: help:account.easy.reconcile,message_last_post:0 +msgid "Date of the last message posted on the record." +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_tree +msgid "Display items partially reconciled on the last run" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_tree +msgid "Display items reconciled on the last run" +msgstr "" + +#. 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 "" + +#. module: account_easy_reconcile +#: model:ir.actions.act_window,name:account_easy_reconcile.action_easy_reconcile_history +msgid "Easy Automatic Reconcile History" +msgstr "" + +#. module: account_easy_reconcile +#: code:addons/account_easy_reconcile/easy_reconcile.py:329 #, python-format -msgid "Partial Reconciliations" +msgid "Error" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile.method,filter:0 +#: field:easy.reconcile.base,filter:0 +#: field:easy.reconcile.options,filter:0 +#: field:easy.reconcile.simple,filter:0 +#: field:easy.reconcile.simple.name,filter:0 +#: field:easy.reconcile.simple.partner,filter:0 +#: field:easy.reconcile.simple.reference,filter:0 +msgid "Filter" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,message_follower_ids:0 +msgid "Followers" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile.method,income_exchange_account_id:0 +#: field:easy.reconcile.base,income_exchange_account_id:0 +#: field:easy.reconcile.options,income_exchange_account_id:0 +#: field:easy.reconcile.simple,income_exchange_account_id:0 +#: field:easy.reconcile.simple.name,income_exchange_account_id:0 +#: field:easy.reconcile.simple.partner,income_exchange_account_id:0 +#: field:easy.reconcile.simple.reference,income_exchange_account_id:0 +msgid "Gain ExchangeRate Account" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Go to partial reconciled items" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +#: view:easy.reconcile.history:account_easy_reconcile.easy_reconcile_history_form +#: view:easy.reconcile.history:account_easy_reconcile.easy_reconcile_history_tree +msgid "Go to partially reconciled items" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +#: view:easy.reconcile.history:account_easy_reconcile.easy_reconcile_history_form +#: view:easy.reconcile.history:account_easy_reconcile.easy_reconcile_history_tree +msgid "Go to reconciled items" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Go to unreconciled items" +msgstr "" + +#. module: account_easy_reconcile +#: view:easy.reconcile.history:account_easy_reconcile.view_easy_reconcile_history_search +msgid "Group By..." +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +#: field:account.easy.reconcile,history_ids:0 +msgid "History" +msgstr "" + +#. module: account_easy_reconcile +#: model:ir.actions.act_window,name:account_easy_reconcile.act_easy_reconcile_to_history +msgid "History Details" +msgstr "" + +#. module: account_easy_reconcile +#: help:account.easy.reconcile,message_summary:0 +msgid "Holds the Chatter summary (number of messages, ...). This summary is directly in html format in order to be inserted in kanban views." +msgstr "" + +#. module: account_easy_reconcile +#: field:res.company,reconciliation_commit_every:0 +msgid "How often to commit when performing automaticreconciliation." +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,id:0 +#: field:account.easy.reconcile.method,id:0 +#: field:easy.reconcile.base,id:0 +#: field:easy.reconcile.history,id:0 +#: field:easy.reconcile.options,id:0 +#: field:easy.reconcile.simple,id:0 +#: field:easy.reconcile.simple.name,id:0 +#: field:easy.reconcile.simple.partner,id:0 +#: field:easy.reconcile.simple.reference,id:0 +msgid "ID" +msgstr "" + +#. module: account_easy_reconcile +#: help:account.easy.reconcile,message_unread:0 +msgid "If checked new messages require your attention." +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Information" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,message_is_follower:0 +msgid "Is a Follower" msgstr "" #. module: account_easy_reconcile @@ -395,13 +311,33 @@ msgid "Journal" msgstr "" #. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_reference -msgid "easy.reconcile.simple.reference" +#: field:account.easy.reconcile,message_last_post:0 +msgid "Last Message Date" msgstr "" #. module: account_easy_reconcile -#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile -msgid "account easy reconcile" +#: field:account.easy.reconcile,write_uid:0 +#: field:account.easy.reconcile.method,write_uid:0 +#: field:easy.reconcile.history,write_uid:0 +#: field:easy.reconcile.simple.name,write_uid:0 +#: field:easy.reconcile.simple.partner,write_uid:0 +#: field:easy.reconcile.simple.reference,write_uid:0 +msgid "Last Updated by" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,write_date:0 +#: field:account.easy.reconcile.method,write_date:0 +#: field:easy.reconcile.history,write_date:0 +#: field:easy.reconcile.simple.name,write_date:0 +#: field:easy.reconcile.simple.partner,write_date:0 +#: field:easy.reconcile.simple.reference,write_date:0 +msgid "Last Updated on" +msgstr "" + +#. module: account_easy_reconcile +#: help:res.company,reconciliation_commit_every:0 +msgid "Leave zero to commit only at the end of the process." msgstr "" #. module: account_easy_reconcile @@ -412,16 +348,220 @@ msgstr "" #: field:easy.reconcile.simple.name,expense_exchange_account_id:0 #: field:easy.reconcile.simple.partner,expense_exchange_account_id:0 #: field:easy.reconcile.simple.reference,expense_exchange_account_id:0 -msgid "Loss Exchange Rate Account" +msgid "Loss ExchangeRate Account" msgstr "" #. module: account_easy_reconcile -#: field:account.easy.reconcile.method,income_exchange_account_id:0 -#: field:easy.reconcile.base,income_exchange_account_id:0 -#: field:easy.reconcile.options,income_exchange_account_id:0 -#: field:easy.reconcile.simple,income_exchange_account_id:0 -#: field:easy.reconcile.simple.name,income_exchange_account_id:0 -#: field:easy.reconcile.simple.partner,income_exchange_account_id:0 -#: field:easy.reconcile.simple.reference,income_exchange_account_id:0 -msgid "Gain Exchange Rate Account" +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same name to be reconciled." msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same partner to be reconciled." +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same reference to be reconciled." +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,message_ids:0 +msgid "Messages" +msgstr "" + +#. module: account_easy_reconcile +#: help:account.easy.reconcile,message_ids:0 +msgid "Messages and communication history" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,reconcile_method:0 +msgid "Method" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,name:0 +msgid "Name" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.config.settings:account_easy_reconcile.view_account_config +msgid "Options" +msgstr "" + +#. module: account_easy_reconcile +#: code:addons/account_easy_reconcile/easy_reconcile_history.py:108 +#: view:easy.reconcile.history:account_easy_reconcile.easy_reconcile_history_form +#: field:easy.reconcile.history,reconcile_ids:0 +#: field:easy.reconcile.history,reconcile_partial_ids:0 +#, python-format +msgid "Partial Reconciliations" +msgstr "" + +#. module: account_easy_reconcile +#: code:addons/account_easy_reconcile/easy_reconcile.py:368 +#, python-format +msgid "Partial reconciled items" +msgstr "" + +#. module: account_easy_reconcile +#: field:easy.reconcile.history,partial_line_ids:0 +msgid "Partially Reconciled Items" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Profile Information" +msgstr "" + +#. module: account_easy_reconcile +#: field:easy.reconcile.history,easy_reconcile_id:0 +msgid "Reconcile Profile" +msgstr "" + +#. module: account_easy_reconcile +#: field:easy.reconcile.history,reconcile_line_ids:0 +msgid "Reconciled Items" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.config.settings:account_easy_reconcile.view_account_config +msgid "Reconciliation" +msgstr "" + +#. module: account_easy_reconcile +#: view:easy.reconcile.history:account_easy_reconcile.view_easy_reconcile_history_search +msgid "Reconciliation Profile" +msgstr "" + +#. module: account_easy_reconcile +#: code:addons/account_easy_reconcile/easy_reconcile_history.py:105 +#: view:easy.reconcile.history:account_easy_reconcile.easy_reconcile_history_form +#, python-format +msgid "Reconciliations" +msgstr "" + +#. module: account_easy_reconcile +#: view:easy.reconcile.history:account_easy_reconcile.view_easy_reconcile_history_search +msgid "Reconciliations of last 7 days" +msgstr "" + +#. module: account_easy_reconcile +#: field:easy.reconcile.base,partner_ids:0 +#: field:easy.reconcile.simple,partner_ids:0 +#: field:easy.reconcile.simple.name,partner_ids:0 +#: field:easy.reconcile.simple.partner,partner_ids:0 +#: field:easy.reconcile.simple.reference,partner_ids:0 +msgid "Restrict on partners" +msgstr "" + +#. module: account_easy_reconcile +#: field:easy.reconcile.history,date:0 +msgid "Run date" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile.method,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Simple. Amount and Name" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Simple. Amount and Partner" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Simple. Amount and Reference" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_tree +msgid "Start Auto Reconcilation" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.easy.reconcile:account_easy_reconcile.account_easy_reconcile_form +msgid "Start Auto Reconciliation" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,message_summary:0 +msgid "Summary" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile.method,task_id:0 +msgid "Task" +msgstr "" + +#. module: account_easy_reconcile +#: help:account.easy.reconcile.method,sequence:0 +msgid "The sequence field is used to order the reconcile method" +msgstr "" + +#. module: account_easy_reconcile +#: code:addons/account_easy_reconcile/easy_reconcile.py:330 +#, python-format +msgid "There is no history of reconciled items on the task: %s." +msgstr "" + +#. module: account_easy_reconcile +#: view:easy.reconcile.history:account_easy_reconcile.view_easy_reconcile_history_search +msgid "Today" +msgstr "" + +#. module: account_easy_reconcile +#: view:easy.reconcile.history:account_easy_reconcile.view_easy_reconcile_history_search +msgid "Todays' Reconcilations" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile.method,name:0 +msgid "Type" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile,message_unread:0 +msgid "Unread Messages" +msgstr "" + +#. module: account_easy_reconcile +#: code:addons/account_easy_reconcile/easy_reconcile.py:356 +#, python-format +msgid "Unreconciled items" +msgstr "" + +#. module: account_easy_reconcile +#: field:account.easy.reconcile.method,write_off:0 +#: field:easy.reconcile.base,write_off:0 +#: field:easy.reconcile.options,write_off:0 +#: field:easy.reconcile.simple,write_off:0 +#: field:easy.reconcile.simple.name,write_off:0 +#: field:easy.reconcile.simple.partner,write_off:0 +#: field:easy.reconcile.simple.reference,write_off:0 +msgid "Write off allowed" +msgstr "" + +#. module: account_easy_reconcile +#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile +msgid "account easy reconcile" +msgstr "" + +#. module: account_easy_reconcile +#: view:account.config.settings:account_easy_reconcile.view_account_config +msgid "eInvoicing & Payments" +msgstr "" + +#. module: account_easy_reconcile +#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile_method +msgid "reconcile method for account_easy_reconcile" +msgstr "" + diff --git a/__unported__/account_easy_reconcile/i18n/es.po b/account_easy_reconcile/i18n/es.po similarity index 100% rename from __unported__/account_easy_reconcile/i18n/es.po rename to account_easy_reconcile/i18n/es.po diff --git a/__unported__/account_easy_reconcile/i18n/fr.po b/account_easy_reconcile/i18n/fr.po similarity index 100% rename from __unported__/account_easy_reconcile/i18n/fr.po rename to account_easy_reconcile/i18n/fr.po diff --git a/__unported__/account_easy_reconcile/res_config.py b/account_easy_reconcile/res_config.py similarity index 53% rename from __unported__/account_easy_reconcile/res_config.py rename to account_easy_reconcile/res_config.py index cb827ab3..090810d3 100644 --- a/__unported__/account_easy_reconcile/res_config.py +++ b/account_easy_reconcile/res_config.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Author: Leonardo Pistone -# Copyright 2014 Camptocamp SA +# Author: Leonardo Pistone, Damien Crier +# Copyright 2014, 2015 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 @@ -19,41 +19,39 @@ # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, api, fields -class AccountConfigSettings(orm.TransientModel): +class AccountConfigSettings(models.TransientModel): _inherit = 'account.config.settings' - _columns = { - 'reconciliation_commit_every': fields.related( - 'company_id', - 'reconciliation_commit_every', - type='integer', - string='How often to commit when performing automatic ' - 'reconciliation.', - help="""Leave zero to commit only at the end of the process."""), - } + reconciliation_commit_every = fields.Integer( + related="company_id.reconciliation_commit_every", + string="How often to commit when performing automatic " + "reconciliation.", + help="Leave zero to commit only at the end of the process." + ) - def onchange_company_id(self, cr, uid, ids, company_id, context=None): - company_obj = self.pool['res.company'] + @api.multi + def onchange_company_id(self, company_id): result = super(AccountConfigSettings, self).onchange_company_id( - cr, uid, ids, company_id, context=None) + company_id + ) if company_id: - company = company_obj.browse(cr, uid, company_id, context=context) + company = self.env['res.company'].browse(company_id) result['value']['reconciliation_commit_every'] = ( company.reconciliation_commit_every ) return result -class Company(orm.Model): +class Company(models.Model): _inherit = "res.company" - _columns = { - 'reconciliation_commit_every': fields.integer( - string='How often to commit when performing automatic ' - 'reconciliation.', - help="""Leave zero to commit only at the end of the process."""), - } + + reconciliation_commit_every = fields.Integer( + string="How often to commit when performing automatic " + "reconciliation.", + help="Leave zero to commit only at the end of the process." + ) diff --git a/__unported__/account_easy_reconcile/res_config_view.xml b/account_easy_reconcile/res_config_view.xml similarity index 100% rename from __unported__/account_easy_reconcile/res_config_view.xml rename to account_easy_reconcile/res_config_view.xml diff --git a/__unported__/account_easy_reconcile/security/ir.model.access.csv b/account_easy_reconcile/security/ir.model.access.csv similarity index 100% rename from __unported__/account_easy_reconcile/security/ir.model.access.csv rename to account_easy_reconcile/security/ir.model.access.csv diff --git a/__unported__/account_easy_reconcile/security/ir_rule.xml b/account_easy_reconcile/security/ir_rule.xml similarity index 100% rename from __unported__/account_easy_reconcile/security/ir_rule.xml rename to account_easy_reconcile/security/ir_rule.xml diff --git a/__unported__/account_easy_reconcile/simple_reconciliation.py b/account_easy_reconcile/simple_reconciliation.py similarity index 76% rename from __unported__/account_easy_reconcile/simple_reconciliation.py rename to account_easy_reconcile/simple_reconciliation.py index f108ff4f..41943517 100644 --- a/__unported__/account_easy_reconcile/simple_reconciliation.py +++ b/account_easy_reconcile/simple_reconciliation.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2012-2013 Camptocamp SA (Guewen Baconnier) +# Copyright 2012-2013, 2015 Camptocamp SA (Guewen Baconnier, Damien Crier) # Copyright (C) 2010 Sébastien Beau # # This program is free software: you can redistribute it and/or modify @@ -19,10 +19,11 @@ # ############################################################################## -from openerp.osv.orm import AbstractModel, TransientModel +# from openerp.osv.orm import AbstractModel, TransientModel +from openerp import models, api -class EasyReconcileSimple(AbstractModel): +class EasyReconcileSimple(models.AbstractModel): _name = 'easy.reconcile.simple' _inherit = 'easy.reconcile.base' @@ -30,7 +31,8 @@ class EasyReconcileSimple(AbstractModel): # 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): + @api.multi + def rec_auto_lines_simple(self, lines): if self._key_field is None: raise ValueError("_key_field has to be defined") count = 0 @@ -51,8 +53,9 @@ class EasyReconcileSimple(AbstractModel): if not check: continue reconciled, dummy = self._reconcile_lines( - cr, uid, rec, [credit_line, debit_line], - allow_partial=False, context=context) + [credit_line, debit_line], + allow_partial=False + ) if reconciled: res += [credit_line['id'], debit_line['id']] del lines[i] @@ -60,29 +63,30 @@ class EasyReconcileSimple(AbstractModel): count += 1 return res, [] # empty list for partial, only full rec in "simple" rec - def _simple_order(self, rec, *args, **kwargs): + @api.multi + def _simple_order(self, *args, **kwargs): return "ORDER BY account_move_line.%s" % self._key_field - def _action_rec(self, cr, uid, rec, context=None): + def _action_rec(self): """Match only 2 move lines, do not allow partial reconcile""" - select = self._select(rec) + select = self._select() select += ", account_move_line.%s " % self._key_field - where, params = self._where(rec) + where, params = self._where() where += " AND account_move_line.%s IS NOT NULL " % self._key_field - where2, params2 = self._get_filter(cr, uid, rec, context=context) + where2, params2 = self._get_filter() query = ' '.join(( select, - self._from(rec), + self._from(), where, where2, - self._simple_order(rec))) + self._simple_order())) - cr.execute(query, params + params2) - lines = cr.dictfetchall() - return self.rec_auto_lines_simple(cr, uid, rec, lines, context) + self.env.cr.execute(query, params + params2) + lines = self.env.cr.dictfetchall() + return self.rec_auto_lines_simple(lines) -class EasyReconcileSimpleName(TransientModel): +class EasyReconcileSimpleName(models.TransientModel): _name = 'easy.reconcile.simple.name' _inherit = 'easy.reconcile.simple' @@ -91,7 +95,7 @@ class EasyReconcileSimpleName(TransientModel): _key_field = 'name' -class EasyReconcileSimplePartner(TransientModel): +class EasyReconcileSimplePartner(models.TransientModel): _name = 'easy.reconcile.simple.partner' _inherit = 'easy.reconcile.simple' @@ -100,7 +104,7 @@ class EasyReconcileSimplePartner(TransientModel): _key_field = 'partner_id' -class EasyReconcileSimpleReference(TransientModel): +class EasyReconcileSimpleReference(models.TransientModel): _name = 'easy.reconcile.simple.reference' _inherit = 'easy.reconcile.simple' diff --git a/account_easy_reconcile/test/easy_reconcile.yml b/account_easy_reconcile/test/easy_reconcile.yml new file mode 100644 index 00000000..6142a597 --- /dev/null +++ b/account_easy_reconcile/test/easy_reconcile.yml @@ -0,0 +1,116 @@ +- + In order to test Confirm Draft Invoice wizard I create an invoice and confirm it with this wizard +- + !record {model: account.invoice, id: account_invoice_state2}: + account_id: account.a_recv + company_id: base.main_company + currency_id: base.EUR + invoice_line: + - account_id: account.a_sale + name: '[PCSC234] PC Assemble SC234' + price_unit: 1000.0 + quantity: 1.0 + product_id: product.product_product_3 + uos_id: product.product_uom_unit + journal_id: account.bank_journal + partner_id: base.res_partner_12 + reference_type: none +- + I called the "Confirm Draft Invoices" wizard +- + !record {model: account.invoice.confirm, id: account_invoice_confirm_0}: + {} +- + I clicked on Confirm Invoices Button +- + !python {model: account.invoice.confirm}: | + self.invoice_confirm(cr, uid, [ref("account_invoice_confirm_0")], {"lang": 'en_US', + "tz": False, "active_model": "account.invoice", "active_ids": [ref("account_invoice_state2")], + "type": "out_invoice", "active_id": ref("account_invoice_state2"), }) +- + I check that customer invoice state is "Open" +- + !assert {model: account.invoice, id: account_invoice_state2}: + - state == 'open' + + +- + In order to test Bank Statement feature of account I create a bank statement line and confirm it and check it's move created +- + I select the period and journal for the bank statement +- + !python {model: account.bank.statement}: | + import time + journal = self._default_journal_id(cr, uid, {'lang': u'en_US', 'tz': False, 'active_model': 'ir.ui.menu', + 'journal_type': 'bank', 'period_id': time.strftime('%m'), 'active_ids': [ref('account.menu_bank_statement_tree')], 'active_id': ref('account.menu_bank_statement_tree')}) + assert journal, 'Journal has not been selected' +- + I create a bank statement with Opening and Closing balance 0. +- + !record {model: account.bank.statement, id: account_bank_statement_0}: + balance_end_real: 0.0 + balance_start: 0.0 + date: !eval time.strftime('%Y-%m-%d') + journal_id: account.bank_journal +- + I create bank statement line +- + !python {model: account.bank.statement.line}: | + vals = { + 'amount': 1000.0, + 'partner_id': ref('base.res_partner_12'), + 'statement_id': ref('account_bank_statement_0'), + 'name': 'EXT001' + } + + line_id = self.create(cr, uid, vals) + assert line_id, "Account bank statement line has not been created" + +- + We process the reconciliation of the invoice line +- + !python {model: account.bank.statement}: | + line_id = None + invoice = self.pool.get('account.invoice').browse(cr, uid, ref("account_invoice_state2")) + for l in invoice.move_id.line_id: + if l.account_id.id == ref('account.a_recv'): + line_id = l + break + statement = self.browse(cr, uid, ref("account_bank_statement_0")) + for statement_line in statement.line_ids: + self.pool.get('account.bank.statement.line').process_reconciliation( + cr, uid, statement_line.id, + [ + { + 'counterpart_move_line_id': line_id.id, + 'credit': 1000.0, + 'debit': 0.0, + 'name': line_id.name + } + ] + ) +- + We unreconcile previous reconciliation so we can create an easy reconcile record to reconcile invoice +- + !python {model: account.move.line}: | + lines_to_unreconcile = self.search(cr, uid, [('reconcile_ref', '!=', False), ('statement_id', '=', ref("account_bank_statement_0"))]) + self._remove_move_reconcile(cr, uid, lines_to_unreconcile) + +- + We create the easy reconcile record +- + !record {model: account.easy.reconcile, id: account_easy_reconcile_0}: + name: 'easy reconcile 1' + account: account.a_recv + reconcile_method: + - name: 'easy.reconcile.simple.partner' +- + We call the automatic reconcilation method +- + !python {model: account.easy.reconcile}: | + self.run_reconcile(cr, uid, [ref('account_easy_reconcile_0')]) +- + I check that customer invoice state is "Paid" +- + !assert {model: account.invoice, id: account_invoice_state2}: + - state == 'paid' \ No newline at end of file diff --git a/account_easy_reconcile/tests/__init__.py b/account_easy_reconcile/tests/__init__.py new file mode 100644 index 00000000..c2feb554 --- /dev/null +++ b/account_easy_reconcile/tests/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Damien Crier +# Copyright 2015 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 . import test_onchange_company +from . import test_reconcile_history +from . import test_reconcile +from . import test_scenario_reconcile diff --git a/account_easy_reconcile/tests/test_onchange_company.py b/account_easy_reconcile/tests/test_onchange_company.py new file mode 100644 index 00000000..815fd468 --- /dev/null +++ b/account_easy_reconcile/tests/test_onchange_company.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Damien Crier +# Copyright 2015 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.tests import common + + +class testOnChange(common.TransactionCase): + + def setUp(self): + super(testOnChange, self).setUp() + self.acc_setting_obj = self.registry('account.config.settings') + self.company_obj = self.registry('res.company') + # analytic defaults account creation + self.main_company = self.ref('base.main_company') + self.sec_company = self.company_obj.create( + self.cr, + self.uid, + { + 'name': 'Second company', + 'reconciliation_commit_every': 80, + } + ) + + def test_retrieve_analytic_account(self): + sec_company_commit = self.company_obj.browse( + self.cr, + self.uid, + self.sec_company).reconciliation_commit_every + main_company_commit = self.company_obj.browse( + self.cr, + self.uid, + self.main_company).reconciliation_commit_every + + res1 = self.acc_setting_obj.onchange_company_id( + self.cr, self.uid, [], self.sec_company) + + self.assertEqual(sec_company_commit, res1.get( + 'value', {}).get('reconciliation_commit_every', False)) + + res2 = self.acc_setting_obj.onchange_company_id( + self.cr, self.uid, [], self.main_company) + self.assertEqual(main_company_commit, res2.get( + 'value', {}).get('reconciliation_commit_every', False)) +# self.assertEqual(self.ref('account.analytic_agrolait'), res2.get( +# 'value', {}).get('reconciliation_commit_every', False)) diff --git a/account_easy_reconcile/tests/test_reconcile.py b/account_easy_reconcile/tests/test_reconcile.py new file mode 100644 index 00000000..72d2de4b --- /dev/null +++ b/account_easy_reconcile/tests/test_reconcile.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Damien Crier +# Copyright 2015 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.tests import common +from openerp import fields, exceptions + + +class testReconcile(common.TransactionCase): + + def setUp(self): + super(testReconcile, self).setUp() + self.rec_history_obj = self.registry('easy.reconcile.history') + self.easy_rec_obj = self.registry('account.easy.reconcile') + self.easy_rec_method_obj = ( + self.registry('account.easy.reconcile.method') + ) + self.easy_rec = self.easy_rec_obj.create( + self.cr, + self.uid, + { + 'name': 'AER2', + 'account': self.ref('account.a_salary_expense'), + } + ) + self.easy_rec_method = self.easy_rec_method_obj.create( + self.cr, + self.uid, + { + 'name': 'easy.reconcile.simple.name', + 'sequence': '10', + 'task_id': self.easy_rec, + } + ) + self.easy_rec_no_history = self.easy_rec_obj.create( + self.cr, + self.uid, + { + 'name': 'AER3', + 'account': self.ref('account.a_salary_expense'), + + } + ) + self.rec_history = self.rec_history_obj.create( + self.cr, + self.uid, + { + 'easy_reconcile_id': self.easy_rec, + 'date': fields.Datetime.now(), + } + ) + + def test_last_history(self): + easy_rec_last_hist = self.easy_rec_obj.browse( + self.cr, + self.uid, + self.easy_rec + ).last_history.id + self.assertEqual(self.rec_history, easy_rec_last_hist) + + def test_last_history_empty(self): + easy_rec_last_hist = self.easy_rec_obj.browse( + self.cr, + self.uid, + self.easy_rec_no_history + ).last_history.id + self.assertEqual(False, easy_rec_last_hist) + + def test_last_history_full_no_history(self): + with self.assertRaises(exceptions.Warning): + self.easy_rec_obj.last_history_reconcile( + self.cr, self.uid, [self.easy_rec_no_history]) + + def test_last_history_partial_no_history(self): + with self.assertRaises(exceptions.Warning): + self.easy_rec_obj.last_history_partial( + self.cr, self.uid, [self.easy_rec_no_history]) + + def test_open_unreconcile(self): + res = self.easy_rec_obj.open_unreconcile( + self.cr, + self.uid, + [self.easy_rec] + ) + self.assertEqual(unicode([('id', 'in', [])]), res.get('domain', [])) + + def test_open_partial_reconcile(self): + res = self.easy_rec_obj.open_partial_reconcile( + self.cr, + self.uid, + [self.easy_rec] + ) + self.assertEqual(unicode([('id', 'in', [])]), res.get('domain', [])) + + def test_prepare_run_transient(self): + res = self.easy_rec_obj._prepare_run_transient( + self.cr, + self.uid, + self.easy_rec_method_obj.browse( + self.cr, + self.uid, + self.easy_rec_method + ) + ) + self.assertEqual(self.ref('account.a_salary_expense'), + res.get('account_id', 0)) + + +class testReconcileNoEasyReconcileAvailable(common.TransactionCase): + + def setUp(self): + super(testReconcileNoEasyReconcileAvailable, self).setUp() + self.rec_history_obj = self.registry('easy.reconcile.history') + self.easy_rec_obj = self.registry('account.easy.reconcile') + +# def test_run_scheduler(self): +# with self.assertRaises(AssertionError): +# self.easy_rec_obj.run_scheduler( +# self.cr, self.uid) diff --git a/account_easy_reconcile/tests/test_reconcile_history.py b/account_easy_reconcile/tests/test_reconcile_history.py new file mode 100644 index 00000000..3c8a616d --- /dev/null +++ b/account_easy_reconcile/tests/test_reconcile_history.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Damien Crier +# Copyright 2015 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.tests import common +from openerp import fields + + +class testReconcileHistory(common.TransactionCase): + + def setUp(self): + super(testReconcileHistory, self).setUp() + self.rec_history_obj = self.registry('easy.reconcile.history') + self.easy_rec_obj = self.registry('account.easy.reconcile') + self.easy_rec = self.easy_rec_obj.create( + self.cr, + self.uid, + { + 'name': 'AER1', + 'account': self.ref('account.a_expense'), + + } + ) + self.rec_history = self.rec_history_obj.create( + self.cr, + self.uid, + { + 'easy_reconcile_id': self.easy_rec, + 'date': fields.Datetime.now(), + } + ) + + def test_assert_open_full_partial(self): + word = 'test' + with self.assertRaises(AssertionError): + self.rec_history_obj._open_move_lines( + self.cr, self.uid, [self.rec_history], word) + + def test_open_full_empty(self): + res = self.rec_history_obj._open_move_lines( + self.cr, self.uid, [self.rec_history], 'full') + self.assertEqual(unicode([('id', 'in', [])]), res.get( + 'domain', [])) + + def test_open_full_empty_from_method(self): + res = self.rec_history_obj.open_reconcile( + self.cr, self.uid, [self.rec_history]) + self.assertEqual(unicode([('id', 'in', [])]), res.get( + 'domain', [])) + + def test_open_partial_empty(self): + res = self.rec_history_obj._open_move_lines( + self.cr, self.uid, [self.rec_history], 'partial') + self.assertEqual(unicode([('id', 'in', [])]), res.get( + 'domain', [])) + + def test_open_partial_empty_from_method(self): + res = self.rec_history_obj.open_partial( + self.cr, self.uid, [self.rec_history]) + self.assertEqual(unicode([('id', 'in', [])]), res.get( + 'domain', [])) diff --git a/account_easy_reconcile/tests/test_scenario_reconcile.py b/account_easy_reconcile/tests/test_scenario_reconcile.py new file mode 100644 index 00000000..08da279f --- /dev/null +++ b/account_easy_reconcile/tests/test_scenario_reconcile.py @@ -0,0 +1,298 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Damien Crier +# Copyright 2015 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.tests import common +import time + + +class testScenarioReconcile(common.TransactionCase): + + def setUp(self): + super(testScenarioReconcile, self).setUp() + self.rec_history_obj = self.registry('easy.reconcile.history') + self.easy_rec_obj = self.registry('account.easy.reconcile') + self.invoice_obj = self.registry('account.invoice') + self.bk_stmt_obj = self.registry('account.bank.statement') + self.bk_stmt_line_obj = self.registry('account.bank.statement.line') + self.acc_move_line_obj = self.registry('account.move.line') + self.easy_rec_method_obj = ( + self.registry('account.easy.reconcile.method') + ) + self.account_fx_income_id = self.ref("account.income_fx_income") + self.account_fx_expense_id = self.ref("account.income_fx_expense") + self.acs_model = self.registry('account.config.settings') + + acs_ids = self.acs_model.search( + self.cr, self.uid, + [('company_id', '=', self.ref("base.main_company"))] + ) + + values = {'group_multi_currency': True, + 'income_currency_exchange_account_id': + self.account_fx_income_id, + 'expense_currency_exchange_account_id': + self.account_fx_expense_id} + + if acs_ids: + self.acs_model.write(self.cr, self.uid, acs_ids, values) + else: + default_vals = self.acs_model.default_get(self.cr, self.uid, []) + default_vals.update(values) + default_vals['date_stop'] = time.strftime('%Y-12-31') + default_vals['date_start'] = time.strftime('%Y-%m-%d') + default_vals['period'] = 'month' + self.acs_model.create(self.cr, self.uid, default_vals) + + def test_scenario_reconcile(self): + # create invoice + inv_id = self.invoice_obj.create( + self.cr, + self.uid, + { + 'type': 'out_invoice', + 'account_id': self.ref('account.a_recv'), + 'company_id': self.ref('base.main_company'), + 'currency_id': self.ref('base.EUR'), + 'journal_id': self.ref('account.sales_journal'), + 'partner_id': self.ref('base.res_partner_12'), + 'invoice_line': [ + (0, 0, { + 'name': '[PCSC234] PC Assemble SC234', + 'price_unit': 1000.0, + 'quantity': 1.0, + 'product_id': self.ref('product.product_product_3'), + 'uos_id': self.ref('product.product_uom_unit'), + } + ) + ] + } + ) + # validate invoice + self.invoice_obj.signal_workflow( + self.cr, + self.uid, + [inv_id], + 'invoice_open' + ) + invoice_record = self.invoice_obj.browse(self.cr, self.uid, [inv_id]) + self.assertEqual('open', invoice_record.state) + + # create bank_statement + bk_stmt_id = self.bk_stmt_obj.create( + self.cr, + self.uid, + { + 'balance_end_real': 0.0, + 'balance_start': 0.0, + 'date': time.strftime('%Y-%m-%d'), + 'journal_id': self.ref('account.bank_journal'), + 'line_ids': [ + (0, 0, { + 'amount': 1000.0, + 'partner_id': self.ref('base.res_partner_12'), + 'name': invoice_record.number, + 'ref': invoice_record.number, + } + ) + ] + } + ) + + # reconcile + line_id = None + for l in invoice_record.move_id.line_id: + if l.account_id.id == self.ref('account.a_recv'): + line_id = l + break + + statement = self.bk_stmt_obj.browse(self.cr, self.uid, bk_stmt_id) + for statement_line in statement.line_ids: + self.bk_stmt_line_obj.process_reconciliation( + self.cr, self.uid, statement_line.id, + [ + { + 'counterpart_move_line_id': line_id.id, + 'credit': 1000.0, + 'debit': 0.0, + 'name': invoice_record.number, + } + ] + ) + # unreconcile journal item created by previous reconciliation + lines_to_unreconcile = self.acc_move_line_obj.search( + self.cr, + self.uid, + [('reconcile_ref', '!=', False), + ('statement_id', '=', bk_stmt_id)] + ) + self.acc_move_line_obj._remove_move_reconcile( + self.cr, + self.uid, + lines_to_unreconcile + ) + + # create the easy reconcile record + easy_rec_id = self.easy_rec_obj.create( + self.cr, + self.uid, + { + 'name': 'easy_reconcile_1', + 'account': self.ref('account.a_recv'), + 'reconcile_method': [ + (0, 0, { + 'name': 'easy.reconcile.simple.partner', + } + ) + ] + } + ) + # call the automatic reconcilation method + self.easy_rec_obj.run_reconcile( + self.cr, + self.uid, + [easy_rec_id] + ) + self.assertEqual( + 'paid', + self.invoice_obj.browse(self.cr, self.uid, inv_id).state + ) + + def test_scenario_reconcile_currency(self): + # create currency rate + self.registry('res.currency.rate').create(self.cr, self.uid, { + 'name': time.strftime('%Y-%m-%d') + ' 00:00:00', + 'currency_id': self.ref('base.USD'), + 'rate': 1.5, + }) + # create invoice + inv_id = self.invoice_obj.create( + self.cr, + self.uid, + { + 'type': 'out_invoice', + 'account_id': self.ref('account.a_recv'), + 'company_id': self.ref('base.main_company'), + 'currency_id': self.ref('base.USD'), + 'journal_id': self.ref('account.bank_journal_usd'), + 'partner_id': self.ref('base.res_partner_12'), + 'invoice_line': [ + (0, 0, { + 'name': '[PCSC234] PC Assemble SC234', + 'price_unit': 1000.0, + 'quantity': 1.0, + 'product_id': self.ref('product.product_product_3'), + 'uos_id': self.ref('product.product_uom_unit'), + } + ) + ] + } + ) + # validate invoice + self.invoice_obj.signal_workflow( + self.cr, + self.uid, + [inv_id], + 'invoice_open' + ) + invoice_record = self.invoice_obj.browse(self.cr, self.uid, [inv_id]) + self.assertEqual('open', invoice_record.state) + + # create bank_statement + bk_stmt_id = self.bk_stmt_obj.create( + self.cr, + self.uid, + { + 'balance_end_real': 0.0, + 'balance_start': 0.0, + 'date': time.strftime('%Y-%m-%d'), + 'journal_id': self.ref('account.bank_journal_usd'), + 'line_ids': [ + (0, 0, { + 'amount': 1000.0, + 'amount_currency': 1500.0, + 'currency_id': self.ref('base.USD'), + 'partner_id': self.ref('base.res_partner_12'), + 'name': invoice_record.number, + 'ref': invoice_record.number, + } + ) + ] + } + ) + + # reconcile + line_id = None + for l in invoice_record.move_id.line_id: + if l.account_id.id == self.ref('account.a_recv'): + line_id = l + break + + statement = self.bk_stmt_obj.browse(self.cr, self.uid, bk_stmt_id) + for statement_line in statement.line_ids: + self.bk_stmt_line_obj.process_reconciliation( + self.cr, self.uid, statement_line.id, + [ + { + 'counterpart_move_line_id': line_id.id, + 'credit': 1000.0, + 'debit': 0.0, + 'name': invoice_record.number, + } + ] + ) + # unreconcile journal item created by previous reconciliation + lines_to_unreconcile = self.acc_move_line_obj.search( + self.cr, + self.uid, + [('reconcile_ref', '!=', False), + ('statement_id', '=', bk_stmt_id)] + ) + self.acc_move_line_obj._remove_move_reconcile( + self.cr, + self.uid, + lines_to_unreconcile + ) + + # create the easy reconcile record + easy_rec_id = self.easy_rec_obj.create( + self.cr, + self.uid, + { + 'name': 'easy_reconcile_1', + 'account': self.ref('account.a_recv'), + 'reconcile_method': [ + (0, 0, { + 'name': 'easy.reconcile.simple.partner', + } + ) + ] + } + ) + # call the automatic reconcilation method + self.easy_rec_obj.run_reconcile( + self.cr, + self.uid, + [easy_rec_id] + ) + self.assertEqual( + 'paid', + self.invoice_obj.browse(self.cr, self.uid, inv_id).state + )