Merge branch '7.0' into 8.0

Conflicts:
	.travis.yml
	__unported__/account_advanced_reconcile/res_config.py
	__unported__/account_advanced_reconcile/res_config_view.xml
	__unported__/account_statement_base_completion/__openerp__.py
	__unported__/account_statement_base_completion/i18n/fr.po
	account_advanced_reconcile/res_config.py
	account_advanced_reconcile/res_config_view.xml
	account_easy_reconcile/res_config.py
	account_easy_reconcile/res_config_view.xml
	account_invoice_reference/__openerp__.py
	account_invoice_reference/account_move.py
	statement_voucher_killer/__openerp__.py
	statement_voucher_killer/voucher.py
This commit is contained in:
Leonardo Pistone
2015-01-13 14:47:22 +01:00
68 changed files with 1174 additions and 348 deletions

View File

@@ -3,6 +3,7 @@ include =
*/bank-statement-reconcile/*
omit =
*/test/*
*/tests/*
*__init__.py

View File

@@ -20,7 +20,6 @@
#
##############################################################################
import easy_reconcile
import base_advanced_reconciliation
import advanced_reconciliation
import res_config # noqa
from . import easy_reconcile
from . import base_advanced_reconciliation
from . import advanced_reconciliation

View File

@@ -64,8 +64,8 @@ The base class to find the reconciliations is built to be as efficient as
possible.
So basically, if you have an invoice with 3 payments (one per month), the first
month, it will partial reconcile the debit move line with the first payment, the
second month, it will partial reconcile the debit move line with 2 first
month, it will partial reconcile the debit move line with the first payment,
the second month, it will partial reconcile the debit move line with 2 first
payments, the third month, it will make the full reconciliation.
This module is perfectly adapted for E-Commerce business where a big volume of
@@ -75,7 +75,7 @@ many offices.
""",
'website': 'http://www.camptocamp.com',
'data': ['easy_reconcile_view.xml',
'res_config_view.xml'],
],
'test': [],
'images': [],
'installable': False,

View File

@@ -23,10 +23,8 @@ import logging
from itertools import product
from openerp.osv import orm
from openerp import pooler
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@@ -188,7 +186,8 @@ class easy_reconcile_advanced(orm.AbstractModel):
If all the matchers match for a move line and an opposite move line,
they are candidate for a reconciliation.
"""
opp_matchers = self._opposite_matchers(cr, uid, rec, opposite_move_line,
opp_matchers = self._opposite_matchers(cr, uid, rec,
opposite_move_line,
context=context)
for matcher in matchers:
try:
@@ -222,29 +221,10 @@ class easy_reconcile_advanced(orm.AbstractModel):
cr, uid, rec, move_line, op, matchers, context=context)]
def _action_rec(self, cr, uid, rec, context=None):
# 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 = {}
ctx = context.copy()
ctx['commit_every'] = (
rec.journal_id.company_id.reconciliation_commit_every
)
if ctx['commit_every']:
new_cr = pooler.get_db(cr.dbname).cursor()
else:
new_cr = cr
try:
credit_lines = self._query_credit(new_cr, uid, rec, context=ctx)
debit_lines = self._query_debit(new_cr, uid, rec, context=ctx)
credit_lines = self._query_credit(cr, uid, rec, context=context)
debit_lines = self._query_debit(cr, uid, rec, context=context)
result = self._rec_auto_lines_advanced(
new_cr, uid, rec, credit_lines, debit_lines, context=ctx)
finally:
if ctx['commit_every']:
new_cr.commit()
new_cr.close()
cr, uid, rec, credit_lines, debit_lines, context=context)
return result
def _skip_line(self, cr, uid, rec, move_line, context=None):

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Matthieu Dietrich
# Copyright 2014 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 easy_reconcile
from . import advanced_reconciliation

View File

@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Matthieu Dietrich
# Copyright 2014 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/>.
#
##############################################################################
{'name': 'Advanced Reconcile Bank Statement',
'description': """
Advanced reconciliation method for the module account_advanced_reconcile
========================================================================
Reconcile rules with bank statement name.
This will reconcile multiple credit move lines (bank statements) with
all the lines from a specific bank statement, debit or credit (to also
reconcile the commission with credit card imports).
""",
'version': '1.0.0',
'author': 'Camptocamp',
'category': 'Finance',
'website': 'http://www.camptocamp.com',
'depends': ['account_advanced_reconcile'],
'data': ['easy_reconcile_view.xml'],
'demo': [],
'test': [],
'auto_install': False,
'installable': True,
'images': []
}

View File

@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Matthieu Dietrich. Copyright 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
class easy_reconcile_advanced_bank_statement(orm.TransientModel):
_name = 'easy.reconcile.advanced.bank_statement'
_inherit = 'easy.reconcile.advanced'
def _base_columns(self, rec):
""" Mandatory columns for move lines queries
An extra column aliased as ``key`` should be defined
in each query."""
aml_cols = super(easy_reconcile_advanced_bank_statement, self).\
_base_columns(rec)
aml_cols += ['account_move_line.statement_id',
'account_bank_statement.name as statement_name',
]
return aml_cols
def _from(self, rec, *args, **kwargs):
result = super(easy_reconcile_advanced_bank_statement, self).\
_from(rec, *args, **kwargs)
result = result + (
" INNER JOIN account_bank_statement "
"ON account_bank_statement.id = account_move_line.statement_id "
)
return result
def _skip_line(self, cr, uid, rec, move_line, context=None):
"""
When True is returned on some conditions, the credit move line
will be skipped for reconciliation. Can be inherited to
skip on some conditions. ie: ref or partner_id is empty.
"""
result = super(easy_reconcile_advanced_bank_statement, self).\
_skip_line(cr, uid, rec, move_line, context=context)
if result:
return result
return not (move_line.get('ref') and
move_line.get('partner_id'))
def _matchers(self, cr, uid, rec, move_line, context=None):
return (('partner_id', move_line['partner_id']),
('ref', move_line['ref'].lower().strip()))
def _opposite_matchers(self, cr, uid, rec, move_line, context=None):
yield ('partner_id', move_line['partner_id'])
yield ('ref',
(move_line['statement_name'] or '').lower().strip())
# Re-defined for this particular rule; since the commission line is a
# credit line inside of the target statement, it should also be considered
# as an opposite to be reconciled.
# And also, given some are refunds, debit lines can be "credit".
def _action_rec(self, cr, uid, rec, context=None):
credit_lines = self._query_credit(cr, uid, rec, context=context)
debit_lines = self._query_debit(cr, uid, rec, context=context)
return self._rec_auto_lines_advanced(cr, uid, rec,
credit_lines + debit_lines,
credit_lines + debit_lines,
context=context)

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Matthieu Dietrich
# Copyright 2014 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
class account_easy_reconcile_method(orm.Model):
_inherit = 'account.easy.reconcile.method'
def _get_all_rec_method(self, cr, uid, context=None):
methods = super(account_easy_reconcile_method, self).\
_get_all_rec_method(cr, uid, context=context)
methods += [
('easy.reconcile.advanced.bank_statement',
'Advanced. Partner and Bank Statement'),
]
return methods

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="view_easy_reconcile_form" model="ir.ui.view">
<field name="name">account.easy.reconcile.form</field>
<field name="model">account.easy.reconcile</field>
<field name="inherit_id" ref="account_easy_reconcile.account_easy_reconcile_form"/>
<field name="arch" type="xml">
<page name="information" position="inside">
<group colspan="2" col="2">
<separator colspan="4" string="Advanced. Partner and Bank Statement"/>
<label string="Match multiple debit vs multiple credit entries. Allow partial reconciliation.
The lines should have the partner, the credit entry reference is matched vs the debit entry bank statement name." colspan="4"/>
</group>
</page>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,107 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * account_advanced_reconcile_bank_statement
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-05-27 08:36+0000\n"
"PO-Revision-Date: 2014-05-27 08:36+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,filter:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_filter
msgid "Filter"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,account_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_account_id
msgid "Account"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,partner_ids:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_partner_ids
msgid "Restrict on partners"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: code:_description:0
#: model:ir.model,name:account_advanced_reconcile_bank_statement.model_easy_reconcile_advanced_bank_statement
#, python-format
msgid "easy.reconcile.advanced.bank_statement"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: view:account.easy.reconcile:0
msgid "Match multiple debit vs multiple credit entries. Allow partial reconciliation. The lines should have the partner, the credit entry reference is matched vs the debit entry bank statement name."
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,journal_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_journal_id
msgid "Journal"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,account_profit_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_account_profit_id
msgid "Account Profit"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,analytic_account_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_analytic_account_id
msgid "Analytic Account"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: code:_description:0
#: model:ir.model,name:account_advanced_reconcile_bank_statement.model_account_easy_reconcile_method
#, python-format
msgid "reconcile method for account_easy_reconcile"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,date_base_on:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_date_base_on
msgid "Date of reconciliation"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: help:easy.reconcile.advanced.bank_statement,analytic_account_id:0
msgid "Analytic account for the write-off"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: view:account.easy.reconcile:0
msgid "Advanced. Partner and Bank Statement"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: code:_description:0
#: model:ir.model,name:account_advanced_reconcile_bank_statement.model_easy_reconcile_advanced
#, python-format
msgid "easy.reconcile.advanced"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,account_lost_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_account_lost_id
msgid "Account Lost"
msgstr ""
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,write_off:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_write_off
msgid "Write off allowed"
msgstr ""

View File

@@ -0,0 +1,103 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * account_advanced_reconcile_bank_statement
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-05-27 08:36+0000\n"
"PO-Revision-Date: 2014-05-27 08:36+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,filter:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_filter
msgid "Filter"
msgstr "Filtre"
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,account_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_account_id
msgid "Account"
msgstr "Compte"
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,partner_ids:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_partner_ids
msgid "Restrict on partners"
msgstr "Restriction sur les partenaires"
#. module: account_advanced_reconcile_bank_statement
#: code:_description:0
#: model:ir.model,name:account_advanced_reconcile_bank_statement.model_easy_reconcile_advanced_bank_statement
#, python-format
msgid "easy.reconcile.advanced.bank_statement"
msgstr "easy.reconcile.advanced.bank_statement"
#. module: account_advanced_reconcile_bank_statement
#: view:account.easy.reconcile:0
msgid ""
"Match multiple debit vs multiple credit entries. Allow partial "
"reconciliation. The lines should have the partner, the credit entry "
"reference is matched vs the debit entry bank statement name."
msgstr ""
"Le lettrage peut s'effectuer sur plusieurs écritures de débit et crédit. Le "
"lettrage partiel est autorisé. Les écritures doivent avoir le même "
"partenaire et la référence sur les écritures de crédit doit se retrouver
"dans le nom du relevé bancaire des écritures de débit."
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,journal_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_journal_id
msgid "Journal"
msgstr "Journal"
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,account_profit_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_account_profit_id
msgid "Account Profit"
msgstr "Compte de produit"
#. module: account_advanced_reconcile_bank_statement
#: code:_description:0
#: model:ir.model,name:account_advanced_reconcile_bank_statement.model_account_easy_reconcile_method
#, python-format
msgid "reconcile method for account_easy_reconcile"
msgstr "Méthode de lettrage pour le module account_easy_reconcile"
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,date_base_on:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_date_base_on
msgid "Date of reconciliation"
msgstr "Date de lettrage"
#. module: account_advanced_reconcile_bank_statement
#: view:account.easy.reconcile:0
msgid "Advanced. Partner and Bank Statement"
msgstr "Avancé. Partenaire et Relevé Bancaire"
#. module: account_advanced_reconcile_bank_statement
#: code:_description:0
#: model:ir.model,name:account_advanced_reconcile_bank_statement.model_easy_reconcile_advanced
#, python-format
msgid "easy.reconcile.advanced"
msgstr "easy.reconcile.advanced"
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,account_lost_id:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_account_lost_id
msgid "Account Lost"
msgstr "Compte de charge"
#. module: account_advanced_reconcile_bank_statement
#: field:easy.reconcile.advanced.bank_statement,write_off:0
#: model:ir.model.fields,field_description:account_advanced_reconcile_bank_statement.field_easy_reconcile_advanced_bank_statement_write_off
msgid "Write off allowed"
msgstr "Ecart autorisé"

View File

@@ -30,16 +30,6 @@ class EasyReconcileAdvanced(orm.AbstractModel):
""" Mandatory columns for move lines queries
An extra column aliased as ``key`` should be defined
in each query."""
aml_cols = (
'id',
'debit',
'credit',
'date',
'period_id',
'ref',
'name',
'partner_id',
'account_id',
'move_id',
'transaction_ref')
return ["account_move_line.%s" % col for col in aml_cols]
aml_cols = super(EasyReconcileAdvanced, self)._base_columns(rec)
aml_cols.append('account_move_line.transaction_ref')
return aml_cols

View File

@@ -19,7 +19,8 @@
#
##############################################################################
import easy_reconcile
import base_reconciliation
import simple_reconciliation
import easy_reconcile_history
from . import easy_reconcile
from . import base_reconciliation
from . import simple_reconciliation
from . import easy_reconcile_history
from . import res_config

View File

@@ -58,7 +58,9 @@ allows multiple lines and partial.
"data": ["easy_reconcile.xml",
"easy_reconcile_history_view.xml",
"security/ir_rule.xml",
"security/ir.model.access.csv"],
"security/ir.model.access.csv",
"res_config_view.xml",
],
'license': 'AGPL-3',
"auto_install": False,
'installable': False,

View File

@@ -71,6 +71,7 @@ class EasyReconcileBase(orm.AbstractModel):
'name',
'partner_id',
'account_id',
'reconcile_partial_id',
'move_id')
return ["account_move_line.%s" % col for col in aml_cols]
@@ -78,10 +79,15 @@ class EasyReconcileBase(orm.AbstractModel):
return "SELECT %s" % ', '.join(self._base_columns(rec))
def _from(self, rec, *args, **kwargs):
return "FROM account_move_line"
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):
where = ("WHERE account_move_line.account_id = %s "
"AND COALESCE(account_move_reconcile.type,'') <> 'manual' "
"AND account_move_line.reconcile_id IS NULL ")
# it would be great to use dict for params
# but as we use _where_calc in _get_filter
@@ -177,7 +183,7 @@ class EasyReconcileBase(orm.AbstractModel):
cr, uid, rec, lines, rec.date_base_on, context=context)
rec_ctx = dict(context, date_p=date)
if below_writeoff:
if sum_credit < sum_debit:
if sum_credit > sum_debit:
writeoff_account_id = rec.account_profit_id.id
else:
writeoff_account_id = rec.account_lost_id.id
@@ -195,10 +201,42 @@ class EasyReconcileBase(orm.AbstractModel):
context=rec_ctx)
return True, True
elif allow_partial:
# Check if the group of move lines was already partially
# reconciled and if all the lines were the same, in such
# case, just skip the group and consider it as partially
# reconciled (no change).
if lines:
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
# We need to give a writeoff_acc_id
# in case we have a multi currency lines
# to reconcile.
# If amount in currency is equal between
# lines to reconcile
# 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
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,
type='manual',
writeoff_acc_id=writeoff_account_id,
writeoff_period_id=period_id,
writeoff_journal_id=rec.journal_id.id,
context=rec_ctx)
return True, False
return False, False

View File

@@ -19,8 +19,14 @@
#
##############################################################################
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):
@@ -60,6 +66,11 @@ class EasyReconcileOptions(orm.AbstractModel):
'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 = {
@@ -131,6 +142,7 @@ class AccountEasyReconcileMethod(orm.Model):
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):
@@ -159,11 +171,16 @@ class AccountEasyReconcile(orm.Model):
def _last_history(self, cr, uid, ids, name, args, context=None):
result = {}
for history in self.browse(cr, uid, ids, context=context):
result[history.id] = False
if history.history_ids:
# history is sorted by date desc
result[history.id] = history.history_ids[0].id
# 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 = {
@@ -202,13 +219,19 @@ class AccountEasyReconcile(orm.Model):
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(fieldname, move_line_ids):
def find_reconcile_ids(cr, fieldname, move_line_ids):
if not move_line_ids:
return []
sql = ("SELECT DISTINCT " + fieldname +
@@ -219,41 +242,75 @@ class AccountEasyReconcile(orm.Model):
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(
cr, uid,
new_cr, uid,
self._prepare_run_transient(
cr, uid, method, context=context),
new_cr, uid, method, context=context),
context=context)
ml_rec_ids, ml_partial_ids = rec_model.automatic_reconcile(
cr, uid, auto_rec_id, context=context)
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(
'reconcile_id', all_ml_rec_ids)
new_cr, 'reconcile_id', all_ml_rec_ids)
partial_ids = find_reconcile_ids(
'reconcile_partial_id', all_ml_partial_ids)
new_cr, 'reconcile_partial_id', all_ml_partial_ids)
self.pool.get('easy.reconcile.history').create(
cr,
uid,
{'easy_reconcile_id': rec.id,
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)
'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 `osv.except_osv` error, supposed to
""" Raise an `orm.except_orm` error, supposed to
be called when there is no history on the reconciliation
task.
"""
@@ -333,3 +390,30 @@ class AccountEasyReconcile(orm.Model):
if not rec.last_history:
self._no_history(cr, uid, rec, context=context)
return rec.last_history.open_partial()
def run_scheduler(self, cr, uid, run_all=None, context=None):
""" Launch the reconcile with the oldest run
This function is mostly here to be used with cron task
:param run_all: if set it will ingore lookup and launch
all reconciliation
:returns: True in case of success or raises an exception
"""
def _get_date(reconcile):
if reconcile.last_history.date:
return datetime.strptime(reconcile.last_history.date,
DEFAULT_SERVER_DATETIME_FORMAT)
else:
return datetime.min
ids = self.search(cr, uid, [], context=context)
assert ids, "No easy reconcile available"
if run_all:
self.run_reconcile(cr, uid, ids, context=context)
return True
reconciles = self.browse(cr, uid, ids, context=context)
reconciles.sort(key=_get_date)
older = reconciles[0]
self.run_reconcile(cr, uid, [older.id], context=context)
return True

View File

@@ -71,6 +71,10 @@ The lines should have the same amount (with the write-off) and the same referenc
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
@@ -129,6 +133,8 @@ The lines should have the same amount (with the write-off) and the same referenc
<field name="write_off"/>
<field name="account_lost_id" attrs="{'required':[('write_off','>',0)]}"/>
<field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>
<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="date_base_on"/>
@@ -147,6 +153,8 @@ The lines should have the same amount (with the write-off) and the same referenc
<field name="write_off"/>
<field name="account_lost_id" attrs="{'required':[('write_off','>',0)]}"/>
<field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>
<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="date_base_on"/>
@@ -161,4 +169,22 @@ The lines should have the same amount (with the write-off) and the same referenc
parent="account.periodical_processing_reconciliation"/>
</data>
<data noupdate="1">
<record forcecreate="True" id="ir_cron_run_reconciliations" model="ir.cron">
<field name="name">Do Automatic Reconciliations</field>
<field eval="False" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">3</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field eval="'account.easy.reconcile'" name="model"/>
<field eval="'run_scheduler'" name="function"/>
<field eval="'()'" name="args"/>
</record>
</data>
</openerp>

View File

@@ -53,7 +53,7 @@ class EasyReconcileHistory(orm.Model):
_columns = {
'easy_reconcile_id': fields.many2one(
'account.easy.reconcile', 'Reconcile Profile', readonly=True),
'date': fields.datetime('Run date', readonly=True),
'date': fields.datetime('Run date', readonly=True, required=True),
'reconcile_ids': fields.many2many(
'account.move.reconcile',
'account_move_reconcile_history_rel',
@@ -62,23 +62,22 @@ class EasyReconcileHistory(orm.Model):
'account.move.reconcile',
'account_move_reconcile_history_partial_rel',
string='Partial Reconciliations', readonly=True),
'reconcile_line_ids':
fields.function(
'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(
'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',
'company_id': fields.related(
'easy_reconcile_id', 'company_id',
relation='res.company',
type='many2one',
string='Company',

View File

@@ -404,3 +404,24 @@ msgstr ""
msgid "account easy reconcile"
msgstr ""
#. module: account_easy_reconcile
#: field:account.easy.reconcile.method,expense_exchange_account_id:0
#: field:easy.reconcile.base,expense_exchange_account_id:0
#: field:easy.reconcile.options,expense_exchange_account_id:0
#: field:easy.reconcile.simple,expense_exchange_account_id:0
#: 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"
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"
msgstr ""

View File

@@ -427,3 +427,25 @@ msgstr "easy.reconcile.simple.reference"
#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile
msgid "account easy reconcile"
msgstr "Lettrage automatisé"
#. module: account_easy_reconcile
#: field:account.easy.reconcile.method,expense_exchange_account_id:0
#: field:easy.reconcile.base,expense_exchange_account_id:0
#: field:easy.reconcile.options,expense_exchange_account_id:0
#: field:easy.reconcile.simple,expense_exchange_account_id:0
#: 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"
msgstr "Compte de perte de change"
#. 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"
msgstr "Compte de gain de change"

View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Leonardo Pistone
# Copyright 2014 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
class AccountConfigSettings(orm.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."""),
}
def onchange_company_id(self, cr, uid, ids, company_id, context=None):
company_obj = self.pool['res.company']
result = super(AccountConfigSettings, self).onchange_company_id(
cr, uid, ids, company_id, context=None)
if company_id:
company = company_obj.browse(cr, uid, company_id, context=context)
result['value']['reconciliation_commit_every'] = (
company.reconciliation_commit_every
)
return result
class Company(orm.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."""),
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_config" model="ir.ui.view">
<field name="name">account settings</field>
<field name="model">account.config.settings</field>
<field name="inherit_id" ref="account.view_account_config_settings"/>
<field name="arch" type="xml">
<separator string="eInvoicing &amp; Payments" position="before">
<separator string="Reconciliation"/>
<group>
<label for="id" string="Options"/>
<div name="reconciliation_config">
<div>
<label for="reconciliation_commit_every"/>
<field name="reconciliation_commit_every" class="oe_inline"/>
</div>
</div>
</group>
</separator>
</field>
</record>
</data>
</openerp>

View File

@@ -18,4 +18,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
import statement
from . import statement
from . import res_partner_bank

View File

@@ -0,0 +1,52 @@
#
# Authors: Laurent Mignon
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# 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.orm import Model
class res_partner_bank(Model):
_inherit = 'res.partner.bank'
def search_by_acc_number(self, cr, uid, acc_number, context=None):
'''
Try to find the Account Number using a 'like' operator to avoid
problems with the input mask used to store the value.
'''
# first try with an exact match
ids = self.search(cr,
uid,
[('acc_number', '=', acc_number)],
context=context)
if ids:
return ids
cr.execute("""
SELECT
id
FROM
res_partner_bank
WHERE
regexp_replace(acc_number,'([^[:alnum:]])', '','g')
ilike
regexp_replace(%s,'([^[:alnum:]])', '','g')
""", (acc_number,))
# apply security constraints by using the orm
return self.search(cr, uid,
[('id', 'in', [r[0] for r in cr.fetchall()])],
context=context)

View File

@@ -23,8 +23,8 @@
from openerp.tools.translate import _
from openerp.osv.orm import Model
from openerp.osv import fields
from openerp.addons.account_statement_base_completion.statement import \
ErrorTooManyPartner
from openerp.addons.account_statement_base_completion.statement \
import ErrorTooManyPartner
class AccountStatementCompletionRule(Model):
@@ -54,21 +54,25 @@ class AccountStatementCompletionRule(Model):
partner_acc_number = st_line['partner_acc_number']
if not partner_acc_number:
return {}
st_obj = self.pool.get('account.bank.statement.line')
st_obj = self.pool['account.bank.statement.line']
res = {}
res_bank_obj = self.pool.get('res.partner.bank')
ids = res_bank_obj.search(cr,
res_bank_obj = self.pool['res.partner.bank']
ids = res_bank_obj.search_by_acc_number(cr,
uid,
[('acc_number', '=', partner_acc_number)],
partner_acc_number,
context=context)
if len(ids) > 1:
raise ErrorTooManyPartner(
_('Line named "%s" (Ref:%s) was matched by more than one '
'partner for account number "%s".') %
(st_line['name'], st_line['ref'], partner_acc_number))
raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched '
'by more than one partner for account '
'number "%s".') %
(st_line['name'],
st_line['ref'],
partner_acc_number))
if len(ids) == 1:
partner = res_bank_obj.browse(
cr, uid, ids[0], context=context).partner_id
partner = res_bank_obj.browse(cr,
uid,
ids[0],
context=context).partner_id
res['partner_id'] = partner.id
st_vals = st_obj.get_values_for_line(
cr, uid, profile_id=st_line['profile_id'],

View File

@@ -27,76 +27,97 @@ ACC_NUMBER = "BE38733040385372"
class bankaccount_completion(common.TransactionCase):
def prepare(self):
def setUp(self):
super(bankaccount_completion, self).setUp()
self.company_a = self.browse_ref('base.main_company')
self.profile_obj = self.registry("account.statement.profile")
self.st_obj = self.registry("account.bank.statement")
self.acc_bk_stmt = self.registry("account.bank.statement")
self.st_line_obj = self.registry("account.bank.statement.line")
self.completion_rule_id = self.ref(
'account_statement_bankaccount_completion.'
self.completion_rule_id = \
self.ref('account_statement_bankaccount_completion.'
'bank_statement_completion_rule_10')
self.journal_id = self.registry("ir.model.data").get_object_reference(
self.cr, self. uid, "account", "bank_journal")[1]
self.journal_id = self.ref("account.bank_journal")
self.partner_id = self.ref('base.main_partner')
self.account_id = self.ref("account.a_recv")
# Create the profile
self.account_id = self.registry("ir.model.data").get_object_reference(
self.cr, self.uid, "account", "a_recv")[1]
self.journal_id = self.registry("ir.model.data").get_object_reference(
self.cr, self. uid, "account", "bank_journal")[1]
self.profile_id = self.profile_obj.create(self.cr, self.uid, {
"name": "TEST",
"commission_account_id": self.account_id,
"journal_id": self.journal_id,
"rule_ids": [(6, 0, [self.completion_rule_id])]})
# Create the completion rule
# Create a bank statement
self.statement_id = self.st_obj.create(
self.cr, self.uid, {
"balance_end_real": 0.0,
vals = {"balance_end_real": 0.0,
"balance_start": 0.0,
"date": time.strftime('%Y-%m-%d'),
"journal_id": self.journal_id,
"profile_id": self.profile_id
})
# Create bank a statement line
self.statement_line_id = self.st_line_obj.create(self.cr, self.uid, {
'amount': 1000.0,
'name': 'EXT001',
'ref': 'My ref',
'statement_id': self.statement_id,
'partner_acc_number': ACC_NUMBER
})
"profile_id": self.profile_id,
}
self.statement_id = self.acc_bk_stmt.create(self.cr,
self.uid,
vals)
# Add a bank account number to the partner
res_bank_obj = self.registry('res.partner.bank')
res_bank_obj.create(self.cr, self.uid, {
"state": "bank",
self.res_partner_bank_obj = self.registry('res.partner.bank')
vals = {"state": "bank",
"company_id": self.company_a.id,
"partner_id": self.partner_id,
"acc_number": ACC_NUMBER,
"footer": True,
"bank_name": "Reserve"
})
"bank_name": "Reserve",
}
self.res_partner_bank_id = self.res_partner_bank_obj.create(self.cr,
self.uid,
vals)
def test_00(self):
"""Test complete partner_id from bank account number
Test the automatic completion of the partner_id based on the account
number associated to the
statement line
number associated to the statement line
"""
self.prepare()
statement_line = self.st_line_obj.browse(self.cr, self.uid,
self.statement_line_id)
# before import, the
self.assertFalse(
statement_line.partner_id,
"Partner_id must be blank before completion")
statement_obj = self.st_obj.browse(self.cr, self.uid, self.statement_id)
for bank_acc_number in [ACC_NUMBER, ACC_NUMBER.replace(" ", ""),
ACC_NUMBER.replace(" ", "-")]:
# check the completion for well formatted and not well
# formatted account number
self.res_partner_bank_obj.write(self.cr,
self.uid,
self.res_partner_bank_id,
{"acc_number": bank_acc_number}
)
for acc_number in [ACC_NUMBER, ACC_NUMBER.replace(" ", ""),
ACC_NUMBER.replace(" ", "-"),
" BE38-7330 4038-5372 "]:
vals = {'amount': 1000.0,
'name': 'EXT001',
'ref': 'My ref',
'statement_id': self.statement_id,
'partner_acc_number': acc_number
}
line_id = self.st_line_obj.create(self.cr, self.uid, vals)
line = self.st_line_obj.browse(self.cr, self.uid, line_id)
self.assertFalse(line.partner_id,
'Partner_id must be blank before completion')
statement_obj = self.acc_bk_stmt.browse(self.cr,
self.uid,
self.statement_id)
statement_obj.button_auto_completion()
statement_line = self.st_line_obj.browse(self.cr, self.uid,
self.statement_line_id)
self.assertEquals(self.partner_id, statement_line.partner_id[
'id'], "Missing expected partner id after completion")
line = self.st_line_obj.browse(self.cr, self.uid, line_id)
self.assertEquals(self.partner_id, line.partner_id['id'],
'Missing expected partner id after '
'completion')
vals = {'amount': 1000.0,
'name': 'EXT001',
'ref': 'My ref',
'statement_id': self.statement_id,
'partner_acc_number': 'BE38a7330.4038-5372.',
}
line_id = self.st_line_obj.create(self.cr, self.uid, vals)
line = self.st_line_obj.browse(self.cr, self.uid, line_id)
self.assertFalse(line.partner_id,
'Partner_id must be blank before completion')
statement_obj = self.acc_bk_stmt.browse(self.cr,
self.uid,
self.statement_id)
statement_obj.button_auto_completion()
line = self.st_line_obj.browse(self.cr, self.uid, line_id)
self.assertFalse(line.partner_id.id)

View File

@@ -26,13 +26,14 @@
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal',
'depends': ['account_statement_ext'],
'depends': ['account_statement_ext',
'account_report_company'],
'description': """
The goal of this module is to improve the basic bank statement, help dealing
with huge volume of reconciliation by providing basic rules to identify the
partner of a bank statement line.
Each bank statement profile can have its own rules to be applied according to a
sequence order.
Each bank statement profile can have its own rules to be applied according to
a sequence order.
Some basic rules are provided in this module:
@@ -41,15 +42,15 @@
2) Match from statement line label (based on partner name)
3) Match from statement line reference (based on Invoice number)
You can easily override this module and add your own rules in your own one. The
basic rules only fill in the partner, but you can use them to fill in any
value of the line (in the future, we will add a rule to automatically match and
reconcile the line).
You can easily override this module and add your own rules in your own one.
The basic rules only fill in the partner, but you can use them to fill in
any value of the line (in the future, we will add a rule to automatically
match and reconcile the line).
It adds as well a label on the bank statement line (on which the pre-define
rules can match) and a char field on the partner called 'Bank Statement Label'.
Using the pre-define rules, you will be able to match various labels for a
partner.
rules can match) and a char field on the partner called 'Bank Statement
Label'. Using the pre-define rules, you will be able to match various
labels for a partner.
The reference of the line is always used by the reconciliation process. We're
supposed to copy there (or write manually) the matching string. This can be:
@@ -59,9 +60,10 @@
You can use it with our account_advanced_reconcile module to automatize the
reconciliation process.
TODO: The rules that look for invoices to find out the partner should take back
the payable / receivable account from there directly instead of retrieving it
from partner properties !
TODO: The rules that look for invoices to find out the partner should take
back the payable / receivable account from there directly instead of
retrieving it from partner properties!
""",
'website': 'http://www.camptocamp.com',
'data': [
@@ -75,6 +77,7 @@
'test/partner.yml',
'test/invoice.yml',
'test/supplier_invoice.yml',
'test/refund.yml',
'test/completion_test.yml'
],
'installable': False,

View File

@@ -158,7 +158,7 @@ msgstr "Erreur système"
#. module: account_statement_base_completion
#: field:account.bank.statement.line,already_completed:0
msgid "Auto-Completed"
msgstr "Auto-Completé"
msgstr "Auto-Complété"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:448
@@ -170,7 +170,7 @@ msgstr "Erreur de bypass de l'ORM"
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,sequence:0
msgid "Sequence"
msgstr "Séquence"
msgstr "Séquence"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:280
@@ -210,7 +210,7 @@ msgstr ""
"Entrez les différentes descriptions/informations sur votre relevé bancaire "
"séparées par un ';' Si l'une d'entre elles figure dans la ligne du "
"relevé, le partenaire correspondant sera automatiquement retrouvé "
"(A condition d'utiliser un règle de lettrage dans le profil)."
"(à condition d'utiliser un règle de lettrage dans le profil)."
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_res_partner

View File

@@ -32,8 +32,8 @@ class ResPartner(orm.Model):
'bank_statement_label': fields.char(
'Bank Statement Label', size=100,
help="Enter the various label found on your bank statement "
"separated by a ; If one of this label is include in the bank "
"statement line, the partner will be automatically filled (as "
"long as you use this method/rules in your statement "
"profile)."),
"separated by a ; If one of this label is include in the "
"bank statement line, the partner will be automatically "
"filled (as long as you use this method/rules in your "
"statement profile)."),
}

View File

@@ -124,7 +124,9 @@ class AccountStatementCompletionRule(orm.Model):
_order = "sequence asc"
def _get_functions(self, cr, uid, context=None):
"""List of available methods for rules. Override this to add you own."""
"""List of available methods for rules.
Override this to add you own."""
return [
('get_from_ref_and_invoice',
'From line reference (based on customer invoice number)'),
@@ -189,13 +191,7 @@ class AccountStatementCompletionRule(orm.Model):
res = {}
inv = self._find_invoice(cr, uid, line, inv_type, context=context)
if inv:
# FIXME use only commercial_partner_id of invoice in 7.1
# this is for backward compatibility in 7.0 before
# the refactoring of res.partner
if hasattr(inv, 'commercial_partner_id'):
partner_id = inv.commercial_partner_id.id
else:
partner_id = inv.partner_id.id
res = {'partner_id': partner_id,
'account_id': inv.account_id.id,
'type': inv_type}
@@ -225,8 +221,8 @@ class AccountStatementCompletionRule(orm.Model):
# Should be private but data are initialised with no update XML
def get_from_ref_and_invoice(self, cr, uid, line, context=None):
"""Match the partner based on the invoice number and the reference of
the statement line. Then, call the generic get_values_for_line method to
complete other values. If more than one partner matched, raise the
the statement line. Then, call the generic get_values_for_line method
to complete other values. If more than one partner matched, raise the
ErrorTooManyPartner error.
:param dict line: read of the concerned account.bank.statement.line
@@ -242,11 +238,11 @@ class AccountStatementCompletionRule(orm.Model):
# Should be private but data are initialised with no update XML
def get_from_label_and_partner_field(self, cr, uid, st_line, context=None):
"""
Match the partner based on the label field of the statement line
and the text defined in the 'bank_statement_label' field of the partner.
Remember that we can have values separated with ; Then, call the generic
get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
Match the partner based on the label field of the statement line and
the text defined in the 'bank_statement_label' field of the partner.
Remember that we can have values separated with ; Then, call the
generic get_values_for_line method to complete other values. If more
than one partner matched, raise the ErrorTooManyPartner error.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
@@ -303,10 +299,10 @@ class AccountStatementCompletionRule(orm.Model):
return res
def get_from_label_and_partner_name(self, cr, uid, st_line, context=None):
"""Match the partner based on the label field of the statement line
and the name of the partner. Then, call the generic get_values_for_line
method to complete other values. If more than one partner matched, raise
the ErrorTooManyPartner error.
"""Match the partner based on the label field of the statement line and
the name of the partner. Then, call the generic get_values_for_line
method to complete other values. If more than one partner matched,
raise the ErrorTooManyPartner error.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
@@ -333,7 +329,7 @@ class AccountStatementCompletionRule(orm.Model):
# to:
# http://www.postgresql.org/docs/9.0/static/functions-matching.html
# in chapter 9.7.3.6. Limits and Compatibility
sql = """
sql = r"""
SELECT id FROM (
SELECT id,
regexp_matches(%s,
@@ -386,9 +382,9 @@ class AccountStatement(orm.Model):
class AccountStatementLine(orm.Model):
"""
Add sparse field on the statement line to allow to store all the
bank infos that are given by a bank/office. You can then add you own in your
module. The idea here is to store all bank/office infos in the
Add sparse field on the statement line to allow to store all the bank infos
that are given by a bank/office. You can then add you own in your module.
The idea here is to store all bank/office infos in the
additionnal_bank_fields serialized field when importing the file. If many
values, add a tab in the bank statement line to store your specific one.
Have a look in account_statement_base_import module to see how we've done
@@ -449,7 +445,8 @@ class AccountStatementLine(orm.Model):
statement_line_obj = self.pool['account.bank.statement.line']
model_cols = statement_line_obj._columns
avail = [
k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')]
k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')
]
keys = [k for k in statement_store[0].keys() if k in avail]
# add sparse fields..
if include_serializable:
@@ -635,7 +632,8 @@ class AccountBankStatement(orm.Model):
st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st)
if res:
# stat_line_obj.write(cr, uid, [line.id], vals, context=ctx)
# stat_line_obj.write(cr, uid, [line.id], vals,
# context=ctx)
try:
stat_line_obj._update_line(
cr, uid, res, context=context)

View File

@@ -38,6 +38,15 @@
ref: T2S12345
date: '2013-12-19'
amount: -65.0
-
I create a statement line for a CR
-
!record {model: account.bank.statement.line, id: statement_line_cr}:
name: Test autocompletion based on Customer Refund Number
statement_id: statement_test1
ref: CR0001
date: '2013-12-19'
amount: -210.0
-
I create a statement line for the Partner Name
-
@@ -76,12 +85,18 @@
!assert {model: account.bank.statement.line, id: statement_line_si, string: Check completion by SI number}:
- partner_id.id == _ref("base.res_partner_17")
-
Line 3. I check that the partner name has been recognised.
Line 3. I expect the Customer refund number to be recognised. It should be
the commercial partner, and not the regular partner.
-
!assert {model: account.bank.statement.line, id: statement_line_cr, string: Check completion by CR number and commercial partner}:
- partner_id.id == _ref("base.res_partner_12")
-
Line 4. I check that the partner name has been recognised.
-
!assert {model: account.bank.statement.line, id: statement_line_partner_name, string: Check completion by partner name}:
- partner_id.name == 'Vauxoo'
-
Line 4. I check that the partner special label has been recognised.
Line 5. I check that the partner special label has been recognised.
-
!assert {model: account.bank.statement.line, id: statement_line_partner_label, string: Check completion by partner label}:
- partner_id.id == _ref("base.res_partner_6")

View File

@@ -0,0 +1,43 @@
-
I create a "child" partner, to use in the invoice
(and have a different commercial_partner_id than itself)
-
!record {model: res.partner, id: res_partner_12_child}:
name: Child Partner
supplier: False
customer: True
is_company: False
parent_id: base.res_partner_12
-
I create a customer refund to be found by the completion.
-
!record {model: account.invoice, id: refund_for_completion_1}:
account_id: account.a_pay
company_id: base.main_company
currency_id: base.EUR
internal_number: CR0001
invoice_line:
- account_id: account.a_expense
name: '[PCSC234] PC Assemble SC234'
price_unit: 210.0
quantity: 1.0
product_id: product.product_product_3
uos_id: product.product_uom_unit
journal_id: account.expenses_journal
partner_id: res_partner_12_child
type: 'out_refund'
reference_type: none
-
I confirm the refund
-
!workflow {model: account.invoice, action: invoice_open, ref: refund_for_completion_1}
-
I check that the refund state is "Open"
-
!assert {model: account.invoice, id: refund_for_completion_1}:
- state == 'open'
-
I check that it is given the number "CR0001"
-
!assert {model: account.invoice, id: refund_for_completion_1, string: Check CI number}:
- number == 'CR0001'

View File

@@ -65,8 +65,8 @@ class base_completion(common.TransactionCase):
def test_name_completion(self):
"""Test complete partner_id from statement line label
Test the automatic completion of the partner_id based if the name of the
partner appears in the statement line label
Test the automatic completion of the partner_id based if the name of
the partner appears in the statement line label
"""
self.completion_rule_id = self.ref(
'account_statement_base_completion.'
@@ -89,7 +89,8 @@ class base_completion(common.TransactionCase):
for case in NAMES_COMPLETION_CASES:
self.partner_obj.write(
self.cr, self.uid, self.partner_id, {'name': case.partner_name})
self.cr, self.uid, self.partner_id, {'name': case.partner_name}
)
statement_line_id = self.account_bank_statement_line_obj.create(
self.cr, self.uid, {
'amount': 1000.0,
@@ -116,5 +117,6 @@ class base_completion(common.TransactionCase):
else:
self.assertNotEquals(
self.partner_id, statement_line.partner_id['id'],
"Partner id should be empty after completion(partner_name: "
"%s, line_name: %s)" % (case.partner_name, case.line_label))
"Partner id should be empty after completion "
"(partner_name: %s, line_name: %s)"
% (case.partner_name, case.line_label))

View File

@@ -18,6 +18,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import parser
import wizard
import statement
from . import parser
from . import wizard
from . import statement

View File

@@ -57,8 +57,8 @@
The goal is here to populate the statement lines of a bank statement with the
infos that the bank or office give you. Fell free to inherit from this module
to add your own format. Then, if you need to complete data from there, add your
own account_statement_*_completion module and implement the needed rules.
to add your own format. Then, if you need to complete data from there, add
your own account_statement_*_completion module and implement the needed rules.
""",
'website': 'http://www.camptocamp.com',
'data': [

View File

@@ -19,7 +19,7 @@
#
##############################################################################
from parser import new_bank_statement_parser
from parser import BankStatementImportParser
import file_parser
import generic_file_parser
from .parser import new_bank_statement_parser
from .parser import BankStatementImportParser
from . import file_parser
from . import generic_file_parser

View File

@@ -21,8 +21,8 @@ from openerp.tools.translate import _
from openerp.osv.orm import except_orm
import tempfile
import datetime
from parser import BankStatementImportParser
from parser import UnicodeDictReader
from .parser import BankStatementImportParser
from .parser import UnicodeDictReader
try:
import xlrd
except:
@@ -41,12 +41,12 @@ class FileParser(BankStatementImportParser):
"""
def __init__(self, parse_name, ftype='csv', extra_fields=None, header=None,
**kwargs):
dialect=None, **kwargs):
"""
:param char: parse_name: The name of the parser
:param char: ftype: extension of the file (could be csv, xls or
xlsx)
:param dict: extra_fields: extra fields to add to the conversion
:param dict: extra_fields: extra fields to put into the conversion
dict. In the format {fieldname: fieldtype}
:param list: header : specify header fields if the csv file has no
header
@@ -58,19 +58,13 @@ class FileParser(BankStatementImportParser):
raise except_orm(
_('User Error'),
_('Invalid file type %s. Please use csv, xls or xlsx') % ftype)
self.conversion_dict = {
'ref': unicode,
'label': unicode,
'date': datetime.datetime,
'amount': float_or_zero,
}
if extra_fields:
self.conversion_dict.update(extra_fields)
self.conversion_dict = extra_fields
self.keys_to_validate = self.conversion_dict.keys()
self.fieldnames = header
self._datemode = 0 # used only for xls documents,
# 0 means Windows mode (1900 based dates).
# Set in _parse_xls, from the contents of the file
self.dialect = dialect
def _custom_format(self, *args, **kwargs):
"""No other work on data are needed in this parser."""
@@ -118,7 +112,8 @@ class FileParser(BankStatementImportParser):
csv_file.write(self.filebuffer)
csv_file.flush()
with open(csv_file.name, 'rU') as fobj:
reader = UnicodeDictReader(fobj, fieldnames=self.fieldnames)
reader = UnicodeDictReader(fobj, fieldnames=self.fieldnames,
dialect=self.dialect)
return list(reader)
def _parse_xls(self):
@@ -181,9 +176,9 @@ class FileParser(BankStatementImportParser):
except Exception as err:
raise except_orm(
_("Date format is not valid"),
_("Please modify the cell formatting to date format"
" for column: %s value: %s\n Please check the "
"line with ref: %s\n \n Detail: %s") %
_("Please modify the cell formatting to date "
"format for column: %s value: %s\n Please check "
"the line with ref: %s\n \n Detail: %s") %
(rule, line.get(rule, _('Missing')),
line.get('ref', line), repr(err)))
else:

View File

@@ -19,18 +19,31 @@
##############################################################################
import datetime
from file_parser import FileParser
from .file_parser import FileParser
from openerp.addons.account_statement_base_import.parser.file_parser import (
float_or_zero
)
from openerp.tools import ustr
class GenericFileParser(FileParser):
"""Standard parser that use a define format in csv or xls to import into a
bank statement. This is mostely an example of how to proceed to create a new
parser, but will also be useful as it allow to import a basic flat file.
bank statement. This is mostely an example of how to proceed to create a
new parser, but will also be useful as it allow to import a basic flat
file.
"""
def __init__(self, parse_name, ftype='csv', **kwargs):
conversion_dict = {
'ref': ustr,
'label': ustr,
'date': datetime.datetime,
'amount': float_or_zero,
}
super(GenericFileParser, self).__init__(
parse_name, ftype=ftype, **kwargs)
parse_name, ftype=ftype,
extra_fields=conversion_dict,
**kwargs)
@classmethod
def parser_for(cls, parser_name):
@@ -42,9 +55,9 @@ class GenericFileParser(FileParser):
def get_st_line_vals(self, line, *args, **kwargs):
"""
This method must return a dict of vals that can be passed to create
method of statement line in order to record it. It is the responsibility
of every parser to give this dict of vals, so each one can implement his
own way of recording the lines.
method of statement line in order to record it. It is the
responsibility of every parser to give this dict of vals, so each one
can implement his own way of recording the lines.
:param: line: a dict of vals that represent a line of
result_row_list
:return: dict of values to give to the create method of statement

View File

@@ -29,11 +29,16 @@ def UnicodeDictReader(utf8_data, **kwargs):
pos = utf8_data.tell()
sample_data = utf8_data.read(2048)
utf8_data.seek(pos)
if not kwargs.get('dialect'):
dialect = sniffer.sniff(sample_data, delimiters=',;\t')
del kwargs['dialect']
else:
dialect = kwargs.pop('dialect')
csv_reader = csv.DictReader(utf8_data, dialect=dialect, **kwargs)
for row in csv_reader:
yield dict([(key, unicode(value, 'utf-8')) for key, value in
row.iteritems()])
yield dict([(unicode(key or '', 'utf-8'),
unicode(value or '', 'utf-8'))
for key, value in row.iteritems()])
class BankStatementImportParser(object):
@@ -48,8 +53,8 @@ class BankStatementImportParser(object):
def __init__(self, profile, *args, **kwargs):
# The name of the parser as it will be called
self.parser_name = profile.import_type
# The result as a list of row. One row per line of data in the file, but
# not the commission one !
# The result as a list of row. One row per line of data in the file,
# but not the commission one!
self.result_row_list = None
# The file buffer on which to work on
self.filebuffer = None
@@ -128,9 +133,9 @@ class BankStatementImportParser(object):
def get_st_line_vals(self, line, *args, **kwargs):
"""Implement a method in your parser that must return a dict of vals
that can be passed to create method of statement line in order to record
it. It is the responsibility of every parser to give this dict of vals,
so each one can implement his own way of recording the lines.
that can be passed to create method of statement line in order to
record it. It is the responsibility of every parser to give this dict
of vals, so each one can implement his own way of recording the lines.
:param: line: a dict of vals that represent a line of result_row_list
:return: dict of values to give to the create method of statement line,

View File

@@ -23,7 +23,7 @@ import traceback
from openerp.tools.translate import _
import datetime
from openerp.osv import fields, orm
from parser import new_bank_statement_parser
from .parser import new_bank_statement_parser
from openerp.tools.config import config
@@ -74,13 +74,13 @@ class AccountStatementProfil(orm.Model):
statement ID
:param: context: global context
"""
pass
def write_logs_after_import(self, cr, uid, ids, statement_id, num_lines,
context):
"""Write the log in the logger
:param int/long statement_id: ID of the concerned account.bank.statement
:param int/long statement_id: ID of the concerned
account.bank.statement
:param int/long num_lines: Number of line that have been parsed
:return: True
"""
@@ -102,7 +102,8 @@ class AccountStatementProfil(orm.Model):
:param dict of vals from parser for account.bank.statement.line
(called by parser.get_st_line_vals)
:param int/long statement_id: ID of the concerned account.bank.statement
:param int/long statement_id: ID of the concerned
account.bank.statement
:return: dict of vals that will be passed to create method of
statement line.
"""
@@ -133,7 +134,7 @@ class AccountStatementProfil(orm.Model):
"""
vals = {'profile_id': profile_id}
vals.update(parser.get_st_vals())
if not vals.get('balance_start'):
if vals.get('balance_start') is None:
# Get starting balance from journal balance if parser doesn't
# fill this data, simulating the manual flow
statement_obj = self.pool['account.bank.statement']
@@ -173,8 +174,8 @@ class AccountStatementProfil(orm.Model):
ftype="csv", context=None):
"""Create a bank statement with the given profile and parser. It will
fullfill the bank statement with the values of the file providen, but
will not complete data (like finding the partner, or the right account).
This will be done in a second step with the completion rules.
will not complete data (like finding the partner, or the right
account). This will be done in a second step with the completion rules.
:param prof : The profile used to import the file
:param parser: the parser

View File

@@ -17,4 +17,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import import_statement
from . import import_statement

View File

@@ -20,6 +20,6 @@
###############################################################################
"""Account Statement Cancel Line."""
import statement # noqa
import statement_line # noqa
import wizard # noqa
from . import statement # noqa
from . import statement_line # noqa
from . import wizard # noqa

View File

@@ -22,7 +22,7 @@
name: line1
statement_id: statement_test
ref: ref1
date: '2014-01-20'
date: !eval "'%s-01-20' %(datetime.now().year)"
amount: 100.0
-
I create a second statement line
@@ -31,7 +31,7 @@
name: line2
statement_id: statement_test
ref: ref2
date: '2014-01-25'
date: !eval "'%s-01-25' %(datetime.now().year)"
amount: 200.0
-
I check that the state of the statement is "Draft"

View File

@@ -23,7 +23,7 @@
name: line11
statement_id: statement_test_10
ref: ref11
date: '2014-01-20'
date: !eval "'%s-01-20' %(datetime.now().year)"
amount: 100.0
-
I create a second statement line
@@ -32,7 +32,7 @@
name: line12
statement_id: statement_test_10
ref: ref12
date: '2014-01-25'
date: !eval "'%s-01-25' %(datetime.now().year)"
amount: 200.0
-
Now I confirm only the first statement line

View File

@@ -25,7 +25,7 @@
name: line1
statement_id: statement_test3
ref: line1
date: '2014-01-20'
date: !eval "'%s-01-20' %(datetime.now().year)"
amount: 10.0
-
Now I confirm the statement line. That should not pass the balance check

View File

@@ -25,7 +25,7 @@
name: line1
statement_id: statement_test4
ref: line1
date: '2014-01-20'
date: !eval "'%s-01-20' %(datetime.now().year)"
amount: 10.0
-
Now I confirm the statement line

View File

@@ -20,5 +20,5 @@
###############################################################################
"""Wizard for asking the confirmation when some move is reconciled."""
import cancel_statement # noqa
import cancel_statement_line # noqa
from . import cancel_statement # noqa
from . import cancel_statement_line # noqa

View File

@@ -19,4 +19,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import commission
from . import commission

View File

@@ -31,9 +31,9 @@
'account_statement_base_import'
],
'description': """
This module brings commission support to bank statement imports. It computes the
sum of a commission field on each transaction and creates a statement entry for
it.
This module brings commission support to bank statement imports. It computes
the sum of a commission field on each transaction and creates a statement
entry for it.
""",
'website': 'http://www.camptocamp.com',
'data': [

View File

@@ -24,7 +24,8 @@ class AccountStatementProfil(orm.Model):
commission_analytic_id = profile.commission_analytic_id.id
comm_values = {
'name': 'IN ' + _('Commission line'),
'date': parser.get_st_vals().get('date') or datetime.datetime.now(),
'date': parser.get_st_vals().get('date') or
datetime.datetime.now(),
'amount': global_commission_amount,
'partner_id': partner_id,
'type': 'general',
@@ -32,8 +33,8 @@ class AccountStatementProfil(orm.Model):
'account_id': commission_account_id,
'ref': 'commission',
'analytic_account_id': commission_analytic_id,
# !! We set the already_completed so auto-completion will not update
# those values!
# !! We set the already_completed so auto-completion will not
# update those values!
'already_completed': True,
}
st_obj = self.pool['account.bank.statement.line']

View File

@@ -92,7 +92,7 @@ class AccountStatementCompletionRule(orm.Model):
ON
st_l.statement_id = s.id
WHERE
st_l.name ~* l.label
(st_l.name ~* l.label OR st_l.ref ~* l.label)
AND
l.profile_id = s.profile_id
AND
@@ -125,8 +125,8 @@ class AccountStatementLabel(orm.Model):
'label': fields.char('Bank Statement Label', size=100),
'account_id': fields.many2one('account.account', 'Account',
required=True,
help='Account corresponding to the label '
'for a given partner'),
help='Account corresponding to the '
'label for a given partner'),
'company_id': fields.related('account_id', 'company_id',
type='many2one',
relation='res.company',

View File

@@ -19,7 +19,7 @@
#
##############################################################################
import statement
import report
import account
import voucher
from . import statement
from . import report
from . import account
from . import voucher

View File

@@ -45,8 +45,8 @@
Features:
1) Improve the bank statement: allows to define profiles (for each Office or
Bank). The bank statement will then generate the entries based on some criteria
chosen in the selected profile. You can setup on the profile:
Bank). The bank statement will then generate the entries based on some
criteria chosen in the selected profile. You can setup on the profile:
- the journal to use
- use balance check or not

View File

@@ -23,4 +23,4 @@
#
##############################################################################
import bank_statement_report
from . import bank_statement_report

View File

@@ -19,7 +19,7 @@
#
##############################################################################
import openerp.addons.account.account_bank_statement as stat_mod
from openerp.osv import fields, orm
from openerp.osv import fields, orm, osv
from openerp.tools.translate import _
@@ -52,8 +52,8 @@ class AccountStatementProfile(orm.Model):
'name': fields.char('Name', required=True),
'sequence': fields.integer(
'Sequence',
help="Gives a sequence in lists, the first profile will be used as "
"default"),
help="Gives a sequence in lists, the first profile will be used "
"as default"),
'partner_id': fields.many2one(
'res.partner',
'Bank/Payment Office partner',
@@ -144,7 +144,8 @@ class AccountBankStatement(orm.Model):
profile_obj = self.pool['account.statement.profile']
user = user_obj.browse(cr, uid, uid, context=context)
profile_ids = profile_obj.search(
cr, uid, [('company_id', '=', user.company_id.id)], context=context)
cr, uid, [('company_id', '=', user.company_id.id)], context=context
)
return profile_ids[0] if profile_ids else False
def _get_statement_from_profile(self, cr, uid, profile_ids, context=None):
@@ -304,7 +305,8 @@ class AccountBankStatement(orm.Model):
line if different from the statement line account ID
:param int/long analytic_id: ID of analytic account to put on the
move line
:param int/long partner_id: ID of the partner to put on the move line
:param int/long partner_id: ID of the partner to put on the move
line
:return: dict of value to create() the account.move.line
"""
if context is None:
@@ -420,7 +422,7 @@ class AccountBankStatement(orm.Model):
self.create_move_from_st_line(
cr, uid, st_line.id, company_currency_id,
st_line_number, context)
except orm.except_orm, exc:
except (orm.except_orm, osv.except_osv) as exc:
msg = "Line ID %s with ref %s had following error: %s" % (
st_line.id, st_line.ref, exc.value)
errors_stack.append(msg)
@@ -445,7 +447,7 @@ class AccountBankStatement(orm.Model):
def get_account_for_counterpart(self, cr, uid, amount, account_receivable,
account_payable):
"""For backward compatibility."""
account_id, type = self.get_account_and_type_for_counterpart(
account_id, account_type = self.get_account_and_type_for_counterpart(
cr, uid, amount, account_receivable, account_payable)
return account_id
@@ -496,10 +498,10 @@ class AccountBankStatement(orm.Model):
self, cr, uid, amount, account_receivable, account_payable,
partner_id=False):
"""
Give the amount, payable and receivable account (that can be found using
get_default_pay_receiv_accounts method) and receive the one to use. This
method should be use when there is no other way to know which one to
take. The rules are:
Give the amount, payable and receivable account (that can be found
using get_default_pay_receiv_accounts method) and receive the one to
use. This method should be use when there is no other way to know which
one to take. The rules are:
- If the customer checkbox is checked on the found partner, type and
account will be customer and receivable
- If the supplier checkbox is checked on the found partner, type and
@@ -606,7 +608,7 @@ class AccountBankStatementLine(orm.Model):
local_context['account_period_prefer_normal'] = True
try:
periods = period_obj.find(cr, uid, dt=date, context=local_context)
except orm.except_orm:
except (orm.except_orm, osv.except_osv):
# if no period defined, we are certainly at installation time
return False
return periods and periods[0] or False
@@ -617,7 +619,8 @@ class AccountBankStatementLine(orm.Model):
_columns = {
# Set them as required + 64 char instead of 32
'ref': fields.char('Reference', size=64, required=True),
'period_id': fields.many2one('account.period', 'Period', required=True),
'period_id': fields.many2one(
'account.period', 'Period', required=True),
}
_defaults = {
'period_id': _get_period,
@@ -732,8 +735,8 @@ class AccountBankStatementLine(orm.Model):
'voucher_id': False}}
return {'value': {'type': line_type}}
def onchange_type(self, cr, uid, line_id, partner_id, line_type, profile_id,
context=None):
def onchange_type(self, cr, uid, line_id, partner_id, line_type,
profile_id, context=None):
"""Keep the same features as in standard and call super. If an account
is returned, call the method to compute line values.
"""

View File

@@ -82,7 +82,8 @@ if not hasattr(std_pos_session, '_prepare_bank_statement'):
cr, uid, [('type', '=', 'cash')], context=context)
if not cashids:
cashids = journal_proxy.search(
cr, uid, [('journal_user', '=', True)], context=context)
cr, uid, [('journal_user', '=', True)],
context=context)
jobj.write(
cr, uid, [pos_config.id], {'journal_ids': [(6, 0, cashids)]})
pos_config = jobj.browse(cr, uid, config_id, context=context)

View File

@@ -18,5 +18,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import statement
import parser
from . import statement
from . import parser

View File

@@ -63,7 +63,8 @@ class AccountBankStatement(orm.Model):
})
return res
def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id,
def create_move_from_st_line(self, cr, uid, st_line_id,
company_currency_id,
st_line_number, context=None):
if context is None:
context = {}

View File

@@ -54,7 +54,7 @@ class AccountStatementCompletionRule(Model):
string="Account to set"),
}
def set_account(self, cr, uid, id, st_line, context=None):
def set_account(self, cr, uid, account_id, st_line, context=None):
"""
If line name match regex, update account_id
Then, call the generic st_line method to complete other values.
@@ -69,7 +69,7 @@ class AccountStatementCompletionRule(Model):
name = st_line['name']
res = {}
if name:
rule = self.browse(cr, uid, id, context=context)
rule = self.browse(cr, uid, account_id, context=context)
if re.match(rule.regex, name):
res['account_id'] = rule.account_id.id
return res

View File

@@ -87,12 +87,14 @@ class test_regex_account_completion(common.TransactionCase):
"""Test the automatic completion on account
"""
self.prepare()
statement_obj = self.st_obj.browse(self.cr, self.uid, self.statement_id)
statement_obj = self.st_obj.browse(
self.cr, self.uid, self.statement_id)
statement_obj.button_auto_completion()
statement_line1 = self.st_line_obj.browse(
self.cr, self.uid, self.statement_line1_id)
self.assertEquals(self.account_id, statement_line1.account_id.id,
"The account should be the account of the completion")
"The account should be the account of the completion"
)
statement_line2 = self.st_line_obj.browse(
self.cr, self.uid, self.statement_line2_id)
self.assertNotEqual(self.account_id, statement_line2.account_id.id,

View File

@@ -19,4 +19,4 @@
#
##############################################################################
import statement
from . import statement

View File

@@ -110,7 +110,7 @@ class AccountStatementCompletionRule(Model):
elif len(invoice_id) == 1:
invoice = invoice_obj.browse(cr, uid, invoice_id[0],
context=context)
res['partner_id'] = invoice.partner_id.id
res['partner_id'] = invoice.commercial_partner_id.id
# we want the move to have the same ref than the found
# invoice's move, thus it will be easier to link them for the
# accountants
@@ -151,7 +151,8 @@ class AccountBankStatement(Model):
:param browse_record st_line: account.bank.statement.line record to
create the move from.
:param int/long move_id: ID of the account.move to link the move line
:param int/long move_id: ID of the account.move to link the move
line
:param float debit: debit amount of the move line
:param float credit: credit amount of the move line
:param int/long currency_id: ID of currency of the move line to
@@ -162,7 +163,8 @@ class AccountBankStatement(Model):
line if different from the statement line account ID
:param int/long analytic_id: ID of analytic account to put on the
move line
:param int/long partner_id: ID of the partner to put on the move line
:param int/long partner_id: ID of the partner to put on the move
line
:return: dict of value to create() the account.move.line
"""
res = super(AccountBankStatement, self)._prepare_move_line_vals(

View File

@@ -29,7 +29,7 @@
statement_id: statement_transactionid_test1
transaction_id: XXX66Z
ref: 6
date: '2014-01-06'
date: !eval "'%s-01-06' %(datetime.now().year)"
amount: 118.4
-
I run the auto complete

View File

@@ -18,5 +18,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import parser
import statement
from . import parser
from . import statement

View File

@@ -35,9 +35,9 @@
the importation of different bank and offices that uses transactionID.
This module allows you to import your bank transactions with a standard .csv
or .xls file (you'll find samples in the 'data' folder). It respects the chosen
profile (model provided by the account_statement_ext module) to generate the
entries.
or .xls file (you'll find samples in the 'data' folder). It respects the
chosen profile (model provided by the account_statement_ext module) to
generate the entries.
This module can handle a commission taken by the payment office and has the
following format:
@@ -48,8 +48,8 @@
* amount: amount paid in the currency of the journal used in the importation
profile
* commission_amount: amount of the comission for each line
* label: the comunication given by the payment office, used as communication in
the generated entries.
* label: the comunication given by the payment office, used as communication
in the generated entries.
""",
'website': 'http://www.camptocamp.com',
'installable': False,

View File

@@ -19,4 +19,4 @@
#
##############################################################################
import transactionid_file_parser
from . import transactionid_file_parser

View File

@@ -18,7 +18,10 @@
#
##############################################################################
import datetime
from account_statement_base_import.parser.file_parser import FileParser
from openerp.tools import ustr
from account_statement_base_import.parser.file_parser import (
FileParser, float_or_zero
)
class TransactionIDFileParser(FileParser):
@@ -36,14 +39,15 @@ class TransactionIDFileParser(FileParser):
:param list: header : specify header fields if the csv file has no
header
"""
extra_fields = {'transaction_id': unicode}
conversion_dict = {
'transaction_id': ustr,
'label': ustr,
'date': datetime.datetime,
'amount': float_or_zero,
}
super(TransactionIDFileParser, self).__init__(
profile, extra_fields=extra_fields, ftype=ftype, header=header,
profile, extra_fields=conversion_dict, ftype=ftype, header=header,
**kwargs)
# ref is replaced by transaction_id thus we delete it from check
self.keys_to_validate = [
k for k in self.keys_to_validate if k != 'ref']
del self.conversion_dict['ref']
@classmethod
def parser_for(cls, parser_name):
@@ -54,9 +58,9 @@ class TransactionIDFileParser(FileParser):
def get_st_line_vals(self, line, *args, **kwargs):
"""This method must return a dict of vals that can be passed to create
method of statement line in order to record it. It is the responsibility
of every parser to give this dict of vals, so each one can implement his
own way of recording the lines.
method of statement line in order to record it. It is the
responsibility of every parser to give this dict of vals, so each one
can implement his own way of recording the lines.
:param: line: a dict of vals that represent a line of
result_row_list
:return: dict of values to give to the create method of statement

View File

@@ -39,10 +39,10 @@ origin, free reference) and above all, to understand which field will be
copied in the reference field of the move and move lines.
The approach here is to state simple rules with one concern: consistency.
The reference of the move lines must be the number of the document at
their very origin (number of a sales order, of an external document
like a supplier invoice, ...). The goal is for the accountant to be
able to trace to the source document from a ledger).
The reference of the move lines must be the number of the document at their
very origin (number of a sales order, of an external document like a supplier
invoice, ...). The goal is for the accountant to be able to trace to the
source document from a ledger).
The description of a line should always be... well, a description. Not a number
or a cryptic reference.