Merge pull request #137 from mdietrichc2c/9.0-account-transaction-base-import

[9.0] account move base import
This commit is contained in:
Yannick Vaucher
2016-07-07 15:23:35 +02:00
committed by GitHub
134 changed files with 2746 additions and 4772 deletions

View File

@@ -34,6 +34,7 @@ install:
- git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly
- pip install xlrd
script:
- travis_run_tests

View File

@@ -0,0 +1,57 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
=================================================
Journal Entry completion from bank account number
=================================================
This module extends the functionality of account_move_base_import
to add a completion method based on the partner bank account number
provided by the bank/office.
Completion will look in the partner with that bank account number
to match the partner, then it will fill in the journal item with
it to ease the reconciliation.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/98/9.0
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.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Laurent Mignon <laurent.mignon@acsone.eu>
* Matthieu Dietrich <matthieu.dietrich@gmail.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 https://odoo-community.org.

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013 ACSONE SA/NV
# © 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import models

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# © 2013 ACSONE SA/NV
# © 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
{
'name': "Journal Entry completion from bank account number",
'version': '9.0.1.0.0',
'author': "ACSONE SA/NV,Odoo Community Association (OCA)",
'maintainer': 'ACSONE SA/NV',
'category': 'Finance',
'complexity': 'normal',
'depends': [
'account_move_base_import',
],
'website': 'http://www.acsone.eu',
'data': [
"data/completion_rule_data.xml",
],
'demo': [],
'installable': True,
'auto_install': False,
'license': 'AGPL-3',
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="bank_statement_completion_rule_10" model="account.move.completion.rule">
<field name="name">Match from bank account number (Normal or IBAN))</field>
<field name="sequence">10</field>
<field name="function_to_call">get_from_bank_account</field>
</record>
</odoo>

View File

@@ -17,18 +17,18 @@ msgstr ""
"X-Launchpad-Export-Date: 2014-06-06 06:36+0000\n"
"X-Generator: Launchpad (build 17031)\n"
#. module: account_statement_bankaccount_completion
#. module: account_move_bankaccount_import
#: help:account.bank.statement.line,partner_acc_number:0
msgid "Account number of the partner"
msgstr "Número de cuenta de la empresa"
#. module: account_statement_bankaccount_completion
#: model:ir.model,name:account_statement_bankaccount_completion.model_account_bank_statement_line
#. module: account_move_bankaccount_import
#: model:ir.model,name:account_move_bankaccount_import.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr "Línea de extracto bancario"
#. module: account_statement_bankaccount_completion
#: code:addons/account_statement_bankaccount_completion/statement.py:68
#. module: account_move_bankaccount_import
#: code:addons/account_move_bankaccount_import/statement.py:68
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner for account "
@@ -37,12 +37,12 @@ msgstr ""
"La línea llamada \"%s\" (Ref: %s) fue casada con más de una empresa al "
"buscar el nº de cuenta \"%s\"."
#. module: account_statement_bankaccount_completion
#. module: account_move_bankaccount_import
#: field:account.bank.statement.line,partner_acc_number:0
msgid "Account Number"
msgstr "Número de cuenta"
#. module: account_statement_bankaccount_completion
#: model:ir.model,name:account_statement_bankaccount_completion.model_account_statement_completion_rule
#. module: account_move_bankaccount_import
#: model:ir.model,name:account_move_bankaccount_import.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr "account.statement.completion.rule"

View File

@@ -17,30 +17,30 @@ msgstr ""
"X-Launchpad-Export-Date: 2014-06-20 06:09+0000\n"
"X-Generator: Launchpad (build 17058)\n"
#. module: account_statement_bankaccount_completion
#. module: account_move_bankaccount_import
#: help:account.bank.statement.line,partner_acc_number:0
msgid "Account number of the partner"
msgstr ""
#. module: account_statement_bankaccount_completion
#: model:ir.model,name:account_statement_bankaccount_completion.model_account_bank_statement_line
#. module: account_move_bankaccount_import
#: model:ir.model,name:account_move_bankaccount_import.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr "Ligne de relevé bancaire"
#. module: account_statement_bankaccount_completion
#: code:addons/account_statement_bankaccount_completion/statement.py:68
#. module: account_move_bankaccount_import
#: code:addons/account_move_bankaccount_import/statement.py:68
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner for account "
"number \"%s\"."
msgstr ""
#. module: account_statement_bankaccount_completion
#. module: account_move_bankaccount_import
#: field:account.bank.statement.line,partner_acc_number:0
msgid "Account Number"
msgstr "Numéro de compte"
#. module: account_statement_bankaccount_completion
#: model:ir.model,name:account_statement_bankaccount_completion.model_account_statement_completion_rule
#. module: account_move_bankaccount_import
#: model:ir.model,name:account_move_bankaccount_import.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr ""

View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# © 2013 ACSONE SA/NV
# © 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import account_move
from . import res_partner_bank

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
# © 2013 ACSONE SA/NV
# © 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp import _, fields, models
from openerp.addons.account_move_base_import.models.account_move \
import ErrorTooManyPartner
class AccountMoveCompletionRule(models.Model):
"""Add a rule based on transaction ID"""
_inherit = "account.move.completion.rule"
function_to_call = fields.Selection(
selection_add=[
('get_from_bank_account',
'From bank account number (Normal or IBAN)')
])
def get_from_bank_account(self, line):
"""
Match the partner based on the partner account number field
Then, call the generic st_line method to complete other values.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
partner_acc_number = line.partner_acc_number
if not partner_acc_number:
return {}
res = {}
res_bank_obj = self.env['res.partner.bank']
banks = res_bank_obj.search_by_acc_number(partner_acc_number)
if len(banks) > 1:
raise ErrorTooManyPartner(_('Line named "%s" was matched '
'by more than one partner for account '
'number "%s".') %
(line.name,
partner_acc_number))
if len(banks) == 1:
partner = banks[0].partner_id
res['partner_id'] = partner.id
return res
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
partner_acc_number = fields.Char(
string='Account Number',
size=64,
help="Account number of the partner")

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# © 2013 ACSONE SA/NV
# © 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp import api, models
class ResPartnerBank(models.Model):
_inherit = 'res.partner.bank'
@api.multi
def search_by_acc_number(self, acc_number):
'''
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
banks = self.search([('acc_number', '=', acc_number)])
if banks:
return banks
self.env.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(
[('id', 'in', [r[0] for r in self.env.cr.fetchall()])])

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013 ACSONE SA/NV
# © 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import test_bankaccount_completion

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# © 2013 ACSONE SA/NV
# © 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp.tests import common
from openerp import fields, tools
from openerp.modules import get_module_resource
ACC_NUMBER = " BE38 7330 4038 5372 "
class BankAccountCompletion(common.TransactionCase):
def setUp(self):
super(BankAccountCompletion, self).setUp()
tools.convert_file(self.cr, 'account',
get_module_resource('account', 'test',
'account_minimal_test.xml'),
{}, 'init', False, 'test')
self.account_move_obj = self.env["account.move"]
self.account_move_line_obj = \
self.env["account.move.line"].with_context(
check_move_validity=False
)
self.company_a = self.browse_ref('base.main_company')
self.completion_rule_id = \
self.ref('account_move_bankaccount_import.'
'bank_statement_completion_rule_10')
self.journal = self.browse_ref("account.bank_journal")
self.partner = self.browse_ref('base.main_partner')
self.account_id = self.ref("account.a_recv")
# Create the profile
self.journal.write({
'used_for_completion': True,
'rule_ids': [(6, 0, [self.completion_rule_id])]
})
# Create a bank statement
self.move = self.account_move_obj.create({
"date": fields.Date.today(),
"journal_id": self.journal.id
})
# Add a bank account number to the partner
self.res_partner_bank_obj = self.env['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",
}
self.partner_bank = self.res_partner_bank_obj.create(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
"""
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.partner_bank.write({"acc_number": bank_acc_number})
for acc_number in [ACC_NUMBER, ACC_NUMBER.replace(" ", ""),
ACC_NUMBER.replace(" ", "-"),
" BE38-7330 4038-5372 "]:
self.move_line = self.account_move_line_obj.create({
'account_id': self.account_id,
'credit': 1000.0,
'name': 'EXT001',
'move_id': self.move.id,
'partner_acc_number': acc_number
})
self.assertFalse(
self.move_line.partner_id,
'Partner_id must be blank before completion')
self.move.button_auto_completion()
self.assertEquals(
self.partner, self.move_line.partner_id,
"Missing expected partner id after completion")
self.move_line = self.account_move_line_obj.create({
'account_id': self.account_id,
'credit': 1000.0,
'name': 'EXT001',
'move_id': self.move.id,
'partner_acc_number': 'BE38a7330.4038-5372.',
})
self.assertFalse(self.move_line.partner_id,
'Partner_id must be blank before completion')
self.move.button_auto_completion()
self.assertFalse(self.move_line.partner_id)

View File

@@ -0,0 +1,103 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
========================
Account move base import
========================
This module is a grouping of 7.0/8.0 modules, used to import accounting files
and completing them automatically:
* account_statement_base_completion
* account_statement_base_import
* account_statement_commission
* account_statement_ext
The main change is that, in order to import financial data, this information
is now imported directly as a Journal Entry.
Most of the information present in the "statement profile" is now located in
the account journal (with 2 boolean parameters which allows to use
this journal for importation and/or auto-completion).
Financial data can be imported using a standard .csv or .xls file (you'll find
it in the 'data' folder). It respects the journal to pass the entries.
This module can handle a commission taken by the payment office and has the
following format:
* __date__: date of the payment
* __amount__: amount paid in the currency of the journal used in the
importation
* __label__: the comunication given by the payment office, used as
communication in the generated entries.
Another column which can be used is __commission_amount__, representing
the amount for the commission taken by line.
Afterwards, the goal is to populate the journal items with information that
the bank or office gave you. For this, completion rules can be specified by
journal.
Some basic rules are provided in this module:
1) Match from statement line label (based on partner field 'Bank Statement
Label')
2) Match from statement line label (based on partner name)
3) Match from statement line label (based on Invoice reference)
Feel free to extend either the importation method, the completion method, or
both.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch}
.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt
.. branch is "8.0" for example
Known issues / Roadmap
======================
* As for now, the module does not handle multicurrency imports.
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.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Joël Grand-Guillaume <joel.grandguillaume@camptocamp.com>
* Nicolas Bessi <nicolas.bessi@camptocamp.com>
* Laurent Mignon <laurent.mignon@acsone.eu>
* Sébastien Beau <sebastien.beau@akretion.com>
* Matthieu Dietrich <matthieu.dietrich@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 https://odoo-community.org.

View File

@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import parser
from . import wizard
from . import models

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
{
'name': "Journal Entry base import",
'version': '9.0.1.0.0',
'author': "Akretion,Camptocamp,Odoo Community Association (OCA)",
'category': 'Finance',
'depends': ['account'],
'website': 'http://www.camptocamp.com',
'data': [
"security/ir.model.access.csv",
"data/completion_rule_data.xml",
"wizard/import_statement_view.xml",
"views/account_move_view.xml",
"views/journal_view.xml",
"views/partner_view.xml",
],
'test': [
'test/partner.yml',
'test/invoice.yml',
'test/supplier_invoice.yml',
'test/refund.yml',
'test/completion_test.yml'
],
'installable': True,
'auto_install': False,
'license': 'AGPL-3',
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="bank_statement_completion_rule_2" model="account.move.completion.rule">
<field name="name">Match from line label (based on partner field 'Bank Statement Label')</field>
<field name="sequence">60</field>
<field name="function_to_call">get_from_name_and_partner_field</field>
</record>
<record id="bank_statement_completion_rule_3" model="account.move.completion.rule">
<field name="name">Match from line label (based on partner name)</field>
<field name="sequence">70</field>
<field name="function_to_call">get_from_name_and_partner_name</field>
</record>
<record id="bank_statement_completion_rule_4" model="account.move.completion.rule">
<field name="name">Match from line label (based on Invoice number)</field>
<field name="sequence">40</field>
<field name="function_to_call">get_from_name_and_invoice</field>
</record>
<record id="bank_statement_completion_rule_5" model="account.move.completion.rule">
<field name="name">Match from line label (based on Invoice Supplier number)</field>
<field name="sequence">45</field>
<field name="function_to_call">get_from_name_and_supplier_invoice</field>
</record>
</odoo>

View File

@@ -0,0 +1,4 @@
"date";"amount";"commission_amount";"label"
2011-03-07 13:45:14;118.4;-11.84;"label a"
2011-03-02 13:45:14;189;-15.12;"label b"
2011-03-02 17:45:14;189;-15.12;"label c"
1 date amount commission_amount label
2 2011-03-07 13:45:14 118.4 -11.84 label a
3 2011-03-02 13:45:14 189 -15.12 label b
4 2011-03-02 17:45:14 189 -15.12 label c

Binary file not shown.

View File

@@ -17,29 +17,29 @@ msgstr ""
"X-Launchpad-Export-Date: 2014-06-06 06:36+0000\n"
"X-Generator: Launchpad (build 17031)\n"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:credit.statement.import:0
#: model:ir.actions.act_window,name:account_statement_base_import.statement_importer_action
#: model:ir.actions.act_window,name:account_move_base_import.statement_importer_action
msgid "Import statement"
msgstr "Importar extracto"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:account.statement.profile:0
msgid "Historical Import Logs"
msgstr "Registro histórico de importaciones"
#. module: account_statement_base_import
#: model:ir.model,name:account_statement_base_import.model_credit_statement_import
#. module: account_move_base_import
#: model:ir.model,name:account_move_base_import.model_credit_statement_import
msgid "credit.statement.import"
msgstr "credit.statement.import"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,input_statement:0
msgid "Statement file"
msgstr "Archivo de extracto"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:168
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:168
#, python-format
msgid ""
"Column %s you try to import is not present in the bank statement line!"
@@ -47,91 +47,91 @@ msgstr ""
"La columna %s que intenta importar no está presente en la línea del extracto "
"bancario."
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:162
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:162
#, python-format
msgid "Nothing to import"
msgstr "Nada que importar"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,journal_id:0
msgid "Financial journal to use transaction"
msgstr "Diario contable a usar"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:102
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:102
#, python-format
msgid "Column %s not present in file"
msgstr "La columna %s no está presente en el archivo"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:account.statement.profile:0
#: model:ir.ui.menu,name:account_statement_base_import.statement_importer_menu
#: model:ir.ui.menu,name:account_move_base_import.statement_importer_menu
msgid "Import Bank Statement"
msgstr "Importar extracto bancario"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:54
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:54
#, python-format
msgid "User Error"
msgstr "Error de usuario"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:223
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:223
#, python-format
msgid "The statement cannot be created: %s"
msgstr "El extracto no puede ser creado: %s"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:167
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:167
#, python-format
msgid "Missing column!"
msgstr "Columna ausente"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/parser.py:166
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/parser.py:166
#, python-format
msgid "No buffer file given."
msgstr "No se ha proporcionado ningún búfer de archivo."
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:107
#: code:addons/account_statement_base_import/parser/file_parser.py:171
#: code:addons/account_statement_base_import/parser/file_parser.py:205
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:107
#: code:addons/account_move_base_import/parser/file_parser.py:171
#: code:addons/account_move_base_import/parser/file_parser.py:205
#, python-format
msgid "Invalid data"
msgstr "Datos inválidos"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:account.statement.profile,launch_import_completion:0
msgid "Launch completion after import"
msgstr "Lanzar el completado después de la importación"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,partner_id:0
msgid "Credit insitute partner"
msgstr "Empresa para el agente financiero"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:account.statement.profile:0
msgid "Import related infos"
msgstr "Importar información relacionada"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:163
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:163
#, python-format
msgid "The file is empty"
msgstr "El archivo está vacío"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/wizard/import_statement.py:93
#. module: account_move_base_import
#: code:addons/account_move_base_import/wizard/import_statement.py:93
#, python-format
msgid "Please use a file with an extention"
msgstr "Use por favor un archivo con extensión"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:172
#: code:addons/account_statement_base_import/parser/file_parser.py:206
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:172
#: code:addons/account_move_base_import/parser/file_parser.py:206
#, python-format
msgid ""
"Value %s of column %s is not valid.\n"
@@ -145,15 +145,15 @@ msgstr ""
" \n"
"Detalles: %s"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:29
#: code:addons/account_statement_base_import/parser/generic_file_parser.py:31
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:29
#: code:addons/account_move_base_import/parser/generic_file_parser.py:31
#, python-format
msgid "Please install python lib xlrd"
msgstr "Por favor instale la librería de Python xlrd"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:160
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:160
#, python-format
msgid ""
" It should be YYYY-MM-DD for column: %s value: %s \n"
@@ -169,24 +169,24 @@ msgstr ""
" \n"
"Detalles: %s"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:account.statement.profile,last_import_date:0
msgid "Last Import Date"
msgstr "Última fecha de importación"
#. module: account_statement_base_import
#: model:ir.model,name:account_statement_base_import.model_account_statement_profile
#. module: account_move_base_import
#: model:ir.model,name:account_move_base_import.model_account_statement_profile
msgid "Statement Profile"
msgstr "Perfil de extracto"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:234
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:234
#, python-format
msgid "Statement import error"
msgstr "Error de importación del extracto"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:193
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:193
#, python-format
msgid ""
"Please modify the cell formatting to date format for column: %s value: %s\n"
@@ -200,18 +200,18 @@ msgstr ""
" \n"
"Detalles: %s"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:192
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:192
#, python-format
msgid "Date format is not valid"
msgstr "El formato de fecha no es válido"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:account.statement.profile,import_type:0
msgid "Type of import"
msgstr "Tipo de importación"
#. module: account_statement_base_import
#. module: account_move_base_import
#: help:account.statement.profile,launch_import_completion:0
msgid ""
"Tic that box to automatically launch the completion on each imported file "
@@ -220,7 +220,7 @@ msgstr ""
"Marque esta casilla para lanzar automáticamente el completado en cada "
"archivo importado usando este perfil."
#. module: account_statement_base_import
#. module: account_move_base_import
#: help:credit.statement.import,balance_check:0
msgid ""
"Tic that box if you want OpenERP to control the start/end balance before "
@@ -231,81 +231,81 @@ msgstr ""
"antes de confirmar un extracto bancaria. Si no está marcada, no se realizará "
"ningún control de saldo."
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:154
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:154
#, python-format
msgid "No Profile!"
msgstr "Sin perfil"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:159
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:159
#, python-format
msgid "Date format is not valid."
msgstr "El formato de fecha no es válido."
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,profile_id:0
msgid "Import configuration parameter"
msgstr "Parámetros de configuración de la importación"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:account.statement.profile,rec_log:0
msgid "log"
msgstr "registro"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:credit.statement.import:0
msgid "Import Parameters Summary"
msgstr "Resumen de parámetros de importación"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,balance_check:0
msgid "Balance check"
msgstr "Comprobar saldo"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,force_partner_on_bank:0
msgid "Force partner on bank move"
msgstr "Forzar empresa en el apunte bancario"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,file_name:0
msgid "File Name"
msgstr "Nombre del archivo"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:55
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:55
#, python-format
msgid "Invalid file type %s. Please use csv or xls"
msgstr "Tipo de archivo %s no válido. Utilice por favor CSV o XLS."
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:155
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:155
#, python-format
msgid "You must provide a valid profile to import a bank statement!"
msgstr "Debe introducir un perfil válido para importar un extracto bancario"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:83
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:83
#, python-format
msgid "Statement ID %s have been imported with %s lines."
msgstr "El extracto con ID %s ha sido importado con %s líneas."
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,receivable_account_id:0
msgid "Force Receivable/Payable Account"
msgstr "Forzar cuenta a cobrar/a pagar"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:164
#: code:addons/account_statement_base_import/parser/file_parser.py:174
#: code:addons/account_statement_base_import/parser/file_parser.py:198
#: code:addons/account_statement_base_import/parser/file_parser.py:208
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:164
#: code:addons/account_move_base_import/parser/file_parser.py:174
#: code:addons/account_move_base_import/parser/file_parser.py:198
#: code:addons/account_move_base_import/parser/file_parser.py:208
#, python-format
msgid "Missing"
msgstr "Ausente"
#. module: account_statement_base_import
#. module: account_move_base_import
#: help:account.statement.profile,import_type:0
msgid ""
"Choose here the method by which you want to import bank statement for this "
@@ -314,12 +314,12 @@ msgstr ""
"Escoja aquí el método con el que quiere importar el extracto bancario para "
"este perfil."
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:credit.statement.import:0
msgid "Cancel"
msgstr "Cancelar"
#. module: account_statement_base_import
#. module: account_move_base_import
#: help:credit.statement.import,force_partner_on_bank:0
msgid ""
"Tic that box if you want to use the credit insitute partner in the "

View File

@@ -17,119 +17,119 @@ msgstr ""
"X-Launchpad-Export-Date: 2014-05-22 06:49+0000\n"
"X-Generator: Launchpad (build 17017)\n"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:credit.statement.import:0
#: model:ir.actions.act_window,name:account_statement_base_import.statement_importer_action
#: model:ir.actions.act_window,name:account_move_base_import.statement_importer_action
msgid "Import statement"
msgstr "Import de relevé"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:account.statement.profile:0
msgid "Historical Import Logs"
msgstr ""
#. module: account_statement_base_import
#: model:ir.model,name:account_statement_base_import.model_credit_statement_import
#. module: account_move_base_import
#: model:ir.model,name:account_move_base_import.model_credit_statement_import
msgid "credit.statement.import"
msgstr "credit.statement.import"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,input_statement:0
msgid "Statement file"
msgstr "Fichier à importer"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:168
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:168
#, python-format
msgid ""
"Column %s you try to import is not present in the bank statement line!"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:162
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:162
#, python-format
msgid "Nothing to import"
msgstr ""
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,journal_id:0
msgid "Financial journal to use transaction"
msgstr "Journal"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:102
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:102
#, python-format
msgid "Column %s not present in file"
msgstr "Colonne %s non présente dans le fichier"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:account.statement.profile:0
#: model:ir.ui.menu,name:account_statement_base_import.statement_importer_menu
#: model:ir.ui.menu,name:account_move_base_import.statement_importer_menu
msgid "Import Bank Statement"
msgstr "Importation de relevé"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:54
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:54
#, python-format
msgid "User Error"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:223
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:223
#, python-format
msgid "The statement cannot be created: %s"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:167
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:167
#, python-format
msgid "Missing column!"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/parser.py:166
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/parser.py:166
#, python-format
msgid "No buffer file given."
msgstr "Pas de fichier tampon donné."
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:107
#: code:addons/account_statement_base_import/parser/file_parser.py:171
#: code:addons/account_statement_base_import/parser/file_parser.py:205
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:107
#: code:addons/account_move_base_import/parser/file_parser.py:171
#: code:addons/account_move_base_import/parser/file_parser.py:205
#, python-format
msgid "Invalid data"
msgstr ""
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:account.statement.profile,launch_import_completion:0
msgid "Launch completion after import"
msgstr "Lancer l'auto-complétion après import"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,partner_id:0
msgid "Credit insitute partner"
msgstr "Organisme bancaire"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:account.statement.profile:0
msgid "Import related infos"
msgstr "Importation des informations liées"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:163
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:163
#, python-format
msgid "The file is empty"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/wizard/import_statement.py:93
#. module: account_move_base_import
#: code:addons/account_move_base_import/wizard/import_statement.py:93
#, python-format
msgid "Please use a file with an extention"
msgstr "Veuillez sélectionner un fichier avec une extension"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:172
#: code:addons/account_statement_base_import/parser/file_parser.py:206
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:172
#: code:addons/account_move_base_import/parser/file_parser.py:206
#, python-format
msgid ""
"Value %s of column %s is not valid.\n"
@@ -138,15 +138,15 @@ msgid ""
" Detail: %s"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:29
#: code:addons/account_statement_base_import/parser/generic_file_parser.py:31
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:29
#: code:addons/account_move_base_import/parser/generic_file_parser.py:31
#, python-format
msgid "Please install python lib xlrd"
msgstr "Veuillez installer la bibliothèque python xlrd"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:160
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:160
#, python-format
msgid ""
" It should be YYYY-MM-DD for column: %s value: %s \n"
@@ -157,24 +157,24 @@ msgid ""
" Detail: %s"
msgstr ""
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:account.statement.profile,last_import_date:0
msgid "Last Import Date"
msgstr "Date de dernier import"
#. module: account_statement_base_import
#: model:ir.model,name:account_statement_base_import.model_account_statement_profile
#. module: account_move_base_import
#: model:ir.model,name:account_move_base_import.model_account_statement_profile
msgid "Statement Profile"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:234
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:234
#, python-format
msgid "Statement import error"
msgstr "Erreur d'import de relevé"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:193
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:193
#, python-format
msgid ""
"Please modify the cell formatting to date format for column: %s value: %s\n"
@@ -183,25 +183,25 @@ msgid ""
" Detail: %s"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:192
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:192
#, python-format
msgid "Date format is not valid"
msgstr ""
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:account.statement.profile,import_type:0
msgid "Type of import"
msgstr "Type d'import"
#. module: account_statement_base_import
#. module: account_move_base_import
#: help:account.statement.profile,launch_import_completion:0
msgid ""
"Tic that box to automatically launch the completion on each imported file "
"using this profile."
msgstr ""
#. module: account_statement_base_import
#. module: account_move_base_import
#: help:credit.statement.import,balance_check:0
msgid ""
"Tic that box if you want OpenERP to control the start/end balance before "
@@ -209,93 +209,93 @@ msgid ""
"done."
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:154
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:154
#, python-format
msgid "No Profile!"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:159
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:159
#, python-format
msgid "Date format is not valid."
msgstr ""
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,profile_id:0
msgid "Import configuration parameter"
msgstr "Paramètres de configuration d'import"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:account.statement.profile,rec_log:0
msgid "log"
msgstr "journal"
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:credit.statement.import:0
msgid "Import Parameters Summary"
msgstr "Résumé des paramètres d'import"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,balance_check:0
msgid "Balance check"
msgstr "Vérification des soldes"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,force_partner_on_bank:0
msgid "Force partner on bank move"
msgstr "Forcer un partenaire sur la ligne du compte de banque"
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,file_name:0
msgid "File Name"
msgstr "Nom du fichier"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:55
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:55
#, python-format
msgid "Invalid file type %s. Please use csv or xls"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:155
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:155
#, python-format
msgid "You must provide a valid profile to import a bank statement!"
msgstr ""
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/statement.py:83
#. module: account_move_base_import
#: code:addons/account_move_base_import/statement.py:83
#, python-format
msgid "Statement ID %s have been imported with %s lines."
msgstr ""
#. module: account_statement_base_import
#. module: account_move_base_import
#: field:credit.statement.import,receivable_account_id:0
msgid "Force Receivable/Payable Account"
msgstr "Forcer le compte Client/Fournisseur"
#. module: account_statement_base_import
#: code:addons/account_statement_base_import/parser/file_parser.py:164
#: code:addons/account_statement_base_import/parser/file_parser.py:174
#: code:addons/account_statement_base_import/parser/file_parser.py:198
#: code:addons/account_statement_base_import/parser/file_parser.py:208
#. module: account_move_base_import
#: code:addons/account_move_base_import/parser/file_parser.py:164
#: code:addons/account_move_base_import/parser/file_parser.py:174
#: code:addons/account_move_base_import/parser/file_parser.py:198
#: code:addons/account_move_base_import/parser/file_parser.py:208
#, python-format
msgid "Missing"
msgstr ""
#. module: account_statement_base_import
#. module: account_move_base_import
#: help:account.statement.profile,import_type:0
msgid ""
"Choose here the method by which you want to import bank statement for this "
"profile."
msgstr "Choisissez la méthode d'import de relevé pour ce profil."
#. module: account_statement_base_import
#. module: account_move_base_import
#: view:credit.statement.import:0
msgid "Cancel"
msgstr "Annulation"
#. module: account_statement_base_import
#. module: account_move_base_import
#: help:credit.statement.import,force_partner_on_bank:0
msgid ""
"Tic that box if you want to use the credit insitute partner in the "

View File

@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import account_journal
from . import account_move
from . import partner

View File

@@ -0,0 +1,334 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import sys
import traceback
import os
from openerp import _, api, fields, models
from ..parser.parser import new_move_parser
from openerp.exceptions import UserError, ValidationError
from operator import attrgetter
class AccountJournal(models.Model):
_name = 'account.journal'
_inherit = ['account.journal', 'mail.thread']
used_for_import = fields.Boolean(
string="Journal used for import")
commission_account_id = fields.Many2one(
comodel_name='account.account',
string='Commission account')
import_type = fields.Selection(
[('generic_csvxls_so', 'Generic .csv/.xls based on SO Name')],
string='Type of import',
default='generic_csvxls_so',
required=True,
help="Choose here the method by which you want to import account "
"moves for this journal.")
last_import_date = fields.Datetime(
string="Last Import Date")
partner_id = fields.Many2one(
comodel_name='res.partner',
string='Bank/Payment Office partner',
help="Put a partner if you want to have it on the commission move "
"(and optionaly on the counterpart of the intermediate/"
"banking move if you tick the corresponding checkbox).")
receivable_account_id = fields.Many2one(
comodel_name='account.account',
string='Receivable/Payable Account',
help="Choose a receivable/payable account to use as the default "
"debit/credit account.")
used_for_completion = fields.Boolean(
string="Journal used for completion")
rule_ids = fields.Many2many(
comodel_name='account.move.completion.rule',
string='Auto-completion rules',
relation='account_journal_completion_rule_rel')
launch_import_completion = fields.Boolean(
string="Launch completion after import",
help="Tic that box to automatically launch the completion "
"on each imported file using this journal.")
create_counterpart = fields.Boolean(
string="Create Counterpart",
help="Tick that box to automatically create the move counterpart",
default=True)
split_counterpart = fields.Boolean(
string="Split Counterpart",
help="Two counterparts will be automatically created : one for "
"the refunds and one for the payments")
def _get_rules(self):
# We need to respect the sequence order
return sorted(self.rule_ids, key=attrgetter('sequence'))
def _find_values_from_rules(self, calls, line):
"""This method will execute all related rules, in their sequence order,
to retrieve all the values returned by the first rules that will match.
:param calls: list of lookup function name available in rules
:param dict line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id: value,
...}
"""
if not calls:
calls = self._get_rules()
rule_obj = self.env['account.move.completion.rule']
for call in calls:
method_to_call = getattr(rule_obj, call.function_to_call)
result = method_to_call(line)
if result:
result['already_completed'] = True
return result
return None
@api.multi
def _prepare_counterpart_line(self, move, amount, date):
if amount > 0.0:
account_id = self.default_debit_account_id.id
credit = 0.0
debit = amount
else:
account_id = self.default_credit_account_id.id
credit = -amount
debit = 0.0
counterpart_values = {
'name': _('/'),
'date_maturity': date,
'credit': credit,
'debit': debit,
'partner_id': self.partner_id.id,
'move_id': move.id,
'account_id': account_id,
'already_completed': True,
'journal_id': self.id,
'company_id': self.company_id.id,
'currency_id': self.currency_id.id,
'company_currency_id': self.company_id.currency_id.id,
'amount_residual': amount,
}
return counterpart_values
@api.multi
def _create_counterpart(self, parser, move):
move_line_obj = self.env['account.move.line']
refund = 0.0
payment = 0.0
transfer_lines = []
for move_line in move.line_ids:
refund -= move_line.debit
payment += move_line.credit
if self.split_counterpart:
if refund:
transfer_lines.append(refund)
if payment:
transfer_lines.append(payment)
else:
total_amount = refund + payment
if total_amount:
transfer_lines.append(total_amount)
counterpart_date = parser.get_move_vals().get('date') or \
fields.Date.today()
transfer_line_count = len(transfer_lines)
check_move_validity = False
for amount in transfer_lines:
transfer_line_count -= 1
if not transfer_line_count:
check_move_validity = True
vals = self._prepare_counterpart_line(move, amount,
counterpart_date)
move_line_obj.with_context(
check_move_validity=check_move_validity
).create(vals)
@api.multi
def _write_extra_move_lines(self, parser, move):
"""Insert extra lines after the main statement lines.
After the main statement lines have been created, you can override this
method to create extra statement lines.
:param: browse_record of the current parser
:param: result_row_list: [{'key':value}]
:param: profile: browserecord of account.statement.profile
:param: statement_id: int/long of the current importing
statement ID
:param: context: global context
"""
move_line_obj = self.env['account.move.line']
global_commission_amount = 0
for row in parser.result_row_list:
global_commission_amount += float(
row.get('commission_amount', '0.0'))
partner_id = self.partner_id.id
# Commission line
if global_commission_amount > 0.0:
raise UserError(_('Commission amount should not be positive.'))
elif global_commission_amount < 0.0:
if not self.commission_account_id:
raise UserError(
_('No commission account is set on the journal.'))
else:
commission_account_id = self.commission_account_id.id
comm_values = {
'name': _('Commission line'),
'date_maturity': parser.get_move_vals().get('date') or
fields.Date.today(),
'debit': -global_commission_amount,
'partner_id': partner_id,
'move_id': move.id,
'account_id': commission_account_id,
'already_completed': True,
}
move_line_obj.with_context(
check_move_validity=False
).create(comm_values)
@api.multi
def write_logs_after_import(self, move, num_lines):
"""Write the log in the logger
: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
"""
self.message_post(
body=_('Move %s have been imported with %s '
'lines.') % (move.name, num_lines))
return True
def prepare_move_line_vals(self, parser_vals, move):
"""Hook to build the values of a line from the parser returned values.
At least it fullfill the basic values. Overide it to add your own
completion if needed.
: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
:return: dict of vals that will be passed to create method of
statement line.
"""
move_line_obj = self.env['account.move.line']
values = parser_vals
values['company_id'] = self.company_id.id
values['currency_id'] = self.currency_id.id
values['company_currency_id'] = self.company_id.currency_id.id
values['journal_id'] = self.id
values['move_id'] = move.id
if not values.get('account_id', False):
values['account_id'] = self.receivable_account_id.id
values = move_line_obj._add_missing_default_values(values)
return values
def prepare_move_vals(self, result_row_list, parser):
"""Hook to build the values of the statement from the parser and
the profile.
"""
vals = {'journal_id': self.id,
'currency_id': self.currency_id.id}
vals.update(parser.get_move_vals())
return vals
def multi_move_import(self, file_stream, ftype="csv"):
"""Create multiple bank statements from values given by the parser for
the given profile.
:param int/long profile_id: ID of the profile used to import the file
:param filebuffer file_stream: binary of the providen file
:param char: ftype represent the file exstension (csv by default)
:return: list: list of ids of the created account.bank.statemênt
"""
filename = self._context.get('file_name', None)
if filename:
(filename, __) = os.path.splitext(filename)
parser = new_move_parser(self, ftype=ftype, move_ref=filename)
res = self.env['account.move']
for result_row_list in parser.parse(file_stream):
move = self._move_import(parser, file_stream, ftype=ftype)
res |= move
return res
def _move_import(self, parser, file_stream, ftype="csv"):
"""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.
:param prof : The profile used to import the file
:param parser: the parser
:param filebuffer file_stream: binary of the providen file
:param char: ftype represent the file exstension (csv by default)
:return: ID of the created account.bank.statemênt
"""
move_obj = self.env['account.move']
move_line_obj = self.env['account.move.line']
attachment_obj = self.env['ir.attachment']
result_row_list = parser.result_row_list
# Check all key are present in account.bank.statement.line!!
if not result_row_list:
raise UserError(_("Nothing to import: "
"The file is empty"))
parsed_cols = parser.get_move_line_vals(result_row_list[0]).keys()
for col in parsed_cols:
if col not in move_line_obj._columns:
raise UserError(
_("Missing column! Column %s you try to import is not "
"present in the bank statement line!") % col)
move_vals = self.prepare_move_vals(result_row_list, parser)
move = move_obj.create(move_vals)
try:
# Record every line in the bank statement
move_store = []
for line in result_row_list:
parser_vals = parser.get_move_line_vals(line)
values = self.prepare_move_line_vals(parser_vals, move)
move_store.append(values)
# Hack to bypass ORM poor perfomance. Sob...
move_line_obj._insert_lines(move_store)
self.env.invalidate_all()
self._write_extra_move_lines(parser, move)
if self.create_counterpart:
self._create_counterpart(parser, move)
attachment_data = {
'name': 'statement file',
'datas': file_stream,
'datas_fname': "%s.%s" % (fields.Date.today(), ftype),
'res_model': 'account.move',
'res_id': move.id,
}
attachment_obj.create(attachment_data)
# If user ask to launch completion at end of import, do it!
if self.launch_import_completion:
move.button_auto_completion()
# Write the needed log infos on profile
self.write_logs_after_import(move, len(result_row_list))
except UserError:
# "Clean" exception, raise as such
raise
except Exception:
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
raise ValidationError(
_("Statement import error"
"The statement cannot be created: %s") % st)
return move

View File

@@ -0,0 +1,407 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import traceback
import sys
import logging
import psycopg2
from openerp import _, api, fields, models
from openerp.exceptions import ValidationError
_logger = logging.getLogger(__name__)
class ErrorTooManyPartner(Exception):
""" New Exception definition that is raised when more than one partner is
matched by the completion rule.
"""
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
def __repr__(self):
return repr(self.value)
class AccountMoveCompletionRule(models.Model):
"""This will represent all the completion method that we can have to
fullfill the bank statement lines. You'll be able to extend them in you own
module and choose those to apply for every statement profile.
The goal of a rule is to fullfill at least the partner of the line, but
if possible also the reference because we'll use it in the reconciliation
process. The reference should contain the invoice number or the SO number
or any reference that will be matched by the invoice accounting move.
"""
_name = "account.move.completion.rule"
_order = "sequence asc"
sequence = fields.Integer(
string='Sequence',
help="Lower means parsed first.")
name = fields.Char(
string='Name')
journal_ids = fields.Many2many(
comodel_name='account.journal',
relation='account_journal_completion_rule_rel',
string='Related journals')
function_to_call = fields.Selection([
('get_from_name_and_invoice',
'From line name (based on customer invoice number)'),
('get_from_name_and_supplier_invoice',
'From line name (based on supplier invoice number)'),
('get_from_name_and_partner_field',
'From line name (based on partner field)'),
('get_from_name_and_partner_name',
'From line name (based on partner name)')
], string='Method')
def _find_invoice(self, line, inv_type):
"""Find invoice related to statement line"""
inv_obj = self.env['account.invoice']
if inv_type == 'supplier':
type_domain = ('in_invoice', 'in_refund')
number_field = 'reference'
elif inv_type == 'customer':
type_domain = ('out_invoice', 'out_refund')
number_field = 'number'
else:
raise ValidationError(
_('Invalid invoice type for completion: %') % inv_type)
invoices = inv_obj.search([(number_field, '=', line.name.strip()),
('type', 'in', type_domain)])
if invoices:
if len(invoices) == 1:
return invoices
else:
raise ErrorTooManyPartner(
_('Line named "%s" was matched by more than one '
'partner while looking on %s invoices') %
(line.name, inv_type))
return False
def _from_invoice(self, line, inv_type):
"""Populate statement line values"""
if inv_type not in ('supplier', 'customer'):
raise ValidationError(
_('Invalid invoice type for completion: %') %
inv_type)
res = {}
invoice = self._find_invoice(line, inv_type)
if invoice:
partner_id = invoice.commercial_partner_id.id
res = {'partner_id': partner_id,
'account_id': invoice.account_id.id}
return res
# Should be private but data are initialised with no update XML
def get_from_name_and_supplier_invoice(self, line):
"""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
ErrorTooManyPartner error.
:param dict line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
return self._from_invoice(line, 'supplier')
# Should be private but data are initialised with no update XML
def get_from_name_and_invoice(self, line):
"""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
ErrorTooManyPartner error.
:param dict line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
return self._from_invoice(line, 'customer')
# Should be private but data are initialised with no update XML
def get_from_name_and_partner_field(self, line):
"""
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 line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
res = {}
partner_obj = self.env['res.partner']
or_regex = ".*;? *%s *;?.*" % line.name
sql = ("SELECT id from res_partner"
" WHERE bank_statement_label ~* %s")
self.env.cr.execute(sql, (or_regex, ))
partner_ids = self.env.cr.fetchall()
partners = partner_obj.browse([x[0] for x in partner_ids])
if partners:
if len(partners) > 1:
msg = (_('Line named "%s" was matched by more than '
'one partner while looking on partner label: %s') %
(line.name,
','.join([x.name for x in partners])))
raise ErrorTooManyPartner(msg)
res['partner_id'] = partners[0].id
return res
def get_from_name_and_partner_name(self, line):
"""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:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
res = {}
# The regexp_replace() escapes the name to avoid false positive
# example: 'John J. Doe (No 1)' is escaped to 'John J\. Doe \(No 1\)'
# See http://stackoverflow.com/a/400316/1504003 for a list of
# chars to escape. Postgres is POSIX-ARE, compatible with
# POSIX-ERE excepted that '\' must be escaped inside brackets according
# to:
# http://www.postgresql.org/docs/9.0/static/functions-matching.html
# in chapter 9.7.3.6. Limits and Compatibility
sql = r"""
SELECT id FROM (
SELECT id,
regexp_matches(%s,
regexp_replace(name,'([\.\^\$\*\+\?\(\)\[\{\\\|])', %s,
'g'), 'i') AS name_match
FROM res_partner)
AS res_partner_matcher
WHERE name_match IS NOT NULL"""
self.env.cr.execute(sql, (line.name, r"\\\1"))
result = self.env.cr.fetchall()
if result:
if len(result) > 1:
raise ErrorTooManyPartner(
_('Line named "%s" was matched by more than one '
'partner while looking on partner by name') %
line.name)
res['partner_id'] = result[0][0]
return res
class AccountMoveLine(models.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
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_move_base_import module to see how we've done
it.
"""
_inherit = "account.move.line"
_order = "already_completed desc, date asc"
already_completed = fields.Boolean(
string="Auto-Completed",
default=False,
help="When this checkbox is ticked, the auto-completion "
"process/button will ignore this line.")
def _get_line_values_from_rules(self, rules):
"""We'll try to find out the values related to the line based on rules
setted on the profile.. We will ignore line for which already_completed
is ticked.
:return:
A dict of dict value that can be passed directly to the write
method of the statement line or {}. The first dict has statement
line ID as a key: {117009: {'partner_id': 100997,
'account_id': 489L}}
"""
journal_obj = self.env['account.journal']
for line in self:
if not line.already_completed:
# Ask the rule
vals = journal_obj._find_values_from_rules(rules, line)
if vals:
vals['id'] = line['id']
return vals
return {}
def _get_available_columns(self, move_store):
"""Return writeable by SQL columns"""
model_cols = self._columns
avail = [
k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')
]
keys = [k for k in move_store[0].keys() if k in avail]
keys.sort()
return keys
def _prepare_insert(self, move, cols):
""" Apply column formating to prepare data for SQL inserting
Return a copy of statement
"""
move_copy = move
for k, col in move_copy.iteritems():
if k in cols:
move_copy[k] = self._columns[k]._symbol_set[1](col)
return move_copy
def _prepare_manyinsert(self, move_store, cols):
""" Apply column formating to prepare multiple SQL inserts
Return a copy of statement_store
"""
values = []
for move in move_store:
values.append(self._prepare_insert(move, cols))
return values
def _insert_lines(self, move_store):
""" Do raw insert into database because ORM is awfully slow
when doing batch write. It is a shame that batch function
does not exist"""
self.check_access_rule('create')
self.check_access_rights('create', raise_exception=True)
cols = self._get_available_columns(move_store)
move_store = self._prepare_manyinsert(move_store, cols)
tmp_vals = (', '.join(cols), ', '.join(['%%(%s)s' % i for i in cols]))
sql = "INSERT INTO account_move_line (%s) " \
"VALUES (%s);" % tmp_vals
try:
self.env.cr.executemany(sql, tuple(move_store))
except psycopg2.Error as sql_err:
self.env.cr.rollback()
raise ValidationError(_("ORM bypass error"),
sql_err.pgerror)
def _update_line(self, vals):
""" Do raw update into database because ORM is awfully slow
when cheking security.
"""
cols = self._get_available_columns([vals])
vals = self._prepare_insert(vals, cols)
tmp_vals = (', '.join(['%s = %%(%s)s' % (i, i) for i in cols]))
sql = "UPDATE account_move_line " \
"SET %s where id = %%(id)s;" % tmp_vals
try:
self.env.cr.execute(sql, vals)
except psycopg2.Error as sql_err:
self.env.cr.rollback()
raise ValidationError(_("ORM bypass error"),
sql_err.pgerror)
class AccountMove(models.Model):
"""We add a basic button and stuff to support the auto-completion
of the bank statement once line have been imported or manually fullfill.
"""
_name = 'account.move'
_inherit = ['account.move', 'mail.thread']
used_for_completion = fields.Boolean(
related='journal_id.used_for_completion',
readonly=True)
completion_logs = fields.Text(string='Completion Log', readonly=True)
def write_completion_log(self, error_msg, number_imported):
"""Write the log in the completion_logs field of the bank statement to
let the user know what have been done. This is an append mode, so we
don't overwrite what already recoded.
:param int/long stat_id: ID of the account.bank.statement
:param char error_msg: Message to add
:number_imported int/long: Number of lines that have been completed
:return True
"""
user_name = self.env.user.name
number_line = len(self.line_ids)
log = self.completion_logs or ""
completion_date = fields.Datetime.now()
message = (_("%s Account Move %s has %s/%s lines completed by "
"%s \n%s\n%s\n") % (completion_date, self.name,
number_imported, number_line,
user_name, error_msg, log))
self.write({'completion_logs': message})
body = (_('Statement ID %s auto-completed for %s/%s lines completed') %
(self.name, number_imported, number_line)),
self.message_post(body=body)
return True
@api.multi
def button_auto_completion(self):
"""Complete line with values given by rules and tic the
already_completed checkbox so we won't compute them again unless the
user untick them!
"""
move_line_obj = self.env['account.move.line']
compl_lines = 0
move_line_obj.check_access_rule('create')
move_line_obj.check_access_rights('create', raise_exception=True)
for move in self:
msg_lines = []
journal = move.journal_id
rules = journal._get_rules()
res = False
for line in move.line_ids:
try:
res = line._get_line_values_from_rules(rules)
if res:
compl_lines += 1
except ErrorTooManyPartner, exc:
msg_lines.append(repr(exc))
except Exception, exc:
msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st)
if res:
try:
move_line_obj._update_line(res)
except Exception as exc:
msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st)
# we can commit as it is not needed to be atomic
# commiting here adds a nice perfo boost
if not compl_lines % 500:
self.env.cr.commit()
msg = u'\n'.join(msg_lines)
self.write_completion_log(msg, compl_lines)
return True

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp import fields, models
class ResPartner(models.Model):
"""Add a bank label on the partner so that we can use it to match
this partner when we found this in a statement line.
"""
_inherit = 'res.partner'
bank_statement_label = fields.Char(
string='Bank Statement Label',
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).")

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from .parser import new_move_parser
from .parser import AccountMoveImportParser
from . import file_parser
from . import generic_file_parser

View File

@@ -1,28 +1,14 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright Camptocamp SA
# Author Nicolas Bessi, Joel Grand-Guillaume
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp.tools.translate import _
from openerp.osv.orm import except_orm
from openerp.exceptions import UserError
import tempfile
import datetime
from .parser import BankStatementImportParser
from .parser import UnicodeDictReader
from .parser import AccountMoveImportParser, UnicodeDictReader
try:
import xlrd
except:
@@ -35,13 +21,13 @@ def float_or_zero(val):
return float(val) if val else 0.0
class FileParser(BankStatementImportParser):
class FileParser(AccountMoveImportParser):
"""Generic abstract class for defining parser for .csv, .xls or .xlsx file
format.
"""
def __init__(self, parse_name, ftype='csv', extra_fields=None, header=None,
dialect=None, **kwargs):
def __init__(self, journal, ftype='csv', extra_fields=None, header=None,
dialect=None, move_ref=None, **kwargs):
"""
:param char: parse_name: The name of the parser
:param char: ftype: extension of the file (could be csv, xls or
@@ -51,12 +37,11 @@ class FileParser(BankStatementImportParser):
:param list: header : specify header fields if the csv file has no
header
"""
super(FileParser, self).__init__(parse_name, **kwargs)
super(FileParser, self).__init__(journal, **kwargs)
if ftype in ('csv', 'xls', 'xlsx'):
self.ftype = ftype[0:3]
else:
raise except_orm(
_('User Error'),
raise UserError(
_('Invalid file type %s. Please use csv, xls or xlsx') % ftype)
self.conversion_dict = extra_fields
self.keys_to_validate = self.conversion_dict.keys()
@@ -65,6 +50,7 @@ class FileParser(BankStatementImportParser):
# 0 means Windows mode (1900 based dates).
# Set in _parse_xls, from the contents of the file
self.dialect = dialect
self.move_ref = move_ref
def _custom_format(self, *args, **kwargs):
"""No other work on data are needed in this parser."""
@@ -96,8 +82,7 @@ class FileParser(BankStatementImportParser):
parsed_cols = self.result_row_list[0].keys()
for col in self.keys_to_validate:
if col not in parsed_cols:
raise except_orm(_('Invalid data'),
_('Column %s not present in file') % col)
raise UserError(_('Column %s not present in file') % col)
return True
def _post(self, *args, **kwargs):
@@ -143,9 +128,9 @@ class FileParser(BankStatementImportParser):
line[rule] = datetime.datetime.strptime(date_string,
'%Y-%m-%d')
except ValueError as err:
raise except_orm(
_("Date format is not valid."),
_(" It should be YYYY-MM-DD for column: %s"
raise UserError(
_("Date format is not valid."
" It should be YYYY-MM-DD for column: %s"
" value: %s \n \n \n Please check the line with "
"ref: %s \n \n Detail: %s") %
(rule, line.get(rule, _('Missing')),
@@ -154,8 +139,7 @@ class FileParser(BankStatementImportParser):
try:
line[rule] = conversion_rules[rule](line[rule])
except Exception as err:
raise except_orm(
_('Invalid data'),
raise UserError(
_("Value %s of column %s is not valid.\n Please "
"check the line with ref %s:\n \n Detail: %s") %
(line.get(rule, _('Missing')), rule,
@@ -174,9 +158,9 @@ class FileParser(BankStatementImportParser):
self._datemode)
line[rule] = datetime.datetime(*t_tuple)
except Exception as err:
raise except_orm(
_("Date format is not valid"),
_("Please modify the cell formatting to date "
raise UserError(
_("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") %
(rule, line.get(rule, _('Missing')),
@@ -185,8 +169,7 @@ class FileParser(BankStatementImportParser):
try:
line[rule] = conversion_rules[rule](line[rule])
except Exception as err:
raise except_orm(
_('Invalid data'),
raise UserError(
_("Value %s of column %s is not valid.\n Please "
"check the line with ref %s:\n \n Detail: %s") %
(line.get(rule, _('Missing')), rule,

View File

@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import datetime
from .file_parser import FileParser
from openerp.addons.account_move_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.
"""
def __init__(self, journal, ftype='csv', **kwargs):
conversion_dict = {
'label': ustr,
'date': datetime.datetime,
'amount': float_or_zero,
}
# set self.env for later ORM searches
self.env = journal.env
super(GenericFileParser, self).__init__(
journal, ftype=ftype,
extra_fields=conversion_dict,
**kwargs)
@classmethod
def parser_for(cls, parser_name):
"""Used by the new_bank_statement_parser class factory. Return true if
the providen name is generic_csvxls_so
"""
return parser_name == 'generic_csvxls_so'
def get_move_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.
: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, it MUST contain at least:
{
'name':value,
'date_maturity':value,
'credit':value,
'debit':value
}
"""
account_obj = self.env['account.account']
partner_obj = self.env['res.partner']
account_id = False
partner_id = False
if line.get('account'):
accounts = account_obj.search([('code', '=', line['account'])])
if len(accounts) == 1:
account_id = accounts[0].id
if line.get('partner'):
partners = partner_obj.search([('name', '=', line['partner'])])
if len(partners) == 1:
partner_id = partners[0].id
amount = line.get('amount', 0.0)
return {
'name': line.get('label', '/'),
'date_maturity': line.get('date', datetime.datetime.now().date()),
'credit': amount > 0.0 and amount or 0.0,
'debit': amount < 0.0 and -amount or 0.0,
'account_id': account_id,
'partner_id': partner_id,
}

View File

@@ -1,27 +1,12 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-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/>.
#
##############################################################################
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import base64
import csv
from datetime import datetime
from openerp.tools.translate import _
from openerp import _, fields
def UnicodeDictReader(utf8_data, **kwargs):
@@ -41,7 +26,7 @@ def UnicodeDictReader(utf8_data, **kwargs):
for key, value in row.iteritems()])
class BankStatementImportParser(object):
class AccountMoveImportParser(object):
"""
Generic abstract class for defining parser for different files and
@@ -50,21 +35,20 @@ class BankStatementImportParser(object):
from the FileParser instead.
"""
def __init__(self, profile, *args, **kwargs):
def __init__(self, journal, *args, **kwargs):
# The name of the parser as it will be called
self.parser_name = profile.import_type
self.parser_name = journal.import_type
# 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
# The profile record to access its parameters in any parser method
self.profile = profile
self.balance_start = None
self.balance_end = None
self.statement_name = None
self.statement_date = None
self.support_multi_statements = False
self.journal = journal
self.move_date = None
self.move_name = None
self.move_ref = None
self.support_multi_moves = None
@classmethod
def parser_for(cls, parser_name):
@@ -119,19 +103,18 @@ class BankStatementImportParser(object):
"""
return NotImplementedError
def get_st_vals(self):
def get_move_vals(self):
"""This method return a dict of vals that ca be passed to create method
of statement.
:return: dict of vals that represent additional infos for the statement
"""
return {
'name': self.statement_name or '/',
'balance_start': self.balance_start,
'balance_end_real': self.balance_end,
'date': self.statement_date or datetime.now()
'name': self.move_name or '/',
'date': self.move_date or fields.Datetime.now(),
'ref': self.move_ref or '/'
}
def get_st_line_vals(self, line, *args, **kwargs):
def get_move_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
@@ -165,7 +148,7 @@ class BankStatementImportParser(object):
raise Exception(_('No buffer file given.'))
self._format(*args, **kwargs)
self._pre(*args, **kwargs)
if self.support_multi_statements:
if self.support_multi_moves:
while self._parse(*args, **kwargs):
self._validate(*args, **kwargs)
self._post(*args, **kwargs)
@@ -218,13 +201,13 @@ def itersubclasses(cls, _seen=None):
yield sub
def new_bank_statement_parser(profile, *args, **kwargs):
def new_move_parser(journal, *args, **kwargs):
"""Return an instance of the good parser class based on the given profile.
:param profile: browse_record of import profile.
:return: class instance for given profile import type.
"""
for cls in itersubclasses(BankStatementImportParser):
if cls.parser_for(profile.import_type):
return cls(profile, *args, **kwargs)
for cls in itersubclasses(AccountMoveImportParser):
if cls.parser_for(journal.import_type):
return cls(journal, *args, **kwargs)
raise ValueError

View File

@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_bank_st_cmpl_user,account.move.completion.rule.user,model_account_move_completion_rule,account.group_account_user,1,0,0,0
access_account_bank_st_cmpl_manager,account.move.completion.rule.manager,model_account_move_completion_rule,account.group_account_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_bank_st_cmpl_user account.move.completion.rule.user model_account_move_completion_rule account.group_account_user 1 0 0 0
3 access_account_bank_st_cmpl_manager account.move.completion.rule.manager model_account_move_completion_rule account.group_account_manager 1 1 1 1

View File

@@ -0,0 +1,122 @@
-
In order to test the banking framework, I first need to create a journal
-
!record {model: account.journal, id: account.bank_journal}:
used_for_completion: True
rule_ids:
- bank_statement_completion_rule_4
- bank_statement_completion_rule_2
- bank_statement_completion_rule_3
- bank_statement_completion_rule_5
-
Now I create a statement. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.move, id: move_test1}:
name: Move 2
journal_id: account.bank_journal
company_id: base.main_company
-
I create a move line for a CI
-
!record {model: account.move.line, id: move_line_ci}:
name: \
account_id: account.a_sale
move_id: move_test1
date_maturity: '2013-12-20'
credit: 0.0
-
I create a move line for a SI
-
!record {model: account.move.line, id: move_line_si}:
name: \
account_id: account.a_expense
move_id: move_test1
date_maturity: '2013-12-19'
debit: 0.0
-
I create a move line for a CR
-
!record {model: account.move.line, id: move_line_cr}:
name: \
account_id: account.a_expense
move_id: move_test1
date_maturity: '2013-12-19'
debit: 0.0
-
I create a move line for the Partner Name
-
!record {model: account.move.line, id: move_line_partner_name}:
name: Test autocompletion based on Partner Name Camptocamp
account_id: account.a_sale
move_id: move_test1
date_maturity: '2013-12-17'
credit: 0.0
-
I create a move line for the Partner Label
-
!record {model: account.move.line, id: move_line_partner_label}:
name: XXX66Z
account_id: account.a_sale
move_id: move_test1
date_maturity: '2013-12-24'
debit: 0.0
-
and add the correct name
-
!python {model: account.move.line}: |
import datetime as dt
context['check_move_validity'] = False
model.write(cr, uid, [ref('move_line_ci')],
{'name': dt.date.today().strftime('TBNK/%Y/0001'),
'credit': 210.0},
context)
model.write(cr, uid, [ref('move_line_si')],
{'name': 'T2S12345',
'debit': 65.0},
context)
model.write(cr, uid, [ref('move_line_cr')],
{'name': dt.date.today().strftime('RTEXJ/%Y/0001'),
'debit': 210.0},
context)
model.write(cr, uid, [ref('move_line_partner_name')],
{'credit': 600.0},
context)
model.write(cr, uid, [ref('move_line_partner_label')],
{'debit': 932.4},
context)
-
I run the auto complete
-
!python {model: account.move}: |
result = self.button_auto_completion(cr, uid, [ref("move_test1")])
-
Now I can check that all is nice and shiny, line 1. I expect the Customer
Invoice Number to be recognised.
I Use _ref, because ref conflicts with the field ref of the statement line
-
!assert {model: account.move.line, id: move_line_ci, string: Check completion by CI number}:
- partner_id.id == _ref("base.res_partner_12")
-
Line 2. I expect the Supplier invoice number to be recognised. The supplier
invoice was created by the account module demo data, and we confirmed it
here.
-
!assert {model: account.move.line, id: move_line_si, string: Check completion by SI number}:
- partner_id.id == _ref("base.res_partner_12")
-
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.move.line, id: move_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.move.line, id: move_line_partner_name, string: Check completion by partner name}:
- partner_id.name == 'Camptocamp'
-
Line 5. I check that the partner special label has been recognised.
-
!assert {model: account.move.line, id: move_line_partner_label, string: Check completion by partner label}:
- partner_id.id == _ref("base.res_partner_4")

View File

@@ -0,0 +1,42 @@
-
I import account minimal data
-
!python {model: account.invoice}: |
openerp.tools.convert_file(cr,
'account',
openerp.modules.get_module_resource(
'account',
'test',
'account_minimal_test.xml'),
{}, 'init', False, 'test')
-
I create a customer Invoice to be found by the completion.
-
!record {model: account.invoice, id: invoice_for_completion_1}:
company_id: base.main_company
currency_id: base.EUR
invoice_line_ids:
- name: '[PCSC234] PC Assemble SC234'
price_unit: 210.0
quantity: 1.0
product_id: product.product_product_3
uom_id: product.product_uom_unit
journal_id: account.bank_journal
partner_id: base.res_partner_12
reference_type: none
-
I confirm the Invoice
-
!workflow {model: account.invoice, action: invoice_open, ref: invoice_for_completion_1}
-
I check that the invoice state is "Open"
-
!assert {model: account.invoice, id: invoice_for_completion_1}:
- state == 'open'
-
I check that it is given the number "TBNK/%Y/0001"
-
!python {model: account.invoice}: |
import datetime as dt
invoice = model.browse(cr, uid, ref('invoice_for_completion_1'), context)
assert invoice.number == dt.date.today().strftime('TBNK/%Y/0001')

View File

@@ -1,5 +1,5 @@
-
I fill in the field Bank Statement Label in a Partner
-
!record {model: res.partner, id: base.res_partner_6}:
!record {model: res.partner, id: base.res_partner_4}:
bank_statement_label: XXX66Z

View File

@@ -12,17 +12,14 @@
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'
invoice_line_ids:
- name: '[PCSC234] PC Assemble SC234'
price_unit: 210.0
quantity: 1.0
product_id: product.product_product_3
uos_id: product.product_uom_unit
uom_id: product.product_uom_unit
journal_id: account.expenses_journal
partner_id: res_partner_12_child
type: 'out_refund'
@@ -37,7 +34,9 @@
!assert {model: account.invoice, id: refund_for_completion_1}:
- state == 'open'
-
I check that it is given the number "CR0001"
I check that it is given the number "RTEXJ/%Y/0001"
-
!assert {model: account.invoice, id: refund_for_completion_1, string: Check CI number}:
- number == 'CR0001'
!python {model: account.invoice}: |
import datetime as dt
invoice = model.browse(cr, uid, ref('refund_for_completion_1'), context)
assert invoice.number == dt.date.today().strftime('RTEXJ/%Y/0001')

View File

@@ -1,3 +1,14 @@
-
I import account minimal data
-
!python {model: account.invoice}: |
openerp.tools.convert_file(cr,
'account',
openerp.modules.get_module_resource(
'account',
'demo',
'account_invoice_demo.yml'),
{}, 'init', False, 'test')
-
I check that my invoice is a supplier invoice
-
@@ -8,7 +19,7 @@
-
!python {model: account.invoice}: |
self.write(cr, uid, ref('account.demo_invoice_0'), {
'supplier_invoice_number': 'T2S12345'
'reference': 'T2S12345'
})
-
I check a second time that my invoice is still a supplier invoice
@@ -23,7 +34,7 @@
I check that the supplier number is there
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check supplier number}:
- supplier_invoice_number == 'T2S12345'
- reference == 'T2S12345'
-
I check a third time that my invoice is still a supplier invoice
-

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import test_base_completion
from . import test_base_import

View File

@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp import fields, tools
from openerp.modules import get_module_resource
from openerp.tests import common
from collections import namedtuple
name_completion_case = namedtuple(
"name_completion_case", ["partner_name", "line_label", "should_match"])
NAMES_COMPLETION_CASES = [
name_completion_case("Acsone", "Line for Acsone SA", True),
name_completion_case("Acsone", "Line for Acsone", True),
name_completion_case("Acsone", "Acsone for line", True),
name_completion_case("acsone", "Acsone for line", True),
name_completion_case("Acsone SA", "Line for Acsone SA test", True),
name_completion_case("Ac..ne", "Acsone for line", False),
name_completion_case("é@|r{}", "Acsone é@|r{} for line", True),
name_completion_case("Acsone", "A..one for line", False),
name_completion_case("A.one SA", "A.one SA for line", True),
name_completion_case(
"Acsone SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", False),
name_completion_case(
"Acsone ([^a-zA-Z0-9 -]) SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA "
"test", True),
name_completion_case(
r"Acsone (.^$*+?()[{\| -]\) SA", r"Line for Acsone (.^$*+?()[{\| -]\) "
r"SA test", True),
name_completion_case("Acšone SA", "Line for Acšone SA test", True),
]
class BaseCompletion(common.TransactionCase):
def setUp(self):
super(BaseCompletion, self).setUp()
tools.convert_file(self.cr, 'account',
get_module_resource('account', 'test',
'account_minimal_test.xml'),
{}, 'init', False, 'test')
self.account_move_obj = self.env["account.move"]
self.account_move_line_obj = \
self.env["account.move.line"]
self.company_a = self.browse_ref('base.main_company')
self.journal = self.browse_ref("account.bank_journal")
self.partner = self.browse_ref("base.res_partner_12")
self.account_id = self.ref("account.a_recv")
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
"""
self.completion_rule_id = self.ref(
'account_move_base_import.bank_statement_completion_rule_3')
# Create the profile
self.journal.write({
'used_for_completion': True,
'rule_ids': [(6, 0, [self.completion_rule_id])]
})
# Create a bank statement
self.move = self.account_move_obj.create({
"date": fields.Date.today(),
"journal_id": self.journal.id
})
for case in NAMES_COMPLETION_CASES:
self.partner.write({'name': case.partner_name})
self.move_line = self.account_move_line_obj.with_context(
check_move_validity=False
).create({
'account_id': self.account_id,
'credit': 1000.0,
'name': case.line_label,
'move_id': self.move.id,
})
self.assertFalse(
self.move_line.partner_id,
"Partner_id must be blank before completion")
self.move.button_auto_completion()
if case.should_match:
self.assertEquals(
self.partner, self.move_line.partner_id,
"Missing expected partner id after completion "
"(partner_name: %s, line_name: %s)" %
(case.partner_name, case.line_label))
else:
self.assertNotEquals(
self.partner, self.move_line.partner_id,
"Partner id should be empty after completion "
"(partner_name: %s, line_name: %s)"
% (case.partner_name, case.line_label))

View File

@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import base64
import inspect
import os
from operator import attrgetter
from openerp.tests import common
from openerp import tools
from openerp.modules import get_module_resource
class TestCodaImport(common.TransactionCase):
def setUp(self):
super(TestCodaImport, self).setUp()
self.company_a = self.browse_ref('base.main_company')
tools.convert_file(self.cr, 'account',
get_module_resource('account', 'test',
'account_minimal_test.xml'),
{}, 'init', False, 'test')
self.account_move_obj = self.env["account.move"]
self.account_move_line_obj = self.env["account.move.line"]
self.account_id = self.ref("account.a_recv")
self.journal = self.browse_ref("account.bank_journal")
self.import_wizard_obj = self.env['credit.statement.import']
self.partner = self.browse_ref("base.res_partner_12")
self.journal.write({
'used_for_import': True,
"import_type": "generic_csvxls_so",
'partner_id': self.partner.id,
'commission_account_id': self.account_id,
'receivable_account_id': self.account_id,
'create_counterpart': True,
})
def _filename_to_abs_filename(self, file_name):
dir_name = os.path.dirname(inspect.getfile(self.__class__))
return os.path.join(dir_name, file_name)
def _import_file(self, file_name):
""" import a file using the wizard
return the create account.bank.statement object
"""
with open(file_name) as f:
content = f.read()
self.wizard = self.import_wizard_obj.create({
"journal_id": self.journal.id,
'input_statement': base64.b64encode(content),
'file_name': os.path.basename(file_name),
})
res = self.wizard.import_statement()
return self.account_move_obj.browse(res['res_id'])
def test_simple_xls(self):
"""Test import from xls
"""
file_name = self._filename_to_abs_filename(
os.path.join("..", "data", "statement.xls"))
move = self._import_file(file_name)
self._validate_imported_move(move)
def test_simple_csv(self):
"""Test import from csv
"""
file_name = self._filename_to_abs_filename(
os.path.join("..", "data", "statement.csv"))
move = self._import_file(file_name)
self._validate_imported_move(move)
def _validate_imported_move(self, move):
self.assertEqual("/", move.name)
self.assertEqual(5, len(move.line_ids))
move_line = sorted(move.line_ids,
key=attrgetter('date_maturity'))[2]
# common infos
self.assertEqual(move_line.date_maturity, "2011-03-07")
self.assertEqual(move_line.credit, 118.4)
self.assertEqual(move_line.name, "label a")

View File

@@ -0,0 +1,67 @@
<odoo>
<record id="view_move_importer_form" model="ir.ui.view">
<field name="name">account.move.view</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<field name="journal_id" position="after">
<field name="used_for_completion" invisible="1"/>
</field>
<button name="button_cancel" position="after">
<button name="button_auto_completion"
string="Auto Completion"
type="object"
class="oe_highlight"
groups="account.group_account_invoice"
attrs="{'invisible': ['|', ('used_for_completion','=',False), ('state','not in', ['draft'])]}"/>
</button>
<xpath expr="//field[@name='line_ids']/tree/field[@name='credit']" position="after">
<field name="already_completed"/>
</xpath>
<xpath expr="/form/sheet/notebook" position="inside">
<page string="Completion Logs" attrs="{'invisible':[('completion_logs','=',False)]}">
<field name="completion_logs" colspan="4" nolabel="1"/>
</page>
</xpath>
</field>
</record>
<record id="move_completion_rule_view_form" model="ir.ui.view">
<field name="name">account.move.completion.rule.view</field>
<field name="model">account.move.completion.rule</field>
<field name="arch" type="xml">
<form string="Move Completion Rule">
<group>
<field name="sequence"/>
<field name="name" select="1" />
<field name="function_to_call"/>
</group>
<separator colspan="4" string="Related Profiles"/>
<field name="journal_ids" nolabel="1" colspan="4"/>
</form>
</field>
</record>
<record id="move_completion_rule_view_tree" model="ir.ui.view">
<field name="name">account.move.completion.rule.view</field>
<field name="model">account.move.completion.rule</field>
<field name="arch" type="xml">
<tree string="Move Completion Rule">
<field name="sequence"/>
<field name="name" select="1" />
<field name="journal_ids" />
<field name="function_to_call"/>
</tree>
</field>
</record>
<record id="action_move_completion_rule_tree" model="ir.actions.act_window">
<field name="name">Move Completion Rule</field>
<field name="res_model">account.move.completion.rule</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem string="Move Completion Rule" action="action_move_completion_rule_tree"
id="menu_action_move_completion_rule_tree_menu" parent="account.account_management_menu"/>
</odoo>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="journal_importer_view_form" model="ir.ui.view">
<field name="name">account.journal.view</field>
<field name="model">account.journal</field>
<field name="inherit_id" ref="account.view_account_journal_form"/>
<field name="arch" type="xml">
<field name="loss_account_id" position="after">
<field name="used_for_import"/>
<field name="used_for_completion"/>
</field>
<notebook position="inside">
<page string="Import related infos" attrs="{'invisible': [('used_for_import', '=', False)]}">
<group>
<field name="launch_import_completion" attrs="{'invisible': ['|',
('used_for_import', '=', False),
('used_for_completion', '=', False)]}"/>
<field name="last_import_date" readonly="1"/>
<field name="import_type" attrs="{'required': [('used_for_import', '=', True)]}"/>
</group>
<group>
<field name="commission_account_id"/>
<field name="receivable_account_id" attrs="{'required': [('used_for_import', '=', True)]}"/>
<field name="partner_id"/>
<field name="create_counterpart"/>
<field name="split_counterpart" attrs="{'invisible': [('create_counterpart', '=', False)]}"/>
</group>
<group>
<button name="%(account_move_base_import.move_importer_action)d"
string="Import batch file"
type="action" icon="gtk-ok"
colspan = "2"/>
</group>
</page>
<page string="Auto-Completion related infos" attrs="{'invisible': [('used_for_completion', '=', False)]}">
<group>
<separator colspan="4" string="Auto-Completion Rules"/>
<field name="rule_ids" colspan="4" nolabel="1"/>
</group>
</page>
</notebook>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,15 @@
<odoo>
<record id="bk_view_partner_form" model="ir.ui.view">
<field name="name">account_bank_statement_import.view.partner.form</field>
<field name="model">res.partner</field>
<field name="priority">20</field>
<field name="inherit_id" ref="account.view_partner_property_form"/>
<field name="arch" type="xml">
<field name="property_account_payable_id" position="after">
<field name="bank_statement_label"/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import import_statement

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
# © 2011 Akretion
# © 2011-2016 Camptocamp SA
# © 2013 Savoir-faire Linux
# © 2014 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
"""
Wizard to import financial institute date in bank statement
"""
from openerp import _, api, fields, models
import os
class CreditPartnerStatementImporter(models.TransientModel):
_name = "credit.statement.import"
@api.model
def default_get(self, fields):
ctx = self._context
res = {}
if (ctx.get('active_model', False) == 'account.journal' and
ctx.get('active_ids', False)):
ids = ctx['active_ids']
assert len(ids) == 1, \
'You cannot use this on more than one journal !'
res['journal_id'] = ids[0]
values = self.onchange_journal_id(res['journal_id'])
res.update(values.get('value', {}))
return res
journal_id = fields.Many2one(
comodel_name='account.journal',
string='Import configuration parameter',
required=True)
input_statement = fields.Binary(
string='Statement file',
required=True)
partner_id = fields.Many2one(
comodel_name='res.partner',
string='Credit institute partner')
file_name = fields.Char()
receivable_account_id = fields.Many2one(
comodel_name='account.account',
string='Force Receivable/Payable Account')
commission_account_id = fields.Many2one(
comodel_name='account.account',
string='Commission account')
@api.multi
def onchange_journal_id(self, journal_id):
if journal_id:
journal = self.env['account.journal'].browse(journal_id)
return {
'value': {
'partner_id': journal.partner_id.id,
'receivable_account_id': journal.receivable_account_id.id,
'commission_account_id': journal.commission_account_id.id,
}
}
@api.multi
def _check_extension(self):
self.ensure_one()
(__, ftype) = os.path.splitext(self.file_name)
if not ftype:
# We do not use osv exception we do not want to have it logged
raise Exception(_('Please use a file with an extension'))
return ftype
@api.multi
def import_statement(self):
"""This Function import credit card agency statement"""
moves = self.env['account.move']
for importer in self:
journal = importer.journal_id
ftype = importer._check_extension()
moves |= journal.with_context(
file_name=importer.file_name).multi_move_import(
importer.input_statement,
ftype.replace('.', '')
)
xmlid = ('account', 'action_move_journal_line')
action = self.env['ir.actions.act_window'].for_xml_id(*xmlid)
if len(moves) > 1:
action['domain'] = [('id', 'in', moves.ids)]
else:
ref = self.env.ref('account.view_move_form')
action['views'] = [(ref.id, 'form')]
action['res_id'] = moves.id if moves else False
return action

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="move_importer_view" model="ir.ui.view">
<field name="name">credit.statement.import.config.view</field>
<field name="model">credit.statement.import</field>
<field name="arch" type="xml">
<form string="Import move">
<group colspan="4" >
<field name="journal_id" on_change="onchange_journal_id(journal_id)" domain="[('used_for_import', '=', True)]"/>
<field name="input_statement" filename="file_name" colspan="2"/>
<field name="file_name" colspan="2" invisible="1"/>
<separator string="Import Parameters Summary" colspan="4"/>
<field name="partner_id" readonly="1"/>
<field name="receivable_account_id" readonly="1"/>
<field name="commission_account_id" readonly="1"/>
</group>
<footer>
<button icon="gtk-ok" name="import_statement" string="Import file" type="object" class="oe_highlight"/>
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
</footer>
</form>
</field>
</record>
<record id="move_importer_action" model="ir.actions.act_window">
<field name="name">Import Batch File</field>
<field name="res_model">credit.statement.import</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="move_importer_view"/>
<field name="target">new</field>
</record>
<menuitem id="move_importer_menu" name="Import Batch File" action="move_importer_action" parent="account.menu_finance_entries"/>
</odoo>

View File

@@ -0,0 +1,55 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
===================================
Journal Entry Sale Order completion
===================================
This module extends the functionality of account_move_base_import
to add support for completion rules based on Sale Orders. This was initially
part of the module account_statement_base_completion, but is now separate to
keep dependencies separate.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/98/9.0
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.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Joël Grand-Guillaume <joel.grandguillaume@camptocamp.com>
* Leonardo Pistone <leonardo.pistone@camptocamp.com>
* Matthieu Dietrich <matthieu.dietrich@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 https://odoo-community.org.

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import models

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
{
'name': "Journal Entry Sale Order completion",
'version': '9.0.1.0.0',
'author': "Camptocamp,Odoo Community Association (OCA)",
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'easy',
'depends': ['account_move_base_import', 'sale'],
'website': 'http://www.camptocamp.com',
'data': [
'data/completion_rule_data.xml',
],
'test': [
'test/completion_so_test.yml'],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="bank_statement_completion_rule_1" model="account.move.completion.rule">
<field name="name">Match from line name (based on SO number)</field>
<field name="sequence">50</field>
<field name="function_to_call">get_from_name_and_so</field>
</record>
</odoo>

View File

@@ -17,8 +17,8 @@ msgstr ""
"X-Launchpad-Export-Date: 2014-06-06 06:36+0000\n"
"X-Generator: Launchpad (build 17031)\n"
#. module: account_statement_so_completion
#: code:addons/account_statement_so_completion/statement.py:77
#. module: account_move_so_import
#: code:addons/account_move_so_import/statement.py:77
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
@@ -27,7 +27,7 @@ msgstr ""
"La línea llamada \"%s\" (Ref: %s) se casó con más de una empresa al buscar "
"por referencia de pedido."
#. module: account_statement_so_completion
#: model:ir.model,name:account_statement_so_completion.model_account_statement_completion_rule
#. module: account_move_so_import
#: model:ir.model,name:account_move_so_import.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr "account.statement.completion.rule"

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import account_move

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp import _, fields, models
from openerp.addons.account_move_base_import.models.account_move \
import ErrorTooManyPartner
class AccountMoveCompletionRule(models.Model):
_name = "account.move.completion.rule"
_inherit = "account.move.completion.rule"
function_to_call = fields.Selection(
selection_add=[
('get_from_name_and_so', 'From line name (based on SO number)')
])
# Should be private but data are initialised with no update XML
def get_from_name_and_so(self, line):
"""
Match the partner based on the SO 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
ErrorTooManyPartner error.
:param int/long st_line: read of the concerned
account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
res = {}
so_obj = self.env['sale.order']
orders = so_obj.search([('name', '=', line.name)])
if len(orders) > 1:
raise ErrorTooManyPartner(
_('Line named "%s" was matched by more '
'than one partner while looking on SO by ref.') %
line.name)
if len(orders) == 1:
res['partner_id'] = orders[0].partner_id.id
return res

View File

@@ -0,0 +1,59 @@
-
I import account minimal data
-
!python {model: account.invoice}: |
openerp.tools.convert_file(cr,
'account',
openerp.modules.get_module_resource(
'account',
'test',
'account_minimal_test.xml'),
{}, 'init', False, 'test')
-
In order to test the banking framework for Sale Orders, I first need to
create a profile
-
!record {model: account.journal, id: account.bank_journal}:
used_for_completion: True
rule_ids:
- account_move_base_import.bank_statement_completion_rule_4
- account_move_base_import.bank_statement_completion_rule_5
- account_move_base_import.bank_statement_completion_rule_2
- account_move_base_import.bank_statement_completion_rule_3
- bank_statement_completion_rule_1
-
Now I create a statement. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.move, id: move_test_sale1}:
name: Statement for SO
journal_id: account.bank_journal
company_id: base.main_company
-
I create a move line for a SO
-
!record {model: account.move.line, id: move_line_so}:
name: SO007
account_id: account.a_sale
move_id: move_test_sale1
date_maturity: '2013-12-20'
credit: 0.0
-
and add the correct name
-
!python {model: account.move.line}: |
context['check_move_validity'] = False
model.write(cr, uid, [ref('move_line_so')],
{'credit': 14981.0},
context)
-
I run the auto complete
-
!python {model: account.move}: |
result = self.button_auto_completion(cr, uid, [ref("move_test_sale1")])
-
Now I can check that all is nice and shiny, line 1. I expect the Sale Order
Number to be recognised.
-
!assert {model: account.move.line, id: move_line_so, string: Check completion by SO number}:
- partner_id.name == u'China Export'

View File

@@ -0,0 +1,54 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
===================================
Journal Entry transaction ID import
===================================
This module extends the functionality of
account_move_base_import, in order to add both importation
and auto-completion for the "transaction_ref" field added in
base_transaction_id.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/98/9.0
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.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Joël Grand-Guillaume <joel.grandguillaume@camptocamp.com>
* Matthieu Dietrich <matthieu.dietrich@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 https://odoo-community.org.

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import parser
from . import models

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
{
'name': "Journal Entry transactionID import",
'version': '9.0.1.0.0',
'author': "Camptocamp,Odoo Community Association (OCA)",
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal',
'depends': [
'account_move_base_import',
'base_transaction_id'
],
'data': [
'data/completion_rule_data.xml'
],
'test': [
'test/sale.yml',
'test/completion_transactionid_test.yml',
'test/invoice.yml',
'test/completion_invoice_transactionid_test.yml',
],
'website': 'http://www.camptocamp.com',
'installable': True,
'auto_install': False,
'license': 'AGPL-3',
}

View File

@@ -1,18 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<odoo noupdate="1">
<record id="bank_statement_completion_rule_4" model="account.statement.completion.rule">
<record id="bank_statement_completion_rule_4" model="account.move.completion.rule">
<field name="name">Match from Sales Order using transaction ID</field>
<field name="sequence">30</field>
<field name="function_to_call">get_from_transaction_id_and_so</field>
</record>
<record id="bank_statement_completion_rule_trans_id_invoice" model="account.statement.completion.rule">
<record id="bank_statement_completion_rule_trans_id_invoice" model="account.move.completion.rule">
<field name="name">Match from Invoice using transaction ID</field>
<field name="sequence">40</field>
<field name="function_to_call">get_from_transaction_id_and_invoice</field>
</record>
</data>
</openerp>
</odoo>

Binary file not shown.

View File

@@ -1,5 +1,6 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * account_statement_transactionid_import
#
msgid ""
msgstr ""
@@ -15,7 +16,12 @@ msgstr ""
"Plural-Forms: \n"
#. module: account_statement_transactionid_import
#: model:ir.model,name:account_statement_transactionid_import.model_account_statement_profile
msgid "Statement Profile"
#: code:addons/account_statement_transactionid_import/account_move.py:65
#, python-format
msgid "Line named \"%s\" was matched by more than one partner."
msgstr ""
#. module: account_statement_transactionid_import
#: model:ir.model,name:account_statement_transactionid_import.model_account_move_completion_rule
msgid "account.move.completion.rule"
msgstr ""

View File

@@ -17,7 +17,13 @@ msgstr ""
"X-Launchpad-Export-Date: 2014-06-06 06:36+0000\n"
"X-Generator: Launchpad (build 17031)\n"
#. module: account_statement_transactionid_import
#: model:ir.model,name:account_statement_transactionid_import.model_account_statement_profile
msgid "Statement Profile"
msgstr "Perfil de extracto"
#. module: account_move_transactionid_import
#: code:addons/account_move_transactionid_import/account_move.py:65
#, python-format
msgid "Line named \"%s\" (Ref:%s) was matched by more than one partner."
msgstr "La línea llamada \"%s\" (Ref: %s) se casó con más de una empresa."
#. module: account_move_transactionid_import
#: model:ir.model,name:account_move_transactionid_import.model_account_move_completion_rule
msgid "account.move.completion.rule"
msgstr "account.move.completion.rule"

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import account_journal
from . import account_move

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp import fields, models
class AccountJournal(models.Model):
_inherit = "account.journal"
import_type = fields.Selection(
selection_add=[
('generic_csvxls_transaction',
'Generic .csv/.xls based on SO transaction ID')
])

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from openerp import _, fields, models
from openerp.addons.account_move_base_import.models.account_move import \
ErrorTooManyPartner
class AccountMoveCompletionRule(models.Model):
"""Add a rule based on transaction ID"""
_inherit = "account.move.completion.rule"
function_to_call = fields.Selection(
selection_add=[
('get_from_transaction_id_and_so',
'Match Sales Order using transaction ID'),
('get_from_transaction_id_and_invoice',
'Match Invoice using transaction ID')
])
def get_from_transaction_id_and_so(self, line):
"""
Match the partner based on the transaction ID field of the SO.
Then, call the generic st_line method to complete other values.
In that case, we always fullfill the reference of the line with the SO
name.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
res = {}
so_obj = self.env['sale.order']
sales = so_obj.search([('transaction_id', '=', line.transaction_ref)])
if len(sales) > 1:
raise ErrorTooManyPartner(
_('Line named "%s" was matched by more than '
'one partner.') % line.name)
if len(sales) == 1:
sale = sales[0]
res['partner_id'] = sale.partner_id.id
return res
def get_from_transaction_id_and_invoice(self, line):
"""Match the partner based on the transaction ID field of the invoice.
Then, call the generic st_line method to complete other values.
In that case, we always fullfill the reference of the line with the
invoice name.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
res = {}
invoice_obj = self.env['account.invoice']
invoices = invoice_obj.search(
[('transaction_id', '=', line.transaction_ref)])
if len(invoices) > 1:
raise ErrorTooManyPartner(
_('Line named "%s" was matched by more than '
'one partner.') % line.name)
elif len(invoices) == 1:
invoice = invoices[0]
res['partner_id'] = invoice.commercial_partner_id.id
res['account_id'] = invoice.account_id.id
return res

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from . import transactionid_file_parser

View File

@@ -1,25 +1,9 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright Camptocamp SA
# Author Joel Grand-Guillaume
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# © 2011-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import datetime
from openerp.tools import ustr
from account_statement_base_import.parser.file_parser import (
from openerp.addons.account_move_base_import.parser.file_parser import (
FileParser, float_or_zero
)
@@ -44,6 +28,7 @@ class TransactionIDFileParser(FileParser):
'label': ustr,
'date': datetime.datetime,
'amount': float_or_zero,
'commission_amount': float_or_zero,
}
super(TransactionIDFileParser, self).__init__(
profile, extra_fields=conversion_dict, ftype=ftype, header=header,
@@ -56,7 +41,7 @@ class TransactionIDFileParser(FileParser):
"""
return parser_name == 'generic_csvxls_transaction'
def get_st_line_vals(self, line, *args, **kwargs):
def get_move_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
@@ -76,12 +61,11 @@ class TransactionIDFileParser(FileParser):
In this generic parser, the commission is given for every line, so we
store it for each one.
"""
amount = line.get('amount', 0.0)
return {
'name': line.get('label', line.get('ref', '/')),
'date': line.get('date', datetime.datetime.now().date()),
'amount': line.get('amount', 0.0),
'ref': line.get('transaction_id', '/'),
'label': line.get('label', ''),
'transaction_id': line.get('transaction_id', '/'),
'commission_amount': line.get('commission_amount', 0.0)
'name': line.get('label', '/'),
'date_maturity': line.get('date', datetime.datetime.now().date()),
'credit': amount > 0.0 and amount or 0.0,
'debit': amount < 0.0 and amount or 0.0,
'transaction_ref': line.get('transaction_id', '/'),
}

View File

@@ -0,0 +1,44 @@
-
In order to test the banking framework, I first need to create a journal
-
!record {model: account.journal, id: account.bank_journal}:
used_for_completion: True
rule_ids:
- bank_statement_completion_rule_trans_id_invoice
-
Now I create a move. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.move, id: move_invoice_transactionid_test1}:
name: Move with transaction ID
journal_id: account.bank_journal
company_id: base.main_company
-
I create a move line for a SO with transaction ID
-
!record {model: account.move.line, id: move_line_invoice_transactionid}:
name: Test autocompletion based on invoice with transaction ID
account_id: account.a_sale
move_id: move_invoice_transactionid_test1
transaction_ref: XXX77Z
date_maturity: !eval time.strftime('%Y-%m-%d')
credit: 0.0
-
and add the correct amount
-
!python {model: account.move.line}: |
context['check_move_validity'] = False
model.write(cr, uid, [ref('move_line_invoice_transactionid')],
{'credit': 450.0},
context)
-
I run the auto complete
-
!python {model: account.move}: |
result = self.button_auto_completion(cr, uid, [ref("move_invoice_transactionid_test1")])
-
Now I can check that all is nice and shiny, line 1. I expect the invoice has been
recognised from the transaction ID.
-
!assert {model: account.move.line, id: move_line_invoice_transactionid, string: Check completion by Invoice transaction ID}:
- partner_id.name == u'Agrolait'

View File

@@ -0,0 +1,48 @@
-
In order to test the banking framework, I first need to create a journal
-
!record {model: account.journal, id: account.bank_journal}:
used_for_completion: True
rule_ids:
- bank_statement_completion_rule_4
- account_move_base_import.bank_statement_completion_rule_4
- account_move_base_import.bank_statement_completion_rule_5
- account_move_base_import.bank_statement_completion_rule_2
- account_move_base_import.bank_statement_completion_rule_3
-
Now I create a move. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.move, id: move_transactionid_test1}:
name: Move with transaction ID
journal_id: account.bank_journal
company_id: base.main_company
-
I create a move line for a SO with transaction ID
-
!record {model: account.move.line, id: move_line_transactionid}:
name: Test autocompletion based on SO with transaction ID
account_id: account.a_sale
move_id: move_transactionid_test1
transaction_ref: XXX66Z
date_maturity: !eval "'%s-01-06' %(datetime.now().year)"
credit: 0.0
-
and add the correct amount
-
!python {model: account.move.line}: |
context['check_move_validity'] = False
model.write(cr, uid, [ref('move_line_transactionid')],
{'credit': 118.4},
context)
-
I run the auto complete
-
!python {model: account.move}: |
result = self.button_auto_completion(cr, uid, [ref("move_transactionid_test1")])
-
Now I can check that all is nice and shiny, line 1. I expect the SO has been
recognised from the transaction ID.
-
!assert {model: account.move.line, id: move_line_transactionid, string: Check completion by SO transaction ID}:
- partner_id.name == u'Agrolait'

View File

@@ -0,0 +1,25 @@
-
I create a new invoice with transaction ID
-
!record {model: account.invoice, id: invoice_with_transaction_id}:
company_id: base.main_company
currency_id: base.EUR
partner_id: base.res_partner_2
transaction_id: XXX77Z
invoice_line_ids:
- name: '[PCSC234] PC Assemble SC234'
price_unit: 450.0
quantity: 1.0
product_id: product.product_product_3
uom_id: product.product_uom_unit
journal_id: account.bank_journal
reference_type: none
-
I confirm the Invoice
-
!workflow {model: account.invoice, action: invoice_open, ref: invoice_with_transaction_id}
-
I check that the invoice state is "Open"
-
!assert {model: account.invoice, id: invoice_with_transaction_id}:
- state == 'open'

View File

@@ -0,0 +1,21 @@
-
I import account minimal data
-
!python {model: account.invoice}: |
openerp.tools.convert_file(cr,
'account',
openerp.modules.get_module_resource(
'account',
'test',
'account_minimal_test.xml'),
{}, 'init', False, 'test')
-
I create a new Sale Order with transaction ID
-
!record {model: sale.order, id: so_with_transaction_id}:
partner_id: base.res_partner_2
note: Invoice after delivery
transaction_id: XXX66Z
order_line:
- product_id: product.product_product_7
product_uom_qty: 8

View File

@@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
#
#
# Author: Laurent Mignon
# Copyright 2013 'ACSONE SA/NV'
#
# 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 statement
from . import res_partner_bank

View File

@@ -1,48 +0,0 @@
# -*- coding: utf-8 -*-
#
#
# Author: Laurent Mignon
# Copyright 2013 'ACSONE SA/NV'
#
# 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': "Bank statement completion from bank account number",
'version': '1.0.1',
'author': "ACSONE SA/NV,Odoo Community Association (OCA)",
'maintainer': 'ACSONE SA/NV',
'category': 'Finance',
'complexity': 'normal',
'depends': [
'account_statement_base_completion',
],
'description': """
Add a completion method based on the partner bank account number
provided by the bank/office.
Completion will look in the partner with that bank account number
to match the partner, then it will fill in the bank statement line
with it to ease the reconciliation.
""",
'website': 'http://www.acsone.eu',
'data': [
"data.xml",
],
'demo': [],
'installable': False,
'auto_install': False,
'license': 'AGPL-3',
}

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="bank_statement_completion_rule_10" model="account.statement.completion.rule">
<field name="name">Match from bank account number (Normal or IBAN))</field>
<field name="sequence">10</field>
<field name="function_to_call">get_from_bank_account</field>
</record>
</data>
</openerp>

View File

@@ -1,52 +0,0 @@
#
# 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

@@ -1,98 +0,0 @@
# -*- coding: utf-8 -*-
#
#
# Author: Laurent Mignon
# Copyright 2013 'ACSONE SA/NV'
#
# 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.tools.translate import _
from openerp.osv.orm import Model
from openerp.osv import fields
from openerp.addons.account_statement_base_completion.statement \
import ErrorTooManyPartner
class AccountStatementCompletionRule(Model):
"""Add a rule based on transaction ID"""
_inherit = "account.statement.completion.rule"
def _get_functions(self, cr, uid, context=None):
res = super(AccountStatementCompletionRule, self)._get_functions(
cr, uid, context=context)
res.append(('get_from_bank_account',
'From bank account number (Normal or IBAN)'))
return res
def get_from_bank_account(self, cr, uid, st_line, context=None):
"""
Match the partner based on the partner account number field
Then, call the generic st_line method to complete other values.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
partner_acc_number = st_line['partner_acc_number']
if not partner_acc_number:
return {}
st_obj = self.pool['account.bank.statement.line']
res = {}
res_bank_obj = self.pool['res.partner.bank']
ids = res_bank_obj.search_by_acc_number(cr,
uid,
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))
if len(ids) == 1:
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'],
master_account_id=st_line['master_account_id'],
partner_id=res.get('partner_id', False),
line_type=st_line['type'],
amount=st_line['amount'] if st_line['amount'] else 0.0,
context=context)
res.update(st_vals)
return res
class AccountStatementLine(Model):
_inherit = "account.bank.statement.line"
_columns = {
'partner_acc_number': fields.sparse(
type='char',
string='Account Number',
size=64,
serialization_field='additionnal_bank_fields',
help="Account number of the partner"),
}

View File

@@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
#
#
# Authors: Laurent Mignon
# Copyright (c) 2013 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 . import test_bankaccount_completion
checks = [
test_bankaccount_completion
]

View File

@@ -1,123 +0,0 @@
# -*- coding: utf-8 -*-
#
#
# Authors: Laurent Mignon
# Copyright (c) 2013 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.tests import common
import time
ACC_NUMBER = " BE38 7330 4038 5372 "
class bankaccount_completion(common.TransactionCase):
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.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.'
'bank_statement_completion_rule_10')
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.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 a bank statement
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,
}
self.statement_id = self.acc_bk_stmt.create(self.cr,
self.uid,
vals)
# Add a bank account number to the partner
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",
}
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
"""
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()
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

@@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-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 . import partner
from . import statement

View File

@@ -1,87 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-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/>.
#
##############################################################################
{
'name': "Bank statement base completion",
'version': '1.0.3',
'author': "Camptocamp,Odoo Community Association (OCA)",
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal',
'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.
Some basic rules are provided in this module:
1) Match from statement line label (based on partner field 'Bank Statement
Label')
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).
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.
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:
the order Number or an invoice number, or anything that will be found in the
invoice accounting entry part to make the match.
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!
""",
'website': 'http://www.camptocamp.com',
'data': [
'statement_view.xml',
'partner_view.xml',
'data.xml',
'security/ir.model.access.csv',
],
'demo': [],
'test': [
'test/partner.yml',
'test/invoice.yml',
'test/supplier_invoice.yml',
'test/refund.yml',
'test/completion_test.yml'
],
'installable': False,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="bank_statement_completion_rule_2" model="account.statement.completion.rule">
<field name="name">Match from line label (based on partner field 'Bank Statement Label')</field>
<field name="sequence">60</field>
<field name="function_to_call">get_from_label_and_partner_field</field>
</record>
<record id="bank_statement_completion_rule_3" model="account.statement.completion.rule">
<field name="name">Match from line label (based on partner name)</field>
<field name="sequence">70</field>
<field name="function_to_call">get_from_label_and_partner_name</field>
</record>
<record id="bank_statement_completion_rule_4" model="account.statement.completion.rule">
<field name="name">Match from line reference (based on Invoice number)</field>
<field name="sequence">40</field>
<field name="function_to_call">get_from_ref_and_invoice</field>
</record>
<record id="bank_statement_completion_rule_5" model="account.statement.completion.rule">
<field name="name">Match from line reference (based on Invoice Supplier number)</field>
<field name="sequence">45</field>
<field name="function_to_call">get_from_ref_and_supplier_invoice</field>
</record>
</data>
</openerp>

View File

@@ -1,216 +0,0 @@
# Abkhazian translation for banking-addons
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
# This file is distributed under the same license as the banking-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: banking-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2014-01-21 11:57+0000\n"
"PO-Revision-Date: 2014-04-02 23:04+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Abkhazian <ab@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-05-22 06:49+0000\n"
"X-Generator: Launchpad (build 17017)\n"
#. module: account_statement_base_completion
#: view:account.statement.completion.rule:0
msgid "Related Profiles"
msgstr ""
#. module: account_statement_base_completion
#: help:account.bank.statement.line,label:0
msgid ""
"Generic field to store a label given from the bank/office on which we can "
"base the default/standard providen rule."
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:169
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on %s invoices"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement,completion_logs:0
msgid "Completion Log"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement.line,label:0
msgid "Label"
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_bank_statement
msgid "Bank Statement"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,function_to_call:0
msgid "Method"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:326
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on partner by name"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:504
#, python-format
msgid "Statement ID %s auto-completed for %s lines completed"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:500
#, python-format
msgid ""
"%s Bank Statement ID %s has %s lines completed by %s \n"
"%s\n"
"%s\n"
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement.line,additionnal_bank_fields:0
msgid "Additionnal infos from bank"
msgstr ""
#. module: account_statement_base_completion
#: view:account.statement.profile:0
msgid "Auto-Completion Rules"
msgstr ""
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Importation related infos"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:159
#: code:addons/account_statement_base_completion/statement.py:179
#, python-format
msgid "Invalid invoice type for completion: %"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,name:0
msgid "Name"
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_statement_profile
msgid "Statement Profile"
msgstr ""
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Auto Completion"
msgstr ""
#. module: account_statement_base_completion
#: view:account.statement.completion.rule:0
#: model:ir.actions.act_window,name:account_statement_base_completion.action_st_completion_rule_tree
#: model:ir.ui.menu,name:account_statement_base_completion.menu_action_st_completion_rule_tree_menu
msgid "Statement Completion Rule"
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr ""
#. module: account_statement_base_completion
#: help:account.bank.statement.line,additionnal_bank_fields:0
msgid ""
"Used by completion and import system. Adds every field that is present in "
"your bank/office statement file"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,profile_ids:0
#: field:account.statement.profile,rule_ids:0
msgid "Related statement profiles"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:158
#: code:addons/account_statement_base_completion/statement.py:178
#, python-format
msgid "System error"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement.line,already_completed:0
msgid "Auto-Completed"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:448
#: code:addons/account_statement_base_completion/statement.py:466
#, python-format
msgid "ORM bypass error"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,sequence:0
msgid "Sequence"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:280
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on partner label: %s"
msgstr ""
#. module: account_statement_base_completion
#: help:account.statement.completion.rule,sequence:0
msgid "Lower means parsed first."
msgstr ""
#. module: account_statement_base_completion
#: field:res.partner,bank_statement_label:0
msgid "Bank Statement Label"
msgstr ""
#. module: account_statement_base_completion
#: help:account.bank.statement.line,already_completed:0
msgid ""
"When this checkbox is ticked, the auto-completion process/button will ignore "
"this line."
msgstr ""
#. module: account_statement_base_completion
#: help:res.partner,bank_statement_label:0
msgid ""
"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)."
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_res_partner
msgid "Partner"
msgstr ""
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Completion Logs"
msgstr ""

View File

@@ -1,199 +0,0 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * account_statement_base_completion
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-21 11:57+0000\n"
"PO-Revision-Date: 2014-01-21 11:57+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_statement_base_completion
#: view:account.statement.completion.rule:0
msgid "Related Profiles"
msgstr ""
#. module: account_statement_base_completion
#: help:account.bank.statement.line,label:0
msgid "Generic field to store a label given from the bank/office on which we can base the default/standard providen rule."
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:169
#, python-format
msgid "Line named \"%s\" (Ref:%s) was matched by more than one partner while looking on %s invoices"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement,completion_logs:0
msgid "Completion Log"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement.line,label:0
msgid "Label"
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_bank_statement
msgid "Bank Statement"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,function_to_call:0
msgid "Method"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:326
#, python-format
msgid "Line named \"%s\" (Ref:%s) was matched by more than one partner while looking on partner by name"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:504
#, python-format
msgid "Statement ID %s auto-completed for %s lines completed"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:500
#, python-format
msgid "%s Bank Statement ID %s has %s lines completed by %s \n"
"%s\n"
"%s\n"
""
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement.line,additionnal_bank_fields:0
msgid "Additionnal infos from bank"
msgstr ""
#. module: account_statement_base_completion
#: view:account.statement.profile:0
msgid "Auto-Completion Rules"
msgstr ""
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Importation related infos"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:159
#: code:addons/account_statement_base_completion/statement.py:179
#, python-format
msgid "Invalid invoice type for completion: %"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,name:0
msgid "Name"
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_statement_profile
msgid "Statement Profile"
msgstr ""
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Auto Completion"
msgstr ""
#. module: account_statement_base_completion
#: view:account.statement.completion.rule:0
#: model:ir.actions.act_window,name:account_statement_base_completion.action_st_completion_rule_tree
#: model:ir.ui.menu,name:account_statement_base_completion.menu_action_st_completion_rule_tree_menu
msgid "Statement Completion Rule"
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr ""
#. module: account_statement_base_completion
#: help:account.bank.statement.line,additionnal_bank_fields:0
msgid "Used by completion and import system. Adds every field that is present in your bank/office statement file"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,profile_ids:0
#: field:account.statement.profile,rule_ids:0
msgid "Related statement profiles"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:158
#: code:addons/account_statement_base_completion/statement.py:178
#, python-format
msgid "System error"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement.line,already_completed:0
msgid "Auto-Completed"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:448
#: code:addons/account_statement_base_completion/statement.py:466
#, python-format
msgid "ORM bypass error"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,sequence:0
msgid "Sequence"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:280
#, python-format
msgid "Line named \"%s\" (Ref:%s) was matched by more than one partner while looking on partner label: %s"
msgstr ""
#. module: account_statement_base_completion
#: help:account.statement.completion.rule,sequence:0
msgid "Lower means parsed first."
msgstr ""
#. module: account_statement_base_completion
#: field:res.partner,bank_statement_label:0
msgid "Bank Statement Label"
msgstr ""
#. module: account_statement_base_completion
#: help:account.bank.statement.line,already_completed:0
msgid "When this checkbox is ticked, the auto-completion process/button will ignore this line."
msgstr ""
#. module: account_statement_base_completion
#: help:res.partner,bank_statement_label:0
msgid "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)."
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_res_partner
msgid "Partner"
msgstr ""
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Completion Logs"
msgstr ""

View File

@@ -1,235 +0,0 @@
# Spanish translation for banking-addons
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
# This file is distributed under the same license as the banking-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: banking-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2014-01-21 11:57+0000\n"
"PO-Revision-Date: 2014-06-05 22:02+0000\n"
"Last-Translator: Pedro Manuel Baeza <pedro.baeza@gmail.com>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-06-06 06:36+0000\n"
"X-Generator: Launchpad (build 17031)\n"
#. module: account_statement_base_completion
#: view:account.statement.completion.rule:0
msgid "Related Profiles"
msgstr "Perfiles relacionados"
#. module: account_statement_base_completion
#: help:account.bank.statement.line,label:0
msgid ""
"Generic field to store a label given from the bank/office on which we can "
"base the default/standard providen rule."
msgstr ""
"Campo genérico para almacenar una etiqueta dad por el banco/entidad en la "
"que podemos basar la regla provista por defecto."
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:169
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on %s invoices"
msgstr ""
"La línea llamada \"%s\" (Ref: %s) fue casada con más de una empresa al "
"buscar en las facturas %s"
#. module: account_statement_base_completion
#: field:account.bank.statement,completion_logs:0
msgid "Completion Log"
msgstr "Registro de completado"
#. module: account_statement_base_completion
#: field:account.bank.statement.line,label:0
msgid "Label"
msgstr "Etiqueta"
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_bank_statement
msgid "Bank Statement"
msgstr "Extracto bancario"
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,function_to_call:0
msgid "Method"
msgstr "Método"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:326
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on partner by name"
msgstr ""
"La línea llamada \"%s\" (Ref: %s) fue casada con más de una empresa al "
"buscar por nombre de empresa"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:504
#, python-format
msgid "Statement ID %s auto-completed for %s lines completed"
msgstr "Se han auto-completado para el extracto con ID %s %s líneas"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:500
#, python-format
msgid ""
"%s Bank Statement ID %s has %s lines completed by %s \n"
"%s\n"
"%s\n"
msgstr ""
"%s El extracto bancario con ID %s tiene %s líneas completadas por %s \n"
"%s\n"
"%s\n"
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr "Línea del extracto bancario"
#. module: account_statement_base_completion
#: field:account.bank.statement.line,additionnal_bank_fields:0
msgid "Additionnal infos from bank"
msgstr "Información adicional del banco"
#. module: account_statement_base_completion
#: view:account.statement.profile:0
msgid "Auto-Completion Rules"
msgstr "Reglas de auto-completado"
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Importation related infos"
msgstr "Información relacionada con la importación"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:159
#: code:addons/account_statement_base_completion/statement.py:179
#, python-format
msgid "Invalid invoice type for completion: %"
msgstr "Tipo de factura no válida para completado: %s"
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,name:0
msgid "Name"
msgstr "Nombre"
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_statement_profile
msgid "Statement Profile"
msgstr "Perfil de extracto"
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Auto Completion"
msgstr "Auto completado"
#. module: account_statement_base_completion
#: view:account.statement.completion.rule:0
#: model:ir.actions.act_window,name:account_statement_base_completion.action_st_completion_rule_tree
#: model:ir.ui.menu,name:account_statement_base_completion.menu_action_st_completion_rule_tree_menu
msgid "Statement Completion Rule"
msgstr "Regla de completado del extracto"
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr "account.statement.completion.rule"
#. module: account_statement_base_completion
#: help:account.bank.statement.line,additionnal_bank_fields:0
msgid ""
"Used by completion and import system. Adds every field that is present in "
"your bank/office statement file"
msgstr ""
"Usado por el sistema de importación y completado. Añade cada campo que está "
"presente en su archivo de extracto del banco/entidad"
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,profile_ids:0
#: field:account.statement.profile,rule_ids:0
msgid "Related statement profiles"
msgstr "Perfiles de extractos relacionados"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:158
#: code:addons/account_statement_base_completion/statement.py:178
#, python-format
msgid "System error"
msgstr "Error de sistema"
#. module: account_statement_base_completion
#: field:account.bank.statement.line,already_completed:0
msgid "Auto-Completed"
msgstr "Auto-completado"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:448
#: code:addons/account_statement_base_completion/statement.py:466
#, python-format
msgid "ORM bypass error"
msgstr "Error al saltar el ORM"
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,sequence:0
msgid "Sequence"
msgstr "Secuencia"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:280
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on partner label: %s"
msgstr ""
"La línea llamada \"%s\" (Ref: %s) fue casada con más de una empresa al "
"buscar por la etiqueta de empresa: %s"
#. module: account_statement_base_completion
#: help:account.statement.completion.rule,sequence:0
msgid "Lower means parsed first."
msgstr "Más bajo significa reconocido primero."
#. module: account_statement_base_completion
#: field:res.partner,bank_statement_label:0
msgid "Bank Statement Label"
msgstr "Etiqueta del extracto bancario"
#. module: account_statement_base_completion
#: help:account.bank.statement.line,already_completed:0
msgid ""
"When this checkbox is ticked, the auto-completion process/button will ignore "
"this line."
msgstr ""
"Cuando la casilla está marcada, el proceso de auto-completado/botón será "
"ignorado para esta línea."
#. module: account_statement_base_completion
#: help:res.partner,bank_statement_label:0
msgid ""
"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)."
msgstr ""
"Introduzca los diversos tipos de etiqueta encontrados en el extracto "
"bancario, separados por un ;. Si una de estas etiquetas está incluida en la "
"línea del extracto bancario, la empresa será rellenada automáticamente "
"(mientras use este método/reglas en el perfil de extracto)."
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_res_partner
msgid "Partner"
msgstr "Empresa"
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Completion Logs"
msgstr "Registro de completado"

View File

@@ -1,223 +0,0 @@
# French translation for banking-addons
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
# This file is distributed under the same license as the banking-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: banking-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2014-01-21 11:57+0000\n"
"PO-Revision-Date: 2014-06-19 13:54+0000\n"
"Last-Translator: Joël Grand-Guillaume @ camptocamp "
"<joel.grandguillaume@camptocamp.com>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-06-20 06:09+0000\n"
"X-Generator: Launchpad (build 17058)\n"
#. module: account_statement_base_completion
#: view:account.statement.completion.rule:0
msgid "Related Profiles"
msgstr "Profils liés"
#. module: account_statement_base_completion
#: help:account.bank.statement.line,label:0
msgid ""
"Generic field to store a label given from the bank/office on which we can "
"base the default/standard providen rule."
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:169
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on %s invoices"
msgstr ""
#. module: account_statement_base_completion
#: field:account.bank.statement,completion_logs:0
msgid "Completion Log"
msgstr "Journal des complétions"
#. module: account_statement_base_completion
#: field:account.bank.statement.line,label:0
msgid "Label"
msgstr "Libellé"
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_bank_statement
msgid "Bank Statement"
msgstr "Relevé bancaire"
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,function_to_call:0
msgid "Method"
msgstr "Méthode"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:326
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on partner by name"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:504
#, python-format
msgid "Statement ID %s auto-completed for %s lines completed"
msgstr ""
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:500
#, python-format
msgid ""
"%s Bank Statement ID %s has %s lines completed by %s \n"
"%s\n"
"%s\n"
msgstr ""
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr "Ligne de relevé bancaire"
#. module: account_statement_base_completion
#: field:account.bank.statement.line,additionnal_bank_fields:0
msgid "Additionnal infos from bank"
msgstr "Informations additionnelles de la banque"
#. module: account_statement_base_completion
#: view:account.statement.profile:0
msgid "Auto-Completion Rules"
msgstr "Règles d'auto-complétion"
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Importation related infos"
msgstr "Importation des informations liées"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:159
#: code:addons/account_statement_base_completion/statement.py:179
#, python-format
msgid "Invalid invoice type for completion: %"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,name:0
msgid "Name"
msgstr "Nom"
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_statement_profile
msgid "Statement Profile"
msgstr ""
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Auto Completion"
msgstr "Auto-complétion"
#. module: account_statement_base_completion
#: view:account.statement.completion.rule:0
#: model:ir.actions.act_window,name:account_statement_base_completion.action_st_completion_rule_tree
#: model:ir.ui.menu,name:account_statement_base_completion.menu_action_st_completion_rule_tree_menu
msgid "Statement Completion Rule"
msgstr "Règle d'auto-complétion du relevé"
#. module: account_statement_base_completion
#: model:ir.model,name:account_statement_base_completion.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr "account.statement.completion.rule"
#. module: account_statement_base_completion
#: help:account.bank.statement.line,additionnal_bank_fields:0
msgid ""
"Used by completion and import system. Adds every field that is present in "
"your bank/office statement file"
msgstr ""
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,profile_ids:0
#: field:account.statement.profile,rule_ids:0
msgid "Related statement profiles"
msgstr "Profils liés"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:158
#: code:addons/account_statement_base_completion/statement.py:178
#, python-format
msgid "System error"
msgstr "Erreur système"
#. module: account_statement_base_completion
#: field:account.bank.statement.line,already_completed:0
msgid "Auto-Completed"
msgstr "Auto-Complété"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:448
#: code:addons/account_statement_base_completion/statement.py:466
#, python-format
msgid "ORM bypass error"
msgstr "Erreur de bypass de l'ORM"
#. module: account_statement_base_completion
#: field:account.statement.completion.rule,sequence:0
msgid "Sequence"
msgstr "Séquence"
#. module: account_statement_base_completion
#: code:addons/account_statement_base_completion/statement.py:280
#, python-format
msgid ""
"Line named \"%s\" (Ref:%s) was matched by more than one partner while "
"looking on partner label: %s"
msgstr ""
#. module: account_statement_base_completion
#: help:account.statement.completion.rule,sequence:0
msgid "Lower means parsed first."
msgstr "Plus petite séquence analysée en premier."
#. module: account_statement_base_completion
#: field:res.partner,bank_statement_label:0
msgid "Bank Statement Label"
msgstr "Description de relevé bancaire"
#. module: account_statement_base_completion
#: help:account.bank.statement.line,already_completed:0
msgid ""
"When this checkbox is ticked, the auto-completion process/button will ignore "
"this line."
msgstr ""
"Les lignes cochées seront ignorées lorsque vous cliquez sur le bouton auto-"
"complétion"
#. module: account_statement_base_completion
#: help:res.partner,bank_statement_label:0
msgid ""
"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)."
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é "
"(à 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
msgid "Partner"
msgstr "Partenaire"
#. module: account_statement_base_completion
#: view:account.bank.statement:0
msgid "Completion Logs"
msgstr "Journaux d'auto-complétion"

View File

@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
#
# Copyright (C) 2011 Akretion & Camptocamp
# Author : Sébastien BEAU, Joel Grand-Guillaume
#
# 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 ResPartner(orm.Model):
"""Add a bank label on the partner so that we can use it to match
this partner when we found this in a statement line.
"""
_inherit = 'res.partner'
_columns = {
'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)."),
}

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="bk_view_partner_form" model="ir.ui.view">
<field name="name">account_bank_statement_import.view.partner.form</field>
<field name="model">res.partner</field>
<field name="priority">20</field>
<field name="inherit_id" ref="account.view_partner_property_form"/>
<field name="arch" type="xml">
<field name="property_account_payable" position="after">
<field name="bank_statement_label"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -1,3 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_bank_st_cmpl_user,account.statement.completion.rule,model_account_statement_completion_rule,account.group_account_user,1,0,0,0
access_account_bank_st_cmpl_manager,account.statement.completion.rule,model_account_statement_completion_rule,account.group_account_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_bank_st_cmpl_user account.statement.completion.rule model_account_statement_completion_rule account.group_account_user 1 0 0 0
3 access_account_bank_st_cmpl_manager account.statement.completion.rule model_account_statement_completion_rule account.group_account_manager 1 1 1 1

View File

@@ -1,654 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi, Joel Grand-Guillaume
# Copyright 2011-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/>.
#
##############################################################################
# TODO replace customer supplier by package constant
import traceback
import sys
import logging
import simplejson
import inspect
import datetime
import psycopg2
from collections import defaultdict
import re
from openerp.tools.translate import _
from openerp.osv import orm, fields
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from operator import attrgetter
_logger = logging.getLogger(__name__)
class ErrorTooManyPartner(Exception):
""" New Exception definition that is raised when more than one partner is
matched by the completion rule.
"""
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
def __repr__(self):
return repr(self.value)
class AccountStatementProfil(orm.Model):
"""Extend the class to add rules per profile that will match at least the
partner, but it could also be used to match other values as well.
"""
_inherit = "account.statement.profile"
_columns = {
# @Akretion: For now, we don't implement this features, but this would
# probably be there: 'auto_completion': fields.text('Auto Completion'),
# 'transferts_account_id':fields.many2one('account.account',
# 'Transferts Account'),
# => You can implement it in a module easily, we design it with your
# needs in mind as well!
'rule_ids': fields.many2many(
'account.statement.completion.rule',
string='Related statement profiles',
rel='as_rul_st_prof_rel'),
}
def _get_rules(self, cr, uid, profile, context=None):
if isinstance(profile, (int, long)):
prof = self.browse(cr, uid, profile, context=context)
else:
prof = profile
# We need to respect the sequence order
return sorted(prof.rule_ids, key=attrgetter('sequence'))
def _find_values_from_rules(self, cr, uid, calls, line, context=None):
"""This method will execute all related rules, in their sequence order,
to retrieve all the values returned by the first rules that will match.
:param calls: list of lookup function name available in rules
:param dict line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id: value,
...}
"""
if not calls:
calls = self._get_rules(
cr, uid, line['profile_id'], context=context)
rule_obj = self.pool.get('account.statement.completion.rule')
for call in calls:
method_to_call = getattr(rule_obj, call.function_to_call)
if len(inspect.getargspec(method_to_call).args) == 6:
result = method_to_call(cr, uid, call.id, line, context)
else:
result = method_to_call(cr, uid, line, context)
if result:
result['already_completed'] = True
return result
return None
class AccountStatementCompletionRule(orm.Model):
"""This will represent all the completion method that we can have to
fullfill the bank statement lines. You'll be able to extend them in you own
module and choose those to apply for every statement profile.
The goal of a rule is to fullfill at least the partner of the line, but
if possible also the reference because we'll use it in the reconciliation
process. The reference should contain the invoice number or the SO number
or any reference that will be matched by the invoice accounting move.
"""
_name = "account.statement.completion.rule"
_order = "sequence asc"
def _get_functions(self, cr, uid, context=None):
"""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)'),
('get_from_ref_and_supplier_invoice',
'From line reference (based on supplier invoice number)'),
('get_from_label_and_partner_field',
'From line label (based on partner field)'),
('get_from_label_and_partner_name',
'From line label (based on partner name)')
]
def __get_functions(self, cr, uid, context=None):
""" Call method which can be inherited """
return self._get_functions(cr, uid, context=context)
_columns = {
'sequence': fields.integer('Sequence',
help="Lower means parsed first."),
'name': fields.char('Name', size=128),
'profile_ids': fields.many2many(
'account.statement.profile',
rel='as_rul_st_prof_rel',
string='Related statement profiles'),
'function_to_call': fields.selection(__get_functions, 'Method'),
}
def _find_invoice(self, cr, uid, st_line, inv_type, context=None):
"""Find invoice related to statement line"""
inv_obj = self.pool.get('account.invoice')
if inv_type == 'supplier':
type_domain = ('in_invoice', 'in_refund')
number_field = 'supplier_invoice_number'
elif inv_type == 'customer':
type_domain = ('out_invoice', 'out_refund')
number_field = 'number'
else:
raise orm.except_orm(
_('System error'),
_('Invalid invoice type for completion: %') % inv_type)
inv_id = inv_obj.search(cr, uid,
[(number_field, '=', st_line['ref'].strip()),
('type', 'in', type_domain)],
context=context)
if inv_id:
if len(inv_id) == 1:
inv = inv_obj.browse(cr, uid, inv_id[0], context=context)
else:
raise ErrorTooManyPartner(
_('Line named "%s" (Ref:%s) was matched by more than one '
'partner while looking on %s invoices') %
(st_line['name'], st_line['ref'], inv_type))
return inv
return False
def _from_invoice(self, cr, uid, line, inv_type, context):
"""Populate statement line values"""
if inv_type not in ('supplier', 'customer'):
raise orm.except_orm(_('System error'),
_('Invalid invoice type for completion: %') %
inv_type)
res = {}
inv = self._find_invoice(cr, uid, line, inv_type, context=context)
if inv:
partner_id = inv.commercial_partner_id.id
res = {'partner_id': partner_id,
'account_id': inv.account_id.id,
'type': inv_type}
override_acc = line['master_account_id']
if override_acc:
res['account_id'] = override_acc
return res
# Should be private but data are initialised with no update XML
def get_from_ref_and_supplier_invoice(self, cr, uid, line, context=None):
"""Match the partner based on the invoice supplier 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 ErrorTooManyPartner error.
:param dict line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
return self._from_invoice(cr, uid, line, 'supplier', context=context)
# 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
ErrorTooManyPartner error.
:param dict line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
return self._from_invoice(cr, uid, line, 'customer', context=context)
# 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.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
partner_obj = self.pool['res.partner']
st_obj = self.pool.get('account.bank.statement.line')
res = {}
# As we have to iterate on each partner for each line,
#  we memoize the pair to avoid
# to redo computation for each line.
# Following code can be done by a single SQL query
# but this option is not really maintanable
if not context.get('label_memoizer'):
context['label_memoizer'] = defaultdict(list)
partner_ids = partner_obj.search(
cr, uid, [('bank_statement_label', '!=', False)],
context=context)
line_ids = context.get('line_ids', [])
for partner in partner_obj.browse(cr, uid, partner_ids,
context=context):
vals = '|'.join(
re.escape(x.strip())
for x in partner.bank_statement_label.split(';'))
or_regex = ".*%s.*" % vals
sql = ("SELECT id from account_bank_statement_line"
" WHERE id in %s"
" AND name ~* %s")
cr.execute(sql, (line_ids, or_regex))
pairs = cr.fetchall()
for pair in pairs:
context['label_memoizer'][pair[0]].append(partner)
if st_line['id'] in context['label_memoizer']:
found_partner = context['label_memoizer'][st_line['id']]
if len(found_partner) > 1:
msg = (_('Line named "%s" (Ref:%s) was matched by more than '
'one partner while looking on partner label: %s') %
(st_line['name'], st_line['ref'],
','.join([x.name for x in found_partner])))
raise ErrorTooManyPartner(msg)
res['partner_id'] = found_partner[0].id
st_vals = st_obj.get_values_for_line(
cr, uid, profile_id=st_line['profile_id'],
master_account_id=st_line['master_account_id'],
partner_id=found_partner[0].id, line_type=False,
amount=st_line['amount'] if st_line['amount'] else 0.0,
context=context)
res.update(st_vals)
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.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
res = {}
# We memoize allowed partner
if not context.get('partner_memoizer'):
context['partner_memoizer'] = tuple(
self.pool['res.partner'].search(cr, uid, []))
if not context['partner_memoizer']:
return res
st_obj = self.pool.get('account.bank.statement.line')
# The regexp_replace() escapes the name to avoid false positive
# example: 'John J. Doe (No 1)' is escaped to 'John J\. Doe \(No 1\)'
# See http://stackoverflow.com/a/400316/1504003 for a list of
# chars to escape. Postgres is POSIX-ARE, compatible with
# POSIX-ERE excepted that '\' must be escaped inside brackets according
# to:
# http://www.postgresql.org/docs/9.0/static/functions-matching.html
# in chapter 9.7.3.6. Limits and Compatibility
sql = r"""
SELECT id FROM (
SELECT id,
regexp_matches(%s,
regexp_replace(name,'([\.\^\$\*\+\?\(\)\[\{\\\|])', %s,
'g'), 'i') AS name_match
FROM res_partner
WHERE id IN %s)
AS res_patner_matcher
WHERE name_match IS NOT NULL"""
cr.execute(
sql, (st_line['name'], r"\\\1", context['partner_memoizer']))
result = cr.fetchall()
if not result:
return res
if len(result) > 1:
raise ErrorTooManyPartner(
_('Line named "%s" (Ref:%s) was matched by more than one '
'partner while looking on partner by name') %
(st_line['name'], st_line['ref']))
res['partner_id'] = result[0][0]
st_vals = st_obj.get_values_for_line(
cr, uid, profile_id=st_line['profile_id'],
master_account_id=st_line['master_account_id'],
partner_id=res['partner_id'], line_type=False,
amount=st_line['amount'] if st_line['amount'] else 0.0,
context=context)
res.update(st_vals)
return res
class AccountStatement(orm.Model):
_inherit = "account.bank.statement"
def button_confirm_bank(self, cr, uid, ids, context=None):
line_obj = self.pool['account.bank.statement.line']
for stat_id in ids:
line_without_account = line_obj.search(cr, uid, [
['statement_id', '=', stat_id],
['account_id', '=', False],
], context=context)
if line_without_account:
stat = self.browse(cr, uid, stat_id, context=context)
raise orm.except_orm(
_('User error'),
_('You should fill all account on the line of the'
' statement %s') % stat.name)
return super(AccountStatement, self).button_confirm_bank(
cr, uid, ids, context=context)
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
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
it.
"""
_inherit = "account.bank.statement.line"
_order = "already_completed desc, date asc"
_columns = {
'additionnal_bank_fields': fields.serialized(
'Additionnal infos from bank',
help="Used by completion and import system. Adds every field that "
"is present in your bank/office statement file"),
'label': fields.sparse(
type='char',
string='Label',
serialization_field='additionnal_bank_fields',
help="Generic field to store a label given from the "
"bank/office on which we can base the default/standard "
"providen rule."),
'already_completed': fields.boolean(
"Auto-Completed",
help="When this checkbox is ticked, the auto-completion "
"process/button will ignore this line."),
# Set account_id field as optional by removing required option.
'account_id': fields.many2one('account.account', 'Account'),
}
_defaults = {
'already_completed': False,
}
def _get_line_values_from_rules(self, cr, uid, line, rules, context=None):
"""We'll try to find out the values related to the line based on rules
setted on the profile.. We will ignore line for which already_completed
is ticked.
:return:
A dict of dict value that can be passed directly to the write
method of the statement line or {}. The first dict has statement
line ID as a key: {117009: {'partner_id': 100997,
'account_id': 489L}}
"""
profile_obj = self.pool['account.statement.profile']
if line.get('already_completed'):
return {}
# Ask the rule
vals = profile_obj._find_values_from_rules(
cr, uid, rules, line, context)
if vals:
vals['id'] = line['id']
return vals
return {}
def _get_available_columns(self, statement_store,
include_serializable=False):
"""Return writeable by SQL columns"""
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')
]
keys = [k for k in statement_store[0].keys() if k in avail]
# add sparse fields..
if include_serializable:
for k, col in model_cols.iteritems():
if k in statement_store[0].keys() and \
isinstance(col, fields.sparse) and \
col.serialization_field not in keys and \
col._type == 'char':
keys.append(col.serialization_field)
keys.sort()
return keys
def _prepare_insert(self, statement, cols):
""" Apply column formating to prepare data for SQL inserting
Return a copy of statement
"""
st_copy = statement
for k, col in st_copy.iteritems():
if k in cols:
st_copy[k] = self._columns[k]._symbol_set[1](col)
return st_copy
def _prepare_manyinsert(self, statement_store, cols):
""" Apply column formating to prepare multiple SQL inserts
Return a copy of statement_store
"""
values = []
for statement in statement_store:
values.append(self._prepare_insert(statement, cols))
return values
def _serialize_sparse_fields(self, cols, statement_store):
""" Serialize sparse fields values in the target serialized field
Return a copy of statement_store
"""
statement_line_obj = self.pool['account.bank.statement.line']
model_cols = statement_line_obj._columns
sparse_fields = dict(
[(k, col) for k, col in model_cols.iteritems() if isinstance(
col, fields.sparse) and col._type == 'char'])
values = []
for statement in statement_store:
to_json_k = set()
st_copy = statement.copy()
for k, col in sparse_fields.iteritems():
if k in st_copy:
to_json_k.add(col.serialization_field)
serialized = st_copy.setdefault(
col.serialization_field, {})
serialized[k] = st_copy[k]
for k in to_json_k:
st_copy[k] = simplejson.dumps(st_copy[k])
values.append(st_copy)
return values
def _insert_lines(self, cr, uid, statement_store, context=None):
""" Do raw insert into database because ORM is awfully slow
when doing batch write. It is a shame that batch function
does not exist"""
statement_line_obj = self.pool['account.bank.statement.line']
statement_line_obj.check_access_rule(cr, uid, [], 'create')
statement_line_obj.check_access_rights(
cr, uid, 'create', raise_exception=True)
cols = self._get_available_columns(
statement_store, include_serializable=True)
statement_store = self._prepare_manyinsert(statement_store, cols)
tmp_vals = (', '.join(cols), ', '.join(['%%(%s)s' % i for i in cols]))
sql = "INSERT INTO account_bank_statement_line (%s) " \
"VALUES (%s);" % tmp_vals
try:
cr.executemany(
sql, tuple(self._serialize_sparse_fields(cols,
statement_store)))
except psycopg2.Error as sql_err:
cr.rollback()
raise orm.except_orm(_("ORM bypass error"),
sql_err.pgerror)
def _update_line(self, cr, uid, vals, context=None):
""" Do raw update into database because ORM is awfully slow
when cheking security.
TODO / WARM: sparse fields are skipped by the method. IOW, if your
completion rule update an sparse field, the updated value will never
be stored in the database. It would be safer to call the update method
from the ORM for records updating this kind of fields.
"""
cols = self._get_available_columns([vals])
vals = self._prepare_insert(vals, cols)
tmp_vals = (', '.join(['%s = %%(%s)s' % (i, i) for i in cols]))
sql = "UPDATE account_bank_statement_line " \
"SET %s where id = %%(id)s;" % tmp_vals
try:
cr.execute(sql, vals)
except psycopg2.Error as sql_err:
cr.rollback()
raise orm.except_orm(_("ORM bypass error"),
sql_err.pgerror)
class AccountBankStatement(orm.Model):
"""We add a basic button and stuff to support the auto-completion
of the bank statement once line have been imported or manually fullfill.
"""
_inherit = "account.bank.statement"
_columns = {
'completion_logs': fields.text('Completion Log', readonly=True),
}
def write_completion_log(self, cr, uid, stat_id, error_msg,
number_imported, context=None):
"""Write the log in the completion_logs field of the bank statement to
let the user know what have been done. This is an append mode, so we
don't overwrite what already recoded.
:param int/long stat_id: ID of the account.bank.statement
:param char error_msg: Message to add
:number_imported int/long: Number of lines that have been completed
:return True
"""
user_name = self.pool.get('res.users').read(
cr, uid, uid, ['name'], context=context)['name']
statement = self.browse(cr, uid, stat_id, context=context)
number_line = len(statement.line_ids)
log = self.read(cr, uid, stat_id, ['completion_logs'],
context=context)['completion_logs']
log = log if log else ""
completion_date = datetime.datetime.now().strftime(
DEFAULT_SERVER_DATETIME_FORMAT)
message = (_("%s Bank Statement ID %s has %s/%s lines completed by "
"%s \n%s\n%s\n") % (completion_date, stat_id,
number_imported, number_line,
user_name, error_msg, log))
self.write(
cr, uid, [stat_id], {'completion_logs': message}, context=context)
body = (_('Statement ID %s auto-completed for %s/%s lines completed') %
(stat_id, number_imported, number_line)),
self.message_post(cr, uid, [stat_id], body=body, context=context)
return True
def button_auto_completion(self, cr, uid, ids, context=None):
"""Complete line with values given by rules and tic the
already_completed checkbox so we won't compute them again unless the
user untick them!
"""
if context is None:
context = {}
stat_line_obj = self.pool['account.bank.statement.line']
profile_obj = self.pool.get('account.statement.profile')
compl_lines = 0
stat_line_obj.check_access_rule(cr, uid, [], 'create')
stat_line_obj.check_access_rights(
cr, uid, 'create', raise_exception=True)
for stat in self.browse(cr, uid, ids, context=context):
msg_lines = []
ctx = context.copy()
ctx['line_ids'] = tuple((x.id for x in stat.line_ids))
b_profile = stat.profile_id
rules = profile_obj._get_rules(cr, uid, b_profile, context=context)
# Only for perfo even it gains almost nothing
profile_id = b_profile.id
master_account_id = b_profile.receivable_account_id
master_account_id = master_account_id.id if \
master_account_id else False
res = False
for line in stat_line_obj.read(cr, uid, ctx['line_ids']):
try:
# performance trick
line['master_account_id'] = master_account_id
line['profile_id'] = profile_id
res = stat_line_obj._get_line_values_from_rules(
cr, uid, line, rules, context=ctx)
if res:
compl_lines += 1
except ErrorTooManyPartner, exc:
msg_lines.append(repr(exc))
except Exception, exc:
msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st)
if res:
# stat_line_obj.write(cr, uid, [line.id], vals,
# context=ctx)
try:
stat_line_obj._update_line(
cr, uid, res, context=context)
except Exception as exc:
msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st)
# we can commit as it is not needed to be atomic
# commiting here adds a nice perfo boost
if not compl_lines % 500:
cr.commit()
msg = u'\n'.join(msg_lines)
self.write_completion_log(cr, uid, stat.id,
msg, compl_lines, context=context)
return True

View File

@@ -1,99 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="bank_statement_view_form" model="ir.ui.view">
<field name="name">account_bank_statement_import_base.bank_statement.view_form</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field eval="16" name="priority"/>
<field name="arch" type="xml">
<data>
<xpath expr="/form/sheet/notebook/page/field[@name='line_ids']/form/group/field[@name='sequence']" position="after">
<separator colspan="4" string="Importation related infos"/>
<field name="label" />
<field name="already_completed" />
</xpath>
<!-- <xpath expr="/form/group[2]" position="attributes">
<attribute name="col">10</attribute>
</xpath> -->
<button name="button_confirm_bank" position="before">
<button name="button_auto_completion" string="Auto Completion" states='draft,open' type="object" class="oe_highlight" icon="gtk-execute"/>
</button>
<xpath expr="/form/sheet/notebook/page[@string='Transactions']" position="after">
<page string="Completion Logs" attrs="{'invisible':[('completion_logs','=',False)]}">
<field name="completion_logs" colspan="4" nolabel="1" attrs="{'invisible':[('completion_logs','=',False)]}"/>
</page>
</xpath>
</data>
</field>
</record>
<record id="bank_statement_view_form2" model="ir.ui.view">
<field name="name">account_bank_statement_import_base.bank_statement.auto_cmpl</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field name="arch" type="xml">
<data>
<xpath expr="/form/sheet/notebook/page/field[@name='line_ids']/tree/field[@name='amount']" position="after">
<field name="already_completed" />
</xpath>
</data>
</field>
</record>
<record id="statement_rules_view_form" model="ir.ui.view">
<field name="name">account.statement.profile.view</field>
<field name="model">account.statement.profile</field>
<field name="inherit_id" ref="account_statement_ext.statement_importer_view_form"/>
<field name="arch" type="xml">
<field name="bank_statement_prefix" position="after">
<separator colspan="4" string="Auto-Completion Rules"/>
<field name="rule_ids" colspan="4" nolabel="1"/>
</field>
</field>
</record>
<record id="statement_st_completion_rule_view_form" model="ir.ui.view">
<field name="name">account.statement.completion.rule.view</field>
<field name="model">account.statement.completion.rule</field>
<field name="arch" type="xml">
<form string="Statement Completion Rule">
<field name="sequence"/>
<field name="name" select="1" />
<field name="function_to_call"/>
<separator colspan="4" string="Related Profiles"/>
<field name="profile_ids" nolabel="1" colspan="4"/>
</form>
</field>
</record>
<record id="statement_st_completion_rule_view_tree" model="ir.ui.view">
<field name="name">account.statement.completion.rule.view</field>
<field name="model">account.statement.completion.rule</field>
<field name="arch" type="xml">
<tree string="Statement Completion Rule">
<field name="sequence"/>
<field name="name" select="1" />
<field name="profile_ids" />
<field name="function_to_call"/>
</tree>
</field>
</record>
<record id="action_st_completion_rule_tree" model="ir.actions.act_window">
<field name="name">Statement Completion Rule</field>
<field name="res_model">account.statement.completion.rule</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem string="Statement Completion Rule" action="action_st_completion_rule_tree"
id="menu_action_st_completion_rule_tree_menu" parent="account.menu_configuration_misc"
sequence="30"/>
</data>
</openerp>

View File

@@ -1,102 +0,0 @@
-
In order to test the banking framework, I first need to create a profile
-
!record {model: account.statement.profile, id: profile_test1}:
name: Bank EUR Profile
journal_id: account.bank_journal
commission_account_id: account.a_expense
company_id: base.main_company
balance_check: True
rule_ids:
- bank_statement_completion_rule_4
- bank_statement_completion_rule_5
- bank_statement_completion_rule_2
- bank_statement_completion_rule_3
-
Now I create a statement. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.bank.statement, id: statement_test1}:
name: Statement 2
profile_id: profile_test1
company_id: base.main_company
-
I create a statement line for a CI
-
!record {model: account.bank.statement.line, id: statement_line_ci}:
name: Test autocompletion based on Customer Invoice Number
statement_id: statement_test1
ref: CI0001
date: '2013-12-20'
amount: 210.0
-
I create a statement line for a SI
-
!record {model: account.bank.statement.line, id: statement_line_si}:
name: Test autocompletion based on Supplier Invoice Number
statement_id: statement_test1
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
-
!record {model: account.bank.statement.line, id: statement_line_partner_name}:
name: Test autocompletion based on Partner Name Vauxoo
statement_id: statement_test1
ref: /
date: '2013-12-17'
amount: 600.0
-
I create a statement line for the Partner Label
-
!record {model: account.bank.statement.line, id: statement_line_partner_label}:
name: test autocompletion based on text (XXX66Z) matching with partner form information (note that Ref does not exist)
statement_id: statement_test1
ref: ZU788
date: '2013-12-24'
amount: -932.4
-
I run the auto complete
-
!python {model: account.bank.statement}: |
result = self.button_auto_completion(cr, uid, [ref("statement_test1")])
-
Now I can check that all is nice and shiny, line 1. I expect the Customer
Invoice Number to be recognised.
I Use _ref, because ref conflicts with the field ref of the statement line
-
!assert {model: account.bank.statement.line, id: statement_line_ci, string: Check completion by CI number}:
- partner_id.id == _ref("base.res_partner_12")
-
Line 2. I expect the Supplier invoice number to be recognised. The supplier
invoice was created by the account module demo data, and we confirmed it
here.
-
!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 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 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

@@ -1,32 +0,0 @@
-
I create a customer Invoice to be found by the completion.
-
!record {model: account.invoice, id: invoice_for_completion_1}:
account_id: account.a_recv
company_id: base.main_company
currency_id: base.EUR
internal_number: CI0001
invoice_line:
- account_id: account.a_sale
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.bank_journal
partner_id: base.res_partner_12
reference_type: none
-
I confirm the Invoice
-
!workflow {model: account.invoice, action: invoice_open, ref: invoice_for_completion_1}
-
I check that the invoice state is "Open"
-
!assert {model: account.invoice, id: invoice_for_completion_1}:
- state == 'open'
-
I check that it is given the number "CI0001"
-
!assert {model: account.invoice, id: invoice_for_completion_1, string: Check CI number}:
- number == 'CI0001'

View File

@@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
#
#
# 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 . import test_base_completion
checks = [
test_base_completion
]

View File

@@ -1,122 +0,0 @@
# -*- coding: utf-8 -*-
#
#
# 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.tests import common
import time
from collections import namedtuple
name_completion_case = namedtuple(
"name_completion_case", ["partner_name", "line_label", "should_match"])
NAMES_COMPLETION_CASES = [
name_completion_case("Acsone", "Line for Acsone SA", True),
name_completion_case("Acsone", "Line for Acsone", True),
name_completion_case("Acsone", "Acsone for line", True),
name_completion_case("acsone", "Acsone for line", True),
name_completion_case("Acsone SA", "Line for Acsone SA test", True),
name_completion_case("Ac..ne", "Acsone for line", False),
name_completion_case("é@|r{}", "Acsone é@|r{} for line", True),
name_completion_case("Acsone", "A..one for line", False),
name_completion_case("A.one SA", "A.one SA for line", True),
name_completion_case(
"Acsone SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", False),
name_completion_case(
"Acsone ([^a-zA-Z0-9 -]) SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA "
"test", True),
name_completion_case(
r"Acsone (.^$*+?()[{\| -]\) SA", r"Line for Acsone (.^$*+?()[{\| -]\) "
r"SA test", True),
name_completion_case("Acšone SA", "Line for Acšone SA test", True),
]
class base_completion(common.TransactionCase):
def setUp(self):
super(base_completion, self).setUp()
self.company_a = self.browse_ref('base.main_company')
self.profile_obj = self.registry("account.statement.profile")
self.partner_obj = self.registry("res.partner")
self.account_bank_statement_obj = self.registry(
"account.bank.statement")
self.account_bank_statement_line_obj = self.registry(
"account.bank.statement.line")
self.journal_id = self.ref("account.bank_journal")
self.partner_id = self.ref('base.main_partner')
self.account_id = self.ref("account.a_recv")
self.partner_id = self.ref("base.res_partner_12")
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
"""
self.completion_rule_id = self.ref(
'account_statement_base_completion.'
'bank_statement_completion_rule_3')
# Create the profile
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 a bank statement
self.statement_id = self.account_bank_statement_obj.create(
self.cr, self.uid, {
"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
})
for case in NAMES_COMPLETION_CASES:
self.partner_obj.write(
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,
'name': case.line_label,
'ref': 'My ref',
'statement_id': self.statement_id,
})
statement_line = self.account_bank_statement_line_obj.browse(
self.cr, self.uid, statement_line_id)
self.assertFalse(
statement_line.partner_id,
"Partner_id must be blank before completion")
statement_obj = self.account_bank_statement_obj.browse(
self.cr, self.uid, self.statement_id)
statement_obj.button_auto_completion()
statement_line = self.account_bank_statement_line_obj.browse(
self.cr, self.uid, statement_line_id)
if case.should_match:
self.assertEquals(
self.partner_id, statement_line.partner_id['id'],
"Missing expected partner id after completion "
"(partner_name: %s, line_name: %s)" %
(case.partner_name, case.line_label))
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))

View File

@@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-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 . import parser
from . import wizard
from . import statement

View File

@@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-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/>.
#
##############################################################################
{'name': "Bank statement base import",
'version': '1.2',
'author': "Camptocamp,Odoo Community Association (OCA)",
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal',
'depends': [
'account_statement_ext',
'account_statement_base_completion'
],
'description': """
This module brings basic methods and fields on bank statement to deal with
the importation of different bank and offices. A generic abstract method is
defined and an example that gives you a basic way of importing bank statement
through a standard file is provided.
This module improves the bank statement and allows you to import your bank
transactions with a standard .csv or .xls file (you'll find it in the 'data'
folder). It respects the profile (provided by the accouhnt_statement_ext
module) to pass the entries. That means, you'll have to choose a file format
for each profile.
In order to achieve this it uses the `xlrd` Python module which you will need
to install separately in your environment.
This module can handle a commission taken by the payment office and has the
following format:
* __ref__: the SO number, INV number or any matching ref found. It'll be used
as reference in the generated entries and will be useful for reconciliation
process
* __date__: date of the payment
* __amount__: amount paid in the currency of the journal used in the
importation profile
* __label__: the comunication given by the payment office, used as
communication in the generated entries.
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.
""",
'website': 'http://www.camptocamp.com',
'data': [
"wizard/import_statement_view.xml",
"statement_view.xml",
],
'test': [],
'installable': False,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}

View File

@@ -1,4 +0,0 @@
"ref";"date";"amount";"commission_amount";"label"
50969286;2011-03-07 13:45:14;118.4;-11.84;"label a"
51065326;2011-03-02 13:45:14;189;-15.12;"label b"
51179306;2011-03-02 17:45:14;189;-15.12;"label c"
1 ref date amount commission_amount label
2 50969286 2011-03-07 13:45:14 118.4 -11.84 label a
3 51065326 2011-03-02 13:45:14 189 -15.12 label b
4 51179306 2011-03-02 17:45:14 189 -15.12 label c

View File

@@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi, Joel Grand-Guillaume
# Copyright 2011-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 .parser import new_bank_statement_parser
from .parser import BankStatementImportParser
from . import file_parser
from . import generic_file_parser

View File

@@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright Camptocamp SA
# Author Joel Grand-Guillaume
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import datetime
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.
"""
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,
extra_fields=conversion_dict,
**kwargs)
@classmethod
def parser_for(cls, parser_name):
"""Used by the new_bank_statement_parser class factory. Return true if
the providen name is generic_csvxls_so
"""
return parser_name == 'generic_csvxls_so'
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.
: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, it MUST contain at least:
{
'name':value,
'date':value,
'amount':value,
'ref':value,
'label':value,
}
"""
return {
'name': line.get('label', line.get('ref', '/')),
'date': line.get('date', datetime.datetime.now().date()),
'amount': line.get('amount', 0.0),
'ref': line.get('ref', '/'),
'label': line.get('label', ''),
}

Some files were not shown because too many files have changed in this diff Show More