mirror of
https://github.com/OCA/account-reconcile.git
synced 2025-01-20 12:27:39 +02:00
Merge branch 'damdam-s-migrate_account_easy_reconcile' into 8.0
This commit is contained in:
@@ -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
|
||||
@@ -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)
|
||||
76
account_easy_reconcile/README.rst
Normal file
76
account_easy_reconcile/README.rst
Normal 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.
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
}
|
||||
@@ -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
|
||||
379
account_easy_reconcile/easy_reconcile.py
Normal file
379
account_easy_reconcile/easy_reconcile.py
Normal 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
|
||||
@@ -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>
|
||||
139
account_easy_reconcile/easy_reconcile_history.py
Normal file
139
account_easy_reconcile/easy_reconcile_history.py
Normal 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')
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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."
|
||||
)
|
||||
@@ -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'
|
||||
|
||||
116
account_easy_reconcile/test/easy_reconcile.yml
Normal file
116
account_easy_reconcile/test/easy_reconcile.yml
Normal 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'
|
||||
25
account_easy_reconcile/tests/__init__.py
Normal file
25
account_easy_reconcile/tests/__init__.py
Normal 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
|
||||
63
account_easy_reconcile/tests/test_onchange_company.py
Normal file
63
account_easy_reconcile/tests/test_onchange_company.py
Normal 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))
|
||||
136
account_easy_reconcile/tests/test_reconcile.py
Normal file
136
account_easy_reconcile/tests/test_reconcile.py
Normal 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)
|
||||
78
account_easy_reconcile/tests/test_reconcile_history.py
Normal file
78
account_easy_reconcile/tests/test_reconcile_history.py
Normal 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', []))
|
||||
298
account_easy_reconcile/tests/test_scenario_reconcile.py
Normal file
298
account_easy_reconcile/tests/test_scenario_reconcile.py
Normal 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
|
||||
)
|
||||
Reference in New Issue
Block a user