Remane import modules
parent
16bacdcd5d
commit
1560419021
|
@ -0,0 +1,101 @@
|
|||
.. 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 statement 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
|
||||
======================
|
||||
|
||||
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.
|
|
@ -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
|
|
@ -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': "Bank statement 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',
|
||||
}
|
|
@ -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>
|
|
@ -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"
|
|
Binary file not shown.
|
@ -0,0 +1,289 @@
|
|||
# Translation of OpenERP Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * account_statement_base_import
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 7.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-01-21 11:58+0000\n"
|
||||
"PO-Revision-Date: 2014-01-21 11:58+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_import
|
||||
#: view:credit.statement.import:0
|
||||
#: model:ir.actions.act_window,name:account_statement_base_import.statement_importer_action
|
||||
msgid "Import statement"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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
|
||||
msgid "credit.statement.import"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,input_statement:0
|
||||
msgid "Statement file"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid "Nothing to import"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,journal_id:0
|
||||
msgid "Financial journal to use transaction"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_base_import/parser/file_parser.py:108
|
||||
#, python-format
|
||||
msgid "Column %s not present in file"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:account.statement.profile:0
|
||||
#: model:ir.ui.menu,name:account_statement_base_import.statement_importer_menu
|
||||
msgid "Import Bank Statement"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid "The statement cannot be created: %s"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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:150
|
||||
#, python-format
|
||||
msgid "No buffer file given."
|
||||
msgstr ""
|
||||
|
||||
#. 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
|
||||
#, python-format
|
||||
msgid "Invalid data"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:account.statement.profile,launch_import_completion:0
|
||||
msgid "Launch completion after import"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,partner_id:0
|
||||
msgid "Credit insitute partner"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:account.statement.profile:0
|
||||
msgid "Import related infos"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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:90
|
||||
#, python-format
|
||||
msgid "Please use a file with an extention"
|
||||
msgstr ""
|
||||
|
||||
#. 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
|
||||
#, python-format
|
||||
msgid "Value %s of column %s is not valid.\n"
|
||||
" Please check the line with ref %s:\n"
|
||||
" \n"
|
||||
" 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:30
|
||||
#, python-format
|
||||
msgid "Please install python lib xlrd"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_base_import/parser/file_parser.py:160
|
||||
#, python-format
|
||||
msgid " 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"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:account.statement.profile,last_import_date:0
|
||||
msgid "Last Import Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: model:ir.model,name:account_statement_base_import.model_account_statement_profile
|
||||
msgid "Statement Profile"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_base_import/statement.py:222
|
||||
#, python-format
|
||||
msgid "Statement import error"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_base_import/parser/file_parser.py:193
|
||||
#, python-format
|
||||
msgid "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"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_base_import/parser/file_parser.py:192
|
||||
#, python-format
|
||||
msgid "Date format is not valid"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:account.statement.profile,import_type:0
|
||||
msgid "Type of import"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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
|
||||
#: help:credit.statement.import,balance_check:0
|
||||
msgid "Tic that box if you want OpenERP to control the start/end balance before confirming a bank statement. If don't ticked, no balance control will be done."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid "Date format is not valid."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,profile_id:0
|
||||
msgid "Import configuration parameter"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:account.statement.profile,rec_log:0
|
||||
msgid "log"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:credit.statement.import:0
|
||||
msgid "Import Parameters Summary"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,balance_check:0
|
||||
msgid "Balance check"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,force_partner_on_bank:0
|
||||
msgid "Force partner on bank move"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,file_name:0
|
||||
msgid "File Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, 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
|
||||
#, python-format
|
||||
msgid "Statement ID %s have been imported with %s lines."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,receivable_account_id:0
|
||||
msgid "Force Receivable/Payable Account"
|
||||
msgstr ""
|
||||
|
||||
#. 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
|
||||
#, python-format
|
||||
msgid "Missing"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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 ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:credit.statement.import:0
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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 counterpart of the treasury/banking move."
|
||||
msgstr ""
|
||||
|
|
@ -0,0 +1,329 @@
|
|||
# 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:58+0000\n"
|
||||
"PO-Revision-Date: 2014-06-05 22:11+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_import
|
||||
#: view:credit.statement.import:0
|
||||
#: model:ir.actions.act_window,name:account_statement_base_import.statement_importer_action
|
||||
msgid "Import statement"
|
||||
msgstr "Importar extracto"
|
||||
|
||||
#. module: account_statement_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
|
||||
msgid "credit.statement.import"
|
||||
msgstr "credit.statement.import"
|
||||
|
||||
#. module: account_statement_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
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Column %s you try to import is not present in the bank statement line!"
|
||||
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
|
||||
#, python-format
|
||||
msgid "Nothing to import"
|
||||
msgstr "Nada que importar"
|
||||
|
||||
#. module: account_statement_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
|
||||
#, python-format
|
||||
msgid "Column %s not present in file"
|
||||
msgstr "La columna %s no está presente en el archivo"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:account.statement.profile:0
|
||||
#: model:ir.ui.menu,name:account_statement_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
|
||||
#, python-format
|
||||
msgid "User Error"
|
||||
msgstr "Error de usuario"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid "Missing column!"
|
||||
msgstr "Columna ausente"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid "Invalid data"
|
||||
msgstr "Datos inválidos"
|
||||
|
||||
#. module: account_statement_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
|
||||
#: field:credit.statement.import,partner_id:0
|
||||
msgid "Credit insitute partner"
|
||||
msgstr "Empresa para el agente financiero"
|
||||
|
||||
#. module: account_statement_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
|
||||
#, 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
|
||||
#, 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
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Value %s of column %s is not valid.\n"
|
||||
" Please check the line with ref %s:\n"
|
||||
" \n"
|
||||
" Detail: %s"
|
||||
msgstr ""
|
||||
"El valor %s de la columna %s no es válido.\n"
|
||||
"\n"
|
||||
"Compruebe por favor la línea con referencia %s\n"
|
||||
" \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
|
||||
#, 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
|
||||
#, python-format
|
||||
msgid ""
|
||||
" 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"
|
||||
msgstr ""
|
||||
" La columna %s debería tener el formato YYYY-MM-DD y es: %s \n"
|
||||
" \n"
|
||||
"Compruebe por favor la línea con referencia %s \n"
|
||||
" \n"
|
||||
"Detalles: %s"
|
||||
|
||||
#. module: account_statement_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
|
||||
msgid "Statement Profile"
|
||||
msgstr "Perfil de extracto"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid ""
|
||||
"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"
|
||||
msgstr ""
|
||||
"Modifique el formato de fecha para la columna %s. Valor: %s\n"
|
||||
"\n"
|
||||
"Compruebe por favor la línea con referencia: %s\n"
|
||||
" \n"
|
||||
"Detalles: %s"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#: field:account.statement.profile,import_type:0
|
||||
msgid "Type of import"
|
||||
msgstr "Tipo de importación"
|
||||
|
||||
#. module: account_statement_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 ""
|
||||
"Marque esta casilla para lanzar automáticamente el completado en cada "
|
||||
"archivo importado usando este perfil."
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: help:credit.statement.import,balance_check:0
|
||||
msgid ""
|
||||
"Tic that box if you want OpenERP to control the start/end balance before "
|
||||
"confirming a bank statement. If don't ticked, no balance control will be "
|
||||
"done."
|
||||
msgstr ""
|
||||
"Marque esta casilla si quiere que el sistema controle el saldo inicial/final "
|
||||
"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
|
||||
#, python-format
|
||||
msgid "No Profile!"
|
||||
msgstr "Sin perfil"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#: 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
|
||||
#: field:account.statement.profile,rec_log:0
|
||||
msgid "log"
|
||||
msgstr "registro"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:credit.statement.import:0
|
||||
msgid "Import Parameters Summary"
|
||||
msgstr "Resumen de parámetros de importación"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,balance_check:0
|
||||
msgid "Balance check"
|
||||
msgstr "Comprobar saldo"
|
||||
|
||||
#. module: account_statement_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
|
||||
#: 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
|
||||
#, 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
|
||||
#, 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
|
||||
#, 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
|
||||
#: 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
|
||||
#, python-format
|
||||
msgid "Missing"
|
||||
msgstr "Ausente"
|
||||
|
||||
#. module: account_statement_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 ""
|
||||
"Escoja aquí el método con el que quiere importar el extracto bancario para "
|
||||
"este perfil."
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:credit.statement.import:0
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#. module: account_statement_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 "
|
||||
"counterpart of the treasury/banking move."
|
||||
msgstr ""
|
||||
"Marque esta casilla si quiere usar la empresa de su institución financiera "
|
||||
"en la contrapartida del movimiento de caja/banco."
|
|
@ -0,0 +1,303 @@
|
|||
# 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:58+0000\n"
|
||||
"PO-Revision-Date: 2014-03-21 15:17+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\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-05-22 06:49+0000\n"
|
||||
"X-Generator: Launchpad (build 17017)\n"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:credit.statement.import:0
|
||||
#: model:ir.actions.act_window,name:account_statement_base_import.statement_importer_action
|
||||
msgid "Import statement"
|
||||
msgstr "Import de relevé"
|
||||
|
||||
#. module: account_statement_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
|
||||
msgid "credit.statement.import"
|
||||
msgstr "credit.statement.import"
|
||||
|
||||
#. module: account_statement_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
|
||||
#, 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
|
||||
#, python-format
|
||||
msgid "Nothing to import"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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
|
||||
#, python-format
|
||||
msgid "Column %s not present in file"
|
||||
msgstr "Colonne %s non présente dans le fichier"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:account.statement.profile:0
|
||||
#: model:ir.ui.menu,name:account_statement_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
|
||||
#, python-format
|
||||
msgid "User Error"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid "Missing column!"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid "Invalid data"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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
|
||||
#: field:credit.statement.import,partner_id:0
|
||||
msgid "Credit insitute partner"
|
||||
msgstr "Organisme bancaire"
|
||||
|
||||
#. module: account_statement_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
|
||||
#, python-format
|
||||
msgid "The file is empty"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Value %s of column %s is not valid.\n"
|
||||
" Please check the line with ref %s:\n"
|
||||
" \n"
|
||||
" 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
|
||||
#, 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
|
||||
#, python-format
|
||||
msgid ""
|
||||
" 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"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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
|
||||
msgid "Statement Profile"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid ""
|
||||
"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"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_base_import/parser/file_parser.py:192
|
||||
#, python-format
|
||||
msgid "Date format is not valid"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:account.statement.profile,import_type:0
|
||||
msgid "Type of import"
|
||||
msgstr "Type d'import"
|
||||
|
||||
#. module: account_statement_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
|
||||
#: help:credit.statement.import,balance_check:0
|
||||
msgid ""
|
||||
"Tic that box if you want OpenERP to control the start/end balance before "
|
||||
"confirming a bank statement. If don't ticked, no balance control will be "
|
||||
"done."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: code:addons/account_statement_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
|
||||
#, python-format
|
||||
msgid "Date format is not valid."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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
|
||||
#: field:account.statement.profile,rec_log:0
|
||||
msgid "log"
|
||||
msgstr "journal"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: view:credit.statement.import:0
|
||||
msgid "Import Parameters Summary"
|
||||
msgstr "Résumé des paramètres d'import"
|
||||
|
||||
#. module: account_statement_base_import
|
||||
#: field:credit.statement.import,balance_check:0
|
||||
msgid "Balance check"
|
||||
msgstr "Vérification des soldes"
|
||||
|
||||
#. module: account_statement_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
|
||||
#: 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
|
||||
#, 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
|
||||
#, 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
|
||||
#, python-format
|
||||
msgid "Statement ID %s have been imported with %s lines."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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
|
||||
#, python-format
|
||||
msgid "Missing"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_statement_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
|
||||
#: view:credit.statement.import:0
|
||||
msgid "Cancel"
|
||||
msgstr "Annulation"
|
||||
|
||||
#. module: account_statement_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 "
|
||||
"counterpart of the treasury/banking move."
|
||||
msgstr ""
|
|
@ -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
|
|
@ -0,0 +1,282 @@
|
|||
# -*- 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']
|
||||
|
||||
def _get_import_type_selection(self):
|
||||
"""This is the method to be inherited for adding the parser"""
|
||||
return [('generic_csvxls_so', 'Generic .csv/.xls based on SO Name')]
|
||||
|
||||
def __get_import_type_selection(self):
|
||||
""" Call method which can be inherited """
|
||||
return self._get_import_type_selection()
|
||||
|
||||
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(
|
||||
__get_import_type_selection,
|
||||
string='Type of import',
|
||||
default='generic_csvxls_so',
|
||||
required=True,
|
||||
help="Choose here the method by which you want to import bank "
|
||||
"statement for this profile.")
|
||||
|
||||
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='Force Receivable/Payable Account',
|
||||
help="Choose a receivable account to force the default "
|
||||
"debit/credit account (eg. an intermediat bank account "
|
||||
"instead of default debitors).")
|
||||
|
||||
used_for_completion = fields.Boolean(
|
||||
string="Journal used for completion")
|
||||
|
||||
rule_ids = fields.Many2many(
|
||||
comodel_name='account.move.completion.rule',
|
||||
string='Auto-completion rules',
|
||||
rel='as_rul_st_prof_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 profile.")
|
||||
|
||||
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 _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
|
||||
total_amount = 0
|
||||
for row in parser.result_row_list:
|
||||
global_commission_amount += float(
|
||||
row.get('commission_amount', '0.0'))
|
||||
total_amount += float(
|
||||
row.get('amount', '0.0'))
|
||||
total_amount += global_commission_amount
|
||||
partner_id = self.partner_id.id
|
||||
# Commission line
|
||||
if global_commission_amount < 0.0:
|
||||
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)
|
||||
# Counterpart line
|
||||
if total_amount > 0.0:
|
||||
receivable_account_id = self.receivable_account_id.id or False
|
||||
counterpart_values = {
|
||||
'name': _('Counterpart line'),
|
||||
'date_maturity': parser.get_move_vals().get('date') or
|
||||
fields.Date.today(),
|
||||
'debit': total_amount,
|
||||
'partner_id': partner_id,
|
||||
'move_id': move.id,
|
||||
'account_id': receivable_account_id,
|
||||
'already_completed': True,
|
||||
}
|
||||
move_line_obj.create(counterpart_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 values['credit'] > 0.0:
|
||||
values['account_id'] = self.default_credit_account_id.id
|
||||
else:
|
||||
values['account_id'] = self.default_debit_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._write_extra_move_lines(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 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
|
|
@ -0,0 +1,418 @@
|
|||
# -*- 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"
|
||||
|
||||
def _get_functions(self):
|
||||
"""List of available methods for rules.
|
||||
|
||||
Override this to add you own."""
|
||||
return [
|
||||
('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)')
|
||||
]
|
||||
|
||||
def __get_functions(self):
|
||||
""" Call method which can be inherited """
|
||||
return self._get_functions()
|
||||
|
||||
sequence = fields.Integer(
|
||||
string='Sequence',
|
||||
help="Lower means parsed first.")
|
||||
name = fields.Char(
|
||||
string='Name')
|
||||
journal_ids = fields.Many2many(
|
||||
comodel_name='account.journal',
|
||||
rel='as_rul_st_prof_rel',
|
||||
string='Related journals')
|
||||
function_to_call = fields.Selection(
|
||||
__get_functions,
|
||||
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}
|
||||
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
|
|
@ -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).")
|
|
@ -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
|
|
@ -0,0 +1,186 @@
|
|||
# -*- 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.tools.translate import _
|
||||
from openerp.exceptions import UserError
|
||||
import tempfile
|
||||
import datetime
|
||||
from .parser import AccountMoveImportParser, UnicodeDictReader
|
||||
try:
|
||||
import xlrd
|
||||
except:
|
||||
raise Exception(_('Please install python lib xlrd'))
|
||||
|
||||
|
||||
def float_or_zero(val):
|
||||
""" Conversion function used to manage
|
||||
empty string into float usecase"""
|
||||
return float(val) if val else 0.0
|
||||
|
||||
|
||||
class FileParser(AccountMoveImportParser):
|
||||
"""Generic abstract class for defining parser for .csv, .xls or .xlsx file
|
||||
format.
|
||||
"""
|
||||
|
||||
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
|
||||
xlsx)
|
||||
:param dict: extra_fields: extra fields to put into the conversion
|
||||
dict. In the format {fieldname: fieldtype}
|
||||
:param list: header : specify header fields if the csv file has no
|
||||
header
|
||||
"""
|
||||
super(FileParser, self).__init__(journal, **kwargs)
|
||||
if ftype in ('csv', 'xls', 'xlsx'):
|
||||
self.ftype = ftype[0:3]
|
||||
else:
|
||||
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()
|
||||
self.fieldnames = header
|
||||
self._datemode = 0 # used only for xls documents,
|
||||
# 0 means Windows mode (1900 based dates).
|
||||
# Set in _parse_xls, from the contents of the file
|
||||
self.dialect = dialect
|
||||
self.move_ref = move_ref
|
||||
|
||||
def _custom_format(self, *args, **kwargs):
|
||||
"""No other work on data are needed in this parser."""
|
||||
return True
|
||||
|
||||
def _pre(self, *args, **kwargs):
|
||||
"""No pre-treatment needed for this parser."""
|
||||
return True
|
||||
|
||||
def _parse(self, *args, **kwargs):
|
||||
"""Launch the parsing through .csv, .xls or .xlsx depending on the
|
||||
given ftype
|
||||
"""
|
||||
res = None
|
||||
if self.ftype == 'csv':
|
||||
res = self._parse_csv()
|
||||
else:
|
||||
res = self._parse_xls()
|
||||
self.result_row_list = res
|
||||
return True
|
||||
|
||||
def _validate(self, *args, **kwargs):
|
||||
"""We check that all the key of the given file (means header) are
|
||||
present in the validation key provided. Otherwise, we raise an
|
||||
Exception. We skip the validation step if the file header is provided
|
||||
separately (in the field: fieldnames).
|
||||
"""
|
||||
if self.fieldnames is None:
|
||||
parsed_cols = self.result_row_list[0].keys()
|
||||
for col in self.keys_to_validate:
|
||||
if col not in parsed_cols:
|
||||
raise UserError(_('Column %s not present in file') % col)
|
||||
return True
|
||||
|
||||
def _post(self, *args, **kwargs):
|
||||
"""Cast row type depending on the file format .csv or .xls after
|
||||
parsing the file."""
|
||||
self.result_row_list = self._cast_rows(*args, **kwargs)
|
||||
return True
|
||||
|
||||
def _parse_csv(self):
|
||||
""":return: list of dict from csv file (line/rows)"""
|
||||
csv_file = tempfile.NamedTemporaryFile()
|
||||
csv_file.write(self.filebuffer)
|
||||
csv_file.flush()
|
||||
with open(csv_file.name, 'rU') as fobj:
|
||||
reader = UnicodeDictReader(fobj, fieldnames=self.fieldnames,
|
||||
dialect=self.dialect)
|
||||
return list(reader)
|
||||
|
||||
def _parse_xls(self):
|
||||
""":return: dict of dict from xls/xlsx file (line/rows)"""
|
||||
wb_file = tempfile.NamedTemporaryFile()
|
||||
wb_file.write(self.filebuffer)
|
||||
# We ensure that cursor is at beginig of file
|
||||
wb_file.seek(0)
|
||||
with xlrd.open_workbook(wb_file.name) as wb:
|
||||
self._datemode = wb.datemode
|
||||
sheet = wb.sheet_by_index(0)
|
||||
header = sheet.row_values(0)
|
||||
res = []
|
||||
for rownum in range(1, sheet.nrows):
|
||||
res.append(dict(zip(header, sheet.row_values(rownum))))
|
||||
return res
|
||||
|
||||
def _from_csv(self, result_set, conversion_rules):
|
||||
"""Handle the converstion from the dict and handle date format from
|
||||
an .csv file.
|
||||
"""
|
||||
for line in result_set:
|
||||
for rule in conversion_rules:
|
||||
if conversion_rules[rule] == datetime.datetime:
|
||||
try:
|
||||
date_string = line[rule].split(' ')[0]
|
||||
line[rule] = datetime.datetime.strptime(date_string,
|
||||
'%Y-%m-%d')
|
||||
except ValueError as err:
|
||||
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')),
|
||||
line.get('ref', line), repr(err)))
|
||||
else:
|
||||
try:
|
||||
line[rule] = conversion_rules[rule](line[rule])
|
||||
except Exception as err:
|
||||
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,
|
||||
line.get('ref', line), repr(err)))
|
||||
return result_set
|
||||
|
||||
def _from_xls(self, result_set, conversion_rules):
|
||||
"""Handle the converstion from the dict and handle date format from
|
||||
an .csv, .xls or .xlsx file.
|
||||
"""
|
||||
for line in result_set:
|
||||
for rule in conversion_rules:
|
||||
if conversion_rules[rule] == datetime.datetime:
|
||||
try:
|
||||
t_tuple = xlrd.xldate_as_tuple(line[rule],
|
||||
self._datemode)
|
||||
line[rule] = datetime.datetime(*t_tuple)
|
||||
except Exception as err:
|
||||
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')),
|
||||
line.get('ref', line), repr(err)))
|
||||
else:
|
||||
try:
|
||||
line[rule] = conversion_rules[rule](line[rule])
|
||||
except Exception as err:
|
||||
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,
|
||||
line.get('ref', line), repr(err)))
|
||||
return result_set
|
||||
|
||||
def _cast_rows(self, *args, **kwargs):
|
||||
"""Convert the self.result_row_list using the self.conversion_dict
|
||||
providen. We call here _from_xls or _from_csv depending on the
|
||||
self.ftype variable.
|
||||
"""
|
||||
func = getattr(self, '_from_%s' % self.ftype)
|
||||
res = func(self.result_row_list, self.conversion_dict)
|
||||
return res
|
|
@ -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,
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
# -*- 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 csv
|
||||
from openerp import _, fields
|
||||
|
||||
|
||||
def UnicodeDictReader(utf8_data, **kwargs):
|
||||
sniffer = csv.Sniffer()
|
||||
pos = utf8_data.tell()
|
||||
sample_data = utf8_data.read(2048)
|
||||
utf8_data.seek(pos)
|
||||
if not kwargs.get('dialect'):
|
||||
dialect = sniffer.sniff(sample_data, delimiters=',;\t')
|
||||
del kwargs['dialect']
|
||||
else:
|
||||
dialect = kwargs.pop('dialect')
|
||||
csv_reader = csv.DictReader(utf8_data, dialect=dialect, **kwargs)
|
||||
for row in csv_reader:
|
||||
yield dict([(unicode(key or '', 'utf-8'),
|
||||
unicode(value or '', 'utf-8'))
|
||||
for key, value in row.iteritems()])
|
||||
|
||||
|
||||
class AccountMoveImportParser(object):
|
||||
|
||||
"""
|
||||
Generic abstract class for defining parser for different files and
|
||||
format to import in a bank statement. Inherit from it to create your
|
||||
own. If your file is a .csv or .xls format, you should consider inheirt
|
||||
from the FileParser instead.
|
||||
"""
|
||||
|
||||
def __init__(self, journal, *args, **kwargs):
|
||||
# The name of the parser as it will be called
|
||||
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.journal = journal
|
||||
self.move_date = None
|
||||
self.move_name = None
|
||||
self.move_ref = None
|
||||
|
||||
@classmethod
|
||||
def parser_for(cls, parser_name):
|
||||
"""Override this method for every new parser, so that
|
||||
new_bank_statement_parser can return the good class from his name.
|
||||
"""
|
||||
return False
|
||||
|
||||
def _decode_64b_stream(self):
|
||||
"""Decode self.filebuffer in base 64 and override it"""
|
||||
self.filebuffer = base64.b64decode(self.filebuffer)
|
||||
return True
|
||||
|
||||
def _format(self, decode_base_64=True, **kwargs):
|
||||
"""Decode into base 64 if asked and Format the given filebuffer by
|
||||
calling _custom_format method.
|
||||
"""
|
||||
if decode_base_64:
|
||||
self._decode_64b_stream()
|
||||
self._custom_format(kwargs)
|
||||
return True
|
||||
|
||||
def _custom_format(self, *args, **kwargs):
|
||||
"""Implement a method in your parser to convert format, encoding and so
|
||||
on before starting to work on datas. Work on self.filebuffer
|
||||
"""
|
||||
return NotImplementedError
|
||||
|
||||
def _pre(self, *args, **kwargs):
|
||||
"""Implement a method in your parser to make a pre-treatment on datas
|
||||
before parsing them, like concatenate stuff, and so... Work on
|
||||
self.filebuffer
|
||||
"""
|
||||
return NotImplementedError
|
||||
|
||||
def _parse(self, *args, **kwargs):
|
||||
"""Implement a method in your parser to save the result of parsing
|
||||
self.filebuffer in self.result_row_list instance property.
|
||||
"""
|
||||
return NotImplementedError
|
||||
|
||||
def _validate(self, *args, **kwargs):
|
||||
"""Implement a method in your parser to validate the
|
||||
self.result_row_list instance property and raise an error if not valid.
|
||||
"""
|
||||
return NotImplementedError
|
||||
|
||||
def _post(self, *args, **kwargs):
|
||||
"""Implement a method in your parser to make some last changes on the
|
||||
result of parsing the datas, like converting dates, computing
|
||||
commission, ...
|
||||
"""
|
||||
return NotImplementedError
|
||||
|
||||
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.move_name or '/',
|
||||
'date': self.move_date or fields.Datetime.now(),
|
||||
'ref': self.move_ref or '/'
|
||||
}
|
||||
|
||||
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
|
||||
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,
|
||||
}
|
||||
"""
|
||||
return NotImplementedError
|
||||
|
||||
def parse(self, filebuffer, *args, **kwargs):
|
||||
"""This will be the method that will be called by wizard, button and so
|
||||
to parse a filebuffer by calling successively all the private method
|
||||
that need to be define for each parser.
|
||||
Return:
|
||||
[] of rows as {'key':value}
|
||||
|
||||
Note: The row_list must contain only value that are present in the
|
||||
account.bank.statement.line object !!!
|
||||
"""
|
||||
if filebuffer:
|
||||
self.filebuffer = filebuffer
|
||||
else:
|
||||
raise Exception(_('No buffer file given.'))
|
||||
self._format(*args, **kwargs)
|
||||
self._pre(*args, **kwargs)
|
||||
self._parse(*args, **kwargs)
|
||||
self._validate(*args, **kwargs)
|
||||
self._post(*args, **kwargs)
|
||||
yield self.result_row_list
|
||||
|
||||
|
||||
def itersubclasses(cls, _seen=None):
|
||||
"""
|
||||
itersubclasses(cls)
|
||||
|
||||
Generator over all subclasses of a given class, in depth first order.
|
||||
|
||||
>>> list(itersubclasses(int)) == [bool]
|
||||
True
|
||||
>>> class A(object): pass
|
||||
>>> class B(A): pass
|
||||
>>> class C(A): pass
|
||||
>>> class D(B,C): pass
|
||||
>>> class E(D): pass
|
||||
>>>
|
||||
>>> for cls in itersubclasses(A):
|
||||
... print(cls.__name__)
|
||||
B
|
||||
D
|
||||
E
|
||||
C
|
||||
>>> # get ALL (new-style) classes currently defined
|
||||
>>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS
|
||||
['type', ...'tuple', ...]
|
||||
"""
|
||||
if not isinstance(cls, type):
|
||||
raise TypeError('itersubclasses must be called with '
|
||||
'new-style classes, not %.100r' % cls)
|
||||
if _seen is None:
|
||||
_seen = set()
|
||||
try:
|
||||
subs = cls.__subclasses__()
|
||||
except TypeError: # fails only when cls is type
|
||||
subs = cls.__subclasses__(cls)
|
||||
for sub in subs:
|
||||
if sub not in _seen:
|
||||
_seen.add(sub)
|
||||
yield sub
|
||||
for sub in itersubclasses(sub, _seen):
|
||||
yield sub
|
||||
|
||||
|
||||
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(AccountMoveImportParser):
|
||||
if cls.parser_for(journal.import_type):
|
||||
return cls(journal, *args, **kwargs)
|
||||
raise ValueError
|
|
@ -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
|
|
|
@ -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")
|
|
@ -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')
|
|
@ -0,0 +1,5 @@
|
|||
-
|
||||
I fill in the field Bank Statement Label in a Partner
|
||||
-
|
||||
!record {model: res.partner, id: base.res_partner_4}:
|
||||
bank_statement_label: XXX66Z
|
|
@ -0,0 +1,42 @@
|
|||
-
|
||||
I create a "child" partner, to use in the invoice
|
||||
(and have a different commercial_partner_id than itself)
|
||||
-
|
||||
!record {model: res.partner, id: res_partner_12_child}:
|
||||
name: Child Partner
|
||||
supplier: False
|
||||
customer: True
|
||||
is_company: False
|
||||
parent_id: base.res_partner_12
|
||||
-
|
||||
I create a customer refund to be found by the completion.
|
||||
-
|
||||
!record {model: account.invoice, id: refund_for_completion_1}:
|
||||
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.expenses_journal
|
||||
partner_id: res_partner_12_child
|
||||
type: 'out_refund'
|
||||
reference_type: none
|
||||
-
|
||||
I confirm the refund
|
||||
-
|
||||
!workflow {model: account.invoice, action: invoice_open, ref: refund_for_completion_1}
|
||||
-
|
||||
I check that the refund state is "Open"
|
||||
-
|
||||
!assert {model: account.invoice, id: refund_for_completion_1}:
|
||||
- state == 'open'
|
||||
-
|
||||
I check that it is given the number "RTEXJ/%Y/0001"
|
||||
-
|
||||
!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')
|
|
@ -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',
|
||||
'demo',
|
||||
'account_invoice_demo.yml'),
|
||||
{}, 'init', False, 'test')
|
||||
-
|
||||
I check that my invoice is a supplier invoice
|
||||
-
|
||||
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type}:
|
||||
- type == 'in_invoice'
|
||||
-
|
||||
I add a reference to an existing supplier invoce
|
||||
-
|
||||
!python {model: account.invoice}: |
|
||||
self.write(cr, uid, ref('account.demo_invoice_0'), {
|
||||
'reference': 'T2S12345'
|
||||
})
|
||||
-
|
||||
I check a second time that my invoice is still a supplier invoice
|
||||
-
|
||||
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type 2}:
|
||||
- type == 'in_invoice'
|
||||
-
|
||||
Now I confirm it
|
||||
-
|
||||
!workflow {model: account.invoice, action: invoice_open, ref: account.demo_invoice_0}
|
||||
-
|
||||
I check that the supplier number is there
|
||||
-
|
||||
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check supplier number}:
|
||||
- reference == 'T2S12345'
|
||||
-
|
||||
I check a third time that my invoice is still a supplier invoice
|
||||
-
|
||||
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type 3}:
|
||||
- type == 'in_invoice'
|
|
@ -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
|
|
@ -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))
|
|
@ -0,0 +1,81 @@
|
|||
# -*- 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,
|
||||
})
|
||||
|
||||
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")
|
|
@ -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="Statement 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>
|
|
@ -0,0 +1,42 @@
|
|||
<?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" attrs="{'required': [('used_for_import', '=', True)]}"/>
|
||||
<field name="receivable_account_id" attrs="{'required': [('used_for_import', '=', True)]}"/>
|
||||
<field name="partner_id" attrs="{'required': [('used_for_import', '=', True)]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<button name="%(account_statement_base_import.move_importer_action)d"
|
||||
string="Import Bank Statement"
|
||||
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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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 statement" 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 Move</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 Bank Statement" action="move_importer_action" parent="account.menu_finance_entries"/>
|
||||
</odoo>
|
Loading…
Reference in New Issue