[MIG] account_banking_sepa_direct_debit: Migration to 9.0

This commit is contained in:
Alexis de Lattre
2016-04-30 01:46:34 +02:00
committed by Carlos Roca
parent e0a0c40439
commit ee031e03a8
32 changed files with 1327 additions and 758 deletions

View File

@@ -1,6 +1,7 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3
=================================
Account Banking SEPA Direct Debit
=================================
@@ -23,8 +24,8 @@ Installation
============
This module depends on :
* account_direct_debit
* account_banking_pain_base',
* account_banking_pain_base
* account_banking_mandate
This module is part of the OCA/bank-payment suite.
@@ -32,18 +33,21 @@ This module is part of the OCA/bank-payment suite.
Configuration
=============
To configure this module, you need to:
* Create a payment mode and select an export type related to debit order ( eg. "SEPA direct debit ...")
Create a Payment Mode dedicated to SEPA Direct Debit and select the
Payment Method *SEPA Direct Debit for customers* (which is automatically
created upon module installation) and check that this payment method
uses the proper version of PAIN.
Usage
=====
To use this module, you must select this payment mode on a direct debit order (Menu :Accounting > Payment > Direct Debit orders)
In the menu *Accounting > Payments > Debit Order*, create a new debit
order and select the Payment Mode dedicated to SEPA Direct Debit that
you created during the configuration step.
For further information, please visit:
* https://www.odoo.com/forum/help-1
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/173/9.0
Known issues / Roadmap
======================
@@ -53,10 +57,10 @@ Known issues / Roadmap
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/bank-payment/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/bank-payment/issues/new?body=module:%20account_banking_sepa_direct_debit%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/bank-payment/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
@@ -64,7 +68,7 @@ Credits
Contributors
------------
* Alexis de Lattre
* Alexis de Lattre <alexis.delattre@akretion.com>
* Pedro M. Baeza
* Stéphane Bidoul <stephane.bidoul@acsone.eu>
* Alexandre Fayolle

View File

@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion (www.akretion.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models
from . import wizard
from .post_install import update_bank_journals

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013-2015 Akretion (www.akretion.com)
# © 2013-2016 Akretion (www.akretion.com)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -7,7 +7,7 @@
{
'name': 'Account Banking SEPA Direct Debit',
'summary': 'Create SEPA files for Direct Debit',
'version': '8.0.0.5.0',
'version': '9.0.1.0.0',
'license': 'AGPL-3',
'author': "Akretion, "
"Serv. Tecnol. Avanzados - Pedro M. Baeza, "
@@ -16,22 +16,21 @@
'website': 'https://github.com/OCA/bank-payment',
'category': 'Banking addons',
'depends': [
'account_direct_debit',
'account_banking_pain_base',
'account_banking_mandate',
],
'data': [
'views/account_banking_mandate_view.xml',
'views/res_company_view.xml',
'views/payment_mode_view.xml',
'wizard/export_sdd_view.xml',
'views/res_config.xml',
'views/account_payment_mode.xml',
'data/mandate_expire_cron.xml',
'data/payment_type_sdd.xml',
'data/account_payment_method.xml',
'data/report_paperformat.xml',
'reports/sepa_direct_debit_mandate.xml',
'views/report_sepa_direct_debit_mandate.xml',
'security/original_mandate_required_security.xml',
],
'demo': ['demo/sepa_direct_debit_demo.xml'],
'post_init_hook': 'update_bank_journals',
'installable': True,
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="sepa_direct_debit" model="account.payment.method">
<field name="name">SEPA Direct Debit for customers</field>
<field name="code">sepa_direct_debit</field>
<field name="payment_type">inbound</field>
<field name="bank_account_required" eval="True"/>
<field name="mandate_required" eval="True"/>
<field name="pain_version">pain.008.001.02</field>
</record>
</data>
</openerp>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com/)
Copyright (C) 2013-2016 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->

View File

@@ -0,0 +1,614 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Mit XMLSpy v2008 rel. 2 (http://www.altova.com) von Wenzel (SIZ Bonn) bearbeitet -->
<!-- Version gemäß DFÜ-Abkommen Anlage 3, Version 2.7, gültig ab November 2013 mit Umsetzung von IBAN Only gemäß EPC SDD Core IG 7.0 bzw. SDD B2B IG 5.0 , zudem Einbau der COR1-Option durch Erweiterung Local Instrument und Erweiterung Service Level auf Externe Codeliste-->
<!-- Mit XMLSpy v2008 rel. 2 sp2 (http://www.altova.com) am 29.11.2012 von der SIZ GmbH bearbeitet -->
<xs:schema xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.003.02" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:iso:std:iso:20022:tech:xsd:pain.008.003.02" elementFormDefault="qualified">
<xs:element name="Document" type="Document"/>
<xs:complexType name="AccountIdentificationSEPA">
<xs:sequence>
<xs:element name="IBAN" type="IBAN2007Identifier"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="ActiveOrHistoricCurrencyAndAmount_SimpleTypeSEPA">
<xs:restriction base="xs:decimal">
<xs:minInclusive value="0.01"/>
<xs:maxInclusive value="999999999.99"/>
<xs:fractionDigits value="2"/>
<xs:totalDigits value="11"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ActiveOrHistoricCurrencyAndAmountSEPA">
<xs:simpleContent>
<xs:extension base="ActiveOrHistoricCurrencyAndAmount_SimpleTypeSEPA">
<xs:attribute name="Ccy" type="ActiveOrHistoricCurrencyCodeEUR" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="ActiveOrHistoricCurrencyCodeEUR">
<xs:restriction base="xs:string">
<xs:enumeration value="EUR"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ActiveOrHistoricCurrencyCode">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{3,3}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="AmendmentInformationDetailsSDD">
<xs:sequence>
<xs:element name="OrgnlMndtId" type="RestrictedIdentificationSEPA2" minOccurs="0">
<xs:annotation>
<xs:documentation>Mandatory if changes occur in Mandate Identification, otherwise not to be used.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="OrgnlCdtrSchmeId" type="PartyIdentificationSEPA4" minOccurs="0">
<xs:annotation>
<xs:documentation>Mandatory if changes occur in 'Creditor Scheme Identification', otherwise not to be used.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="OrgnlDbtrAcct" type="CashAccountSEPA2" minOccurs="0">
<xs:annotation>
<xs:documentation>To be used only for changes of accounts within the same bank.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="OrgnlDbtrAgt" type="BranchAndFinancialInstitutionIdentificationSEPA2" minOccurs="0">
<xs:annotation>
<xs:documentation>To use 'Identification under 'Other' under 'Financial Institution Identifier with code SMNDA to indicate same mandate with new Debtor Agent. To be used with the FRST indicator in the Sequence Type.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="AnyBICIdentifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="BICIdentifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="BatchBookingIndicator">
<xs:restriction base="xs:boolean"/>
</xs:simpleType>
<xs:complexType name="BranchAndFinancialInstitutionIdentificationSEPA3">
<xs:sequence>
<xs:element name="FinInstnId" type="FinancialInstitutionIdentificationSEPA3"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BranchAndFinancialInstitutionIdentificationSEPA2">
<xs:sequence>
<xs:element name="FinInstnId" type="FinancialInstitutionIdentificationSEPA2"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CashAccountSEPA1">
<xs:sequence>
<xs:element name="Id" type="AccountIdentificationSEPA"/>
<xs:element name="Ccy" type="ActiveOrHistoricCurrencyCode" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CashAccountSEPA2">
<xs:sequence>
<xs:element name="Id" type="AccountIdentificationSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CategoryPurposeSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalCategoryPurpose1Code"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="ChargeBearerTypeSEPACode">
<xs:restriction base="xs:string">
<xs:enumeration value="SLEV"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="CountryCode">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{2,2}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="CreditorReferenceInformationSEPA1">
<xs:sequence>
<xs:element name="Tp" type="CreditorReferenceTypeSEPA"/>
<xs:element name="Ref" type="Max35Text">
<xs:annotation>
<xs:documentation>If a Creditor Reference contains a check digit, the receiving bank is not required to validate this.
If the receiving bank validates the check digit and if this validation fails, the bank may continue its processing and send the transaction to the next party in the chain.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceTypeSEPA">
<xs:sequence>
<xs:element name="CdOrPrtry" type="CreditorReferenceTypeCodeSEPA"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceTypeCodeSEPA">
<xs:sequence>
<xs:element name="Cd" type="DocumentType3CodeSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CustomerDirectDebitInitiationV02">
<xs:sequence>
<xs:element name="GrpHdr" type="GroupHeaderSDD"/>
<xs:element name="PmtInf" type="PaymentInstructionInformationSDD" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DateAndPlaceOfBirth">
<xs:sequence>
<xs:element name="BirthDt" type="ISODate"/>
<xs:element name="PrvcOfBirth" type="Max35Text" minOccurs="0"/>
<xs:element name="CityOfBirth" type="Max35Text"/>
<xs:element name="CtryOfBirth" type="CountryCode"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DecimalNumber">
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="17"/>
<xs:totalDigits value="18"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="DirectDebitTransactionSDD">
<xs:sequence>
<xs:element name="MndtRltdInf" type="MandateRelatedInformationSDD"/>
<xs:element name="CdtrSchmeId" type="PartyIdentificationSEPA3" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that all transactions within the same Payment Information block have the same Creditor Scheme Identification.
This data element must be present at either Payment Information or Direct Debit
Transaction level.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DirectDebitTransactionInformationSDD">
<xs:sequence>
<xs:element name="PmtId" type="PaymentIdentificationSEPA"/>
<xs:element name="InstdAmt" type="ActiveOrHistoricCurrencyAndAmountSEPA"/>
<xs:element name="ChrgBr" type="ChargeBearerTypeSEPACode" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that this element be specified at Payment Information level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DrctDbtTx" type="DirectDebitTransactionSDD"/>
<xs:element name="UltmtCdtr" type="PartyIdentificationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>This data element may be present either at Payment Information or at Direct Debit Transaction Information level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DbtrAgt" type="BranchAndFinancialInstitutionIdentificationSEPA3"/>
<xs:element name="Dbtr" type="PartyIdentificationSEPA2"/>
<xs:element name="DbtrAcct" type="CashAccountSEPA2"/>
<xs:element name="UltmtDbtr" type="PartyIdentificationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>Mandatory if provided by the debtor in the mandate.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Purp" type="PurposeSEPA" minOccurs="0"/>
<xs:element name="RmtInf" type="RemittanceInformationSEPA1Choice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Document">
<xs:sequence>
<xs:element name="CstmrDrctDbtInitn" type="CustomerDirectDebitInitiationV02"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DocumentType3CodeSEPA">
<xs:restriction base="xs:string">
<xs:enumeration value="SCOR"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalCategoryPurpose1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalLocalInstrument1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="35"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalOrganisationIdentification1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalPersonIdentification1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalPurpose1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalServiceLevel1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="FinancialInstitutionIdentificationSEPA3">
<xs:sequence>
<xs:choice>
<xs:element name="BIC" type="BICIdentifier"/>
<xs:element name="Othr" type="OthrIdentification"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OthrIdentification">
<xs:sequence>
<xs:element name="Id" type="OthrIdentificationCode"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="OthrIdentificationCode">
<xs:restriction base="xs:string">
<xs:enumeration value="NOTPROVIDED"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="FinancialInstitutionIdentificationSEPA2">
<xs:sequence>
<xs:element name="Othr" type="RestrictedFinancialIdentificationSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RestrictedFinancialIdentificationSEPA">
<xs:sequence>
<xs:element name="Id" type="RestrictedSMNDACode"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RestrictedSMNDACode">
<xs:restriction base="xs:string">
<xs:enumeration value="SMNDA"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="GenericOrganisationIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element name="SchmeNm" type="OrganisationIdentificationSchemeName1Choice" minOccurs="0"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GenericPersonIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element name="SchmeNm" type="PersonIdentificationSchemeName1Choice" minOccurs="0"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RestrictedPersonIdentificationSEPA">
<xs:sequence>
<xs:element name="Id" type="RestrictedPersonIdentifierSEPA"/>
<xs:element name="SchmeNm" type="RestrictedPersonIdentificationSchemeNameSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RestrictedPersonIdentifierSEPA">
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-Z]{2,2}[0-9]{2,2}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){3,3}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,28}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="GroupHeaderSDD">
<xs:sequence>
<xs:element name="MsgId" type="RestrictedIdentificationSEPA1"/>
<xs:element name="CreDtTm" type="ISODateTime"/>
<xs:element name="NbOfTxs" type="Max15NumericText"/>
<xs:element name="CtrlSum" type="DecimalNumber" minOccurs="0"/>
<xs:element name="InitgPty" type="PartyIdentificationSEPA1"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="IBAN2007Identifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ISODate">
<xs:restriction base="xs:date"/>
</xs:simpleType>
<xs:simpleType name="ISODateTime">
<xs:restriction base="xs:dateTime"/>
</xs:simpleType>
<xs:complexType name="LocalInstrumentSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalLocalInstrument1Code"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="MandateRelatedInformationSDD">
<xs:sequence>
<xs:element name="MndtId" type="RestrictedIdentificationSEPA2"/>
<xs:element name="DtOfSgntr" type="ISODate"/>
<xs:element name="AmdmntInd" type="TrueFalseIndicator" minOccurs="0"/>
<xs:element name="AmdmntInfDtls" type="AmendmentInformationDetailsSDD" minOccurs="0">
<xs:annotation>
<xs:documentation>Mandatory if 'Amendment Indicator' is 'TRUE'
The reason code from the Rulebook is indicated using one of the following message subelements.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ElctrncSgntr" type="Max1025Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="Max1025Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="1025"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max140Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="140"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max15NumericText">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]{1,15}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max35Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="35"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max70Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="70"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="OrganisationIdentificationSEPAChoice">
<xs:sequence>
<xs:choice>
<xs:element name="BICOrBEI" type="AnyBICIdentifier"/>
<xs:element name="Othr" type="GenericOrganisationIdentification1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrganisationIdentificationSchemeName1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Cd" type="ExternalOrganisationIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartySEPAChoice">
<xs:sequence>
<xs:choice>
<xs:element name="OrgId" type="OrganisationIdentificationSEPAChoice">
<xs:annotation>
<xs:documentation>Either BIC or BEI or one
occurrence of Other is allowed.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PrvtId" type="PersonIdentificationSEPA1Choice">
<xs:annotation>
<xs:documentation>Either Date and Place of Birth or one occurrence of Other is allowed</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartySEPA2">
<xs:sequence>
<xs:element name="PrvtId" type="PersonIdentificationSEPA2">
<xs:annotation>
<xs:documentation>Private Identification is used to identify either an organisation or a private
person.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA1">
<xs:sequence>
<xs:element name="Nm" type="Max70Text" minOccurs="0">
<xs:annotation>
<xs:documentation>Name is limited to 70 characters in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Id" type="PartySEPAChoice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA2">
<xs:sequence>
<xs:element name="Nm" type="Max70Text">
<xs:annotation>
<xs:documentation>Name is limited to 70 characters in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PstlAdr" type="PostalAddressSEPA" minOccurs="0"/>
<xs:element name="Id" type="PartySEPAChoice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA3">
<xs:sequence>
<xs:element name="Id" type="PartySEPA2"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA4">
<xs:sequence>
<xs:element name="Nm" type="Max70Text" minOccurs="0">
<xs:annotation>
<xs:documentation>If present the new Name must be specified under Creditor. Name is limited to 70 characters in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Id" type="PartySEPA2" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA5">
<xs:sequence>
<xs:element name="Nm" type="Max70Text">
<xs:annotation>
<xs:documentation>Name is limited to 70 characters in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PstlAdr" type="PostalAddressSEPA" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentIdentificationSEPA">
<xs:sequence>
<xs:element name="InstrId" type="RestrictedIdentificationSEPA1" minOccurs="0"/>
<xs:element name="EndToEndId" type="RestrictedIdentificationSEPA1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentInstructionInformationSDD">
<xs:sequence>
<xs:element name="PmtInfId" type="RestrictedIdentificationSEPA1"/>
<xs:element name="PmtMtd" type="PaymentMethod2Code"/>
<xs:element name="BtchBookg" type="BatchBookingIndicator" minOccurs="0">
<xs:annotation>
<xs:documentation>If present and contains true, batch booking is requested. If present and contains false, booking per transaction is requested. If element is not present, pre-agreed customer-to-bank conditions apply.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="NbOfTxs" type="Max15NumericText" minOccurs="0"/>
<xs:element name="CtrlSum" type="DecimalNumber" minOccurs="0"/>
<xs:element name="PmtTpInf" type="PaymentTypeInformationSDD"/>
<xs:element name="ReqdColltnDt" type="ISODate"/>
<xs:element name="Cdtr" type="PartyIdentificationSEPA5"/>
<xs:element name="CdtrAcct" type="CashAccountSEPA1"/>
<xs:element name="CdtrAgt" type="BranchAndFinancialInstitutionIdentificationSEPA3"/>
<xs:element name="UltmtCdtr" type="PartyIdentificationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>This data element may be present either at Payment Information or at Direct Debit Transaction Information level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ChrgBr" type="ChargeBearerTypeSEPACode" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that this element be specified at Payment Information level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="CdtrSchmeId" type="PartyIdentificationSEPA3" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that all transactions within the same Payment Information block have the same Creditor Scheme Identification.
This data element must be present at either Payment Information or Direct Debit
Transaction level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DrctDbtTxInf" type="DirectDebitTransactionInformationSDD" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="PaymentMethod2Code">
<xs:restriction base="xs:string">
<xs:enumeration value="DD"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PaymentTypeInformationSDD">
<xs:sequence>
<xs:element name="SvcLvl" type="ServiceLevelSEPA"/>
<xs:element name="LclInstrm" type="LocalInstrumentSEPA">
<xs:annotation>
<xs:documentation>Only B2B, 'CORE' or 'COR1' is allowed. The mixing of different Local Instrument values is not allowed in the same message.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SeqTp" type="SequenceType1Code">
<xs:annotation>
<xs:documentation>If 'Amendment Indicator' is 'true' and 'Original Debtor Agent' is set to 'SMNDA' this message element must indicate 'FRST'</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="CtgyPurp" type="CategoryPurposeSEPA" minOccurs="0">
<xs:annotation>
<xs:documentation>Depending on the agreement between the Creditor and the Creditor Bank, Category Purpose may be forwarded to the Debtor Bank.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSEPA1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="DtAndPlcOfBirth" type="DateAndPlaceOfBirth"/>
<xs:element name="Othr" type="GenericPersonIdentification1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSEPA2">
<xs:sequence>
<xs:element name="Othr" type="RestrictedPersonIdentificationSEPA">
<xs:annotation>
<xs:documentation>Only one occurrence of Other is allowed, and no other sub-elements are allowed.
Identification must be used with an identifier described in General Message Element Specifications, Chapter 1.5.2 of the Implementation Guide.
Scheme Name under Other must specify SEPA under Proprietary</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSchemeName1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Cd" type="ExternalPersonIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RestrictedPersonIdentificationSchemeNameSEPA">
<xs:sequence>
<xs:element name="Prtry" type="IdentificationSchemeNameSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="IdentificationSchemeNameSEPA">
<xs:restriction base="xs:string">
<xs:enumeration value="SEPA"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PostalAddressSEPA">
<xs:sequence>
<xs:element name="Ctry" type="CountryCode" minOccurs="0"/>
<xs:element name="AdrLine" type="Max70Text" minOccurs="0" maxOccurs="2"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PurposeSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalPurpose1Code">
<xs:annotation>
<xs:documentation>Only codes from the ISO 20022 ExternalPurposeCode list are allowed.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RemittanceInformationSEPA1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Ustrd" type="Max140Text"/>
<xs:element name="Strd" type="StructuredRemittanceInformationSEPA1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="SequenceType1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="FRST"/>
<xs:enumeration value="RCUR"/>
<xs:enumeration value="FNAL"/>
<xs:enumeration value="OOFF"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ServiceLevelSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalServiceLevel1Code"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="StructuredRemittanceInformationSEPA1">
<xs:sequence>
<xs:element name="CdtrRefInf" type="CreditorReferenceInformationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>When present, the receiving bank is not obliged to validate the reference information.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="TrueFalseIndicator">
<xs:restriction base="xs:boolean"/>
</xs:simpleType>
<xs:simpleType name="RestrictedIdentificationSEPA1">
<xs:restriction base="xs:string">
<xs:pattern value="([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|'| ]){1,35}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="RestrictedIdentificationSEPA2">
<xs:restriction base="xs:string">
<xs:pattern value="([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,35}"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="export_sdd_008_001_02" model="payment.mode.type">
<field name="name">SEPA Direct Debit v02 (recommended)</field>
<field name="code">pain.008.001.02</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sdd_wizard"/>
<field name="payment_order_type">debit</field>
</record>
<record id="export_sdd_008_001_03" model="payment.mode.type">
<field name="name">SEPA Direct Debit v03</field>
<field name="code">pain.008.001.03</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sdd_wizard"/>
<field name="payment_order_type">debit</field>
</record>
<record id="export_sdd_008_001_04" model="payment.mode.type">
<field name="name">SEPA Direct Debit v04</field>
<field name="code">pain.008.001.04</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sdd_wizard"/>
<field name="payment_order_type">debit</field>
</record>
</data>
</openerp>

View File

@@ -3,13 +3,11 @@
<openerp>
<data noupdate="1">
<record id="sepa_direct_debit_mode" model="payment.mode">
<field name="name">SEPA Direct Debit La Banque Postale</field>
<field name="journal" ref="account.bank_journal"/>
<field name="bank_id" ref="account_banking_payment_export.main_company_iban"/>
<record id="payment_mode_inbound_sepa_dd1" model="account.payment.mode">
<field name="name">SEPA Direct Debit of customers</field>
<field name="company_id" ref="base.main_company"/>
<field name="type" ref="export_sdd_008_001_02"/>
<field name="purchase_ok" eval="False"/>
<field name="bank_account_link">variable</field>
<field name="payment_method_id" ref="sepa_direct_debit"/>
<field name="default_journal_ids" search="[('type', 'in', ('sale', 'sale_refund'))]"/>
</record>
@@ -17,8 +15,9 @@
<field name="sepa_creditor_identifier">FR78ZZZ424242</field>
</record>
<!-- Camptocamp -->
<record id="res_partner_12_mandate" model="account.banking.mandate">
<field name="partner_bank_id" ref="account_banking_payment_export.res_partner_12_iban"/>
<field name="partner_bank_id" ref="account_payment_mode.res_partner_12_iban"/>
<field name="format">sepa</field>
<field name="type">recurrent</field>
<field name="recurrent_sequence_type">first</field>
@@ -26,8 +25,14 @@
<field name="state">valid</field>
</record>
<record id="base.res_partner_12" model="res.partner">
<field name="customer_payment_mode_id" ref="payment_mode_inbound_sepa_dd1"/>
</record>
<!-- Agrolait -->
<record id="res_partner_2_mandate" model="account.banking.mandate">
<field name="partner_bank_id" ref="account_banking_payment_export.res_partner_2_iban"/>
<field name="partner_bank_id" ref="account_payment_mode.res_partner_2_iban"/>
<field name="format">sepa</field>
<field name="type">recurrent</field>
<field name="recurrent_sequence_type">first</field>
@@ -35,5 +40,10 @@
<field name="state">valid</field>
</record>
<record id="base.res_partner_2" model="res.partner">
<field name="customer_payment_mode_id" ref="payment_mode_inbound_sepa_dd1"/>
</record>
</data>
</openerp>

View File

@@ -319,11 +319,6 @@ msgstr ""
msgid "SEPA Direct Debit Files"
msgstr ""
#. module: account_banking_sepa_direct_debit
#: model:ir.actions.act_window,name:account_banking_sepa_direct_debit.mandate_action
msgid "SEPA Direct Debit Mandates"
msgstr ""
#. module: account_banking_sepa_direct_debit
#: view:banking.export.sdd.wizard:account_banking_sepa_direct_debit.banking_export_sdd_wizard_view
msgid "SEPA Direct Debit XML file generation"

View File

@@ -402,11 +402,6 @@ msgstr "Mandats SEPA"
msgid "SEPA Creditor Identifier"
msgstr "Identifiant créancier SEPA"
#. module: account_banking_sepa_direct_debit
#: model:ir.actions.act_window,name:account_banking_sepa_direct_debit.mandate_action
msgid "SEPA Direct Debit Mandates"
msgstr "Mandats de prélèvement SEPA"
#. module: account_banking_sepa_direct_debit
#: view:banking.export.sdd.wizard:account_banking_sepa_direct_debit.banking_export_sdd_wizard_view
msgid "SEPA Direct Debit XML file generation"

View File

@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*-
from . import res_company
from . import res_config
from . import account_banking_mandate
from . import bank_payment_line
from . import payment_mode
from . import account_payment_mode
from . import account_payment_method
from . import account_payment_order

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -16,29 +16,14 @@ logger = logging.getLogger(__name__)
class AccountBankingMandate(models.Model):
"""SEPA Direct Debit Mandate"""
_inherit = 'account.banking.mandate'
_track = {
'recurrent_sequence_type': {
'account_banking_sepa_direct_debit.recurrent_sequence_type_first':
lambda self, cr, uid, obj, ctx=None:
obj['recurrent_sequence_type'] == 'first',
'account_banking_sepa_direct_debit.'
'recurrent_sequence_type_recurring':
lambda self, cr, uid, obj, ctx=None:
obj['recurrent_sequence_type'] == 'recurring',
'account_banking_sepa_direct_debit.recurrent_sequence_type_final':
lambda self, cr, uid, obj, ctx=None:
obj['recurrent_sequence_type'] == 'final',
}
}
_rec_name = 'display_name'
format = fields.Selection(
selection_add=[('sepa', _('Sepa Mandate'))],
default='sepa',
)
selection_add=[('sepa', 'Sepa Mandate')], default='sepa')
type = fields.Selection([('recurrent', 'Recurrent'),
('oneoff', 'One-Off')],
string='Type of Mandate',
track_visibility='always')
track_visibility='onchange')
recurrent_sequence_type = fields.Selection(
[('first', 'First'),
('recurring', 'Recurring'),
@@ -46,25 +31,12 @@ class AccountBankingMandate(models.Model):
string='Sequence Type for Next Debit', track_visibility='onchange',
help="This field is only used for Recurrent mandates, not for "
"One-Off mandates.", default="first")
sepa_migrated = fields.Boolean(
string='Migrated to SEPA', track_visibility='onchange',
help="If this field is not active, the mandate section of the next "
"direct debit file that include this mandate will contain the "
"'Original Mandate Identification' and the 'Original Creditor "
"Scheme Identification'. This is required in a few countries "
"(Belgium for instance), but not in all countries. If this is "
"not required in your country, you should keep this field always "
"active.", default=True)
original_mandate_identification = fields.Char(
string='Original Mandate Identification', track_visibility='onchange',
size=35,
help="When the field 'Migrated to SEPA' is not active, this field "
"will be used as the Original Mandate Identification in the "
"Direct Debit file.")
scheme = fields.Selection([('CORE', 'Basic (CORE)'),
('B2B', 'Enterprise (B2B)')],
string='Scheme', default="CORE")
scheme = fields.Selection([
('CORE', 'Basic (CORE)'),
('B2B', 'Enterprise (B2B)')],
string='Scheme', default="CORE", track_visibility='onchange')
unique_mandate_reference = fields.Char(size=35) # cf ISO 20022
display_name = fields.Char(compute='compute_display_name', store=True)
@api.multi
@api.constrains('type', 'recurrent_sequence_type')
@@ -77,28 +49,16 @@ class AccountBankingMandate(models.Model):
% mandate.unique_mandate_reference)
@api.multi
@api.constrains('type', 'recurrent_sequence_type', 'sepa_migrated')
def _check_migrated_to_sepa(self):
@api.depends('unique_mandate_reference', 'recurrent_sequence_type')
def compute_display_name(self):
for mandate in self:
if (mandate.type == 'recurrent' and not mandate.sepa_migrated and
mandate.recurrent_sequence_type != 'first'):
raise exceptions.Warning(
_("The recurrent mandate '%s' which is not marked as "
"'Migrated to SEPA' must have its recurrent sequence "
"type set to 'First'.")
% mandate.unique_mandate_reference)
@api.multi
@api.constrains('type', 'original_mandate_identification', 'sepa_migrated')
def _check_original_mandate_identification(self):
for mandate in self:
if (mandate.type == 'recurrent' and not mandate.sepa_migrated and
not mandate.original_mandate_identification):
raise exceptions.Warning(
_("You must set the 'Original Mandate Identification' on "
"the recurrent mandate '%s' which is not marked as "
"'Migrated to SEPA'.")
% mandate.unique_mandate_reference)
if mandate.format == 'sepa':
name = '%s (%s)' % (
mandate.unique_mandate_reference,
mandate.recurrent_sequence_type)
else:
name = mandate.unique_mandate_reference
mandate.display_name = name
@api.multi
@api.onchange('partner_bank_id')
@@ -137,5 +97,5 @@ class AccountBankingMandate(models.Model):
'The following SDD Mandate IDs has been set to expired: %s'
% expired_mandates.ids)
else:
logger.info('0 SDD Mandates must be set to Expired')
logger.info('0 SDD Mandates had to be set to Expired')
return True

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class AccountPaymentMethod(models.Model):
_inherit = 'account.payment.method'
pain_version = fields.Selection(selection_add=[
('pain.008.001.02', 'pain.008.001.02 (recommended for direct debit)'),
('pain.008.001.03', 'pain.008.001.03'),
('pain.008.001.04', 'pain.008.001.04'),
('pain.008.003.02', 'pain.008.003.02 (direct debit in Germany)'),
])
@api.multi
def get_xsd_file_path(self):
self.ensure_one()
if self.pain_version in [
'pain.008.001.02', 'pain.008.001.03', 'pain.008.001.04',
'pain.008.003.02']:
path = 'account_banking_sepa_direct_debit/data/%s.xsd'\
% self.pain_version
return path
return super(AccountPaymentMethod, self).get_xsd_file_path()

View File

@@ -2,12 +2,13 @@
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, exceptions, _
from openerp import models, fields, api, _
from .common import is_sepa_creditor_identifier_valid
from openerp.exceptions import ValidationError
class PaymentMode(models.Model):
_inherit = 'payment.mode'
class AccountPaymentMode(models.Model):
_inherit = 'account.payment.mode'
sepa_creditor_identifier = fields.Char(
string='SEPA Creditor Identifier', size=35,
@@ -19,13 +20,9 @@ class PaymentMode(models.Model):
"- a 2-digits checkum\n"
"- a 3-letters business code\n"
"- a country-specific identifier")
original_creditor_identifier = fields.Char(
string='Original Creditor Identifier', size=70,
help="If not defined, Original Creditor Identifier from company "
"will be used.")
def _sepa_type_get(self):
res = super(PaymentMode, self)._sepa_type_get()
res = super(AccountPaymentMode, self)._sepa_type_get()
if not res:
if self.type.code and self.type.code.startswith('pain.008'):
res = 'sepa_direct_debit'
@@ -38,6 +35,6 @@ class PaymentMode(models.Model):
if payment_mode.sepa_creditor_identifier:
if not is_sepa_creditor_identifier_valid(
payment_mode.sepa_creditor_identifier):
raise exceptions.Warning(
_('Error'),
_("Invalid SEPA Creditor Identifier."))
raise ValidationError(
_("The SEPA Creditor Identifier '%s' is invalid.")
% payment_mode.sepa_creditor_identifier)

View File

@@ -0,0 +1,298 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, fields, api, _
from openerp.exceptions import UserError
from lxml import etree
class AccountPaymentOrder(models.Model):
_inherit = 'account.payment.order'
def _get_previous_bank(self, payline):
previous_bank = False
older_lines = self.env['account.payment.line'].search([
('mandate_id', '=', payline.mandate_id.id),
('partner_bank_id', '!=', payline.partner_bank_id.id)])
if older_lines:
previous_date = False
previous_payline = False
for older_line in older_lines:
if hasattr(older_line.order_id, 'date_sent'):
older_line_date = older_line.order_id.date_sent
else:
older_line_date = older_line.order_id.date_done
if (older_line_date and
older_line_date > previous_date):
previous_date = older_line_date
previous_payline = older_line
if previous_payline:
previous_bank = previous_payline.partner_bank_id
return previous_bank
@api.multi
def generate_payment_file(self):
"""Creates the SEPA Direct Debit file. That's the important code !"""
self.ensure_one()
if self.payment_method_id.code != 'sepa_direct_debit':
return super(AccountPaymentOrder, self).generate_payment_file()
pain_flavor = self.payment_method_id.pain_version
# We use pain_flavor.startswith('pain.008.001.xx')
# to support country-specific extensions such as
# pain.008.001.02.ch.01 (cf l10n_ch_sepa)
if pain_flavor.startswith('pain.008.001.02'):
bic_xml_tag = 'BIC'
name_maxsize = 70
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor.startswith('pain.008.003.02'):
bic_xml_tag = 'BIC'
name_maxsize = 70
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor.startswith('pain.008.001.03'):
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor.startswith('pain.008.001.04'):
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrDrctDbtInitn'
else:
raise UserError(
_("Payment Type Code '%s' is not supported. The only "
"Payment Type Code supported for SEPA Direct Debit are "
"'pain.008.001.02', 'pain.008.001.03' and "
"'pain.008.001.04'.") % pain_flavor)
pay_method = self.payment_mode_id.payment_method_id
xsd_file = pay_method.get_xsd_file_path()
gen_args = {
'bic_xml_tag': bic_xml_tag,
'name_maxsize': name_maxsize,
'convert_to_ascii': pay_method.convert_to_ascii,
'payment_method': 'DD',
'file_prefix': 'sdd_',
'pain_flavor': pain_flavor,
'pain_xsd_file': xsd_file,
}
nsmap = self.generate_pain_nsmap()
attrib = self.generate_pain_attrib()
xml_root = etree.Element('Document', nsmap=nsmap, attrib=attrib)
pain_root = etree.SubElement(xml_root, root_xml_tag)
# A. Group header
group_header, nb_of_transactions_a, control_sum_a = \
self.generate_group_header_block(pain_root, gen_args)
transactions_count_a = 0
amount_control_sum_a = 0.0
lines_per_group = {}
# key = (requested_date, priority, sequence type)
# value = list of lines as objects
for line in self.bank_line_ids:
transactions_count_a += 1
priority = line.priority
# The field line.date is the requested payment date
# taking into account the 'date_prefered' setting
# cf account_banking_payment_export/models/account_payment.py
# in the inherit of action_open()
if not line.mandate_id:
raise UserError(
_("Missing SEPA Direct Debit mandate on the "
"bank payment line with partner '%s' "
"(reference '%s').")
% (line.partner_id.name, line.name))
scheme = line.mandate_id.scheme
if line.mandate_id.state != 'valid':
raise Warning(
_("The SEPA Direct Debit mandate with reference '%s' "
"for partner '%s' has expired.")
% (line.mandate_id.unique_mandate_reference,
line.mandate_id.partner_id.name))
if line.mandate_id.type == 'oneoff':
seq_type = 'OOFF'
if line.mandate_id.last_debit_date:
raise Warning(
_("The mandate with reference '%s' for partner "
"'%s' has type set to 'One-Off' and it has a "
"last debit date set to '%s', so we can't use "
"it.")
% (line.mandate_id.unique_mandate_reference,
line.mandate_id.partner_id.name,
line.mandate_id.last_debit_date))
elif line.mandate_id.type == 'recurrent':
seq_type_map = {
'recurring': 'RCUR',
'first': 'FRST',
'final': 'FNAL',
}
seq_type_label = \
line.mandate_id.recurrent_sequence_type
assert seq_type_label is not False
seq_type = seq_type_map[seq_type_label]
key = (line.date, priority, seq_type, scheme)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
lines_per_group[key] = [line]
for (requested_date, priority, sequence_type, scheme), lines in \
lines_per_group.items():
# B. Payment info
payment_info, nb_of_transactions_b, control_sum_b = \
self.generate_start_payment_info_block(
pain_root,
"self.name + '-' + "
"sequence_type + '-' + requested_date.replace('-', '') "
"+ '-' + priority",
priority, scheme, sequence_type, requested_date, {
'self': self,
'sequence_type': sequence_type,
'priority': priority,
'requested_date': requested_date,
}, gen_args)
self.generate_party_block(
payment_info, 'Cdtr', 'B',
self.company_partner_bank_id, gen_args)
charge_bearer = etree.SubElement(payment_info, 'ChrgBr')
if self.sepa:
charge_bearer_text = 'SLEV'
else:
charge_bearer_text = self.charge_bearer
charge_bearer.text = charge_bearer_text
creditor_scheme_identification = etree.SubElement(
payment_info, 'CdtrSchmeId')
self.generate_creditor_scheme_identification(
creditor_scheme_identification,
'self.payment_mode_id.sepa_creditor_identifier or '
'self.company_id.sepa_creditor_identifier',
'SEPA Creditor Identifier', {'self': self}, 'SEPA', gen_args)
transactions_count_b = 0
amount_control_sum_b = 0.0
for line in lines:
transactions_count_b += 1
# C. Direct Debit Transaction Info
dd_transaction_info = etree.SubElement(
payment_info, 'DrctDbtTxInf')
payment_identification = etree.SubElement(
dd_transaction_info, 'PmtId')
if pain_flavor == 'pain.008.001.02.ch.01':
instruction_identification = etree.SubElement(
payment_identification, 'InstrId')
instruction_identification.text = self._prepare_field(
'Intruction Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
end2end_identification = etree.SubElement(
payment_identification, 'EndToEndId')
end2end_identification.text = self._prepare_field(
'End to End Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
currency_name = self._prepare_field(
'Currency Code', 'line.currency_id.name',
{'line': line}, 3, gen_args=gen_args)
instructed_amount = etree.SubElement(
dd_transaction_info, 'InstdAmt', Ccy=currency_name)
instructed_amount.text = '%.2f' % line.amount_currency
amount_control_sum_a += line.amount_currency
amount_control_sum_b += line.amount_currency
dd_transaction = etree.SubElement(
dd_transaction_info, 'DrctDbtTx')
mandate_related_info = etree.SubElement(
dd_transaction, 'MndtRltdInf')
mandate_identification = etree.SubElement(
mandate_related_info, 'MndtId')
mandate_identification.text = self._prepare_field(
'Unique Mandate Reference',
'line.mandate_id.unique_mandate_reference',
{'line': line}, 35, gen_args=gen_args)
mandate_signature_date = etree.SubElement(
mandate_related_info, 'DtOfSgntr')
mandate_signature_date.text = self._prepare_field(
'Mandate Signature Date',
'line.mandate_id.signature_date',
{'line': line}, 10, gen_args=gen_args)
if sequence_type == 'FRST' and line.mandate_id.last_debit_date:
previous_bank = self._get_previous_bank(line)
if previous_bank:
amendment_indicator = etree.SubElement(
mandate_related_info, 'AmdmntInd')
amendment_indicator.text = 'true'
amendment_info_details = etree.SubElement(
mandate_related_info, 'AmdmntInfDtls')
if (
previous_bank.bank_bic ==
line.partner_bank_id.bank_bic):
ori_debtor_account = etree.SubElement(
amendment_info_details, 'OrgnlDbtrAcct')
ori_debtor_account_id = etree.SubElement(
ori_debtor_account, 'Id')
ori_debtor_account_iban = etree.SubElement(
ori_debtor_account_id, 'IBAN')
ori_debtor_account_iban.text = self._validate_iban(
self._prepare_field(
'Original Debtor Account',
'previous_bank.sanitized_acc_number',
{'previous_bank': previous_bank},
gen_args=gen_args))
else:
ori_debtor_agent = etree.SubElement(
amendment_info_details, 'OrgnlDbtrAgt')
ori_debtor_agent_institution = etree.SubElement(
ori_debtor_agent, 'FinInstnId')
ori_debtor_agent_bic = etree.SubElement(
ori_debtor_agent_institution, bic_xml_tag)
ori_debtor_agent_bic.text = self._prepare_field(
'Original Debtor Agent',
'previous_bank.bank_bic',
{'previous_bank': previous_bank},
gen_args=gen_args)
ori_debtor_agent_other = etree.SubElement(
ori_debtor_agent_institution, 'Othr')
ori_debtor_agent_other_id = etree.SubElement(
ori_debtor_agent_other, 'Id')
ori_debtor_agent_other_id.text = 'SMNDA'
# SMNDA = Same Mandate New Debtor Agent
self.generate_party_block(
dd_transaction_info, 'Dbtr', 'C',
line.partner_bank_id, gen_args, line)
self.generate_remittance_info_block(
dd_transaction_info, line, gen_args)
nb_of_transactions_b.text = unicode(transactions_count_b)
control_sum_b.text = '%.2f' % amount_control_sum_b
nb_of_transactions_a.text = unicode(transactions_count_a)
control_sum_a.text = '%.2f' % amount_control_sum_a
return self.finalize_sepa_file_creation(
xml_root, gen_args)
@api.multi
def finalize_sepa_file_creation(self, xml_root, gen_args):
"""Save the SEPA Direct Debit file: mark all payments in the file
as 'sent'. Write 'last debit date' on mandate and set oneoff
mandate to expired.
"""
abmo = self.env['account.banking.mandate']
to_expire_mandates = abmo.browse([])
first_mandates = abmo.browse([])
all_mandates = abmo.browse([])
for bline in self.bank_line_ids:
if bline.mandate_id in all_mandates:
continue
all_mandates += bline.mandate_id
if bline.mandate_id.type == 'oneoff':
to_expire_mandates += bline.mandate_id
elif bline.mandate_id.type == 'recurrent':
seq_type = bline.mandate_id.recurrent_sequence_type
if seq_type == 'final':
to_expire_mandates += bline.mandate_id
elif seq_type == 'first':
first_mandates += bline.mandate_id
all_mandates.write(
{'last_debit_date': fields.Date.context_today(self)})
to_expire_mandates.write({'state': 'expired'})
first_mandates.write({
'recurrent_sequence_type': 'recurring',
})
return super(AccountPaymentOrder, self).finalize_sepa_file_creation(
xml_root, gen_args)

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2015-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, api
@@ -9,7 +9,7 @@ class BankPaymentLine(models.Model):
_inherit = 'bank.payment.line'
@api.multi
def move_line_transfer_account_hashcode(self):
def move_line_offsetting_account_hashcode(self):
"""
From my experience, even when you ask several direct debits
at the same date with enough delay, you will have several credits
@@ -18,6 +18,6 @@ class BankPaymentLine(models.Model):
reconciliation of the bank statement.
"""
hashcode = super(BankPaymentLine, self).\
move_line_transfer_account_hashcode()
move_line_offsetting_account_hashcode()
hashcode += '-' + unicode(self.mandate_id.recurrent_sequence_type)
return hashcode

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion (www.akretion.com)
# © 2013-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -21,7 +21,7 @@ def is_sepa_creditor_identifier_valid(sepa_creditor_identifier):
sci = str(sepa_creditor_identifier).lower()
except:
logger.warning(
"SEPA Creditor ID should contain only ASCII caracters.")
"SEPA Creditor ID should contain only ASCII characters.")
return False
if len(sci) < 9:
return False

View File

@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion (www.akretion.com)
# © 2013-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, exceptions, _
from openerp import models, fields, api, _
from .common import is_sepa_creditor_identifier_valid
from openerp.exceptions import ValidationError
class ResCompany(models.Model):
@@ -28,6 +29,6 @@ class ResCompany(models.Model):
if company.sepa_creditor_identifier:
if not is_sepa_creditor_identifier_valid(
company.sepa_creditor_identifier):
raise exceptions.Warning(
_('Error'),
_("Invalid SEPA Creditor Identifier."))
raise ValidationError(
_("The SEPA Creditor Identifier '%s' is invalid.")
% company.sepa_creditor_identifier)

View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
class AccountConfigSettings(models.TransientModel):
_inherit = 'account.config.settings'
sepa_creditor_identifier = fields.Char(
related='company_id.sepa_creditor_identifier')

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import SUPERUSER_ID
def update_bank_journals(cr, pool):
ajo = pool['account.journal']
journal_ids = ajo.search(cr, SUPERUSER_ID, [('type', '=', 'bank')])
sdd_id = pool['ir.model.data'].xmlid_to_res_id(
cr, SUPERUSER_ID,
'account_banking_sepa_direct_debit.sepa_direct_debit')
if sdd_id:
ajo.write(cr, SUPERUSER_ID, journal_ids, {
'inbound_payment_method_ids': [(4, sdd_id)],
})
return

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data noupdate="1">
<record id="group_original_mandate_required" model="res.groups">
<field name="name">Original Mandate Required (SEPA)</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import test_sdd

View File

@@ -0,0 +1,167 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp.addons.account.tests.account_test_classes\
import AccountingTestCase
from openerp.tools import float_compare
import time
from lxml import etree
class TestSDD(AccountingTestCase):
def test_sdd(self):
self.company = self.env['res.company']
self.account_model = self.env['account.account']
self.move_model = self.env['account.move']
self.journal_model = self.env['account.journal']
self.payment_order_model = self.env['account.payment.order']
self.payment_line_model = self.env['account.payment.line']
self.mandate_model = self.env['account.banking.mandate']
self.bank_line_model = self.env['bank.payment.line']
self.partner_bank_model = self.env['res.partner.bank']
self.attachment_model = self.env['ir.attachment']
self.invoice_model = self.env['account.invoice']
self.invoice_line_model = self.env['account.invoice.line']
company = self.env.ref('base.main_company')
self.partner_agrolait = self.env.ref('base.res_partner_2')
self.partner_c2c = self.env.ref('base.res_partner_12')
self.account_revenue = self.account_model.search([(
'user_type_id',
'=',
self.env.ref('account.data_account_type_revenue').id)], limit=1)
self.account_receivable = self.account_model.search([(
'user_type_id',
'=',
self.env.ref('account.data_account_type_receivable').id)], limit=1)
# create journal
self.bank_journal = self.journal_model.create({
'name': 'Company Bank journal',
'type': 'bank',
'code': 'BNKFC',
'bank_account_id':
self.env.ref('account_payment_mode.main_company_iban').id,
'bank_id':
self.env.ref('account_payment_mode.bank_la_banque_postale').id,
})
# update payment mode
self.payment_mode = self.env.ref(
'account_banking_sepa_direct_debit.'
'payment_mode_inbound_sepa_dd1')
self.payment_mode.write({
'bank_account_link': 'fixed',
'fixed_journal_id': self.bank_journal.id,
})
eur_currency_id = self.env.ref('base.EUR').id
company.currency_id = eur_currency_id
self.env.ref(
'account_banking_sepa_direct_debit.res_partner_2_mandate').\
recurrent_sequence_type = 'first'
invoice1 = self.create_invoice(
self.partner_agrolait.id,
'account_banking_sepa_direct_debit.res_partner_2_mandate', 42.0)
invoice2 = self.create_invoice(
self.partner_c2c.id,
'account_banking_sepa_direct_debit.res_partner_12_mandate', 11.0)
for inv in [invoice1, invoice2]:
action = inv.create_account_payment_line()
self.assertEquals(action['res_model'], 'account.payment.order')
self.payment_order = self.payment_order_model.browse(action['res_id'])
self.assertEquals(
self.payment_order.payment_type, 'inbound')
self.assertEquals(
self.payment_order.payment_mode_id, self.payment_mode)
self.assertEquals(
self.payment_order.journal_id, self.bank_journal)
# Check payment line
pay_lines = self.payment_line_model.search([
('partner_id', '=', self.partner_agrolait.id),
('order_id', '=', self.payment_order.id)])
self.assertEquals(len(pay_lines), 1)
agrolait_pay_line1 = pay_lines[0]
accpre = self.env['decimal.precision'].precision_get('Account')
self.assertEquals(agrolait_pay_line1.currency_id.id, eur_currency_id)
self.assertEquals(
agrolait_pay_line1.mandate_id, invoice1.mandate_id)
self.assertEquals(
agrolait_pay_line1.partner_bank_id,
invoice1.mandate_id.partner_bank_id)
self.assertEquals(float_compare(
agrolait_pay_line1.amount_currency, 42, precision_digits=accpre),
0)
self.assertEquals(agrolait_pay_line1.communication_type, 'normal')
self.assertEquals(agrolait_pay_line1.communication, invoice1.number)
self.payment_order.draft2open()
self.assertEquals(self.payment_order.state, 'open')
self.assertEquals(self.payment_order.sepa, True)
# Check bank payment line
bank_lines = self.bank_line_model.search([
('partner_id', '=', self.partner_agrolait.id)])
self.assertEquals(len(bank_lines), 1)
agrolait_bank_line = bank_lines[0]
self.assertEquals(agrolait_bank_line.currency_id.id, eur_currency_id)
self.assertEquals(float_compare(
agrolait_bank_line.amount_currency, 42.0, precision_digits=accpre),
0)
self.assertEquals(agrolait_bank_line.communication_type, 'normal')
self.assertEquals(
agrolait_bank_line.communication, invoice1.number)
self.assertEquals(
agrolait_bank_line.mandate_id, invoice1.mandate_id)
self.assertEquals(
agrolait_bank_line.partner_bank_id,
invoice1.mandate_id.partner_bank_id)
action = self.payment_order.open2generated()
self.assertEquals(self.payment_order.state, 'generated')
self.assertEquals(action['res_model'], 'ir.attachment')
attachment = self.attachment_model.browse(action['res_id'])
self.assertEquals(attachment.datas_fname[-4:], '.xml')
xml_file = attachment.datas.decode('base64')
xml_root = etree.fromstring(xml_file)
# print "xml_file=", etree.tostring(xml_root, pretty_print=True)
namespaces = xml_root.nsmap
namespaces['p'] = xml_root.nsmap[None]
namespaces.pop(None)
pay_method_xpath = xml_root.xpath(
'//p:PmtInf/p:PmtMtd', namespaces=namespaces)
self.assertEquals(pay_method_xpath[0].text, 'DD')
sepa_xpath = xml_root.xpath(
'//p:PmtInf/p:PmtTpInf/p:SvcLvl/p:Cd', namespaces=namespaces)
self.assertEquals(sepa_xpath[0].text, 'SEPA')
debtor_acc_xpath = xml_root.xpath(
'//p:PmtInf/p:CdtrAcct/p:Id/p:IBAN', namespaces=namespaces)
self.assertEquals(
debtor_acc_xpath[0].text,
self.payment_order.company_partner_bank_id.sanitized_acc_number)
self.payment_order.generated2uploaded()
self.assertEquals(self.payment_order.state, 'uploaded')
for inv in [invoice1, invoice2]:
self.assertEquals(inv.state, 'paid')
self.assertEquals(self.env.ref(
'account_banking_sepa_direct_debit.res_partner_2_mandate').
recurrent_sequence_type, 'recurring')
return
def create_invoice(
self, partner_id, mandate_xmlid, price_unit, type='out_invoice'):
invoice = self.invoice_model.create({
'partner_id': partner_id,
'reference_type': 'none',
'currency_id': self.env.ref('base.EUR').id,
'name': 'test',
'account_id': self.account_receivable.id,
'type': type,
'date_invoice': time.strftime('%Y-%m-%d'),
'payment_mode_id': self.payment_mode.id,
'mandate_id': self.env.ref(mandate_xmlid).id,
})
self.invoice_line_model.create({
'invoice_id': invoice.id,
'price_unit': price_unit,
'quantity': 1,
'name': 'Great service',
'account_id': self.account_revenue.id,
})
invoice.signal_workflow('invoice_open')
return invoice

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
Copyright (C) 2013-2016 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
@@ -11,41 +11,37 @@
<data>
<record id="sdd_mandate_form" model="ir.ui.view">
<record id="view_mandate_form" model="ir.ui.view">
<field name="name">sdd.mandate.form</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_form"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="scheme" attrs="{'invisible': [('format', '!=', 'sepa')],
'required': [('format', '=', 'sepa')]}"/>
<field name="type" attrs="{'invisible': [('format', '!=', 'sepa')],
'required': [('format', '=', 'sepa')]}"/>
<field name="recurrent_sequence_type"
attrs="{'invisible': ['|', ('type', '=', 'oneoff'), ('format', '!=', 'sepa')],
'required': [('type', '=', 'recurrent')]}"/>
<field name="scheme" attrs="{'invisible': [('format', '!=', 'sepa')],
'required': [('format', '=', 'sepa')]}"/>
</field>
<field name="last_debit_date" position="after">
<field name="sepa_migrated" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
<field name="original_mandate_identification" attrs="{'invisible': [('sepa_migrated', '=', True)], 'required': [('sepa_migrated', '=', False)]}" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
</field>
</field>
</record>
<record id="sdd_mandate_tree" model="ir.ui.view">
<record id="view_mandate_tree" model="ir.ui.view">
<field name="name">sdd.mandate.tree</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_tree"/>
<field name="arch" type="xml">
<field name="unique_mandate_reference" position="after">
<field name="type" string="Type"/>
<field name="scheme"/>
<field name="type" string="Type"/>
<field name="recurrent_sequence_type" string="Sequence Type"/>
</field>
</field>
</record>
<record id="sdd_mandate_search" model="ir.ui.view">
<record id="view_mandate_search" model="ir.ui.view">
<field name="name">sdd.mandate.search</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_search"/>
@@ -66,67 +62,5 @@
</field>
</record>
<record id="mandate_action" model="ir.actions.act_window">
<field name="name">SEPA Direct Debit Mandates</field>
<field name="res_model">account.banking.mandate</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new SEPA Direct Debit Mandate.
</p><p>
A SEPA Direct Debit Mandate is a document signed by your customer that gives you the autorization to do one or several direct debits on his bank account.
</p>
</field>
</record>
<menuitem id="account_banking_mandate.mandate_menu"
parent="account_payment.menu_main_payment"
action="mandate_action"
sequence="20"
/>
<record id="sdd_mandate_partner_bank_tree" model="ir.ui.view">
<field name="name">sdd.mandate.res.partner.bank.tree</field>
<field name="model">res.partner.bank</field>
<field name="inherit_id" ref="account_banking_mandate.mandate_partner_bank_tree"/>
<field name="arch" type="xml">
<field name="mandate_ids" position="attributes">
<attribute name="string">SDD Mandates</attribute>
</field>
</field>
</record>
<record id="sdd_mandate_partner_form" model="ir.ui.view">
<field name="name">sdd.mandate.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account_banking_mandate.mandate_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='bank_ids']/tree/field[@name='mandate_ids']" position="attributes">
<attribute name="string">SDD Mandates</attribute>
</xpath>
</field>
</record>
<record id="recurrent_sequence_type_first" model="mail.message.subtype">
<field name="name">Sequence Type set to First</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to First</field>
</record>
<record id="recurrent_sequence_type_recurring" model="mail.message.subtype">
<field name="name">Sequence Type set to Recurring</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to Recurring</field>
</record>
<record id="recurrent_sequence_type_final" model="mail.message.subtype">
<field name="name">Sequence Type set to Final</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to Final</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
© 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<data>
<record id="account_payment_mode_form" model="ir.ui.view">
<field name="name">Add SEPA identifiers on payment mode form</field>
<field name="model">account.payment.mode</field>
<field name="inherit_id" ref="account_banking_pain_base.account_payment_mode_form"/>
<field name="arch" type="xml">
<group name="main" position="inside">
<field name="sepa_creditor_identifier" invisible="1"/>
<!-- This field should be set visible by localization modules
for countries that may have several sepa_creditor_identifier for
the same company (I guess only Spain) -->
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<data>
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
<field name="name">Add SEPA identifiers</field>
<field name="model">payment.mode</field>
<field name="inherit_id" ref="account_banking_pain_base.view_payment_mode_form_inherit"/>
<field name="arch" type="xml">
<group name="sepa_identifiers" position="inside">
<group>
<field name="sepa_creditor_identifier"
attrs="{'invisible': [('sepa_type', '!=', 'sepa_direct_debit')]}"/>
<field name="original_creditor_identifier"
attrs="{'invisible': [('sepa_type', '!=', 'sepa_direct_debit')]}"/>
</group>
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -3,6 +3,8 @@
<data>
<template id="sepa_direct_debit_mandate_document">
<t t-call="report.external_layout">
<t t-set="doc" t-value="doc.with_context({'lang':doc.partner_id.lang})" />
<div class="page">
<style type="text/css">
.under-line{
@@ -20,11 +22,11 @@
</style>
<div class="row mt0">
<div class="col-xs-3">
<img t-if="o.company_id.logo" t-att-src="'data:image/png;base64,%s' % o.company_id.logo" style="max-height: 45px;"/>
<img t-if="doc.company_id.logo" t-att-src="'data:image/png;base64,%s' % doc.company_id.logo" style="max-height: 45px;"/>
</div>
<div class="col-xs-12 text-center">
<h4 t-if="o.scheme != 'B2B'"><strong>Sepa Direct Debit Mandate</strong></h4>
<h4 t-if="o.scheme == 'B2B'"><strong>Sepa Business-To-Business Direct debit Mandate</strong></h4>
<h4 t-if="doc.scheme != 'B2B'"><strong>Sepa Direct Debit Mandate</strong></h4>
<h4 t-if="doc.scheme == 'B2B'"><strong>Sepa Business-To-Business Direct debit Mandate</strong></h4>
</div>
</div>
<div class="row mt8">
@@ -32,38 +34,38 @@
<div class="panel panel-default">
<span class="col-xs-12 text-right" style="font-size:8px;">To be completed by the creditor</span>
<div class="panel-body mb8">
<div class="col-xs-12"><em>Mandate Reference:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.unique_mandate_reference"/></div>
<div class="col-xs-12"><em>Identifier:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.company_id.sepa_creditor_identifier"/></div>
<div class="col-xs-12"><em>Creditor's Name:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.company_id.partner_id.name"/></div>
<div class="col-xs-12"><em>Address:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.company_id.partner_id.street"/></div>
<div class="col-xs-12"><em>Mandate Reference:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.unique_mandate_reference"/></div>
<div class="col-xs-12"><em>Identifier:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.company_id.sepa_creditor_identifier"/></div>
<div class="col-xs-12"><em>Creditor's Name:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.company_id.partner_id.name"/></div>
<div class="col-xs-12"><em>Address:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.company_id.partner_id.street"/></div>
<div class="col-xs-12"><em>Postal Code - City - Town:</em></div>
<div class="col-xs-10 col-xs-offset-2 under-line mt8">
<span t-field="o.company_id.partner_id.zip"/> -
<span t-field="o.company_id.partner_id.city"/> -
<span t-field="o.company_id.partner_id.state_id"/>
<span t-field="doc.company_id.partner_id.zip"/> -
<span t-field="doc.company_id.partner_id.city"/> -
<span t-field="doc.company_id.partner_id.state_id"/>
</div>
<div class="col-xs-12"><em>Country:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.company_id.partner_id.country_id"/></div>
<div class="col-xs-12"><em>Country:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.company_id.partner_id.country_id"/></div>
</div>
</div>
</div>
</div>
<div class="row mt0">
<div class="col-xs-12">
<p>By signing this mandate form, you authorise (A) <strong t-if="o.scheme == 'B2B'"><span t-field="o.company_id.name"/></strong>
<p>By signing this mandate form, you authorise (A) <strong t-if="doc.scheme == 'B2B'"><span t-field="doc.company_id.name"/></strong>
to send instructions to your bank to debit your account and (B) your bank to
debit your account in accordance with the instructions from <strong t-if="o.scheme == 'B2B'"><span t-field="o.company_id.name"/></strong>.
debit your account in accordance with the instructions from <strong t-if="doc.scheme == 'B2B'"><span t-field="doc.company_id.name"/></strong>.
</p>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<p t-if="o.scheme == 'B2B'">
<p t-if="doc.scheme == 'B2B'">
This mandate is only intended for business-to-business transactions.
You are not entitled to a refund from your bank after your account has
been debited, but you are entitled to request your bank
not to debit your account up until the day on which the payment is due.
</p>
<p t-if="o.scheme != 'B2B'">
<p t-if="doc.scheme != 'B2B'">
As part of your rights, you are entitled to a refund from
your bank under the terms and conditions of your agreement
with your bank.
@@ -76,21 +78,21 @@
<div class="panel panel-default">
<span class="col-xs-12 text-right" style="font-size:8px;">To be completed by the debtor</span>
<div class="panel-body">
<div class="col-xs-12"><em>Debtor's Name:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.partner_id"/></div>
<div class="col-xs-12"><em>Address of the Debtor:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.partner_id.street"/></div>
<div class="col-xs-12"><em>Debtor's Name:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.partner_id"/></div>
<div class="col-xs-12"><em>Address of the Debtor:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.partner_id.street"/></div>
<div class="col-xs-12"><em>Postal Code - City - Town:</em></div>
<div class="col-xs-10 col-xs-offset-2 under-line mt4">
<span t-field="o.partner_id.zip"/> -
<span t-field="o.partner_id.city"/> -
<span t-field="o.partner_id.state_id"/>
<span t-field="doc.partner_id.zip"/> -
<span t-field="doc.partner_id.city"/> -
<span t-field="doc.partner_id.state_id"/>
</div>
<div class="col-xs-12"><em>Country of the debtor:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.partner_id.country_id"/></div>
<div class="col-xs-12"><em>Swift BIC (up to 8 or 11 characteres):</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.partner_bank_id.bank_bic"/></div>
<div class="col-xs-12"><em>Account Number - IBAN:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="o.partner_bank_id.acc_number"/></div>
<div class="col-xs-12"><em>Country of the debtor:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.partner_id.country_id"/></div>
<div class="col-xs-12"><em>Swift BIC (up to 8 or 11 characteres):</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.partner_bank_id.bank_bic"/></div>
<div class="col-xs-12"><em>Account Number - IBAN:</em></div><div class="col-xs-10 col-xs-offset-2 under-line mt4"><span t-field="doc.partner_bank_id.acc_number"/></div>
<div class="col-xs-12"><em>Type of payment:</em></div>
<div class="col-xs-10 col-xs-offset-2 mt4">
<input type="checkbox" t-att-checked="o.type=='recurrent' or None"> Recurrent</input>
<input type="checkbox" t-att-checked="o.type=='oneoff' or None"> One-Off</input>
<input type="checkbox" t-att-checked="doc.type=='recurrent' or None"> Recurrent</input>
<input type="checkbox" t-att-checked="doc.type=='oneoff' or None"> One-Off</input>
</div>
<div class="col-xs-12"><em>Date - Location:</em></div>
<div class="col-xs-10 col-xs-offset-2 under-line mt16"/>
@@ -102,19 +104,20 @@
</div>
<div class="row">
<div class="col-xs-12 text-center">
<p t-if="o.scheme != 'B2B'">ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE.</p>
<p t-if="o.scheme == 'B2B'">ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE.
<p t-if="doc.scheme != 'B2B'">ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE.</p>
<p t-if="doc.scheme == 'B2B'">ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE.
NEVERTHELESS, THE BANK OF DEBTOR REQUIRES DEBTORS AUTHORIZATION BEFORE DEBITING B2B DIRECT DEBITS IN THE ACCOUNT.
THE DEBTOR WILL BE ABLE TO MANAGE THE MENTIONED AUTHORIZATION THROUGH THE MEANS PROVIDED BY HIS BANK.</p>
</div>
</div>
</div>
</t>
</template>
<template id="sepa_direct_debit_mandate">
<t t-call="report.html_container">
<t t-foreach="doc_ids" t-as="doc_id">
<t t-raw="translate_doc(doc_id, doc_model, 'partner_id.lang', 'account_banking_sepa_direct_debit.sepa_direct_debit_mandate_document')"/>
<t t-foreach="docs" t-as="doc">
<t t-call="account_banking_sepa_direct_debit.sepa_direct_debit_mandate_document" t-lang="doc.partner_id.lang"/>
</t>
</t>
</template>

View File

@@ -1,20 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
Copyright (C) 2013-2016 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="sdd_res_company_form" model="ir.ui.view">
<record id="view_company_form" model="ir.ui.view">
<field name="name">sepa_direct_debit.res.company.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="account_banking_pain_base.view_company_form"/>
<field name="arch" type="xml">
<group name="pain" position="inside">
<field name="sepa_creditor_identifier"/>
<field name="original_creditor_identifier" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
</group>
</field>
</record>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_account_config_settings" model="ir.ui.view">
<field name="name">sepa_direct_debit.account_config_settings.form</field>
<field name="model">account.config.settings</field>
<field name="inherit_id" ref="account.view_account_config_settings"/>
<field name="arch" type="xml">
<div name="payment_acquirer" position="before">
<div name="sepa_direct_debit">
<label for="sepa_creditor_identifier"/>
<field name="sepa_creditor_identifier" class="oe_inline"
placeholder="Write the ICS of your company"/>
</div>
</div>
</field>
</record>
</data>
</odoo>

View File

@@ -1,23 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import export_sdd

View File

@@ -1,394 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for Odoo
# Copyright (C) 2013-2015 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api, _
from openerp.exceptions import Warning
from openerp import workflow
from lxml import etree
class BankingExportSddWizard(models.TransientModel):
_name = 'banking.export.sdd.wizard'
_inherit = ['banking.export.pain']
_description = 'Export SEPA Direct Debit File'
state = fields.Selection([
('create', 'Create'),
('finish', 'Finish'),
], string='State', readonly=True, default='create')
batch_booking = fields.Boolean(
string='Batch Booking',
help="If true, the bank statement will display only one credit "
"line for all the direct debits of the SEPA file ; if false, "
"the bank statement will display one credit line per direct "
"debit of the SEPA file.")
charge_bearer = fields.Selection([
('SLEV', 'Following Service Level'),
('SHAR', 'Shared'),
('CRED', 'Borne by Creditor'),
('DEBT', 'Borne by Debtor'),
], string='Charge Bearer', required=True, default='SLEV',
help="Following service level : transaction charges are to be "
"applied following the rules agreed in the service level "
"and/or scheme (SEPA Core messages must use this). Shared : "
"transaction charges on the creditor side are to be borne "
"by the creditor, transaction charges on the debtor side are "
"to be borne by the debtor. Borne by creditor : all "
"transaction charges are to be borne by the creditor. Borne "
"by debtor : all transaction charges are to be borne by the debtor.")
nb_transactions = fields.Integer(
string='Number of Transactions', readonly=True)
total_amount = fields.Float(string='Total Amount', readonly=True)
file = fields.Binary(string="File", readonly=True)
filename = fields.Char(string="Filename", readonly=True)
payment_order_ids = fields.Many2many(
'payment.order', 'wiz_sdd_payorders_rel', 'wizard_id',
'payment_order_id', string='Payment Orders', readonly=True)
@api.model
def create(self, vals):
payment_order_ids = self._context.get('active_ids', [])
vals.update({
'payment_order_ids': [[6, 0, payment_order_ids]],
})
return super(BankingExportSddWizard, self).create(vals)
def _get_previous_bank(self, payline):
previous_bank = False
older_lines = self.env['payment.line'].search([
('mandate_id', '=', payline.mandate_id.id),
('bank_id', '!=', payline.bank_id.id)])
if older_lines:
previous_date = False
previous_payline = False
for older_line in older_lines:
if hasattr(older_line.order_id, 'date_sent'):
older_line_date = older_line.order_id.date_sent
else:
older_line_date = older_line.order_id.date_done
if (older_line_date and
older_line_date > previous_date):
previous_date = older_line_date
previous_payline = older_line
if previous_payline:
previous_bank = previous_payline.bank_id
return previous_bank
@api.multi
def create_sepa(self):
"""Creates the SEPA Direct Debit file. That's the important code !"""
pain_flavor = self.payment_order_ids[0].mode.type.code
convert_to_ascii = \
self.payment_order_ids[0].mode.convert_to_ascii
if pain_flavor == 'pain.008.001.02':
bic_xml_tag = 'BIC'
name_maxsize = 70
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor == 'pain.008.001.03':
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor == 'pain.008.001.04':
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrDrctDbtInitn'
else:
raise Warning(
_("Payment Type Code '%s' is not supported. The only "
"Payment Type Code supported for SEPA Direct Debit are "
"'pain.008.001.02', 'pain.008.001.03' and "
"'pain.008.001.04'.") % pain_flavor)
gen_args = {
'bic_xml_tag': bic_xml_tag,
'name_maxsize': name_maxsize,
'convert_to_ascii': convert_to_ascii,
'payment_method': 'DD',
'file_prefix': 'sdd_',
'pain_flavor': pain_flavor,
'pain_xsd_file':
'account_banking_sepa_direct_debit/data/%s.xsd' % pain_flavor,
}
pain_ns = {
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
None: 'urn:iso:std:iso:20022:tech:xsd:%s' % pain_flavor,
}
xml_root = etree.Element('Document', nsmap=pain_ns)
pain_root = etree.SubElement(xml_root, root_xml_tag)
# A. Group header
group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 = \
self.generate_group_header_block(pain_root, gen_args)
transactions_count_1_6 = 0
total_amount = 0.0
amount_control_sum_1_7 = 0.0
lines_per_group = {}
# key = (requested_date, priority, sequence type)
# value = list of lines as objects
# Iterate on payment orders
for payment_order in self.payment_order_ids:
total_amount = total_amount + payment_order.total
# Iterate each payment lines
for line in payment_order.bank_line_ids:
transactions_count_1_6 += 1
priority = line.priority
# The field line.date is the requested payment date
# taking into account the 'date_prefered' setting
# cf account_banking_payment_export/models/account_payment.py
# in the inherit of action_open()
if not line.mandate_id:
raise Warning(
_("Missing SEPA Direct Debit mandate on the "
"bank payment line with partner '%s' "
"(reference '%s').")
% (line.partner_id.name, line.name))
scheme = line.mandate_id.scheme
if line.mandate_id.state != 'valid':
raise Warning(
_("The SEPA Direct Debit mandate with reference '%s' "
"for partner '%s' has expired.")
% (line.mandate_id.unique_mandate_reference,
line.mandate_id.partner_id.name))
if line.mandate_id.type == 'oneoff':
seq_type = 'OOFF'
if line.mandate_id.last_debit_date:
raise Warning(
_("The mandate with reference '%s' for partner "
"'%s' has type set to 'One-Off' and it has a "
"last debit date set to '%s', so we can't use "
"it.")
% (line.mandate_id.unique_mandate_reference,
line.mandate_id.partner_id.name,
line.mandate_id.last_debit_date))
elif line.mandate_id.type == 'recurrent':
seq_type_map = {
'recurring': 'RCUR',
'first': 'FRST',
'final': 'FNAL',
}
seq_type_label = \
line.mandate_id.recurrent_sequence_type
assert seq_type_label is not False
seq_type = seq_type_map[seq_type_label]
key = (line.date, priority, seq_type, scheme)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
lines_per_group[key] = [line]
for (requested_date, priority, sequence_type, scheme), lines in \
lines_per_group.items():
# B. Payment info
payment_info_2_0, nb_of_transactions_2_4, control_sum_2_5 = \
self.generate_start_payment_info_block(
pain_root,
"self.payment_order_ids[0].reference + '-' + "
"sequence_type + '-' + requested_date.replace('-', '') "
"+ '-' + priority",
priority, scheme, sequence_type, requested_date, {
'self': self,
'sequence_type': sequence_type,
'priority': priority,
'requested_date': requested_date,
}, gen_args)
self.generate_party_block(
payment_info_2_0, 'Cdtr', 'B',
'self.payment_order_ids[0].mode.bank_id.partner_id.'
'name',
'self.payment_order_ids[0].mode.bank_id.acc_number',
'self.payment_order_ids[0].mode.bank_id.bank.bic or '
'self.payment_order_ids[0].mode.bank_id.bank_bic',
{'self': self}, gen_args)
charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr')
charge_bearer_2_24.text = self.charge_bearer
creditor_scheme_identification_2_27 = etree.SubElement(
payment_info_2_0, 'CdtrSchmeId')
self.generate_creditor_scheme_identification(
creditor_scheme_identification_2_27,
'self.payment_order_ids[0].mode.'
'sepa_creditor_identifier or '
'self.payment_order_ids[0].company_id.'
'sepa_creditor_identifier',
'SEPA Creditor Identifier', {'self': self}, 'SEPA', gen_args)
transactions_count_2_4 = 0
amount_control_sum_2_5 = 0.0
for line in lines:
transactions_count_2_4 += 1
# C. Direct Debit Transaction Info
dd_transaction_info_2_28 = etree.SubElement(
payment_info_2_0, 'DrctDbtTxInf')
payment_identification_2_29 = etree.SubElement(
dd_transaction_info_2_28, 'PmtId')
end2end_identification_2_31 = etree.SubElement(
payment_identification_2_29, 'EndToEndId')
end2end_identification_2_31.text = self._prepare_field(
'End to End Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
currency_name = self._prepare_field(
'Currency Code', 'line.currency.name',
{'line': line}, 3, gen_args=gen_args)
instructed_amount_2_44 = etree.SubElement(
dd_transaction_info_2_28, 'InstdAmt', Ccy=currency_name)
instructed_amount_2_44.text = '%.2f' % line.amount_currency
amount_control_sum_1_7 += line.amount_currency
amount_control_sum_2_5 += line.amount_currency
dd_transaction_2_46 = etree.SubElement(
dd_transaction_info_2_28, 'DrctDbtTx')
mandate_related_info_2_47 = etree.SubElement(
dd_transaction_2_46, 'MndtRltdInf')
mandate_identification_2_48 = etree.SubElement(
mandate_related_info_2_47, 'MndtId')
mandate_identification_2_48.text = self._prepare_field(
'Unique Mandate Reference',
'line.mandate_id.unique_mandate_reference',
{'line': line}, 35, gen_args=gen_args)
mandate_signature_date_2_49 = etree.SubElement(
mandate_related_info_2_47, 'DtOfSgntr')
mandate_signature_date_2_49.text = self._prepare_field(
'Mandate Signature Date',
'line.mandate_id.signature_date',
{'line': line}, 10, gen_args=gen_args)
if sequence_type == 'FRST' and (
line.mandate_id.last_debit_date or
not line.mandate_id.sepa_migrated):
previous_bank = self._get_previous_bank(line)
if previous_bank or not line.mandate_id.sepa_migrated:
amendment_indicator_2_50 = etree.SubElement(
mandate_related_info_2_47, 'AmdmntInd')
amendment_indicator_2_50.text = 'true'
amendment_info_details_2_51 = etree.SubElement(
mandate_related_info_2_47, 'AmdmntInfDtls')
if previous_bank:
if (previous_bank.bank.bic or
previous_bank.bank_bic) == \
(line.bank_id.bank.bic or
line.bank_id.bank_bic):
ori_debtor_account_2_57 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlDbtrAcct')
ori_debtor_account_id = etree.SubElement(
ori_debtor_account_2_57, 'Id')
ori_debtor_account_iban = etree.SubElement(
ori_debtor_account_id, 'IBAN')
ori_debtor_account_iban.text = self._validate_iban(
self._prepare_field(
'Original Debtor Account',
'previous_bank.acc_number',
{'previous_bank': previous_bank},
gen_args=gen_args))
else:
ori_debtor_agent_2_58 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlDbtrAgt')
ori_debtor_agent_institution = etree.SubElement(
ori_debtor_agent_2_58, 'FinInstnId')
ori_debtor_agent_bic = etree.SubElement(
ori_debtor_agent_institution, bic_xml_tag)
ori_debtor_agent_bic.text = self._prepare_field(
'Original Debtor Agent',
'previous_bank.bank.bic or '
'previous_bank.bank_bic',
{'previous_bank': previous_bank},
gen_args=gen_args)
ori_debtor_agent_other = etree.SubElement(
ori_debtor_agent_institution, 'Othr')
ori_debtor_agent_other_id = etree.SubElement(
ori_debtor_agent_other, 'Id')
ori_debtor_agent_other_id.text = 'SMNDA'
# SMNDA = Same Mandate New Debtor Agent
elif not line.mandate_id.sepa_migrated:
ori_mandate_identification_2_52 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlMndtId')
ori_mandate_identification_2_52.text = \
self._prepare_field(
'Original Mandate Identification',
'line.mandate_id.'
'original_mandate_identification',
{'line': line},
gen_args=gen_args)
ori_creditor_scheme_id_2_53 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlCdtrSchmeId')
self.generate_creditor_scheme_identification(
ori_creditor_scheme_id_2_53,
'self.payment_order_ids[0].mode.'
'original_creditor_identifier or '
'self.payment_order_ids[0].company_id.'
'original_creditor_identifier',
'Original Creditor Identifier',
{'self': self}, 'SEPA', gen_args)
self.generate_party_block(
dd_transaction_info_2_28, 'Dbtr', 'C',
'line.partner_id.name',
'line.bank_id.acc_number',
'line.bank_id.bank.bic or '
'line.bank_id.bank_bic',
{'line': line}, gen_args)
self.generate_remittance_info_block(
dd_transaction_info_2_28, line, gen_args)
nb_of_transactions_2_4.text = unicode(transactions_count_2_4)
control_sum_2_5.text = '%.2f' % amount_control_sum_2_5
nb_of_transactions_1_6.text = unicode(transactions_count_1_6)
control_sum_1_7.text = '%.2f' % amount_control_sum_1_7
return self.finalize_sepa_file_creation(
xml_root, total_amount, transactions_count_1_6, gen_args)
@api.multi
def save_sepa(self):
"""Save the SEPA Direct Debit file: mark all payments in the file
as 'sent'. Write 'last debit date' on mandate and set oneoff
mandate to expired.
"""
abmo = self.env['account.banking.mandate']
for order in self.payment_order_ids:
workflow.trg_validate(
self._uid, 'payment.order', order.id, 'done', self._cr)
self.env['ir.attachment'].create({
'res_model': 'payment.order',
'res_id': order.id,
'name': self.filename,
'datas': self.file,
})
to_expire_mandates = abmo.browse([])
first_mandates = abmo.browse([])
all_mandates = abmo.browse([])
for bline in order.bank_line_ids:
if bline.mandate_id in all_mandates:
continue
all_mandates += bline.mandate_id
if bline.mandate_id.type == 'oneoff':
to_expire_mandates += bline.mandate_id
elif bline.mandate_id.type == 'recurrent':
seq_type = bline.mandate_id.recurrent_sequence_type
if seq_type == 'final':
to_expire_mandates += bline.mandate_id
elif seq_type == 'first':
first_mandates += bline.mandate_id
all_mandates.write(
{'last_debit_date': fields.Date.context_today(self)})
to_expire_mandates.write({'state': 'expired'})
first_mandates.write({
'recurrent_sequence_type': 'recurring',
'sepa_migrated': True,
})
return True

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2010-2012 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="banking_export_sdd_wizard_view" model="ir.ui.view">
<field name="name">banking.export.sdd.wizard.view</field>
<field name="model">banking.export.sdd.wizard</field>
<field name="arch" type="xml">
<form string="SEPA Direct Debit XML file generation">
<field name="state" invisible="True"/>
<group states="create">
<field name="batch_booking" />
<field name="charge_bearer" />
</group>
<group states="finish">
<field name="total_amount" />
<field name="nb_transactions" />
<field name="file" filename="filename" />
<field name="filename" invisible="True"/>
</group>
<footer>
<button string="Generate" name="create_sepa" type="object" class="oe_highlight" states="create"/>
<button string="Validate" name="save_sepa" type="object" class="oe_highlight" states="finish"/>
<button string="Cancel" special="cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
</data>
</openerp>