Merge branch 'damdam-s-migrate_account_easy_reconcile' into 8.0

This commit is contained in:
Yannick Vaucher
2015-09-10 13:20:29 +02:00
24 changed files with 1877 additions and 1018 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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)

View File

@@ -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 <https://github.com/OCA/bank-statement-reconcile/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 <https://github.com/OCA/bank-statement-reconcile/issues/new?body=module:%20account_easy_reconcile%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
Contributors
------------
* Sébastien Beau <sebastien.beau@akretion.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* Vincent Renaville <vincent.renaville@camptocamp.com>
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
* Joël Grand-Guillaume <joel.grandguillaume@camptocamp.com>
* Nicolas Bessis <nicolas.bessi@camptocamp.com>
* Pedro M.Baeza <pedro.baeza@gmail.com>
* Matthieu Dietrich <matthieu.dietrich@camptocamp.com>
* Leonardo Pistone <leonardo.pistone@camptocamp.com>
* Ecino <ecino@compassion.ch>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Rudolf Schnapka <rs@techno-flex.de>
* Florian Dacosta <florian.dacosta@akretion.com>
* Laetitia Gangloff <laetitia.gangloff@acsone.eu>
* Frédéric Clémenti <frederic.clementi@camptocamp.com>
* Damien Crier <damien.crier@camptocamp.com>
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.

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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

View File

@@ -136,7 +136,7 @@ The lines should have the same amount (with the write-off) and the same referenc
<field name="income_exchange_account_id" groups="base.group_multi_currency"/>
<field name="expense_exchange_account_id" groups="base.group_multi_currency"/>
<field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_account_id" domain="[('type', '!=', 'view')]" groups="analytic.group_analytic_accounting"/>
<field name="date_base_on"/>
</form>
</field>
@@ -156,7 +156,7 @@ The lines should have the same amount (with the write-off) and the same referenc
<field name="income_exchange_account_id" groups="base.group_multi_currency"/>
<field name="expense_exchange_account_id" groups="base.group_multi_currency"/>
<field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_account_id" domain="[('type', '!=', 'view')]" groups="analytic.group_analytic_accounting"/>
<field name="date_base_on"/>
</tree>
</field>

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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')

View File

@@ -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 "<p class=\"oe_view_nocontent_create\">\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 ""

View File

@@ -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."
)

View File

@@ -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'

View File

@@ -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'

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_onchange_company
from . import test_reconcile_history
from . import test_reconcile
from . import test_scenario_reconcile

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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))

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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)

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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', []))

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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
)