mirror of
				https://github.com/brain-tec/account_ebics.git
				synced 2025-11-03 22:50:59 +00:00 
			
		
		
		
	add ebics modules
This commit is contained in:
		
							
								
								
									
										69
									
								
								account_ebics/README.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								account_ebics/README.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
 | 
			
		||||
   :target: https://www.gnu.org/licenses/agpl
 | 
			
		||||
   :alt: License: AGPL-3
 | 
			
		||||
 | 
			
		||||
======================
 | 
			
		||||
EBICS banking protocol
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
Implementation of the  EBICS banking protocol.
 | 
			
		||||
 | 
			
		||||
This module facilitates the exchange of files with banks via the EBICS protocol.
 | 
			
		||||
 | 
			
		||||
Installation
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
The module depends upon
 | 
			
		||||
 | 
			
		||||
- https://pypi.python.org/pypi/fintech
 | 
			
		||||
- https://pypi.python.org/pypi/cryptography
 | 
			
		||||
 | 
			
		||||
Remark:
 | 
			
		||||
 | 
			
		||||
The EBICS 'Test Mode' for uploading orders requires Fintech 4.3.4 or higher.
 | 
			
		||||
 | 
			
		||||
Fintech license
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
If you have a valid Fintech.ebics license, you should add the following
 | 
			
		||||
licensing parameters to the odoo server configuration file:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- fintech_register_name
 | 
			
		||||
 | 
			
		||||
The name of the licensee.
 | 
			
		||||
 | 
			
		||||
- fintech_register_keycode
 | 
			
		||||
 | 
			
		||||
The keycode of the licensed version.
 | 
			
		||||
 | 
			
		||||
- fintech_register_users
 | 
			
		||||
 | 
			
		||||
The licensed EBICS user ids. It must be a string or a list of user ids.
 | 
			
		||||
 | 
			
		||||
Configuration
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
Go to **Settings > Users**
 | 
			
		||||
 | 
			
		||||
Add the users that are authorised to maintain the EBICS configuration to the 'EBICS Manager' Group.
 | 
			
		||||
 | 
			
		||||
Go to **Accounting > Configuration > Miscellaneous > EBICS > EBICS Configuration**
 | 
			
		||||
 | 
			
		||||
Configure your EBICS configuration according to the contract with your bank.
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
=====
 | 
			
		||||
 | 
			
		||||
Go to **Accounting > Bank and Cash > EBICS Processing**
 | 
			
		||||
 | 
			
		||||
EBICS Return Codes
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
During the processing of your EBICS upload/download, your bank may return an Error Code, e.g.
 | 
			
		||||
 | 
			
		||||
EBICS Functional Error:
 | 
			
		||||
EBICS_NO_DOWNLOAD_DATA_AVAILABLE (code: 90005)
 | 
			
		||||
 | 
			
		||||
A detailled explanation of the codes can be found on http://www.ebics.org.
 | 
			
		||||
You can also find this information in the doc folder of this module (file EBICS_Annex1_ReturnCodes).
 | 
			
		||||
							
								
								
									
										6
									
								
								account_ebics/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								account_ebics/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2017 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
from . import models
 | 
			
		||||
from . import wizard
 | 
			
		||||
							
								
								
									
										25
									
								
								account_ebics/__manifest__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								account_ebics/__manifest__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2018 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    'name': 'EBICS banking protocol',
 | 
			
		||||
    'version': '10.0.1.4.0',
 | 
			
		||||
    'license': 'AGPL-3',
 | 
			
		||||
    'author': 'Noviat',
 | 
			
		||||
    'category': 'Accounting & Finance',
 | 
			
		||||
    'depends': ['account'],
 | 
			
		||||
    'data': [
 | 
			
		||||
        'security/ebics_security.xml',
 | 
			
		||||
        'security/ir.model.access.csv',
 | 
			
		||||
        'data/ebics_file_format.xml',
 | 
			
		||||
        'views/menuitem.xml',
 | 
			
		||||
        'views/ebics_config.xml',
 | 
			
		||||
        'views/ebics_file.xml',
 | 
			
		||||
        'views/ebics_file_format.xml',
 | 
			
		||||
        'wizard/ebics_change_passphrase.xml',
 | 
			
		||||
        'wizard/ebics_xfer.xml',
 | 
			
		||||
    ],
 | 
			
		||||
    'installable': True,
 | 
			
		||||
    'application': True,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										94
									
								
								account_ebics/data/ebics_file_format.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								account_ebics/data/ebics_file_format.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<odoo>
 | 
			
		||||
  <data noupdate="1">
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
    File format tested with the following banks:
 | 
			
		||||
    - GLS Gemeinschaftsbank (Germany)
 | 
			
		||||
    -->
 | 
			
		||||
    <record id="ebics_ff_camt_053_001_02_stm" model="ebics.file.format">
 | 
			
		||||
      <field name="name">camt.053.001.02.stm</field>
 | 
			
		||||
      <field name="type">down</field>
 | 
			
		||||
      <field name="order_type">C53</field>
 | 
			
		||||
      <field name="description">Bank Statement in Format camt.053</field>
 | 
			
		||||
      <field name="suffix">c53.xml</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
    File format tested with the following banks:
 | 
			
		||||
    - GLS Gemeinschaftsbank (Germany)
 | 
			
		||||
    -->
 | 
			
		||||
    <record id="ebics_ff_pain_001_001_03_sct" model="ebics.file.format">
 | 
			
		||||
      <field name="name">pain.001.001.03.sct</field>
 | 
			
		||||
      <field name="type">up</field>
 | 
			
		||||
      <field name="order_type">CCT</field>
 | 
			
		||||
      <field name="description">Payment Order in Format pain.001.001.03</field>
 | 
			
		||||
      <field name="suffix">xml</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
    <record id="ebics_ff_pain_008_001_02_sdd" model="ebics.file.format">
 | 
			
		||||
      <field name="name">pain.008.001.02.sdd</field>
 | 
			
		||||
      <field name="type">up</field>
 | 
			
		||||
      <field name="order_type">CDD</field>
 | 
			
		||||
      <field name="description">Sepa Core Direct Debit Order in Format pain.008.001.02</field>
 | 
			
		||||
      <field name="suffix">xml</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
    <record id="ebics_ff_pain_008_001_02_sbb" model="ebics.file.format">
 | 
			
		||||
      <field name="name">pain.008.001.02.sbb</field>
 | 
			
		||||
      <field name="type">up</field>
 | 
			
		||||
      <field name="order_type">CDB</field>
 | 
			
		||||
      <field name="description">Sepa Direct Debit (B2B) Order in Format pain.008.001.02</field>
 | 
			
		||||
      <field name="suffix">xml</field>
 | 
			
		||||
    </record>
 | 
			
		||||
    
 | 
			
		||||
    <!--
 | 
			
		||||
    File format tested with the following banks:
 | 
			
		||||
    - Credit Suisse (Switzerland)
 | 
			
		||||
    -->
 | 
			
		||||
    <record id="ebics_ff_pain_001" model="ebics.file.format">
 | 
			
		||||
      <field name="name">pain.001</field>
 | 
			
		||||
      <field name="type">up</field>
 | 
			
		||||
      <field name="order_type">XE2</field>
 | 
			
		||||
      <field name="description">Payment Order in Format pain.001.001.03</field>
 | 
			
		||||
      <field name="suffix">xml</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
    File format tested with the following banks:
 | 
			
		||||
    - Credit Suisse (Switzerland)
 | 
			
		||||
    -->
 | 
			
		||||
    <record id="ebics_ff_pain_008" model="ebics.file.format">
 | 
			
		||||
      <field name="name">pain.008</field>
 | 
			
		||||
      <field name="type">up</field>
 | 
			
		||||
      <field name="order_type">XE3</field>
 | 
			
		||||
      <field name="description">Direct Debit Order in Format pain.008.001.02</field>
 | 
			
		||||
      <field name="suffix">xml</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
    File format tested with the following banks:
 | 
			
		||||
    - CIC (France)
 | 
			
		||||
    -->
 | 
			
		||||
    <record id="ebics_ff_camt_xxx_cfonb120_stm" model="ebics.file.format">
 | 
			
		||||
      <field name="name">camt.xxx.cfonb120.stm</field>
 | 
			
		||||
      <field name="type">down</field>
 | 
			
		||||
      <field name="order_type">FDL</field>
 | 
			
		||||
      <field name="description">Bank Statement in Format cfonb120</field>
 | 
			
		||||
      <field name="suffix">cfonb120.dat</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
    File format tested with the following banks:
 | 
			
		||||
    - CIC (France)
 | 
			
		||||
    -->
 | 
			
		||||
    <record id="ebics_ff_pain_001_001_02_sct" model="ebics.file.format">
 | 
			
		||||
      <field name="name">pain.001.001.02.sct</field>
 | 
			
		||||
      <field name="type">up</field>
 | 
			
		||||
      <field name="order_type">FUL</field>
 | 
			
		||||
      <field name="description">Payment Order in Format pain.001.001.02</field>
 | 
			
		||||
      <field name="suffix">xml</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
  </data>
 | 
			
		||||
</odoo>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								account_ebics/doc/EBICS_Annex1_ReturnCodes_final-16-05-2011.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								account_ebics/doc/EBICS_Annex1_ReturnCodes_final-16-05-2011.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								account_ebics/doc/EBICS_Common_IG_based_EBICS_2.5.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								account_ebics/doc/EBICS_Common_IG_based_EBICS_2.5.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								account_ebics/doc/EBICS_Specification_2.5_final-16-05-2011.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								account_ebics/doc/EBICS_Specification_2.5_final-16-05-2011.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										6
									
								
								account_ebics/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								account_ebics/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
from . import fintech_ebics_register
 | 
			
		||||
from . import account_bank_statement
 | 
			
		||||
from . import ebics_config
 | 
			
		||||
from . import ebics_file
 | 
			
		||||
from . import ebics_file_format
 | 
			
		||||
							
								
								
									
										12
									
								
								account_ebics/models/account_bank_statement.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								account_ebics/models/account_bank_statement.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2018 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
from odoo import fields, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AccountBankStatement(models.Model):
 | 
			
		||||
    _inherit = 'account.bank.statement'
 | 
			
		||||
 | 
			
		||||
    ebics_file_id = fields.Many2one(
 | 
			
		||||
        comodel_name='ebics.file', string='EBICS Data File')
 | 
			
		||||
							
								
								
									
										494
									
								
								account_ebics/models/ebics_config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										494
									
								
								account_ebics/models/ebics_config.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,494 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2018 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
import logging
 | 
			
		||||
logging.basicConfig(
 | 
			
		||||
    level=logging.DEBUG,
 | 
			
		||||
    format='[%(asctime)s] %(levelname)s - %(name)s: %(message)s')
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
import logging
 | 
			
		||||
import re
 | 
			
		||||
import os
 | 
			
		||||
from sys import exc_info
 | 
			
		||||
from urllib2 import URLError
 | 
			
		||||
 | 
			
		||||
from odoo import api, fields, models, _
 | 
			
		||||
from odoo.exceptions import UserError
 | 
			
		||||
 | 
			
		||||
_logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import fintech
 | 
			
		||||
    from fintech.ebics import EbicsKeyRing, EbicsBank, EbicsUser,\
 | 
			
		||||
        EbicsClient, EbicsFunctionalError, EbicsTechnicalError
 | 
			
		||||
    fintech.cryptolib = 'cryptography'
 | 
			
		||||
except ImportError:
 | 
			
		||||
    EbicsBank = object
 | 
			
		||||
    _logger.warning('Failed to import fintech')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EbicsBank(EbicsBank):
 | 
			
		||||
 | 
			
		||||
    def _next_order_id(self, partnerid):
 | 
			
		||||
        """
 | 
			
		||||
        EBICS protocol version H003 requires generation of the OrderID.
 | 
			
		||||
        The OrderID must be a string between 'A000' and 'ZZZZ' and
 | 
			
		||||
        unique for each partner id.
 | 
			
		||||
        """
 | 
			
		||||
        return hasattr(self, '_order_number') and self._order_number or 'A000'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EbicsConfig(models.Model):
 | 
			
		||||
    """
 | 
			
		||||
    EBICS configuration is stored in a separate object in order to
 | 
			
		||||
    allow extra security policies on this object.
 | 
			
		||||
 | 
			
		||||
    Remark:
 | 
			
		||||
    This Configuration model implements a simple model of the relationship
 | 
			
		||||
    between users and authorizations and may need to be adapted
 | 
			
		||||
    in next versions of this module to cope with higher complexity .
 | 
			
		||||
    """
 | 
			
		||||
    _name = 'ebics.config'
 | 
			
		||||
    _description = 'EBICS Configuration'
 | 
			
		||||
    _order = 'name'
 | 
			
		||||
 | 
			
		||||
    name = fields.Char(string='Name', required=True)
 | 
			
		||||
    bank_id = fields.Many2one(
 | 
			
		||||
        comodel_name='res.partner.bank',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        string='Bank Account', required=True)
 | 
			
		||||
    ebics_host = fields.Char(
 | 
			
		||||
        string='EBICS HostID', required=True,
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        help="Contact your bank to get the EBICS HostID."
 | 
			
		||||
             "\nIn France the BIC is usually allocated to the HostID "
 | 
			
		||||
             "whereas in Germany it tends to be an institute specific string "
 | 
			
		||||
             "of 8 characters.")
 | 
			
		||||
    ebics_url = fields.Char(
 | 
			
		||||
        string='EBICS URL', required=True,
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        help="Contact your bank to get the EBICS URL.")
 | 
			
		||||
    ebics_version = fields.Selection(
 | 
			
		||||
        selection=[('H003', 'H003 (2.4)'),
 | 
			
		||||
                   ('H004', 'H004 (2.5)')],
 | 
			
		||||
        string='EBICS protocol version',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        required=True, default='H004')
 | 
			
		||||
    ebics_partner = fields.Char(
 | 
			
		||||
        string='EBICS PartnerID', required=True,
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        help="Organizational unit (company or individual) "
 | 
			
		||||
             "that concludes a contract with the bank. "
 | 
			
		||||
             "\nIn this contract it will be agreed which order types "
 | 
			
		||||
             "(file formats) are used, which accounts are concerned, "
 | 
			
		||||
             "which of the customer's users (subscribers) "
 | 
			
		||||
             "communicate with the EBICS bank server and the authorisations "
 | 
			
		||||
             "that these users will possess. "
 | 
			
		||||
             "\nIt is identified by the PartnerID.")
 | 
			
		||||
    ebics_user = fields.Char(
 | 
			
		||||
        string='EBICS UserID', required=True,
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        help="Human users or a technical system that is/are "
 | 
			
		||||
             "assigned to a customer. "
 | 
			
		||||
             "\nOn the EBICS bank server it is identified "
 | 
			
		||||
             "by the combination of UserID and PartnerID. "
 | 
			
		||||
             "The technical subscriber serves only for the data exchange "
 | 
			
		||||
             "between customer and financial institution. "
 | 
			
		||||
             "The human user also can authorise orders.")
 | 
			
		||||
    # Currently only a singe signature class per user is supported
 | 
			
		||||
    # Classes A and B are not yet supported.
 | 
			
		||||
    signature_class = fields.Selection(
 | 
			
		||||
        selection=[('E', 'Single signature'),
 | 
			
		||||
                   ('T', 'Transport signature')],
 | 
			
		||||
        string='Signature Class',
 | 
			
		||||
        required=True, default='T',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        help="Default signature class."
 | 
			
		||||
             "This default can be overriden for specific "
 | 
			
		||||
             "EBICS transactions (cf. File Formats).")
 | 
			
		||||
    ebics_files = fields.Char(
 | 
			
		||||
        string='EBICS Files Root', required=True,
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        default=lambda self: self._default_ebics_files(),
 | 
			
		||||
        help="Root Directory for EBICS File Transfer Folders.")
 | 
			
		||||
 | 
			
		||||
    # We store the EBICS keys in a separate directory in the file system.
 | 
			
		||||
    # This directory requires special protection to reduce fraude.
 | 
			
		||||
    ebics_keys = fields.Char(
 | 
			
		||||
        string='EBICS Keys', required=True,
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        default=lambda self: self._default_ebics_keys(),
 | 
			
		||||
        help="File holding the EBICS Keys."
 | 
			
		||||
             "\nSpecify the full path (directory + filename).")
 | 
			
		||||
    ebics_keys_found = fields.Boolean(
 | 
			
		||||
        compute='_compute_ebics_keys_found')
 | 
			
		||||
    ebics_passphrase = fields.Char(
 | 
			
		||||
        string='EBICS Passphrase')
 | 
			
		||||
    ebics_key_version = fields.Selection(
 | 
			
		||||
        selection=[('A005', 'A005 (RSASSA-PKCS1-v1_5)'),
 | 
			
		||||
                   ('A006', 'A006 (RSASSA-PSS)')],
 | 
			
		||||
        string='EBICS key version',
 | 
			
		||||
        default='A006',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        help="The key version of the electronic signature.")
 | 
			
		||||
    ebics_key_bitlength = fields.Integer(
 | 
			
		||||
        string='EBICS key bitlength',
 | 
			
		||||
        default=2048,
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        help="The bit length of the generated keys. "
 | 
			
		||||
             "\nThe value must be between 1536 and 4096.")
 | 
			
		||||
    ebics_ini_letter = fields.Binary(
 | 
			
		||||
        string='EBICS INI Letter', readonly=True,
 | 
			
		||||
        help="INI-letter PDF document to be sent to your bank.")
 | 
			
		||||
    ebics_ini_letter_fn = fields.Char(
 | 
			
		||||
        string='INI-letter Filename', readonly=True)
 | 
			
		||||
    ebics_public_bank_keys = fields.Binary(
 | 
			
		||||
        string='EBICS Public Bank Keys', readonly=True,
 | 
			
		||||
        help="EBICS Public Bank Keys to be checked for consistency.")
 | 
			
		||||
    ebics_public_bank_keys_fn = fields.Char(
 | 
			
		||||
        string='EBICS Public Bank Keys Filename', readonly=True)
 | 
			
		||||
 | 
			
		||||
    # X.509 Distinguished Name attributes used to
 | 
			
		||||
    # create self-signed X.509 certificates
 | 
			
		||||
    ebics_key_x509 = fields.Boolean(
 | 
			
		||||
        string='X509 support',
 | 
			
		||||
        help="Set this flag in order to work with "
 | 
			
		||||
             "self-signed X.509 certificates")
 | 
			
		||||
    ebics_key_x509_dn_cn = fields.Char(
 | 
			
		||||
        string='Common Name [CN]',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
    )
 | 
			
		||||
    ebics_key_x509_dn_o = fields.Char(
 | 
			
		||||
        string='Organization Name [O]',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
    )
 | 
			
		||||
    ebics_key_x509_dn_ou = fields.Char(
 | 
			
		||||
        string='Organizational Unit Name [OU]',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
    )
 | 
			
		||||
    ebics_key_x509_dn_c = fields.Char(
 | 
			
		||||
        string='Country Name [C]',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
    )
 | 
			
		||||
    ebics_key_x509_dn_st = fields.Char(
 | 
			
		||||
        string='State Or Province Name [ST]',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
    )
 | 
			
		||||
    ebics_key_x509_dn_l = fields.Char(
 | 
			
		||||
        string='Locality Name [L]',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
    )
 | 
			
		||||
    ebics_key_x509_dn_e = fields.Char(
 | 
			
		||||
        string='Email Address',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
    )
 | 
			
		||||
    ebics_file_format_ids = fields.Many2many(
 | 
			
		||||
        comodel_name='ebics.file.format',
 | 
			
		||||
        column1='config_id', column2='format_id',
 | 
			
		||||
        string='EBICS File Formats',
 | 
			
		||||
        readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
    )
 | 
			
		||||
    state = fields.Selection(
 | 
			
		||||
        [('draft', 'Draft'),
 | 
			
		||||
         ('init', 'Initialisation'),
 | 
			
		||||
         ('get_bank_keys', 'Get Keys from Bank'),
 | 
			
		||||
         ('to_verify', 'Verification'),
 | 
			
		||||
         ('active', 'Active')],
 | 
			
		||||
        string='State',
 | 
			
		||||
        default='draft',
 | 
			
		||||
        required=True, readonly=True)
 | 
			
		||||
    order_number = fields.Char(
 | 
			
		||||
        size=4, readonly=True, states={'draft': [('readonly', False)]},
 | 
			
		||||
        help="Specify the number for the next order."
 | 
			
		||||
             "\nThis number should match the following pattern : "
 | 
			
		||||
             "[A-Z]{1}[A-Z0-9]{3}")
 | 
			
		||||
    active = fields.Boolean(
 | 
			
		||||
        string='Active', default=True)
 | 
			
		||||
    company_id = fields.Many2one(
 | 
			
		||||
        'res.company', string='Company',
 | 
			
		||||
        default=lambda self: self.env.user.company_id,
 | 
			
		||||
        required=True)
 | 
			
		||||
 | 
			
		||||
    @api.model
 | 
			
		||||
    def _default_ebics_files(self):
 | 
			
		||||
        return '/'.join(['/home/odoo/ebics_files', self._cr.dbname])
 | 
			
		||||
 | 
			
		||||
    @api.model
 | 
			
		||||
    def _default_ebics_keys(self):
 | 
			
		||||
        return '/'.join(['/etc/odoo/ebics_keys',
 | 
			
		||||
                         self._cr.dbname,
 | 
			
		||||
                         'mykeys'])
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def _compute_ebics_keys_found(self):
 | 
			
		||||
        for cfg in self:
 | 
			
		||||
            if cfg.ebics_keys:
 | 
			
		||||
                dirname = os.path.dirname(self.ebics_keys)
 | 
			
		||||
                self.ebics_keys_found = os.path.exists(dirname)
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    @api.constrains('order_number')
 | 
			
		||||
    def _check_order_number(self):
 | 
			
		||||
        for cfg in self:
 | 
			
		||||
            nbr = cfg.order_number
 | 
			
		||||
            ok = True
 | 
			
		||||
            if nbr:
 | 
			
		||||
                if len(nbr) != 4:
 | 
			
		||||
                    ok = False
 | 
			
		||||
                else:
 | 
			
		||||
                    pattern = re.compile("[A-Z]{1}[A-Z0-9]{3}")
 | 
			
		||||
                    if not pattern.match(nbr):
 | 
			
		||||
                        ok = False
 | 
			
		||||
            if not ok:
 | 
			
		||||
                raise UserError(_(
 | 
			
		||||
                    "Order Number should comply with the following pattern:"
 | 
			
		||||
                    "\n[A-Z]{1}[A-Z0-9]{3}"))
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def unlink(self):
 | 
			
		||||
        for ebics_config in self:
 | 
			
		||||
            if ebics_config.state == 'active':
 | 
			
		||||
                raise UserError(_(
 | 
			
		||||
                    "You cannot remove active EBICS congirations."))
 | 
			
		||||
        return super(EbicsConfig, self).unlink()
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def set_to_draft(self):
 | 
			
		||||
        return self.write({'state': 'draft'})
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def set_to_active(self):
 | 
			
		||||
        return self.write({'state': 'active'})
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def ebics_init_1(self):
 | 
			
		||||
        """
 | 
			
		||||
        Initialization of bank keys - Step 1:
 | 
			
		||||
        Create new keys and certificates for this user
 | 
			
		||||
        """
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        self._check_ebics_files()
 | 
			
		||||
        if self.state != 'draft':
 | 
			
		||||
            raise UserError(
 | 
			
		||||
                _("Set state to 'draft' before Bank Key (re)initialisation."))
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            keyring = EbicsKeyRing(
 | 
			
		||||
                keys=self.ebics_keys,
 | 
			
		||||
                passphrase=self.ebics_passphrase or None)
 | 
			
		||||
            bank = EbicsBank(
 | 
			
		||||
                keyring=keyring, hostid=self.ebics_host, url=self.ebics_url)
 | 
			
		||||
            user = EbicsUser(
 | 
			
		||||
                keyring=keyring, partnerid=self.ebics_partner,
 | 
			
		||||
                userid=self.ebics_user)
 | 
			
		||||
        except:
 | 
			
		||||
            exctype, value = exc_info()[:2]
 | 
			
		||||
            error = _("EBICS Initialisation Error:")
 | 
			
		||||
            error += '\n' + str(exctype) + '\n' + str(value)
 | 
			
		||||
            raise UserError(error)
 | 
			
		||||
 | 
			
		||||
        self._check_ebics_keys()
 | 
			
		||||
        if not os.path.isfile(self.ebics_keys):
 | 
			
		||||
            user.create_keys(
 | 
			
		||||
                keyversion=self.ebics_key_version,
 | 
			
		||||
                bitlength=self.ebics_key_bitlength)
 | 
			
		||||
 | 
			
		||||
        if self.ebics_key_x509:
 | 
			
		||||
            dn_attrs = {
 | 
			
		||||
                'commonName': self.ebics_key_x509_dn_cn,
 | 
			
		||||
                'organizationName': self.ebics_key_x509_dn_o,
 | 
			
		||||
                'organizationalUnitName': self.ebics_key_x509_dn_ou,
 | 
			
		||||
                'countryName': self.ebics_key_x509_dn_c,
 | 
			
		||||
                'stateOrProvinceName': self.ebics_key_x509_dn_st,
 | 
			
		||||
                'localityName': self.ebics_key_x509_dn_l,
 | 
			
		||||
                'emailAddress': self.ebics_key_x509_dn_e,
 | 
			
		||||
            }
 | 
			
		||||
            kwargs = {k: v for k, v in dn_attrs.items() if v}
 | 
			
		||||
            user.create_certificates(**kwargs)
 | 
			
		||||
 | 
			
		||||
        client = EbicsClient(bank, user, version=self.ebics_version)
 | 
			
		||||
 | 
			
		||||
        # Send the public electronic signature key to the bank.
 | 
			
		||||
        try:
 | 
			
		||||
            if self.ebics_version == 'H003':
 | 
			
		||||
                bank._order_number = self._get_order_number()
 | 
			
		||||
            OrderID = client.INI()
 | 
			
		||||
            _logger.info(
 | 
			
		||||
                '%s, EBICS INI command, OrderID=%s', self._name, OrderID)
 | 
			
		||||
            if self.ebics_version == 'H003':
 | 
			
		||||
                self._update_order_number(OrderID)
 | 
			
		||||
        except URLError:
 | 
			
		||||
            e = exc_info()
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "urlopen error:\n url '%s' - %s")
 | 
			
		||||
                % (self.ebics_url, e[1].reason.strerror))
 | 
			
		||||
        except EbicsFunctionalError:
 | 
			
		||||
            e = exc_info()
 | 
			
		||||
            error = _("EBICS Functional Error:")
 | 
			
		||||
            error += '\n'
 | 
			
		||||
            error += '%s (code: %s)' % (e[1].message, e[1].code)
 | 
			
		||||
            raise UserError(error)
 | 
			
		||||
        except EbicsTechnicalError:
 | 
			
		||||
            e = exc_info()
 | 
			
		||||
            error = _("EBICS Technical Error:")
 | 
			
		||||
            error += '\n'
 | 
			
		||||
            error += '%s (code: %s)' % (e[1].message, e[1].code)
 | 
			
		||||
            raise UserError(error)
 | 
			
		||||
 | 
			
		||||
        # Send the public authentication and encryption keys to the bank.
 | 
			
		||||
        if self.ebics_version == 'H003':
 | 
			
		||||
            bank._order_number = self._get_order_number()
 | 
			
		||||
        OrderID = client.HIA()
 | 
			
		||||
        _logger.info('%s, EBICS HIA command, OrderID=%s', self._name, OrderID)
 | 
			
		||||
        if self.ebics_version == 'H003':
 | 
			
		||||
            self._update_order_number(OrderID)
 | 
			
		||||
 | 
			
		||||
        # Create an INI-letter which must be printed and sent to the bank.
 | 
			
		||||
        lang = self.env.user.lang[:2]
 | 
			
		||||
        cc = self.bank_id.bank_id.country.code
 | 
			
		||||
        if cc in ['FR', 'DE']:
 | 
			
		||||
            lang = cc
 | 
			
		||||
        tmp_dir = os.path.normpath(self.ebics_files + '/tmp')
 | 
			
		||||
        if not os.path.isdir(tmp_dir):
 | 
			
		||||
            os.makedirs(tmp_dir, 0700)
 | 
			
		||||
        fn_date = fields.Date.today()
 | 
			
		||||
        fn = '_'.join([self.ebics_host, 'ini_letter', fn_date]) + '.pdf'
 | 
			
		||||
        full_tmp_fn = os.path.normpath(tmp_dir + '/' + fn)
 | 
			
		||||
        user.create_ini_letter(
 | 
			
		||||
            bankname=self.bank_id.bank_id.name,
 | 
			
		||||
            path=full_tmp_fn,
 | 
			
		||||
            lang=lang)
 | 
			
		||||
        with open(full_tmp_fn, 'rb') as f:
 | 
			
		||||
            letter = f.read()
 | 
			
		||||
            self.write({
 | 
			
		||||
                'ebics_ini_letter': base64.encodestring(letter),
 | 
			
		||||
                'ebics_ini_letter_fn': fn,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        return self.write({'state': 'init'})
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def ebics_init_2(self):
 | 
			
		||||
        """
 | 
			
		||||
        Initialization of bank keys - Step 2:
 | 
			
		||||
        Activation of the account by the bank.
 | 
			
		||||
        """
 | 
			
		||||
        if self.state != 'init':
 | 
			
		||||
            raise UserError(
 | 
			
		||||
                _("Set state to 'Initialisation'."))
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        return self.write({'state': 'get_bank_keys'})
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def ebics_init_3(self):
 | 
			
		||||
        """
 | 
			
		||||
        Initialization of bank keys - Step 3:
 | 
			
		||||
 | 
			
		||||
        After the account has been activated the public bank keys
 | 
			
		||||
        must be downloaded and checked for consistency.
 | 
			
		||||
        """
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        self._check_ebics_files()
 | 
			
		||||
        if self.state != 'get_bank_keys':
 | 
			
		||||
            raise UserError(
 | 
			
		||||
                _("Set state to 'Get Keys from Bank'."))
 | 
			
		||||
        keyring = EbicsKeyRing(
 | 
			
		||||
            keys=self.ebics_keys, passphrase=self.ebics_passphrase)
 | 
			
		||||
        bank = EbicsBank(
 | 
			
		||||
            keyring=keyring, hostid=self.ebics_host, url=self.ebics_url)
 | 
			
		||||
        user = EbicsUser(
 | 
			
		||||
            keyring=keyring, partnerid=self.ebics_partner,
 | 
			
		||||
            userid=self.ebics_user)
 | 
			
		||||
        client = EbicsClient(
 | 
			
		||||
            bank, user, version=self.ebics_version)
 | 
			
		||||
 | 
			
		||||
        public_bank_keys = client.HPB()
 | 
			
		||||
        tmp_dir = os.path.normpath(self.ebics_files + '/tmp')
 | 
			
		||||
        if not os.path.isdir(tmp_dir):
 | 
			
		||||
            os.makedirs(tmp_dir, 0700)
 | 
			
		||||
        fn_date = fields.Date.today()
 | 
			
		||||
        fn = '_'.join([self.ebics_host, 'public_bank_keys', fn_date]) + '.txt'
 | 
			
		||||
        self.write({
 | 
			
		||||
            'ebics_public_bank_keys': base64.encodestring(public_bank_keys),
 | 
			
		||||
            'ebics_public_bank_keys_fn': fn,
 | 
			
		||||
            'state': 'to_verify',
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def ebics_init_4(self):
 | 
			
		||||
        """
 | 
			
		||||
        Initialization of bank keys - Step 2:
 | 
			
		||||
        Confirm Verification of the public bank keys
 | 
			
		||||
        and activate the bank keyu.
 | 
			
		||||
        """
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        if self.state != 'to_verify':
 | 
			
		||||
            raise UserError(
 | 
			
		||||
                _("Set state to 'Verification'."))
 | 
			
		||||
 | 
			
		||||
        keyring = EbicsKeyRing(
 | 
			
		||||
            keys=self.ebics_keys, passphrase=self.ebics_passphrase)
 | 
			
		||||
        bank = EbicsBank(
 | 
			
		||||
            keyring=keyring, hostid=self.ebics_host, url=self.ebics_url)
 | 
			
		||||
        bank.activate_keys()
 | 
			
		||||
        return self.write({'state': 'active'})
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def change_passphrase(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        ctx = dict(self._context, default_ebics_config_id=self.id)
 | 
			
		||||
        module = __name__.split('addons.')[1].split('.')[0]
 | 
			
		||||
        view = self.env.ref(
 | 
			
		||||
            '%s.ebics_change_passphrase_view_form' % module)
 | 
			
		||||
        return {
 | 
			
		||||
            'name': _('EBICS keys change passphrase'),
 | 
			
		||||
            'view_type': 'form',
 | 
			
		||||
            'view_mode': 'form',
 | 
			
		||||
            'res_model': 'ebics.change.passphrase',
 | 
			
		||||
            'view_id': view.id,
 | 
			
		||||
            'target': 'new',
 | 
			
		||||
            'context': ctx,
 | 
			
		||||
            'type': 'ir.actions.act_window',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def _get_order_number(self):
 | 
			
		||||
        return self.order_number
 | 
			
		||||
 | 
			
		||||
    def _update_order_number(self, OrderID):
 | 
			
		||||
        o_list = list(OrderID)
 | 
			
		||||
        for i, c in enumerate(reversed(o_list), start=1):
 | 
			
		||||
            if c == '9':
 | 
			
		||||
                o_list[-i] = 'A'
 | 
			
		||||
                break
 | 
			
		||||
            if c == 'Z':
 | 
			
		||||
                continue
 | 
			
		||||
            else:
 | 
			
		||||
                o_list[-i] = chr(ord(c) + 1)
 | 
			
		||||
                break
 | 
			
		||||
        next = ''.join(o_list)
 | 
			
		||||
        if next == 'ZZZZ':
 | 
			
		||||
            next = 'A000'
 | 
			
		||||
        self.order_number = next
 | 
			
		||||
 | 
			
		||||
    def _check_ebics_keys(self):
 | 
			
		||||
        if self.ebics_keys:
 | 
			
		||||
            dirname = os.path.dirname(self.ebics_keys)
 | 
			
		||||
            if not os.path.exists(dirname):
 | 
			
		||||
                raise UserError(_(
 | 
			
		||||
                    "EBICS Keys Directory '%s' is not available."
 | 
			
		||||
                    "\nPlease contact your system administrator.")
 | 
			
		||||
                    % dirname)
 | 
			
		||||
 | 
			
		||||
    def _check_ebics_files(self):
 | 
			
		||||
        dirname = self.ebics_files or ''
 | 
			
		||||
        if not os.path.exists(dirname):
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "EBICS Files Root Directory %s is not available."
 | 
			
		||||
                "\nPlease contact your system administrator.")
 | 
			
		||||
                % dirname)
 | 
			
		||||
							
								
								
									
										263
									
								
								account_ebics/models/ebics_file.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								account_ebics/models/ebics_file.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,263 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2018 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from odoo import api, fields, models, _
 | 
			
		||||
from odoo.exceptions import UserError
 | 
			
		||||
 | 
			
		||||
_logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EbicsFile(models.Model):
 | 
			
		||||
    _name = 'ebics.file'
 | 
			
		||||
    _description = 'Object to store EBICS Data Files'
 | 
			
		||||
    _order = 'date desc'
 | 
			
		||||
 | 
			
		||||
    name = fields.Char(string='Filename')
 | 
			
		||||
    data = fields.Binary(string='File', readonly=True)
 | 
			
		||||
    format_id = fields.Many2one(
 | 
			
		||||
        comodel_name='ebics.file.format',
 | 
			
		||||
        string='EBICS File Formats',
 | 
			
		||||
        readonly=True)
 | 
			
		||||
    type = fields.Selection(
 | 
			
		||||
        related='format_id.type',
 | 
			
		||||
        readonly=True)
 | 
			
		||||
    date_from = fields.Date(
 | 
			
		||||
        readonly=True,
 | 
			
		||||
        help="'Date From' as entered in the download wizard.")
 | 
			
		||||
    date_to = fields.Date(
 | 
			
		||||
        readonly=True,
 | 
			
		||||
        help="'Date To' as entered in the download wizard.")
 | 
			
		||||
    date = fields.Datetime(
 | 
			
		||||
        required=True, readonly=True,
 | 
			
		||||
        help='File Upload/Download date')
 | 
			
		||||
    bank_statement_ids = fields.One2many(
 | 
			
		||||
        comodel_name='account.bank.statement',
 | 
			
		||||
        inverse_name='ebics_file_id',
 | 
			
		||||
        string='Generated Bank Statements', readonly=True)
 | 
			
		||||
    state = fields.Selection(
 | 
			
		||||
        [('draft', 'Draft'),
 | 
			
		||||
         ('done', 'Done')],
 | 
			
		||||
        string='State',
 | 
			
		||||
        default='draft',
 | 
			
		||||
        required=True, readonly=True)
 | 
			
		||||
    user_id = fields.Many2one(
 | 
			
		||||
        comodel_name='res.users', string='User',
 | 
			
		||||
        default=lambda self: self.env.user,
 | 
			
		||||
        readonly=True)
 | 
			
		||||
    note = fields.Text(string='Notes')
 | 
			
		||||
    note_process = fields.Text(string='Notes')
 | 
			
		||||
    company_id = fields.Many2one(
 | 
			
		||||
        comodel_name='res.company',
 | 
			
		||||
        string='Company',
 | 
			
		||||
        default=lambda self: self._default_company_id())
 | 
			
		||||
 | 
			
		||||
    _sql_constraints = [
 | 
			
		||||
        ('name_company_uniq', 'unique (name, company_id, format_id)',
 | 
			
		||||
         'This File has already been imported !')
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    @api.model
 | 
			
		||||
    def _default_company_id(self):
 | 
			
		||||
        """
 | 
			
		||||
        Adapt this method in case your bank provides transactions
 | 
			
		||||
        of multiple legal entities in a single EBICS File.
 | 
			
		||||
        """
 | 
			
		||||
        return self.env.user.company_id
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def unlink(self):
 | 
			
		||||
        ff_methods = self._file_format_methods()
 | 
			
		||||
        for ebics_file in self:
 | 
			
		||||
            if ebics_file.state == 'done':
 | 
			
		||||
                raise UserError(_(
 | 
			
		||||
                    "You can only remove EBICS files in state 'Draft'."))
 | 
			
		||||
            # execute format specific actions
 | 
			
		||||
            ff = ebics_file.format_id.name
 | 
			
		||||
            if ff in ff_methods:
 | 
			
		||||
                if ff_methods[ff].get('unlink'):
 | 
			
		||||
                    ff_methods[ff]['unlink'](ebics_file)
 | 
			
		||||
            # remove bank statements
 | 
			
		||||
            ebics_file.bank_statement_ids.unlink()
 | 
			
		||||
        return super(EbicsFile, self).unlink()
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def set_to_draft(self):
 | 
			
		||||
        return self.write({'state': 'draft'})
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def set_to_done(self):
 | 
			
		||||
        return self.write({'state': 'done'})
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def process(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        self.note_process = ''
 | 
			
		||||
        ff_methods = self._file_format_methods()
 | 
			
		||||
        ff = self.format_id.name
 | 
			
		||||
        if ff in ff_methods:
 | 
			
		||||
            if ff_methods[ff].get('process'):
 | 
			
		||||
                res = ff_methods[ff]['process'](self)
 | 
			
		||||
                self.state = 'done'
 | 
			
		||||
                return res
 | 
			
		||||
        else:
 | 
			
		||||
            return self._process_undefined_format()
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def action_open_bank_statements(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        action = self.env['ir.actions.act_window'].for_xml_id(
 | 
			
		||||
            'account', 'action_bank_statement_tree')
 | 
			
		||||
        domain = eval(action.get('domain') or '[]')
 | 
			
		||||
        domain += [('id', 'in', self._context.get('statement_ids'))]
 | 
			
		||||
        action.update({'domain': domain})
 | 
			
		||||
        return action
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def button_close(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        return {'type': 'ir.actions.act_window_close'}
 | 
			
		||||
 | 
			
		||||
    def _file_format_methods(self):
 | 
			
		||||
        """
 | 
			
		||||
        Extend this dictionary in order to add support
 | 
			
		||||
        for extra file formats.
 | 
			
		||||
        """
 | 
			
		||||
        res = {
 | 
			
		||||
            'camt.xxx.cfonb120.stm':
 | 
			
		||||
                {'process': self._process_cfonb120,
 | 
			
		||||
                 'unlink': self._unlink_cfonb120},
 | 
			
		||||
            'camt.053.001.02.stm':
 | 
			
		||||
                {'process': self._process_camt053,
 | 
			
		||||
                 'unlink': self._unlink_camt053},
 | 
			
		||||
        }
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def _check_import_module(self, module):
 | 
			
		||||
        mod = self.env['ir.module.module'].search(
 | 
			
		||||
            [('name', '=', module),
 | 
			
		||||
             ('state', '=', 'installed')])
 | 
			
		||||
        if not mod:
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "The module to process the '%s' format is not installed "
 | 
			
		||||
                "on your system. "
 | 
			
		||||
                "\nPlease install module '%s'")
 | 
			
		||||
                % (self.format_id.name, module))
 | 
			
		||||
 | 
			
		||||
    def _process_result_action(self, ctx):
 | 
			
		||||
        module = __name__.split('addons.')[1].split('.')[0]
 | 
			
		||||
        result_view = self.env.ref('%s.ebics_file_view_form_result' % module)
 | 
			
		||||
        return {
 | 
			
		||||
            'name': _('Import EBICS File'),
 | 
			
		||||
            'res_id': self.id,
 | 
			
		||||
            'view_type': 'form',
 | 
			
		||||
            'view_mode': 'form',
 | 
			
		||||
            'res_model': self._name,
 | 
			
		||||
            'view_id': result_view.id,
 | 
			
		||||
            'target': 'new',
 | 
			
		||||
            'context': ctx,
 | 
			
		||||
            'type': 'ir.actions.act_window',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _process_cfonb120(self):
 | 
			
		||||
        """
 | 
			
		||||
        TODO:
 | 
			
		||||
        adapt OCA import logic to find correct journal on the basis
 | 
			
		||||
        of both account number and currency.
 | 
			
		||||
        Prompt for journal in case the journal is not found.
 | 
			
		||||
        """
 | 
			
		||||
        import_module = 'account_bank_statement_import_fr_cfonb'
 | 
			
		||||
        self._check_import_module(import_module)
 | 
			
		||||
        wiz_model = 'account.bank.statement.import'
 | 
			
		||||
        wiz_vals = {'data_file': self.data}
 | 
			
		||||
        wiz = self.env[wiz_model].create(wiz_vals)
 | 
			
		||||
        res = wiz.import_file()
 | 
			
		||||
        notifications = []
 | 
			
		||||
        statement_ids = []
 | 
			
		||||
        if res.get('context'):
 | 
			
		||||
            notifications = res['context'].get('notifications', [])
 | 
			
		||||
            statement_ids = res['context'].get('statement_ids', [])
 | 
			
		||||
        if notifications:
 | 
			
		||||
            for notif in notifications:
 | 
			
		||||
                parts = []
 | 
			
		||||
                for k in ['type', 'message', 'details']:
 | 
			
		||||
                    if notif.get(k):
 | 
			
		||||
                        msg = '%s: %s' % (k, notif[k])
 | 
			
		||||
                        parts.append(msg)
 | 
			
		||||
                self.note_process += '\n'.join(parts)
 | 
			
		||||
                self.note_process += '\n'
 | 
			
		||||
            self.note_process += '\n'
 | 
			
		||||
        self.note_process += _(
 | 
			
		||||
            "Number of Bank Statements: %s"
 | 
			
		||||
        ) % len(statement_ids)
 | 
			
		||||
        if statement_ids:
 | 
			
		||||
            self.bank_statement_ids = [(6, 0, statement_ids)]
 | 
			
		||||
        ctx = dict(self._context, statement_ids=statement_ids)
 | 
			
		||||
        return self._process_result_action(ctx)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _unlink_cfonb120(self):
 | 
			
		||||
        """
 | 
			
		||||
        Placeholder for cfonb120 specific actions before removing the
 | 
			
		||||
        EBICS data file and its related bank statements.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _process_camt053(self):
 | 
			
		||||
        import_module = 'account_bank_statement_import_camt'
 | 
			
		||||
        self._check_import_module(import_module)
 | 
			
		||||
        wiz_model = 'account.bank.statement.import'
 | 
			
		||||
        wiz_vals = {
 | 
			
		||||
            'data_file': self.data,
 | 
			
		||||
            'filename': self.name,
 | 
			
		||||
        }
 | 
			
		||||
        wiz = self.env[wiz_model].create(wiz_vals)
 | 
			
		||||
        res = wiz.import_file()
 | 
			
		||||
        if res.get('res_model') \
 | 
			
		||||
                == 'account.bank.statement.import.journal.creation':
 | 
			
		||||
            if res.get('context'):
 | 
			
		||||
                bank_account = res['context'].get('default_bank_acc_number')
 | 
			
		||||
                raise UserError(_(
 | 
			
		||||
                    "No financial journal found for Company Bank Account %s"
 | 
			
		||||
                ) % bank_account)
 | 
			
		||||
        notifications = []
 | 
			
		||||
        statement_ids = []
 | 
			
		||||
        if res.get('context'):
 | 
			
		||||
            notifications = res['context'].get('notifications', [])
 | 
			
		||||
            statement_ids = res['context'].get('statement_ids', [])
 | 
			
		||||
        if notifications:
 | 
			
		||||
            for notif in notifications:
 | 
			
		||||
                parts = []
 | 
			
		||||
                for k in ['type', 'message', 'details']:
 | 
			
		||||
                    if notif.get(k):
 | 
			
		||||
                        msg = '%s: %s' % (k, notif[k])
 | 
			
		||||
                        parts.append(msg)
 | 
			
		||||
                self.note_process += '\n'.join(parts)
 | 
			
		||||
                self.note_process += '\n'
 | 
			
		||||
            self.note_process += '\n'
 | 
			
		||||
        self.note_process += _(
 | 
			
		||||
            "Number of Bank Statements: %s"
 | 
			
		||||
        ) % len(statement_ids)
 | 
			
		||||
        if statement_ids:
 | 
			
		||||
            self.bank_statement_ids = [(6, 0, statement_ids)]
 | 
			
		||||
        ctx = dict(self._context, statement_ids=statement_ids)
 | 
			
		||||
        return self._process_result_action(ctx)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _unlink_camt053(self):
 | 
			
		||||
        """
 | 
			
		||||
        Placeholder for camt053 specific actions before removing the
 | 
			
		||||
        EBICS data file and its related bank statements.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def _process_undefined_format(self):
 | 
			
		||||
        raise UserError(_(
 | 
			
		||||
            "The current version of the 'account_ebics' module "
 | 
			
		||||
            "has no support to automatically process EBICS files "
 | 
			
		||||
            "with format %s."
 | 
			
		||||
        ) % self.format_id.name)
 | 
			
		||||
							
								
								
									
										80
									
								
								account_ebics/models/ebics_file_format.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								account_ebics/models/ebics_file_format.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2018 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
from odoo import api, fields, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EbicsFileFormat(models.Model):
 | 
			
		||||
    _name = 'ebics.file.format'
 | 
			
		||||
    _description = 'EBICS File Formats'
 | 
			
		||||
    _order = 'type,name'
 | 
			
		||||
 | 
			
		||||
    name = fields.Selection(
 | 
			
		||||
        selection=lambda self: self._selection_name(),
 | 
			
		||||
        string='Request Type', required=True)
 | 
			
		||||
    type = fields.Selection(
 | 
			
		||||
        selection=[('down', 'Download'),
 | 
			
		||||
                   ('up', 'Upload')],
 | 
			
		||||
        required=True)
 | 
			
		||||
    order_type = fields.Selection(
 | 
			
		||||
        selection=lambda self: self._selection_order_type(),
 | 
			
		||||
        string='Order Type',
 | 
			
		||||
        help="For most banks is France you should use the "
 | 
			
		||||
             "format neutral Order Types 'FUL' for upload "
 | 
			
		||||
             "and 'FDL' for download.")
 | 
			
		||||
    signature_class = fields.Selection(
 | 
			
		||||
        selection=[('E', 'Single signature'),
 | 
			
		||||
                   ('T', 'Transport signature')],
 | 
			
		||||
        string='Signature Class',
 | 
			
		||||
        help="Please doublecheck the security of your Odoo "
 | 
			
		||||
             "ERP system when using class 'E' to prevent unauthorised "
 | 
			
		||||
             "users to make supplier payments."
 | 
			
		||||
             "\nLeave this field empty to use the default "
 | 
			
		||||
             "defined for your bank connection.")
 | 
			
		||||
    description = fields.Char()
 | 
			
		||||
    suffix = fields.Char(
 | 
			
		||||
        required=True,
 | 
			
		||||
        help="Specify the filename suffix for this File Format."
 | 
			
		||||
             "\nE.g. camt.053.xml")
 | 
			
		||||
 | 
			
		||||
    @api.model
 | 
			
		||||
    def _selection_order_type(self):
 | 
			
		||||
        up = self._supported_upload_order_types()
 | 
			
		||||
        down = self._supported_download_order_types()
 | 
			
		||||
        selection = [(x, x) for x in up + down]
 | 
			
		||||
        return selection
 | 
			
		||||
 | 
			
		||||
    def _supported_upload_order_types(self):
 | 
			
		||||
        return ['FUL', 'CCT', 'CDD', 'CDB', 'XE2', 'XE3']
 | 
			
		||||
 | 
			
		||||
    def _supported_download_order_types(self):
 | 
			
		||||
        return ['FDL', 'C53']
 | 
			
		||||
 | 
			
		||||
    @api.model
 | 
			
		||||
    def _selection_name(self):
 | 
			
		||||
        """
 | 
			
		||||
        List of supported EBICS Request Types.
 | 
			
		||||
        Extend this method via a custom module when testing
 | 
			
		||||
        a new Request Type and make a PR for the
 | 
			
		||||
        account_ebics module when this new Request Type
 | 
			
		||||
        is working correctly.
 | 
			
		||||
        This PR should include at least updates to
 | 
			
		||||
        - 'data/ebics_file_format.xml'
 | 
			
		||||
        - 'models/ebics_file_format.py'
 | 
			
		||||
        An overview of the EBICS Request Types can be found in
 | 
			
		||||
        the doc folder of this module (EBICS_Annex2).
 | 
			
		||||
        """
 | 
			
		||||
        request_types = [
 | 
			
		||||
            'camt.053.001.02.stm',
 | 
			
		||||
            'pain.001.001.03.sct',
 | 
			
		||||
            'pain.008.001.02.sdd',
 | 
			
		||||
            'pain.008.001.02.sbb',
 | 
			
		||||
            'camt.xxx.cfonb120.stm',
 | 
			
		||||
            'pain.001.001.02.sct',
 | 
			
		||||
            'camt.053',
 | 
			
		||||
            'pain.001',
 | 
			
		||||
            'pain.008',
 | 
			
		||||
        ]
 | 
			
		||||
        selection = [(x, x) for x in request_types]
 | 
			
		||||
        return selection
 | 
			
		||||
							
								
								
									
										41
									
								
								account_ebics/models/fintech_ebics_register.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								account_ebics/models/fintech_ebics_register.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2018 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
from sys import exc_info
 | 
			
		||||
from traceback import format_exception
 | 
			
		||||
 | 
			
		||||
from odoo.tools import config
 | 
			
		||||
 | 
			
		||||
_logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import fintech
 | 
			
		||||
except ImportError:
 | 
			
		||||
    fintech = None
 | 
			
		||||
    _logger.warning('Failed to import fintech')
 | 
			
		||||
 | 
			
		||||
fintech_register_name = config.get('fintech_register_name')
 | 
			
		||||
fintech_register_keycode = config.get('fintech_register_keycode')
 | 
			
		||||
fintech_register_users = config.get('fintech_register_users')
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    if fintech:
 | 
			
		||||
        fintech.cryptolib = 'cryptography'
 | 
			
		||||
        fintech.register(
 | 
			
		||||
            fintech_register_name,
 | 
			
		||||
            fintech_register_keycode,
 | 
			
		||||
            fintech_register_users)
 | 
			
		||||
except RuntimeError, e:
 | 
			
		||||
    if e.message == "'register' can be called only once":
 | 
			
		||||
        pass
 | 
			
		||||
    else:
 | 
			
		||||
        _logger.error(e.message)
 | 
			
		||||
        fintech.register()
 | 
			
		||||
except:
 | 
			
		||||
    msg = "fintech.register error"
 | 
			
		||||
    tb = ''.join(format_exception(*exc_info()))
 | 
			
		||||
    msg += '\n%s' % tb
 | 
			
		||||
    _logger.error(msg)
 | 
			
		||||
    fintech.register()
 | 
			
		||||
							
								
								
									
										27
									
								
								account_ebics/security/ebics_security.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								account_ebics/security/ebics_security.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<odoo>
 | 
			
		||||
 | 
			
		||||
  <record model="res.groups" id="group_ebics_manager">
 | 
			
		||||
    <field name="name">EBICS Manager</field>
 | 
			
		||||
    <field name="category_id" ref="base.module_category_hidden"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <data noupdate="1">
 | 
			
		||||
 | 
			
		||||
    <record id="ebics_config_comp_rule" model="ir.rule">
 | 
			
		||||
      <field name="name">EBICS Configuration model company rule</field>
 | 
			
		||||
      <field name="model_id" ref="model_ebics_config"/>
 | 
			
		||||
      <field eval="True" name="global"/>
 | 
			
		||||
      <field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
    <record id="ebics_file_comp_rule" model="ir.rule">
 | 
			
		||||
      <field name="name">EBICS File model company rule</field>
 | 
			
		||||
      <field name="model_id" ref="model_ebics_file"/>
 | 
			
		||||
      <field eval="True" name="global"/>
 | 
			
		||||
      <field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
  </data>
 | 
			
		||||
 | 
			
		||||
</odoo>
 | 
			
		||||
							
								
								
									
										7
									
								
								account_ebics/security/ir.model.access.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								account_ebics/security/ir.model.access.csv
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
 | 
			
		||||
access_ebics_config_manager,ebics_config manager,model_ebics_config,account_ebics.group_ebics_manager,1,1,1,1
 | 
			
		||||
access_ebics_config_user,ebics_config user,model_ebics_config,account.group_account_user,1,0,0,0
 | 
			
		||||
access_ebics_file_format_manager,ebics_file_format manager,model_ebics_file_format,account_ebics.group_ebics_manager,1,1,1,1
 | 
			
		||||
access_ebics_file_format_user,ebics_file_format user,model_ebics_file_format,account.group_account_user,1,0,0,0
 | 
			
		||||
access_ebics_file_manager,ebics_file manager,model_ebics_file,account_ebics.group_ebics_manager,1,1,1,1
 | 
			
		||||
access_ebics_file_user,ebics_file user,model_ebics_file,account.group_account_user,1,0,0,0
 | 
			
		||||
		
		
			
  | 
							
								
								
									
										
											BIN
										
									
								
								account_ebics/static/description/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								account_ebics/static/description/icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 17 KiB  | 
							
								
								
									
										122
									
								
								account_ebics/views/ebics_config.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								account_ebics/views/ebics_config.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
<?xml version="1.0" ?>
 | 
			
		||||
<odoo>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_config_view_tree" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.config.tree</field>
 | 
			
		||||
    <field name="model">ebics.config</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <tree string="EBICS Configuration" colors="blue:state!='active'">
 | 
			
		||||
        <field name="name"/>
 | 
			
		||||
        <field name="bank_id"/>
 | 
			
		||||
        <field name="ebics_host"/>
 | 
			
		||||
        <field name="ebics_user"/>
 | 
			
		||||
        <field name="state"/>
 | 
			
		||||
        <field name="company_id" groups="base.group_multi_company"/>
 | 
			
		||||
      </tree>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_config_view_form" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.config.form</field>
 | 
			
		||||
    <field name="model">ebics.config</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS Configuration">
 | 
			
		||||
        <header>
 | 
			
		||||
          <button name="set_to_draft" states="active" string="Set to Draft" type="object"
 | 
			
		||||
                  groups="account.group_account_manager"
 | 
			
		||||
                  help="Set to Draft in order to reinitialize your bank connection."/>
 | 
			
		||||
          <button name="set_to_active" states="draft" string="Force Active" type="object"
 | 
			
		||||
                  groups="account.group_account_manager"
 | 
			
		||||
                  help="Use this button to bypass the EBICS initialization (e.g. in case you have manually transferred active EBICS keys from another system."/>
 | 
			
		||||
          <field name="state" widget="statusbar"/>
 | 
			
		||||
        </header>
 | 
			
		||||
        <group col="4">
 | 
			
		||||
          <field name="name" colspan="2"/>
 | 
			
		||||
          <field name="bank_id" domain="[('company_id', '=', company_id)]"/>
 | 
			
		||||
          <field name="ebics_host"/>
 | 
			
		||||
          <field name="ebics_url"/>
 | 
			
		||||
          <field name="ebics_partner"/>
 | 
			
		||||
          <field name="ebics_user"/>
 | 
			
		||||
          <field name="ebics_version"/>
 | 
			
		||||
          <field name="signature_class"/>
 | 
			
		||||
          <field name="ebics_files"/>
 | 
			
		||||
          <field name="order_number"
 | 
			
		||||
                 attrs="{'invisible': [('ebics_version', '=', 'H004')]}"/>
 | 
			
		||||
          <field name="active"/>
 | 
			
		||||
          <field name="company_id" widget='selection' groups="base.group_multi_company"/>
 | 
			
		||||
        </group>
 | 
			
		||||
        <notebook>
 | 
			
		||||
          <page string="Keys" groups="account_ebics.group_ebics_manager">
 | 
			
		||||
            <label string=""/>
 | 
			
		||||
            <header>
 | 
			
		||||
              <button name="ebics_init_1" states="draft" string="EBICS Initialisation" type="object" class="oe_highlight"
 | 
			
		||||
                      groups="account.group_account_manager"
 | 
			
		||||
                      help="Initialise EBICS Bank Keys"/>
 | 
			
		||||
              <button name="ebics_init_2" states="init" string="Account activated" type="object" class="oe_highlight"
 | 
			
		||||
                      groups="account.group_account_manager"
 | 
			
		||||
                      help="EBICS Initialisation - Push this button when the account has been activated by the bank."/>
 | 
			
		||||
              <button name="ebics_init_3" states="get_bank_keys" string="Get Bank Keys" type="object" class="oe_highlight"
 | 
			
		||||
                      groups="account.group_account_manager"
 | 
			
		||||
                      help="EBICS Initialisation - After the account has been activated the public bank keys must be downloaded and checked for consistency."/>
 | 
			
		||||
              <button name="ebics_init_4" states="to_verify" string="Bank Keys Verified" type="object" class="oe_highlight"
 | 
			
		||||
                      groups="account.group_account_manager"
 | 
			
		||||
                      help="EBICS Initialisation - Push this button when the public have been checked for consistency."/>
 | 
			
		||||
              <button name="change_passphrase" string="Change Passphrase" type="object" class="oe_highlight"
 | 
			
		||||
                      attrs="{'invisible': [('ebics_keys_found', '=', False)]}"
 | 
			
		||||
                      groups="account.group_account_manager"/>
 | 
			
		||||
            </header>
 | 
			
		||||
            <group col="4" name="ebics_key">
 | 
			
		||||
              <field name="ebics_keys"/>
 | 
			
		||||
              <field name="ebics_keys_found" invisible="1"/>
 | 
			
		||||
              <field name="ebics_passphrase" password="True" attrs="{'invisible': [('ebics_keys_found', '!=', False)]}"/>
 | 
			
		||||
              <newline/>
 | 
			
		||||
              <field name="ebics_key_version"/>
 | 
			
		||||
              <field name="ebics_key_bitlength"/>
 | 
			
		||||
              <field name="ebics_key_x509"/>
 | 
			
		||||
            </group>
 | 
			
		||||
            <group colspan="4" name="ebics_ini_letter" attrs="{'invisible': [('ebics_ini_letter', '=', False)]}">
 | 
			
		||||
              <field name="ebics_ini_letter_fn" invisible="1"/>
 | 
			
		||||
              <field name="ebics_ini_letter" filename="ebics_ini_letter_fn"/>
 | 
			
		||||
            </group>
 | 
			
		||||
            <group colspan="4" name="ebics_public_bank_keys" attrs="{'invisible': [('ebics_public_bank_keys', '=', False)]}">
 | 
			
		||||
              <field name="ebics_public_bank_keys_fn" invisible="1"/>
 | 
			
		||||
              <field name="ebics_public_bank_keys" filename="ebics_public_bank_keys_fn"/>
 | 
			
		||||
            </group>
 | 
			
		||||
            <group col="4" name="dn" attrs="{'invisible': [('ebics_key_x509', '=', False)]}">
 | 
			
		||||
              <label string="Distinguished Name attributes used to create self-signed X.509 certificates:" colspan="4"/>
 | 
			
		||||
              <group name="dn_l" colspan="2">
 | 
			
		||||
                <field name="ebics_key_x509_dn_cn"/>
 | 
			
		||||
                <field name="ebics_key_x509_dn_o"/>
 | 
			
		||||
                <field name="ebics_key_x509_dn_l"/>
 | 
			
		||||
                <field name="ebics_key_x509_dn_c"/>
 | 
			
		||||
              </group>
 | 
			
		||||
              <group name="dn_r" colspan="2">
 | 
			
		||||
                <field name="ebics_key_x509_dn_e"/>
 | 
			
		||||
                <field name="ebics_key_x509_dn_ou"/>
 | 
			
		||||
                <field name="ebics_key_x509_dn_st"/>
 | 
			
		||||
              </group>
 | 
			
		||||
            </group>
 | 
			
		||||
          </page>
 | 
			
		||||
          <page string="File Formats" groups="account_ebics.group_ebics_manager">
 | 
			
		||||
            <field name="ebics_file_format_ids"/>
 | 
			
		||||
          </page>
 | 
			
		||||
        </notebook>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_config_action" model="ir.actions.act_window">
 | 
			
		||||
    <field name="name">EBICS Configuration</field>
 | 
			
		||||
    <field name="res_model">ebics.config</field>
 | 
			
		||||
    <field name="view_type">form</field>
 | 
			
		||||
    <field name="view_mode">tree,form</field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <menuitem id="ebics_config_menu"
 | 
			
		||||
            name="EBICS Configuration"
 | 
			
		||||
            parent="ebics_menu"
 | 
			
		||||
            action="ebics_config_action"
 | 
			
		||||
            groups="account_ebics.group_ebics_manager"
 | 
			
		||||
            sequence="10"/>
 | 
			
		||||
 | 
			
		||||
</odoo>
 | 
			
		||||
							
								
								
									
										216
									
								
								account_ebics/views/ebics_file.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								account_ebics/views/ebics_file.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,216 @@
 | 
			
		||||
<?xml version="1.0" ?>
 | 
			
		||||
<odoo>
 | 
			
		||||
 | 
			
		||||
  <menuitem id="ebics_file_menu"
 | 
			
		||||
        name="EBICS Files"
 | 
			
		||||
        parent="ebics_processing_menu"
 | 
			
		||||
        sequence="30"/>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_view_search" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.file.search</field>
 | 
			
		||||
    <field name="model">ebics.file</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <search string="Search EBICS Files">
 | 
			
		||||
        <group col="10" colspan="4">
 | 
			
		||||
          <field name="date_from"/>
 | 
			
		||||
          <field name="date_to"/>
 | 
			
		||||
          <field name="name"/>
 | 
			
		||||
          <field name="format_id"/>
 | 
			
		||||
          <field name="user_id"/>
 | 
			
		||||
          <field name="company_id" widget="selection" groups="base.group_multi_company"/>
 | 
			
		||||
        </group>
 | 
			
		||||
        <newline/>
 | 
			
		||||
        <group expand="0" string="Group By">
 | 
			
		||||
          <filter string="File Format" context="{'group_by':'format_id'}"/>
 | 
			
		||||
          <filter string="State" context="{'group_by':'state'}"/>
 | 
			
		||||
          <filter string="User" context="{'group_by':'user_id'}"/>
 | 
			
		||||
          <filter string="Company" domain="[]" groups="base.group_multi_company" context="{'group_by':'company_id'}"/>
 | 
			
		||||
        </group>
 | 
			
		||||
      </search>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <!-- Download -->
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_view_tree_download" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.file.tree</field>
 | 
			
		||||
    <field name="model">ebics.file</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <tree string="EBICS Files" colors="blue:state=='draft'" create="false">
 | 
			
		||||
        <field name="date" string="Download Date"/>
 | 
			
		||||
        <field name="name"/>
 | 
			
		||||
        <field name="date_from"/>
 | 
			
		||||
        <field name="date_to"/>
 | 
			
		||||
        <field name="user_id"/>
 | 
			
		||||
        <field name="state"/>
 | 
			
		||||
        <field name="format_id"/>
 | 
			
		||||
        <field name="company_id" groups="base.group_multi_company"/>
 | 
			
		||||
      </tree>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_view_form_download" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.file.form</field>
 | 
			
		||||
    <field name="model">ebics.file</field>
 | 
			
		||||
    <field name="priority">1</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS File" create="false">
 | 
			
		||||
        <header>
 | 
			
		||||
          <button name="set_to_draft" states="done" string="Set to Draft" type="object" groups="account.group_account_manager"/>
 | 
			
		||||
          <button name="process"
 | 
			
		||||
                  class="oe_highlight"
 | 
			
		||||
                  states="draft"
 | 
			
		||||
                  string="Process"
 | 
			
		||||
                  type="object"
 | 
			
		||||
                  groups="account.group_account_manager"
 | 
			
		||||
                  help="Process the EBICS File"/>
 | 
			
		||||
          <field name="state" widget="statusbar"/>
 | 
			
		||||
        </header>
 | 
			
		||||
        <group colspan="4" col="4">
 | 
			
		||||
          <field name="date" string="Download Date"/>
 | 
			
		||||
          <field name="name"/>
 | 
			
		||||
          <field name="data" filename="name"/>
 | 
			
		||||
          <field name="format_id"/>
 | 
			
		||||
          <field name="date_from"/>
 | 
			
		||||
          <field name="date_to"/>
 | 
			
		||||
          <field name="user_id"/>
 | 
			
		||||
          <field name="company_id" groups="base.group_multi_company"/>
 | 
			
		||||
        </group>
 | 
			
		||||
        <notebook>
 | 
			
		||||
          <page string="Additional Information">
 | 
			
		||||
            <field name="note" nolabel="1"/>
 | 
			
		||||
          </page>
 | 
			
		||||
          <page string="Bank Statements" attrs="{'invisible':[('bank_statement_ids','=',[])]}">
 | 
			
		||||
            <field name="bank_statement_ids" nolabel="1"/>
 | 
			
		||||
          </page>
 | 
			
		||||
        </notebook>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_view_form_result" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.file.process.result</field>
 | 
			
		||||
    <field name="model">ebics.file</field>
 | 
			
		||||
    <field name="priority">2</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="Process EBICS File">
 | 
			
		||||
        <separator colspan="4" string="Results :" />
 | 
			
		||||
        <field name="note_process" colspan="4" nolabel="1" width="850" height="400"/>
 | 
			
		||||
        <footer>
 | 
			
		||||
          <button name="action_open_bank_statements" string="View Bank Statement(s)"
 | 
			
		||||
                  type="object" class="oe_highlight"
 | 
			
		||||
                  invisible="not context.get('statement_ids')"/>
 | 
			
		||||
          <button name="button_close" type="object" string="Close"/>
 | 
			
		||||
        </footer>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_action_download" model="ir.actions.act_window">
 | 
			
		||||
    <field name="name">EBICS Download Files</field>
 | 
			
		||||
    <field name="type">ir.actions.act_window</field>
 | 
			
		||||
    <field name="res_model">ebics.file</field>
 | 
			
		||||
    <field name="view_type">form</field>
 | 
			
		||||
    <field name="view_mode">tree,form</field>
 | 
			
		||||
    <field name="view_id" eval="False"/>
 | 
			
		||||
    <field name="domain">[('type','=','down')]</field>
 | 
			
		||||
    <field name="search_view_id" ref="ebics_file_view_search"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_action_download_tree" model="ir.actions.act_window.view">
 | 
			
		||||
    <field eval="1" name="sequence"/>
 | 
			
		||||
    <field name="view_mode">tree</field>
 | 
			
		||||
    <field name="view_id" ref="ebics_file_view_tree_download"/>
 | 
			
		||||
    <field name="act_window_id" ref="ebics_file_action_download"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_action_download_form" model="ir.actions.act_window.view">
 | 
			
		||||
    <field eval="2" name="sequence"/>
 | 
			
		||||
    <field name="view_mode">form</field>
 | 
			
		||||
    <field name="view_id" ref="ebics_file_view_form_download"/>
 | 
			
		||||
    <field name="act_window_id" ref="ebics_file_action_download"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <menuitem id="ebics_file_menu_download"
 | 
			
		||||
            name="Download"
 | 
			
		||||
            parent="ebics_file_menu"
 | 
			
		||||
            action="ebics_file_action_download"
 | 
			
		||||
            sequence="31"/>
 | 
			
		||||
 | 
			
		||||
  <!-- Upload -->
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_view_tree_upload" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.file.tree</field>
 | 
			
		||||
    <field name="model">ebics.file</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <tree string="EBICS Files" colors="blue:state=='draft'" create="false">
 | 
			
		||||
        <field name="date" string="Upload Date"/>
 | 
			
		||||
        <field name="name"/>
 | 
			
		||||
        <field name="user_id"/>
 | 
			
		||||
        <field name="state"/>
 | 
			
		||||
        <field name="format_id"/>
 | 
			
		||||
        <field name="company_id" groups="base.group_multi_company"/>
 | 
			
		||||
      </tree>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_view_form_upload" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.file.form</field>
 | 
			
		||||
    <field name="model">ebics.file</field>
 | 
			
		||||
    <field name="priority">1</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS File" create="false">
 | 
			
		||||
        <header>
 | 
			
		||||
          <button name="set_to_draft" states="done" string="Set to Draft" type="object" groups="account.group_account_manager"/>
 | 
			
		||||
          <button name="set_to_done" states="draft" string="Set to Done" type="object" groups="account.group_account_manager"/>
 | 
			
		||||
          <field name="state" widget="statusbar"/>
 | 
			
		||||
        </header>
 | 
			
		||||
        <group colspan="4" col="4">
 | 
			
		||||
          <field name="date" string="Upload Date"/>
 | 
			
		||||
          <field name="name"/>
 | 
			
		||||
          <field name="data" filename="name"/>
 | 
			
		||||
          <field name="format_id"/>
 | 
			
		||||
          <field name="user_id"/>
 | 
			
		||||
          <field name="company_id" groups="base.group_multi_company"/>
 | 
			
		||||
        </group>
 | 
			
		||||
        <notebook>
 | 
			
		||||
          <page string="Additional Information">
 | 
			
		||||
            <field name="note" nolabel="1"/>
 | 
			
		||||
          </page>
 | 
			
		||||
        </notebook>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_action_upload" model="ir.actions.act_window">
 | 
			
		||||
    <field name="name">EBICS Upload Files</field>
 | 
			
		||||
    <field name="type">ir.actions.act_window</field>
 | 
			
		||||
    <field name="res_model">ebics.file</field>
 | 
			
		||||
    <field name="view_type">form</field>
 | 
			
		||||
    <field name="view_mode">tree,form</field>
 | 
			
		||||
    <field name="view_id" eval="False"/>
 | 
			
		||||
    <field name="domain">[('type','=','up')]</field>
 | 
			
		||||
    <field name="search_view_id" ref="ebics_file_view_search"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_action_upload_tree" model="ir.actions.act_window.view">
 | 
			
		||||
    <field eval="1" name="sequence"/>
 | 
			
		||||
    <field name="view_mode">tree</field>
 | 
			
		||||
    <field name="view_id" ref="ebics_file_view_tree_upload"/>
 | 
			
		||||
    <field name="act_window_id" ref="ebics_file_action_upload"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_action_upload_form" model="ir.actions.act_window.view">
 | 
			
		||||
    <field eval="2" name="sequence"/>
 | 
			
		||||
    <field name="view_mode">form</field>
 | 
			
		||||
    <field name="view_id" ref="ebics_file_view_form_upload"/>
 | 
			
		||||
    <field name="act_window_id" ref="ebics_file_action_upload"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <menuitem id="ebics_file_menu_upload"
 | 
			
		||||
            name="Upload"
 | 
			
		||||
            parent="ebics_file_menu"
 | 
			
		||||
            action="ebics_file_action_upload"
 | 
			
		||||
            sequence="31"/>
 | 
			
		||||
 | 
			
		||||
</odoo>
 | 
			
		||||
							
								
								
									
										50
									
								
								account_ebics/views/ebics_file_format.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								account_ebics/views/ebics_file_format.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
<?xml version="1.0" ?>
 | 
			
		||||
<odoo>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_format_view_tree" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.file.format.tree</field>
 | 
			
		||||
    <field name="model">ebics.file.format</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <tree string="EBICS File Formats">
 | 
			
		||||
        <field name="type"/>
 | 
			
		||||
        <field name="order_type"/>
 | 
			
		||||
        <field name="signature_class"/>
 | 
			
		||||
        <field name="name"/>
 | 
			
		||||
        <field name="description"/>
 | 
			
		||||
      </tree>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_format_view_form" model="ir.ui.view">
 | 
			
		||||
    <field name="name">ebics.file.format.form</field>
 | 
			
		||||
    <field name="model">ebics.file.format</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS File Format">
 | 
			
		||||
        <group col="4">
 | 
			
		||||
          <field name="type"/>
 | 
			
		||||
          <field name="order_type"/>
 | 
			
		||||
          <field name="name"/>
 | 
			
		||||
          <field name="signature_class"/>
 | 
			
		||||
          <field name="suffix"/>
 | 
			
		||||
          <newline/>
 | 
			
		||||
          <field name="description"/>
 | 
			
		||||
        </group>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_file_format_action" model="ir.actions.act_window">
 | 
			
		||||
    <field name="name">EBICS File Formats</field>
 | 
			
		||||
    <field name="res_model">ebics.file.format</field>
 | 
			
		||||
    <field name="view_type">form</field>
 | 
			
		||||
    <field name="view_mode">tree,form</field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <menuitem id="ebics_file_format_menu"
 | 
			
		||||
            name="EBICS File Formats"
 | 
			
		||||
            parent="ebics_menu"
 | 
			
		||||
            action="ebics_file_format_action"
 | 
			
		||||
            groups="account_ebics.group_ebics_manager"
 | 
			
		||||
            sequence="20"/>
 | 
			
		||||
 | 
			
		||||
</odoo>
 | 
			
		||||
							
								
								
									
										15
									
								
								account_ebics/views/menuitem.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								account_ebics/views/menuitem.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
<?xml version="1.0" ?>
 | 
			
		||||
<odoo>
 | 
			
		||||
 | 
			
		||||
  <menuitem id="ebics_processing_menu"
 | 
			
		||||
            name="EBICS Processing"
 | 
			
		||||
            parent="account.menu_finance"
 | 
			
		||||
            sequence="4"/>
 | 
			
		||||
 | 
			
		||||
  <menuitem id="ebics_menu"
 | 
			
		||||
            name="EBICS"
 | 
			
		||||
            parent='account.menu_finance_configuration'
 | 
			
		||||
            groups="account_ebics.group_ebics_manager"
 | 
			
		||||
            sequence="100"/>
 | 
			
		||||
 | 
			
		||||
</odoo>
 | 
			
		||||
							
								
								
									
										3
									
								
								account_ebics/wizard/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								account_ebics/wizard/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
from . import ebics_change_passphrase
 | 
			
		||||
from . import ebics_xfer
 | 
			
		||||
							
								
								
									
										78
									
								
								account_ebics/wizard/ebics_change_passphrase.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								account_ebics/wizard/ebics_change_passphrase.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2018 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from odoo import api, fields, models, _
 | 
			
		||||
from odoo.exceptions import UserError
 | 
			
		||||
 | 
			
		||||
_logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import fintech
 | 
			
		||||
    from fintech.ebics import EbicsKeyRing
 | 
			
		||||
    fintech.cryptolib = 'cryptography'
 | 
			
		||||
except ImportError:
 | 
			
		||||
    _logger.warning('Failed to import fintech')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EbicsChangePassphrase(models.TransientModel):
 | 
			
		||||
    _name = 'ebics.change.passphrase'
 | 
			
		||||
    _description = 'Change EBICS keys passphrase'
 | 
			
		||||
 | 
			
		||||
    ebics_config_id = fields.Many2one(
 | 
			
		||||
        comodel_name='ebics.config',
 | 
			
		||||
        string='EBICS Configuration',
 | 
			
		||||
        readonly=True)
 | 
			
		||||
    old_pass = fields.Char(
 | 
			
		||||
        string='Old Passphrase',
 | 
			
		||||
        required=True)
 | 
			
		||||
    new_pass = fields.Char(
 | 
			
		||||
        string='New Passphrase',
 | 
			
		||||
        required=True)
 | 
			
		||||
    new_pass_check = fields.Char(
 | 
			
		||||
        string='New Passphrase (verification)',
 | 
			
		||||
        required=True)
 | 
			
		||||
    note = fields.Text(string='Notes', readonly=True)
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def change_passphrase(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        if self.old_pass != self.ebics_config_id.ebics_passphrase:
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "Incorrect old passphrase."))
 | 
			
		||||
        if self.new_pass != self.new_pass_check:
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "New passphrase verification error."))
 | 
			
		||||
        if self.new_pass == self.ebics_config_id.ebics_passphrase:
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "New passphrase equal to old passphrase."))
 | 
			
		||||
        try:
 | 
			
		||||
            keyring = EbicsKeyRing(
 | 
			
		||||
                keys=self.ebics_config_id.ebics_keys,
 | 
			
		||||
                passphrase=self.ebics_config_id.ebics_passphrase)
 | 
			
		||||
            keyring.change_passphrase(self.new_pass)
 | 
			
		||||
        except ValueError, e:
 | 
			
		||||
            raise UserError(str(e))
 | 
			
		||||
        self.ebics_config_id.ebics_passphrase = self.new_pass
 | 
			
		||||
        self.note = "The EBICS Passphrase has been changed."
 | 
			
		||||
 | 
			
		||||
        module = __name__.split('addons.')[1].split('.')[0]
 | 
			
		||||
        result_view = self.env.ref(
 | 
			
		||||
            '%s.ebics_change_passphrase_view_form_result' % module)
 | 
			
		||||
        return {
 | 
			
		||||
            'name': _('EBICS Keys Change Passphrase'),
 | 
			
		||||
            'res_id': self.id,
 | 
			
		||||
            'view_type': 'form',
 | 
			
		||||
            'view_mode': 'form',
 | 
			
		||||
            'res_model': 'ebics.change.passphrase',
 | 
			
		||||
            'view_id': result_view.id,
 | 
			
		||||
            'target': 'new',
 | 
			
		||||
            'type': 'ir.actions.act_window',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def button_close(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        return {'type': 'ir.actions.act_window_close'}
 | 
			
		||||
							
								
								
									
										39
									
								
								account_ebics/wizard/ebics_change_passphrase.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								account_ebics/wizard/ebics_change_passphrase.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<odoo>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_change_passphrase_view_form" model="ir.ui.view">
 | 
			
		||||
    <field name="name">EBICS Keys Change Passphrase</field>
 | 
			
		||||
    <field name="model">ebics.change.passphrase</field>
 | 
			
		||||
    <field name="priority">1</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS Keys Change Passphrase">
 | 
			
		||||
        <group>
 | 
			
		||||
          <field name="old_pass" password="True"/>
 | 
			
		||||
          <field name="new_pass" password="True"/>
 | 
			
		||||
          <field name="new_pass_check" password="True"/>
 | 
			
		||||
        </group>
 | 
			
		||||
        <footer>
 | 
			
		||||
          <button name="change_passphrase" string="Change Passphrase" type="object" class="oe_highlight"/>
 | 
			
		||||
          or
 | 
			
		||||
          <button string="Cancel" class="oe_link" special="cancel"/>
 | 
			
		||||
        </footer>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_change_passphrase_view_form_result" model="ir.ui.view">
 | 
			
		||||
    <field name="name">EBICS Keys Change Passphrase</field>
 | 
			
		||||
    <field name="model">ebics.change.passphrase</field>
 | 
			
		||||
    <field name="priority">2</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS Keys Change Passphrase">
 | 
			
		||||
        <separator colspan="4" string="Results :" />
 | 
			
		||||
        <field name="note" colspan="4" nolabel="1" width="850" height="400"/>
 | 
			
		||||
        <footer>
 | 
			
		||||
          <button name="button_close" type="object" string="Close"/>
 | 
			
		||||
        </footer>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
</odoo>
 | 
			
		||||
							
								
								
									
										542
									
								
								account_ebics/wizard/ebics_xfer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										542
									
								
								account_ebics/wizard/ebics_xfer.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,542 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2018 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
import logging
 | 
			
		||||
logging.basicConfig(
 | 
			
		||||
    level=logging.DEBUG,
 | 
			
		||||
    format='[%(asctime)s] %(levelname)s - %(name)s: %(message)s')
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
from sys import exc_info
 | 
			
		||||
from traceback import format_exception
 | 
			
		||||
 | 
			
		||||
from odoo import api, fields, models, _
 | 
			
		||||
from odoo.exceptions import UserError
 | 
			
		||||
 | 
			
		||||
_logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import fintech
 | 
			
		||||
    from fintech.ebics import EbicsKeyRing, EbicsBank, EbicsUser, EbicsClient,\
 | 
			
		||||
        EbicsFunctionalError, EbicsTechnicalError, EbicsVerificationError
 | 
			
		||||
    fintech.cryptolib = 'cryptography'
 | 
			
		||||
except ImportError:
 | 
			
		||||
    EbicsBank = object
 | 
			
		||||
    _logger.warning('Failed to import fintech')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EbicsBank(EbicsBank):
 | 
			
		||||
 | 
			
		||||
    def _next_order_id(self, partnerid):
 | 
			
		||||
        """
 | 
			
		||||
        EBICS protocol version H003 requires generation of the OrderID.
 | 
			
		||||
        The OrderID must be a string between 'A000' and 'ZZZZ' and
 | 
			
		||||
        unique for each partner id.
 | 
			
		||||
        """
 | 
			
		||||
        return hasattr(self, '_order_number') and self._order_number or 'A000'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EbicsXfer(models.TransientModel):
 | 
			
		||||
    _name = 'ebics.xfer'
 | 
			
		||||
    _description = 'EBICS file transfer'
 | 
			
		||||
 | 
			
		||||
    ebics_config_id = fields.Many2one(
 | 
			
		||||
        comodel_name='ebics.config',
 | 
			
		||||
        string='EBICS Configuration',
 | 
			
		||||
        domain=[('state', '=', 'active')],
 | 
			
		||||
        required=True,
 | 
			
		||||
        default=lambda self: self._default_ebics_config_id())
 | 
			
		||||
    ebics_passphrase = fields.Char(
 | 
			
		||||
        string='EBICS Passphrase')
 | 
			
		||||
    date_from = fields.Date()
 | 
			
		||||
    date_to = fields.Date()
 | 
			
		||||
    upload_data = fields.Binary(string='File to Upload')
 | 
			
		||||
    upload_fname = fields.Char(
 | 
			
		||||
        string='Upload Filename', default='')
 | 
			
		||||
    upload_fname_dummy = fields.Char(
 | 
			
		||||
        related='upload_fname', string='Upload Filename', readonly=True)
 | 
			
		||||
    format_id = fields.Many2one(
 | 
			
		||||
        comodel_name='ebics.file.format',
 | 
			
		||||
        string='EBICS File Format',
 | 
			
		||||
        help="Select EBICS File Format to upload/download."
 | 
			
		||||
             "\nLeave blank to download all available files.")
 | 
			
		||||
    order_type = fields.Selection(
 | 
			
		||||
        selection=lambda self: self._selection_order_type(),
 | 
			
		||||
        string='Order Type',
 | 
			
		||||
        help="For most banks is France you should use the "
 | 
			
		||||
             "format neutral Order Types 'FUL' for upload "
 | 
			
		||||
             "and 'FDL' for download.")
 | 
			
		||||
    test_mode = fields.Boolean(
 | 
			
		||||
        string='Test Mode',
 | 
			
		||||
        help="Select this option to test if the syntax of "
 | 
			
		||||
             "the upload file is correct."
 | 
			
		||||
             "\nThis option is only available for "
 | 
			
		||||
             "Order Type 'FUL'.")
 | 
			
		||||
    note = fields.Text(string='EBICS file transfer Log', readonly=True)
 | 
			
		||||
 | 
			
		||||
    @api.model
 | 
			
		||||
    def _default_ebics_config_id(self):
 | 
			
		||||
        cfg_mod = self.env['ebics.config']
 | 
			
		||||
        cfg = cfg_mod.search(
 | 
			
		||||
            [('company_id', '=', self.env.user.company_id.id),
 | 
			
		||||
             ('state', '=', 'active')])
 | 
			
		||||
        if cfg and len(cfg) == 1:
 | 
			
		||||
            return cfg
 | 
			
		||||
        else:
 | 
			
		||||
            return cfg_mod
 | 
			
		||||
 | 
			
		||||
    @api.model
 | 
			
		||||
    def _selection_order_type(self):
 | 
			
		||||
        return self.env['ebics.file.format']._selection_order_type()
 | 
			
		||||
 | 
			
		||||
    @api.onchange('ebics_config_id')
 | 
			
		||||
    def _onchange_ebics_config_id(self):
 | 
			
		||||
        domain = {}
 | 
			
		||||
        if self._context.get('ebics_download'):
 | 
			
		||||
            download_formats = self.ebics_config_id.ebics_file_format_ids\
 | 
			
		||||
                .filtered(lambda r: r.type == 'down')
 | 
			
		||||
            if len(download_formats) == 1:
 | 
			
		||||
                self.format_id = download_formats
 | 
			
		||||
            domain['format_id'] = [('type', '=', 'down'),
 | 
			
		||||
                                   ('id', 'in', download_formats.ids)]
 | 
			
		||||
        else:
 | 
			
		||||
            upload_formats = self.ebics_config_id.ebics_file_format_ids\
 | 
			
		||||
                .filtered(lambda r: r.type == 'up')
 | 
			
		||||
            if len(upload_formats) == 1:
 | 
			
		||||
                self.format_id = upload_formats
 | 
			
		||||
            domain['format_id'] = [('type', '=', 'up'),
 | 
			
		||||
                                   ('id', 'in', upload_formats.ids)]
 | 
			
		||||
        return {'domain': domain}
 | 
			
		||||
 | 
			
		||||
    @api.onchange('upload_data')
 | 
			
		||||
    def _onchange_upload_data(self):
 | 
			
		||||
        self.upload_fname_dummy = self.upload_fname
 | 
			
		||||
        self._detect_upload_format()
 | 
			
		||||
        upload_formats = self.format_id \
 | 
			
		||||
            or self.ebics_config_id.ebics_file_format_ids.filtered(
 | 
			
		||||
                lambda r: r.type == 'up')
 | 
			
		||||
        if len(upload_formats) == 1:
 | 
			
		||||
            self.format_id = upload_formats
 | 
			
		||||
 | 
			
		||||
    @api.onchange('format_id')
 | 
			
		||||
    def _onchange_format_id(self):
 | 
			
		||||
        self.order_type = self.format_id.order_type
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def ebics_upload(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        ctx = self._context.copy()
 | 
			
		||||
        ebics_file = self._ebics_upload()
 | 
			
		||||
        if ebics_file:
 | 
			
		||||
            ctx['ebics_file_id'] = ebics_file.id
 | 
			
		||||
        module = __name__.split('addons.')[1].split('.')[0]
 | 
			
		||||
        result_view = self.env.ref(
 | 
			
		||||
            '%s.ebics_xfer_view_form_result' % module)
 | 
			
		||||
        return {
 | 
			
		||||
            'name': _('EBICS file transfer result'),
 | 
			
		||||
            'res_id': self.id,
 | 
			
		||||
            'view_type': 'form',
 | 
			
		||||
            'view_mode': 'form',
 | 
			
		||||
            'res_model': 'ebics.xfer',
 | 
			
		||||
            'view_id': result_view.id,
 | 
			
		||||
            'target': 'new',
 | 
			
		||||
            'context': ctx,
 | 
			
		||||
            'type': 'ir.actions.act_window',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def ebics_download(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        self.ebics_config_id._check_ebics_files()
 | 
			
		||||
        ctx = self._context.copy()
 | 
			
		||||
        self.note = ''
 | 
			
		||||
        client = self._setup_client()
 | 
			
		||||
        if client:
 | 
			
		||||
            download_formats = self.format_id \
 | 
			
		||||
                or self.ebics_config_id.ebics_file_format_ids.filtered(
 | 
			
		||||
                    lambda r: r.type == 'down')
 | 
			
		||||
            ebics_files = self.env['ebics.file']
 | 
			
		||||
            for df in download_formats:
 | 
			
		||||
                success = False
 | 
			
		||||
                order_type = df.order_type or 'FDL'
 | 
			
		||||
                params = {}
 | 
			
		||||
                if order_type == 'FDL':
 | 
			
		||||
                    params['filetype'] = df.name
 | 
			
		||||
                if order_type in ['FDL', 'C53']:
 | 
			
		||||
                    params.update({
 | 
			
		||||
                        'start': self.date_from or None,
 | 
			
		||||
                        'end': self.date_to or None,
 | 
			
		||||
                    })
 | 
			
		||||
                kwargs = {k: v for k, v in params.items() if v}
 | 
			
		||||
                try:
 | 
			
		||||
                    method = getattr(client, order_type)
 | 
			
		||||
                    data = method(**kwargs)
 | 
			
		||||
                    ebics_files += self._handle_download_data(data, df)
 | 
			
		||||
                    success = True
 | 
			
		||||
                except EbicsFunctionalError:
 | 
			
		||||
                    e = exc_info()
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += _("EBICS Functional Error:")
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += '%s (code: %s)' % (e[1].message, e[1].code)
 | 
			
		||||
                except EbicsTechnicalError:
 | 
			
		||||
                    e = exc_info()
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += _("EBICS Technical Error:")
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += '%s (code: %s)' % (e[1].message, e[1].code)
 | 
			
		||||
                except EbicsVerificationError:
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += _("EBICS Verification Error:")
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += _("The EBICS response could not be verified.")
 | 
			
		||||
                except UserError, e:
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += _("Warning:")
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += e.message
 | 
			
		||||
                except:
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += _("Unknown Error")
 | 
			
		||||
                    tb = ''.join(format_exception(*exc_info()))
 | 
			
		||||
                    self.note += '\n%s' % tb
 | 
			
		||||
                else:
 | 
			
		||||
                    # mark received data so that it is not included in further
 | 
			
		||||
                    # downloads
 | 
			
		||||
                    trans_id = client.last_trans_id
 | 
			
		||||
                    client.confirm_download(trans_id=trans_id, success=success)
 | 
			
		||||
 | 
			
		||||
            ctx['ebics_file_ids'] = ebics_files._ids
 | 
			
		||||
 | 
			
		||||
            if ebics_files:
 | 
			
		||||
                self.note += '\n'
 | 
			
		||||
                for f in ebics_files:
 | 
			
		||||
                    self.note += _(
 | 
			
		||||
                        "EBICS File '%s' is available for further processing."
 | 
			
		||||
                    ) % f.name
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
 | 
			
		||||
        module = __name__.split('addons.')[1].split('.')[0]
 | 
			
		||||
        result_view = self.env.ref(
 | 
			
		||||
            '%s.ebics_xfer_view_form_result' % module)
 | 
			
		||||
        return {
 | 
			
		||||
            'name': _('EBICS file transfer result'),
 | 
			
		||||
            'res_id': self.id,
 | 
			
		||||
            'view_type': 'form',
 | 
			
		||||
            'view_mode': 'form',
 | 
			
		||||
            'res_model': 'ebics.xfer',
 | 
			
		||||
            'view_id': result_view.id,
 | 
			
		||||
            'target': 'new',
 | 
			
		||||
            'context': ctx,
 | 
			
		||||
            'type': 'ir.actions.act_window',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def button_close(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        return {'type': 'ir.actions.act_window_close'}
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def view_ebics_file(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        module = __name__.split('addons.')[1].split('.')[0]
 | 
			
		||||
        act = self.env['ir.actions.act_window'].for_xml_id(
 | 
			
		||||
            module, 'ebics_file_action_download')
 | 
			
		||||
        act['domain'] = [('id', 'in', self._context['ebics_file_ids'])]
 | 
			
		||||
        return act
 | 
			
		||||
 | 
			
		||||
    def _ebics_upload(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        ebics_file = self.env['ebics.file']
 | 
			
		||||
        self.note = ''
 | 
			
		||||
        client = self._setup_client()
 | 
			
		||||
        if client:
 | 
			
		||||
            upload_data = base64.decodestring(self.upload_data)
 | 
			
		||||
            ef_format = self.format_id
 | 
			
		||||
            OrderID = False
 | 
			
		||||
            try:
 | 
			
		||||
                order_type = ef_format.order_type or 'FUL'
 | 
			
		||||
                method = hasattr(client, order_type) \
 | 
			
		||||
                    and getattr(client, order_type)
 | 
			
		||||
                if order_type == 'FUL':
 | 
			
		||||
                    kwargs = {}
 | 
			
		||||
                    # bank = self.ebics_config_id.bank_id.bank v8.0
 | 
			
		||||
                    bank = self.ebics_config_id.bank_id.bank_id
 | 
			
		||||
                    if bank.country:
 | 
			
		||||
                        kwargs['country'] = bank.country.code
 | 
			
		||||
                    if self.test_mode:
 | 
			
		||||
                        kwargs['TEST'] = 'TRUE'
 | 
			
		||||
                    OrderID = method(ef_format.name, upload_data, **kwargs)
 | 
			
		||||
                elif order_type in ['CCT', 'CDD', 'CDB']:
 | 
			
		||||
                    OrderID = method(upload_data)
 | 
			
		||||
                elif order_type in ['XE2', 'XE3']:
 | 
			
		||||
                    OrderID = client.upload(order_type, upload_data)
 | 
			
		||||
                else:
 | 
			
		||||
                    # TODO: investigate if it makes sense to support
 | 
			
		||||
                    # a generic upload for a non-predefined order_type
 | 
			
		||||
                    pass
 | 
			
		||||
                if OrderID:
 | 
			
		||||
                    self.note += '\n'
 | 
			
		||||
                    self.note += _(
 | 
			
		||||
                        "EBICS File has been uploaded (OrderID %s)."
 | 
			
		||||
                    ) % OrderID
 | 
			
		||||
                    ef_note = _("EBICS OrderID: %s") % OrderID
 | 
			
		||||
                    if self._context.get('origin'):
 | 
			
		||||
                        ef_note += '\n' + _(
 | 
			
		||||
                            "Origin: %s") % self._context['origin']
 | 
			
		||||
                    suffix = self.format_id.suffix
 | 
			
		||||
                    fn = self.upload_fname
 | 
			
		||||
                    if not fn.endswith(suffix):
 | 
			
		||||
                        fn = '.'.join[fn, suffix]
 | 
			
		||||
                    ef_vals = {
 | 
			
		||||
                        'name': self.upload_fname,
 | 
			
		||||
                        'data': self.upload_data,
 | 
			
		||||
                        'date': fields.Datetime.now(),
 | 
			
		||||
                        'format_id': self.format_id.id,
 | 
			
		||||
                        'state': 'done',
 | 
			
		||||
                        'user_id': self._uid,
 | 
			
		||||
                        'note': ef_note,
 | 
			
		||||
                    }
 | 
			
		||||
                    self._update_ef_vals(ef_vals)
 | 
			
		||||
                    ebics_file = self.env['ebics.file'].create(ef_vals)
 | 
			
		||||
 | 
			
		||||
            except EbicsFunctionalError:
 | 
			
		||||
                e = exc_info()
 | 
			
		||||
                self.note += '\n'
 | 
			
		||||
                self.note += _("EBICS Functional Error:")
 | 
			
		||||
                self.note += '\n'
 | 
			
		||||
                self.note += '%s (code: %s)' % (e[1].message, e[1].code)
 | 
			
		||||
            except EbicsTechnicalError:
 | 
			
		||||
                e = exc_info()
 | 
			
		||||
                self.note += '\n'
 | 
			
		||||
                self.note += _("EBICS Technical Error:")
 | 
			
		||||
                self.note += '\n'
 | 
			
		||||
                self.note += '%s (code: %s)' % (e[1].message, e[1].code)
 | 
			
		||||
            except EbicsVerificationError:
 | 
			
		||||
                self.note += '\n'
 | 
			
		||||
                self.note += _("EBICS Verification Error:")
 | 
			
		||||
                self.note += '\n'
 | 
			
		||||
                self.note += _("The EBICS response could not be verified.")
 | 
			
		||||
            except:
 | 
			
		||||
                self.note += '\n'
 | 
			
		||||
                self.note += _("Unknown Error")
 | 
			
		||||
                tb = ''.join(format_exception(*exc_info()))
 | 
			
		||||
                self.note += '\n%s' % tb
 | 
			
		||||
 | 
			
		||||
            if self.ebics_config_id.ebics_version == 'H003' and OrderID:
 | 
			
		||||
                self.ebics_config_id._update_order_number(OrderID)
 | 
			
		||||
 | 
			
		||||
        return ebics_file
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def _setup_client(self):
 | 
			
		||||
        self.ebics_config_id._check_ebics_keys()
 | 
			
		||||
        passphrase = self._get_passphrase()
 | 
			
		||||
        keyring = EbicsKeyRing(
 | 
			
		||||
            keys=self.ebics_config_id.ebics_keys,
 | 
			
		||||
            passphrase=passphrase)
 | 
			
		||||
 | 
			
		||||
        bank = EbicsBank(
 | 
			
		||||
            keyring=keyring,
 | 
			
		||||
            hostid=self.ebics_config_id.ebics_host,
 | 
			
		||||
            url=self.ebics_config_id.ebics_url)
 | 
			
		||||
        if self.ebics_config_id.ebics_version == 'H003':
 | 
			
		||||
            bank._order_number = self.ebics_config_id._get_order_number()
 | 
			
		||||
 | 
			
		||||
        user = EbicsUser(
 | 
			
		||||
            keyring=keyring,
 | 
			
		||||
            partnerid=self.ebics_config_id.ebics_partner,
 | 
			
		||||
            userid=self.ebics_config_id.ebics_user)
 | 
			
		||||
        signature_class = self.format_id.signature_class \
 | 
			
		||||
            or self.ebics_config_id.signature_class
 | 
			
		||||
        if signature_class == 'T':
 | 
			
		||||
            user.manual_approval = True
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            client = EbicsClient(
 | 
			
		||||
                bank, user, version=self.ebics_config_id.ebics_version)
 | 
			
		||||
        except:
 | 
			
		||||
            self.note += '\n'
 | 
			
		||||
            self.note += _("Unknown Error")
 | 
			
		||||
            tb = ''.join(format_exception(*exc_info()))
 | 
			
		||||
            self.note += '\n%s' % tb
 | 
			
		||||
            client = False
 | 
			
		||||
 | 
			
		||||
        return client
 | 
			
		||||
 | 
			
		||||
    def _get_passphrase(self):
 | 
			
		||||
        passphrase = self.ebics_config_id.ebics_passphrase
 | 
			
		||||
 | 
			
		||||
        if passphrase:
 | 
			
		||||
            return passphrase
 | 
			
		||||
 | 
			
		||||
        module = __name__.split('addons.')[1].split('.')[0]
 | 
			
		||||
        passphrase_view = self.env.ref(
 | 
			
		||||
            '%s.ebics_xfer_view_form_passphrase' % module)
 | 
			
		||||
        return {
 | 
			
		||||
            'name': _('EBICS file transfer'),
 | 
			
		||||
            'res_id': self.id,
 | 
			
		||||
            'view_type': 'form',
 | 
			
		||||
            'view_mode': 'form',
 | 
			
		||||
            'res_model': 'ebics.xfer',
 | 
			
		||||
            'view_id': passphrase_view.id,
 | 
			
		||||
            'target': 'new',
 | 
			
		||||
            'context': self._context,
 | 
			
		||||
            'type': 'ir.actions.act_window',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def _file_format_methods(self):
 | 
			
		||||
        """
 | 
			
		||||
        Extend this dictionary in order to add support
 | 
			
		||||
        for extra file formats.
 | 
			
		||||
        """
 | 
			
		||||
        res = {
 | 
			
		||||
            'camt.xxx.cfonb120.stm': self._handle_cfonb120,
 | 
			
		||||
            'camt.053.001.02.stm': self._handle_camt053,
 | 
			
		||||
        }
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def _update_ef_vals(self, ef_vals):
 | 
			
		||||
        """
 | 
			
		||||
        Adapt this method to customize the EBICS File values.
 | 
			
		||||
        """
 | 
			
		||||
        if self.format_id and self.format_id.type == 'up':
 | 
			
		||||
            fn = ef_vals['name']
 | 
			
		||||
            dups = self._check_duplicate_ebics_file(
 | 
			
		||||
                fn, self.format_id)
 | 
			
		||||
            if dups:
 | 
			
		||||
                n = 1
 | 
			
		||||
                fn = '_'.join([fn, str(n)])
 | 
			
		||||
                while self._check_duplicate_ebics_file(fn, self.format_id):
 | 
			
		||||
                    n += 1
 | 
			
		||||
                    fn = '_'.join([fn, str(n)])
 | 
			
		||||
                ef_vals['name'] = fn
 | 
			
		||||
 | 
			
		||||
    def _handle_download_data(self, data, file_format):
 | 
			
		||||
        ebics_files = self.env['ebics.file']
 | 
			
		||||
        if isinstance(data, dict):
 | 
			
		||||
            for doc in data:
 | 
			
		||||
                ebics_files += self._create_ebics_file(
 | 
			
		||||
                    data[doc], file_format, docname=doc)
 | 
			
		||||
        else:
 | 
			
		||||
            ebics_files += self._create_ebics_file(data, file_format)
 | 
			
		||||
        return ebics_files
 | 
			
		||||
 | 
			
		||||
    def _create_ebics_file(self, data, file_format, docname=None):
 | 
			
		||||
        """
 | 
			
		||||
        Write the data as received over the EBICS connection
 | 
			
		||||
        to a temporary file so that is is available for
 | 
			
		||||
        analysis (e.g. in case formats are received that cannot
 | 
			
		||||
        be handled in the current version of this module).
 | 
			
		||||
 | 
			
		||||
        TODO: add code to clean-up /tmp on a regular basis.
 | 
			
		||||
 | 
			
		||||
        After saving the data received we call the method to perform
 | 
			
		||||
        file format specific processing.
 | 
			
		||||
        """
 | 
			
		||||
        ebics_files_root = self.ebics_config_id.ebics_files
 | 
			
		||||
        tmp_dir = os.path.normpath(ebics_files_root + '/tmp')
 | 
			
		||||
        if not os.path.isdir(tmp_dir):
 | 
			
		||||
            os.makedirs(tmp_dir, 0700)
 | 
			
		||||
        fn_parts = [self.ebics_config_id.ebics_host]
 | 
			
		||||
        if docname:
 | 
			
		||||
            fn_parts.append(docname)
 | 
			
		||||
        else:
 | 
			
		||||
            fn_date = self.date_to or fields.Date.today()
 | 
			
		||||
            fn_parts.append(fn_date)
 | 
			
		||||
        base_fn = '_'.join(fn_parts)
 | 
			
		||||
        n = 1
 | 
			
		||||
        full_tmp_fn = os.path.normpath(tmp_dir + '/' + base_fn)
 | 
			
		||||
        while os.path.exists(full_tmp_fn):
 | 
			
		||||
            n += 1
 | 
			
		||||
            tmp_fn = base_fn + '_' + str(n).rjust(3, '0')
 | 
			
		||||
            full_tmp_fn = os.path.normpath(tmp_dir + '/' + tmp_fn)
 | 
			
		||||
 | 
			
		||||
        with open(full_tmp_fn, 'wb') as f:
 | 
			
		||||
            f.write(data)
 | 
			
		||||
 | 
			
		||||
        ff_methods = self._file_format_methods()
 | 
			
		||||
        if file_format.name in ff_methods:
 | 
			
		||||
            data = ff_methods[file_format.name](data)
 | 
			
		||||
 | 
			
		||||
        fn = '.'.join([base_fn, file_format.suffix])
 | 
			
		||||
        dups = self._check_duplicate_ebics_file(fn, file_format)
 | 
			
		||||
        if dups:
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "EBICS File with name '%s' has already been downloaded."
 | 
			
		||||
                "\nPlease check this file and rename in case there is "
 | 
			
		||||
                "no risk on duplicate transactions.")
 | 
			
		||||
                % fn)
 | 
			
		||||
        data = base64.encodestring(data)
 | 
			
		||||
        ef_vals = {
 | 
			
		||||
            'name': fn,
 | 
			
		||||
            'data': data,
 | 
			
		||||
            'date': fields.Datetime.now(),
 | 
			
		||||
            'date_from': self.date_from,
 | 
			
		||||
            'date_to': self.date_from,
 | 
			
		||||
            'format_id': file_format.id,
 | 
			
		||||
            'user_id': self._uid,
 | 
			
		||||
        }
 | 
			
		||||
        self._update_ef_vals(ef_vals)
 | 
			
		||||
        ebics_file = self.env['ebics.file'].create(ef_vals)
 | 
			
		||||
        return ebics_file
 | 
			
		||||
 | 
			
		||||
    def _check_duplicate_ebics_file(self, fn, file_format):
 | 
			
		||||
        dups = self.env['ebics.file'].search(
 | 
			
		||||
            [('name', '=', fn),
 | 
			
		||||
             ('format_id', '=', file_format.id),
 | 
			
		||||
             '|',
 | 
			
		||||
             ('company_id', '=', self.env.user.company_id.id),
 | 
			
		||||
             ('company_id', '=', False)])
 | 
			
		||||
        return dups
 | 
			
		||||
 | 
			
		||||
    def _detect_upload_format(self):
 | 
			
		||||
        """
 | 
			
		||||
        Use this method in order to automatically detect and set the
 | 
			
		||||
        EBICS upload file format.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def _update_order_number(self, OrderID):
 | 
			
		||||
        o_list = list(OrderID)
 | 
			
		||||
        for i, c in enumerate(reversed(o_list), start=1):
 | 
			
		||||
            if c == '9':
 | 
			
		||||
                o_list[-i] = 'A'
 | 
			
		||||
                break
 | 
			
		||||
            if c == 'Z':
 | 
			
		||||
                continue
 | 
			
		||||
            else:
 | 
			
		||||
                o_list[-i] = chr(ord(c) + 1)
 | 
			
		||||
                break
 | 
			
		||||
        next = ''.join(o_list)
 | 
			
		||||
        if next == 'ZZZZ':
 | 
			
		||||
            next = 'A000'
 | 
			
		||||
        self.ebics_config_id.order_number = next
 | 
			
		||||
 | 
			
		||||
    def _insert_line_terminator(self, data_in, line_len):
 | 
			
		||||
        data_out = ''
 | 
			
		||||
        max = len(data_in)
 | 
			
		||||
        i = 0
 | 
			
		||||
        while i + line_len <= max:
 | 
			
		||||
            data_out += data_in[i:i + line_len] + '\n'
 | 
			
		||||
            i += line_len
 | 
			
		||||
        return data_out
 | 
			
		||||
 | 
			
		||||
    def _handle_cfonb120(self, data_in):
 | 
			
		||||
        return self._insert_line_terminator(data_in, 120)
 | 
			
		||||
 | 
			
		||||
    def _handle_cfonb240(self, data_in):
 | 
			
		||||
        return self._insert_line_terminator(data_in, 240)
 | 
			
		||||
 | 
			
		||||
    def _handle_camt053(self, data_in):
 | 
			
		||||
        """
 | 
			
		||||
        Use this method if you need to fix camt files received
 | 
			
		||||
        from your bank before passing them to the
 | 
			
		||||
        Odoo Enterprise or Community CAMT parser.
 | 
			
		||||
        """
 | 
			
		||||
        return data_in
 | 
			
		||||
							
								
								
									
										94
									
								
								account_ebics/wizard/ebics_xfer.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								account_ebics/wizard/ebics_xfer.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<odoo>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_xfer_view_form_download" model="ir.ui.view">
 | 
			
		||||
    <field name="name">EBICS File Download</field>
 | 
			
		||||
    <field name="model">ebics.xfer</field>
 | 
			
		||||
    <field name="priority">1</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS File Download">
 | 
			
		||||
        <group>
 | 
			
		||||
          <separator string="Select your bank :" colspan="2"/>
 | 
			
		||||
          <field name="ebics_config_id" options="{'no_create': True, 'no_open': True}"/>
 | 
			
		||||
          <field name="date_from"/>
 | 
			
		||||
          <field name="date_to"/>
 | 
			
		||||
          <field name="format_id"/>
 | 
			
		||||
        </group>
 | 
			
		||||
        <footer>
 | 
			
		||||
          <button name="ebics_download" string="Download Files" type="object" class="oe_highlight"/>
 | 
			
		||||
          or
 | 
			
		||||
          <button string="Cancel" class="oe_link" special="cancel"/>
 | 
			
		||||
        </footer>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_xfer_view_form_upload" model="ir.ui.view">
 | 
			
		||||
    <field name="name">EBICS File Upload</field>
 | 
			
		||||
    <field name="model">ebics.xfer</field>
 | 
			
		||||
    <field name="priority">1</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS File Upload">
 | 
			
		||||
        <group>
 | 
			
		||||
          <separator string="Select your bank :" colspan="2"/>
 | 
			
		||||
          <field name="ebics_config_id" options="{'no_create': True, 'no_open': True}"/>
 | 
			
		||||
          <separator string="Select your file :" colspan="2"/>
 | 
			
		||||
          <field name="upload_data" filename="upload_fname" required="1"/>
 | 
			
		||||
          <field name="upload_fname" invisible="1"/>
 | 
			
		||||
          <field name="upload_fname_dummy"/>
 | 
			
		||||
          <field name="format_id" required="1"/>
 | 
			
		||||
          <field name="order_type"/>
 | 
			
		||||
          <field name="test_mode" attrs="{'invisible': [('order_type', '!=', 'FUL')]}"/>
 | 
			
		||||
        </group>
 | 
			
		||||
        <footer>
 | 
			
		||||
          <button name="ebics_upload" string="Upload File" type="object" class="oe_highlight"/>
 | 
			
		||||
          or
 | 
			
		||||
          <button string="Cancel" class="oe_link" special="cancel"/>
 | 
			
		||||
        </footer>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_xfer_view_form_result" model="ir.ui.view">
 | 
			
		||||
    <field name="name">EBICS File Transfer</field>
 | 
			
		||||
    <field name="model">ebics.xfer</field>
 | 
			
		||||
    <field name="priority">2</field>
 | 
			
		||||
    <field name="arch" type="xml">
 | 
			
		||||
      <form string="EBICS File Transfer">
 | 
			
		||||
        <separator colspan="4" string="Results :" />
 | 
			
		||||
        <field name="note" colspan="4" nolabel="1" width="850" height="400"/>
 | 
			
		||||
        <footer>
 | 
			
		||||
          <button name="view_ebics_file" type="object" string="View EBICS File(s)" class="oe_highlight"
 | 
			
		||||
                  invisible="not context.get('ebics_file_ids')"/>
 | 
			
		||||
          <button name="button_close" type="object" string="Close"/>
 | 
			
		||||
        </footer>
 | 
			
		||||
      </form>
 | 
			
		||||
    </field>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_xfer_action_download" model="ir.actions.act_window">
 | 
			
		||||
    <field name="name">EBICS File Transfer</field>
 | 
			
		||||
    <field name="type">ir.actions.act_window</field>
 | 
			
		||||
    <field name="res_model">ebics.xfer</field>
 | 
			
		||||
    <field name="view_type">form</field>
 | 
			
		||||
    <field name="view_mode">form</field>
 | 
			
		||||
    <field name="target">new</field>
 | 
			
		||||
    <field name="context">{'ebics_download': 1}</field>
 | 
			
		||||
    <field name="view_id" ref="ebics_xfer_view_form_download"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <record id="ebics_xfer_action_upload" model="ir.actions.act_window">
 | 
			
		||||
    <field name="name">EBICS File Transfer</field>
 | 
			
		||||
    <field name="type">ir.actions.act_window</field>
 | 
			
		||||
    <field name="res_model">ebics.xfer</field>
 | 
			
		||||
    <field name="view_type">form</field>
 | 
			
		||||
    <field name="view_mode">form</field>
 | 
			
		||||
    <field name="target">new</field>
 | 
			
		||||
    <field name="context">{'ebics_upload': 1}</field>
 | 
			
		||||
    <field name="view_id" ref="ebics_xfer_view_form_upload"/>
 | 
			
		||||
  </record>
 | 
			
		||||
 | 
			
		||||
  <menuitem id="ebics_xfer_menu_download" name="EBICS Download" parent="ebics_processing_menu" action="ebics_xfer_action_download" sequence="10"/>
 | 
			
		||||
  <menuitem id="ebics_xfer_menu_upload" name="EBICS Upload" parent="ebics_processing_menu" action="ebics_xfer_action_upload" sequence="20"/>
 | 
			
		||||
 | 
			
		||||
</odoo>
 | 
			
		||||
							
								
								
									
										23
									
								
								account_ebics_payment_order/README.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								account_ebics_payment_order/README.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
 | 
			
		||||
   :target: https://www.gnu.org/licenses/agpl
 | 
			
		||||
   :alt: License: AGPL-3
 | 
			
		||||
 | 
			
		||||
==============================
 | 
			
		||||
Upload Payment Order via EBICS
 | 
			
		||||
==============================
 | 
			
		||||
 | 
			
		||||
This module allows to upload a Payment Order to the bank via the EBICS protocol.
 | 
			
		||||
 | 
			
		||||
Installation
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
This module depends upon the following modules (cf. apps.odoo.com):
 | 
			
		||||
 | 
			
		||||
- account_ebics
 | 
			
		||||
- account_payment_order
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
=====
 | 
			
		||||
 | 
			
		||||
Create your Payment Order and generate the bank file.
 | 
			
		||||
Upload the generated file via the 'EBICS Upload' button on the payment order.
 | 
			
		||||
							
								
								
									
										5
									
								
								account_ebics_payment_order/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								account_ebics_payment_order/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2017 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
from . import models
 | 
			
		||||
							
								
								
									
										18
									
								
								account_ebics_payment_order/__manifest__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								account_ebics_payment_order/__manifest__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2017 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    'name': 'Upload Payment Order via EBICS',
 | 
			
		||||
    'version': '10.0.1.0.0',
 | 
			
		||||
    'license': 'AGPL-3',
 | 
			
		||||
    'author': 'Noviat',
 | 
			
		||||
    'category': 'Accounting & Finance',
 | 
			
		||||
    'depends': [
 | 
			
		||||
        'account_ebics',
 | 
			
		||||
        'account_payment_order'],
 | 
			
		||||
    'data': [
 | 
			
		||||
        'views/account_payment_order.xml',
 | 
			
		||||
    ],
 | 
			
		||||
    'installable': True,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								account_ebics_payment_order/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								account_ebics_payment_order/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
from . import account_payment_order
 | 
			
		||||
							
								
								
									
										51
									
								
								account_ebics_payment_order/models/account_payment_order.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								account_ebics_payment_order/models/account_payment_order.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright 2009-2017 Noviat.
 | 
			
		||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
			
		||||
 | 
			
		||||
from odoo import api, models, _
 | 
			
		||||
from odoo.exceptions import UserError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AccountPaymentOrder(models.Model):
 | 
			
		||||
    _inherit = 'account.payment.order'
 | 
			
		||||
 | 
			
		||||
    @api.multi
 | 
			
		||||
    def ebics_upload(self):
 | 
			
		||||
        self.ensure_one()
 | 
			
		||||
        ctx = self._context.copy()
 | 
			
		||||
        attach = self.env['ir.attachment'].search(
 | 
			
		||||
            [('res_model', '=', self._name),
 | 
			
		||||
             ('res_id', '=', self.id)])
 | 
			
		||||
        if not attach:
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "This payment order doesn't contains attachements."
 | 
			
		||||
                "\nPlease generate first the Payment Order file first."))
 | 
			
		||||
        elif len(attach) > 1:
 | 
			
		||||
            raise UserError(_(
 | 
			
		||||
                "This payment order contains multiple attachments."
 | 
			
		||||
                "\nPlease remove the obsolete attachments or upload "
 | 
			
		||||
                "the payment order file via the "
 | 
			
		||||
                "EBICS Processing > EBICS Upload menu"))
 | 
			
		||||
        else:
 | 
			
		||||
            origin = _("Payment Order") + ': ' + self.name
 | 
			
		||||
            ctx.update({
 | 
			
		||||
                'default_upload_data': attach.datas,
 | 
			
		||||
                'default_upload_fname': attach.datas_fname,
 | 
			
		||||
                'origin': origin,
 | 
			
		||||
            })
 | 
			
		||||
            ebics_xfer = self.env['ebics.xfer'].with_context(ctx).create({})
 | 
			
		||||
            ebics_xfer._onchange_upload_data()
 | 
			
		||||
            ebics_xfer._onchange_format_id()
 | 
			
		||||
            view = self.env.ref('account_ebics.ebics_xfer_view_form_upload')
 | 
			
		||||
            act = {
 | 
			
		||||
                'name': _('EBICS Upload'),
 | 
			
		||||
                'view_type': 'form',
 | 
			
		||||
                'view_mode': 'form',
 | 
			
		||||
                'res_model': 'ebics.xfer',
 | 
			
		||||
                'view_id': view.id,
 | 
			
		||||
                'res_id': ebics_xfer.id,
 | 
			
		||||
                'type': 'ir.actions.act_window',
 | 
			
		||||
                'target': 'new',
 | 
			
		||||
                'context': ctx,
 | 
			
		||||
            }
 | 
			
		||||
            return act
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								account_ebics_payment_order/static/description/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								account_ebics_payment_order/static/description/icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 17 KiB  | 
							
								
								
									
										18
									
								
								account_ebics_payment_order/views/account_payment_order.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								account_ebics_payment_order/views/account_payment_order.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<odoo>
 | 
			
		||||
  <data>
 | 
			
		||||
 | 
			
		||||
    <record id="account_payment_order_form" model="ir.ui.view">
 | 
			
		||||
      <field name="name">account.payment.order.form</field>
 | 
			
		||||
      <field name="model">account.payment.order</field>
 | 
			
		||||
      <field name="inherit_id" ref="account_payment_order.account_payment_order_form"/>
 | 
			
		||||
      <field name="arch" type="xml">
 | 
			
		||||
        <button name="open2generated" position="after">
 | 
			
		||||
          <button name="ebics_upload" type="object" states="generated"
 | 
			
		||||
                  string="EBICS Upload" class="oe_highlight"/>
 | 
			
		||||
        </button>
 | 
			
		||||
      </field>
 | 
			
		||||
    </record>
 | 
			
		||||
 | 
			
		||||
  </data>
 | 
			
		||||
</odoo>
 | 
			
		||||
		Reference in New Issue
	
	Block a user