Add support for pain.001.001.05 Fix dependencies (I need the field "payment_order_type" which is in account_banking_payment) Only use ASCII characters in the XML file (that's how banks use XML !) Use the payment order reference as the Message Identification of the XML file. Use the payment line sequence as the "End to End Identification" Remove the Instruction Identification ('InstrId') because it is not a required field (only the End to End identification is required) Rename variables in the function create_sepa() to include the number of each field as used in the EPC guidelines Reduce the number of flake8 warnings Make code more modular : - self.pool.get('banking.export.sepa').create() has a _prepare fonction - most of the content of the XML file can be inherited via _prepare_field()

This commit is contained in:
Alexis de Lattre
2013-11-06 16:42:24 +01:00
committed by Enric Tobella
parent 56693ea1cf
commit f42492897c
7 changed files with 1269 additions and 214 deletions

View File

@@ -21,12 +21,15 @@
{ {
'name': 'Account Banking SEPA Credit Transfer', 'name': 'Account Banking SEPA Credit Transfer',
'summary': 'Create SEPA XML files for Credit Transfers', 'summary': 'Create SEPA XML files for Credit Transfers',
'version': '0.1', 'version': '0.2',
'license': 'AGPL-3', 'license': 'AGPL-3',
'author': 'Akretion', 'author': 'Akretion',
'website': 'http://www.akretion.com', 'website': 'http://www.akretion.com',
'category': 'Banking addons', 'category': 'Banking addons',
'depends': ['account_banking_payment_export'], 'depends': ['account_banking_payment'],
'external_dependencies': {
'python': ['unidecode', 'lxml'],
},
'data': [ 'data': [
'account_banking_sepa_view.xml', 'account_banking_sepa_view.xml',
'wizard/export_sepa_view.xml', 'wizard/export_sepa_view.xml',
@@ -36,7 +39,7 @@
'description': ''' 'description': '''
Module to export payment orders in SEPA XML file format. Module to export payment orders in SEPA XML file format.
SEPA PAIN (PAyment INitiation) is the new european standard for Customer-to-Bank payment instructions. This module implements SEPA Credit Transfer (SCT), more specifically PAIN versions 001.001.02, 001.001.03 and 001.001.04. It is part of the ISO 20022 standard, available on http://www.iso20022.org. SEPA PAIN (PAyment INitiation) is the new european standard for Customer-to-Bank payment instructions. This module implements SEPA Credit Transfer (SCT), more specifically PAIN versions 001.001.02, 001.001.03, 001.001.04 and 001.001.05. It is part of the ISO 20022 standard, available on http://www.iso20022.org.
The Implementation Guidelines for SEPA Credit Transfer published by the European Payments Council (http://http://www.europeanpaymentscouncil.eu) use PAIN version 001.001.03, so it's probably the version of PAIN that you should try first. The Implementation Guidelines for SEPA Credit Transfer published by the European Payments Council (http://http://www.europeanpaymentscouncil.eu) use PAIN version 001.001.03, so it's probably the version of PAIN that you should try first.

View File

@@ -21,18 +21,24 @@
from openerp.osv import orm, fields from openerp.osv import orm, fields
from openerp.addons.decimal_precision import decimal_precision as dp from openerp.addons.decimal_precision import decimal_precision as dp
from unidecode import unidecode
class banking_export_sepa(orm.Model): class banking_export_sepa(orm.Model):
'''SEPA export''' '''SEPA export'''
_name = 'banking.export.sepa' _name = 'banking.export.sepa'
_description = __doc__ _description = __doc__
_rec_name = 'msg_identification' _rec_name = 'filename'
def _generate_filename(self, cr, uid, ids, name, arg, context=None): def _generate_filename(self, cr, uid, ids, name, arg, context=None):
res = {} res = {}
for sepa_file in self.browse(cr, uid, ids, context=context): for sepa_file in self.browse(cr, uid, ids, context=context):
res[sepa_file.id] = 'sepa_' + (sepa_file.msg_identification or '') + '.xml' ref = sepa_file.payment_order_ids[0].reference
if ref:
label = ref.replace('/', '-')
else:
label = 'error'
res[sepa_file.id] = 'sct_%s.xml' % label
return res return res
_columns = { _columns = {
@@ -40,32 +46,34 @@ class banking_export_sepa(orm.Model):
'payment.order', 'payment.order',
'account_payment_order_sepa_rel', 'account_payment_order_sepa_rel',
'banking_export_sepa_id', 'account_order_id', 'banking_export_sepa_id', 'account_order_id',
'Payment orders', 'Payment Orders',
readonly=True), readonly=True),
'prefered_exec_date': fields.date('Prefered execution date', readonly=True), 'prefered_exec_date': fields.date(
'nb_transactions': fields.integer('Number of transactions', readonly=True), 'Prefered Execution Date', readonly=True),
'total_amount': fields.float('Total amount', 'nb_transactions': fields.integer(
'Number of Transactions', readonly=True),
'total_amount': fields.float('Total Amount',
digits_compute=dp.get_precision('Account'), readonly=True), digits_compute=dp.get_precision('Account'), readonly=True),
'msg_identification': fields.char('Message identification', size=35, 'batch_booking': fields.boolean(
readonly=True), 'Batch Booking', readonly=True,
'batch_booking': fields.boolean('Batch booking', readonly=True,
help="If true, the bank statement will display only one debit line for all the wire transfers of the SEPA XML file ; if false, the bank statement will display one debit line per wire transfer of the SEPA XML file."), help="If true, the bank statement will display only one debit line for all the wire transfers of the SEPA XML file ; if false, the bank statement will display one debit line per wire transfer of the SEPA XML file."),
'charge_bearer': fields.selection([ 'charge_bearer': fields.selection([
('SHAR', 'Shared'), ('SHAR', 'Shared'),
('CRED', 'Borne by creditor'), ('CRED', 'Borne by creditor'),
('DEBT', 'Borne by debtor'), ('DEBT', 'Borne by debtor'),
('SLEV', 'Following service level'), ('SLEV', 'Following service level'),
], 'Charge bearer', readonly=True, ], 'Charge Bearer', readonly=True,
help='Shared : transaction charges on the sender side are to be borne by the debtor, transaction charges on the receiver side are to be borne by the creditor (most transfers use this). 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. Following service level : transaction charges are to be applied following the rules agreed in the service level and/or scheme.'), help='Shared : transaction charges on the sender side are to be borne by the debtor, transaction charges on the receiver side are to be borne by the creditor (most transfers use this). 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. Following service level : transaction charges are to be applied following the rules agreed in the service level and/or scheme.'),
'generation_date': fields.datetime('Generation date', 'generation_date': fields.datetime(
'Generation Date', readonly=True),
'file': fields.binary('SEPA XML File', readonly=True),
'filename': fields.function(
_generate_filename, type='char', size=256, string='Filename',
readonly=True), readonly=True),
'file': fields.binary('SEPA XML file', readonly=True),
'filename': fields.function(_generate_filename, type='char', size=256,
method=True, string='Filename', readonly=True),
'state': fields.selection([ 'state': fields.selection([
('draft', 'Draft'), ('draft', 'Draft'),
('sent', 'Sent'), ('sent', 'Sent'),
('done', 'Reconciled'), ('done', 'Reconciled'),
], 'State', readonly=True), ], 'State', readonly=True),
} }

View File

@@ -14,7 +14,6 @@
<form string="SEPA Credit Transfer"> <form string="SEPA Credit Transfer">
<notebook> <notebook>
<page string="General Information"> <page string="General Information">
<field name="msg_identification" select="1" />
<field name="total_amount" /> <field name="total_amount" />
<field name="nb_transactions" /> <field name="nb_transactions" />
<field name="prefered_exec_date" /> <field name="prefered_exec_date" />
@@ -47,7 +46,7 @@
<field name="model">banking.export.sepa</field> <field name="model">banking.export.sepa</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="SEPA Credit Transfer"> <tree string="SEPA Credit Transfer">
<field name="msg_identification"/> <field name="filename"/>
<field name="prefered_exec_date"/> <field name="prefered_exec_date"/>
<field name="generation_date"/> <field name="generation_date"/>
<field name="nb_transactions"/> <field name="nb_transactions"/>

View File

@@ -0,0 +1,931 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated by Standards Editor (build:R1.0.41.3) on 2013 Mar 05 13:39:40, ISO 20022 version : 2013-->
<xs:schema xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.05" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:iso:std:iso:20022:tech:xsd:pain.001.001.05">
<xs:element name="Document" type="Document"/>
<xs:complexType name="AccountIdentification4Choice">
<xs:choice>
<xs:element name="IBAN" type="IBAN2007Identifier"/>
<xs:element name="Othr" type="GenericAccountIdentification1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="AccountSchemeName1Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalAccountIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:simpleType name="ActiveOrHistoricCurrencyAndAmount_SimpleType">
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="5"/>
<xs:totalDigits value="18"/>
<xs:minInclusive value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ActiveOrHistoricCurrencyAndAmount">
<xs:simpleContent>
<xs:extension base="ActiveOrHistoricCurrencyAndAmount_SimpleType">
<xs:attribute name="Ccy" type="ActiveOrHistoricCurrencyCode" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="ActiveOrHistoricCurrencyCode">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{3,3}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="AddressType2Code">
<xs:restriction base="xs:string">
<xs:enumeration value="ADDR"/>
<xs:enumeration value="PBOX"/>
<xs:enumeration value="HOME"/>
<xs:enumeration value="BIZZ"/>
<xs:enumeration value="MLTO"/>
<xs:enumeration value="DLVY"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="AmountType3Choice">
<xs:choice>
<xs:element name="InstdAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element name="EqvtAmt" type="EquivalentAmount2"/>
</xs:choice>
</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:complexType name="Authorisation1Choice">
<xs:choice>
<xs:element name="Cd" type="Authorisation1Code"/>
<xs:element name="Prtry" type="Max128Text"/>
</xs:choice>
</xs:complexType>
<xs:simpleType name="Authorisation1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="AUTH"/>
<xs:enumeration value="FDET"/>
<xs:enumeration value="FSUM"/>
<xs:enumeration value="ILEV"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="BICFIIdentifier">
<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="BaseOneRate">
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="10"/>
<xs:totalDigits value="11"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="BatchBookingIndicator">
<xs:restriction base="xs:boolean"/>
</xs:simpleType>
<xs:complexType name="BranchAndFinancialInstitutionIdentification5">
<xs:sequence>
<xs:element name="FinInstnId" type="FinancialInstitutionIdentification8"/>
<xs:element maxOccurs="1" minOccurs="0" name="BrnchId" type="BranchData2"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BranchData2">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Id" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Nm" type="Max140Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="PstlAdr" type="PostalAddress6"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CashAccount24">
<xs:sequence>
<xs:element name="Id" type="AccountIdentification4Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="CashAccountType2Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="Ccy" type="ActiveOrHistoricCurrencyCode"/>
<xs:element maxOccurs="1" minOccurs="0" name="Nm" type="Max70Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CashAccountType2Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalCashAccountType1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="CategoryPurpose1Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalCategoryPurpose1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:simpleType name="ChargeBearerType1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="DEBT"/>
<xs:enumeration value="CRED"/>
<xs:enumeration value="SHAR"/>
<xs:enumeration value="SLEV"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Cheque7">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="ChqTp" type="ChequeType2Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="ChqNb" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="ChqFr" type="NameAndAddress10"/>
<xs:element maxOccurs="1" minOccurs="0" name="DlvryMtd" type="ChequeDeliveryMethod1Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="DlvrTo" type="NameAndAddress10"/>
<xs:element maxOccurs="1" minOccurs="0" name="InstrPrty" type="Priority2Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="ChqMtrtyDt" type="ISODate"/>
<xs:element maxOccurs="1" minOccurs="0" name="FrmsCd" type="Max35Text"/>
<xs:element maxOccurs="2" minOccurs="0" name="MemoFld" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="RgnlClrZone" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="PrtLctn" type="Max35Text"/>
<xs:element maxOccurs="5" minOccurs="0" name="Sgntr" type="Max70Text"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="ChequeDelivery1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="MLDB"/>
<xs:enumeration value="MLCD"/>
<xs:enumeration value="MLFA"/>
<xs:enumeration value="CRDB"/>
<xs:enumeration value="CRCD"/>
<xs:enumeration value="CRFA"/>
<xs:enumeration value="PUDB"/>
<xs:enumeration value="PUCD"/>
<xs:enumeration value="PUFA"/>
<xs:enumeration value="RGDB"/>
<xs:enumeration value="RGCD"/>
<xs:enumeration value="RGFA"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ChequeDeliveryMethod1Choice">
<xs:choice>
<xs:element name="Cd" type="ChequeDelivery1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:simpleType name="ChequeType2Code">
<xs:restriction base="xs:string">
<xs:enumeration value="CCHQ"/>
<xs:enumeration value="CCCH"/>
<xs:enumeration value="BCHQ"/>
<xs:enumeration value="DRFT"/>
<xs:enumeration value="ELDR"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ClearingSystemIdentification2Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalClearingSystemIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="ClearingSystemMemberIdentification2">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="ClrSysId" type="ClearingSystemIdentification2Choice"/>
<xs:element name="MmbId" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ContactDetails2">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="NmPrfx" type="NamePrefix1Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="Nm" type="Max140Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="PhneNb" type="PhoneNumber"/>
<xs:element maxOccurs="1" minOccurs="0" name="MobNb" type="PhoneNumber"/>
<xs:element maxOccurs="1" minOccurs="0" name="FaxNb" type="PhoneNumber"/>
<xs:element maxOccurs="1" minOccurs="0" name="EmailAdr" type="Max2048Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Othr" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="CountryCode">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{2,2}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="CreditDebitCode">
<xs:restriction base="xs:string">
<xs:enumeration value="CRDT"/>
<xs:enumeration value="DBIT"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="CreditTransferTransaction6">
<xs:sequence>
<xs:element name="PmtId" type="PaymentIdentification1"/>
<xs:element maxOccurs="1" minOccurs="0" name="PmtTpInf" type="PaymentTypeInformation19"/>
<xs:element name="Amt" type="AmountType3Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="XchgRateInf" type="ExchangeRate1"/>
<xs:element maxOccurs="1" minOccurs="0" name="ChrgBr" type="ChargeBearerType1Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="ChqInstr" type="Cheque7"/>
<xs:element maxOccurs="1" minOccurs="0" name="UltmtDbtr" type="PartyIdentification43"/>
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt1" type="BranchAndFinancialInstitutionIdentification5"/>
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt1Acct" type="CashAccount24"/>
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt2" type="BranchAndFinancialInstitutionIdentification5"/>
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt2Acct" type="CashAccount24"/>
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt3" type="BranchAndFinancialInstitutionIdentification5"/>
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt3Acct" type="CashAccount24"/>
<xs:element maxOccurs="1" minOccurs="0" name="CdtrAgt" type="BranchAndFinancialInstitutionIdentification5"/>
<xs:element maxOccurs="1" minOccurs="0" name="CdtrAgtAcct" type="CashAccount24"/>
<xs:element maxOccurs="1" minOccurs="0" name="Cdtr" type="PartyIdentification43"/>
<xs:element maxOccurs="1" minOccurs="0" name="CdtrAcct" type="CashAccount24"/>
<xs:element maxOccurs="1" minOccurs="0" name="UltmtCdtr" type="PartyIdentification43"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="InstrForCdtrAgt" type="InstructionForCreditorAgent1"/>
<xs:element maxOccurs="1" minOccurs="0" name="InstrForDbtrAgt" type="Max140Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Purp" type="Purpose2Choice"/>
<xs:element maxOccurs="10" minOccurs="0" name="RgltryRptg" type="RegulatoryReporting3"/>
<xs:element maxOccurs="1" minOccurs="0" name="Tax" type="TaxInformation3"/>
<xs:element maxOccurs="10" minOccurs="0" name="RltdRmtInf" type="RemittanceLocation2"/>
<xs:element maxOccurs="1" minOccurs="0" name="RmtInf" type="RemittanceInformation7"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="SplmtryData" type="SupplementaryData1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceInformation2">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="CreditorReferenceType2"/>
<xs:element maxOccurs="1" minOccurs="0" name="Ref" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceType1Choice">
<xs:choice>
<xs:element name="Cd" type="DocumentType3Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="CreditorReferenceType2">
<xs:sequence>
<xs:element name="CdOrPrtry" type="CreditorReferenceType1Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="Issr" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CustomerCreditTransferInitiationV05">
<xs:sequence>
<xs:element name="GrpHdr" type="GroupHeader48"/>
<xs:element maxOccurs="unbounded" minOccurs="1" name="PmtInf" type="PaymentInstruction9"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="SplmtryData" type="SupplementaryData1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DateAndPlaceOfBirth">
<xs:sequence>
<xs:element name="BirthDt" type="ISODate"/>
<xs:element maxOccurs="1" minOccurs="0" name="PrvcOfBirth" type="Max35Text"/>
<xs:element name="CityOfBirth" type="Max35Text"/>
<xs:element name="CtryOfBirth" type="CountryCode"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DatePeriodDetails">
<xs:sequence>
<xs:element name="FrDt" type="ISODate"/>
<xs:element name="ToDt" type="ISODate"/>
</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="DiscountAmountAndType1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="DiscountAmountType1Choice"/>
<xs:element name="Amt" type="ActiveOrHistoricCurrencyAndAmount"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DiscountAmountType1Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalDiscountAmountType1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="Document">
<xs:sequence>
<xs:element name="CstmrCdtTrfInitn" type="CustomerCreditTransferInitiationV05"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DocumentAdjustment1">
<xs:sequence>
<xs:element name="Amt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element maxOccurs="1" minOccurs="0" name="CdtDbtInd" type="CreditDebitCode"/>
<xs:element maxOccurs="1" minOccurs="0" name="Rsn" type="Max4Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="AddtlInf" type="Max140Text"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DocumentType3Code">
<xs:restriction base="xs:string">
<xs:enumeration value="RADM"/>
<xs:enumeration value="RPIN"/>
<xs:enumeration value="FXDR"/>
<xs:enumeration value="DISP"/>
<xs:enumeration value="PUOR"/>
<xs:enumeration value="SCOR"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="DocumentType5Code">
<xs:restriction base="xs:string">
<xs:enumeration value="MSIN"/>
<xs:enumeration value="CNFA"/>
<xs:enumeration value="DNFA"/>
<xs:enumeration value="CINV"/>
<xs:enumeration value="CREN"/>
<xs:enumeration value="DEBN"/>
<xs:enumeration value="HIRI"/>
<xs:enumeration value="SBIN"/>
<xs:enumeration value="CMCN"/>
<xs:enumeration value="SOAC"/>
<xs:enumeration value="DISP"/>
<xs:enumeration value="BOLD"/>
<xs:enumeration value="VCHR"/>
<xs:enumeration value="AROI"/>
<xs:enumeration value="TSUT"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="EquivalentAmount2">
<xs:sequence>
<xs:element name="Amt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element name="CcyOfTrf" type="ActiveOrHistoricCurrencyCode"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ExchangeRate1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="UnitCcy" type="ActiveOrHistoricCurrencyCode"/>
<xs:element maxOccurs="1" minOccurs="0" name="XchgRate" type="BaseOneRate"/>
<xs:element maxOccurs="1" minOccurs="0" name="RateTp" type="ExchangeRateType1Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="CtrctId" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="ExchangeRateType1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="SPOT"/>
<xs:enumeration value="SALE"/>
<xs:enumeration value="AGRD"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalAccountIdentification1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalCashAccountType1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</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="ExternalClearingSystemIdentification1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="5"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalDiscountAmountType1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalFinancialInstitutionIdentification1Code">
<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:simpleType name="ExternalTaxAmountType1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="FinancialIdentificationSchemeName1Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalFinancialInstitutionIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="FinancialInstitutionIdentification8">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="BICFI" type="BICFIIdentifier"/>
<xs:element maxOccurs="1" minOccurs="0" name="ClrSysMmbId" type="ClearingSystemMemberIdentification2"/>
<xs:element maxOccurs="1" minOccurs="0" name="Nm" type="Max140Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="PstlAdr" type="PostalAddress6"/>
<xs:element maxOccurs="1" minOccurs="0" name="Othr" type="GenericFinancialIdentification1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GenericAccountIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max34Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="SchmeNm" type="AccountSchemeName1Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="Issr" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GenericFinancialIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="SchmeNm" type="FinancialIdentificationSchemeName1Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="Issr" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GenericOrganisationIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="SchmeNm" type="OrganisationIdentificationSchemeName1Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="Issr" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GenericPersonIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="SchmeNm" type="PersonIdentificationSchemeName1Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="Issr" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GroupHeader48">
<xs:sequence>
<xs:element name="MsgId" type="Max35Text"/>
<xs:element name="CreDtTm" type="ISODateTime"/>
<xs:element maxOccurs="2" minOccurs="0" name="Authstn" type="Authorisation1Choice"/>
<xs:element name="NbOfTxs" type="Max15NumericText"/>
<xs:element maxOccurs="1" minOccurs="0" name="CtrlSum" type="DecimalNumber"/>
<xs:element name="InitgPty" type="PartyIdentification43"/>
<xs:element maxOccurs="1" minOccurs="0" name="FwdgAgt" type="BranchAndFinancialInstitutionIdentification5"/>
</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:simpleType name="Instruction3Code">
<xs:restriction base="xs:string">
<xs:enumeration value="CHQB"/>
<xs:enumeration value="HOLD"/>
<xs:enumeration value="PHOB"/>
<xs:enumeration value="TELB"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="InstructionForCreditorAgent1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Cd" type="Instruction3Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="InstrInf" type="Max140Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="LocalInstrument2Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalLocalInstrument1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:simpleType name="Max10Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="10"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max128Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="128"/>
</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="Max16Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="16"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max2048Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="2048"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max34Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="34"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max350Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="350"/>
</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="Max4Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</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="NameAndAddress10">
<xs:sequence>
<xs:element name="Nm" type="Max140Text"/>
<xs:element name="Adr" type="PostalAddress6"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="NamePrefix1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="DOCT"/>
<xs:enumeration value="MIST"/>
<xs:enumeration value="MISS"/>
<xs:enumeration value="MADM"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Number">
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="0"/>
<xs:totalDigits value="18"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="OrganisationIdentification8">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="AnyBIC" type="AnyBICIdentifier"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Othr" type="GenericOrganisationIdentification1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrganisationIdentificationSchemeName1Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalOrganisationIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="Party11Choice">
<xs:choice>
<xs:element name="OrgId" type="OrganisationIdentification8"/>
<xs:element name="PrvtId" type="PersonIdentification5"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="PartyIdentification43">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Nm" type="Max140Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="PstlAdr" type="PostalAddress6"/>
<xs:element maxOccurs="1" minOccurs="0" name="Id" type="Party11Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="CtryOfRes" type="CountryCode"/>
<xs:element maxOccurs="1" minOccurs="0" name="CtctDtls" type="ContactDetails2"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentIdentification1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="InstrId" type="Max35Text"/>
<xs:element name="EndToEndId" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentInstruction9">
<xs:sequence>
<xs:element name="PmtInfId" type="Max35Text"/>
<xs:element name="PmtMtd" type="PaymentMethod3Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="BtchBookg" type="BatchBookingIndicator"/>
<xs:element maxOccurs="1" minOccurs="0" name="NbOfTxs" type="Max15NumericText"/>
<xs:element maxOccurs="1" minOccurs="0" name="CtrlSum" type="DecimalNumber"/>
<xs:element maxOccurs="1" minOccurs="0" name="PmtTpInf" type="PaymentTypeInformation19"/>
<xs:element name="ReqdExctnDt" type="ISODate"/>
<xs:element maxOccurs="1" minOccurs="0" name="PoolgAdjstmntDt" type="ISODate"/>
<xs:element name="Dbtr" type="PartyIdentification43"/>
<xs:element name="DbtrAcct" type="CashAccount24"/>
<xs:element name="DbtrAgt" type="BranchAndFinancialInstitutionIdentification5"/>
<xs:element maxOccurs="1" minOccurs="0" name="DbtrAgtAcct" type="CashAccount24"/>
<xs:element maxOccurs="1" minOccurs="0" name="InstrForDbtrAgt" type="Max140Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="UltmtDbtr" type="PartyIdentification43"/>
<xs:element maxOccurs="1" minOccurs="0" name="ChrgBr" type="ChargeBearerType1Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="ChrgsAcct" type="CashAccount24"/>
<xs:element maxOccurs="1" minOccurs="0" name="ChrgsAcctAgt" type="BranchAndFinancialInstitutionIdentification5"/>
<xs:element maxOccurs="unbounded" minOccurs="1" name="CdtTrfTxInf" type="CreditTransferTransaction6"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="PaymentMethod3Code">
<xs:restriction base="xs:string">
<xs:enumeration value="CHK"/>
<xs:enumeration value="TRF"/>
<xs:enumeration value="TRA"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PaymentTypeInformation19">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="InstrPrty" type="Priority2Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="SvcLvl" type="ServiceLevel8Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="LclInstrm" type="LocalInstrument2Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="CtgyPurp" type="CategoryPurpose1Choice"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="PercentageRate">
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="10"/>
<xs:totalDigits value="11"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PersonIdentification5">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="DtAndPlcOfBirth" type="DateAndPlaceOfBirth"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Othr" type="GenericPersonIdentification1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSchemeName1Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalPersonIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:simpleType name="PhoneNumber">
<xs:restriction base="xs:string">
<xs:pattern value="\+[0-9]{1,3}-[0-9()+\-]{1,30}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PostalAddress6">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="AdrTp" type="AddressType2Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="Dept" type="Max70Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="SubDept" type="Max70Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="StrtNm" type="Max70Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="BldgNb" type="Max16Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="PstCd" type="Max16Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="TwnNm" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="CtrySubDvsn" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Ctry" type="CountryCode"/>
<xs:element maxOccurs="7" minOccurs="0" name="AdrLine" type="Max70Text"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="Priority2Code">
<xs:restriction base="xs:string">
<xs:enumeration value="HIGH"/>
<xs:enumeration value="NORM"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Purpose2Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalPurpose1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="ReferredDocumentInformation3">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="ReferredDocumentType2"/>
<xs:element maxOccurs="1" minOccurs="0" name="Nb" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="RltdDt" type="ISODate"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ReferredDocumentType1Choice">
<xs:choice>
<xs:element name="Cd" type="DocumentType5Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="ReferredDocumentType2">
<xs:sequence>
<xs:element name="CdOrPrtry" type="ReferredDocumentType1Choice"/>
<xs:element maxOccurs="1" minOccurs="0" name="Issr" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RegulatoryAuthority2">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Nm" type="Max140Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Ctry" type="CountryCode"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RegulatoryReporting3">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="DbtCdtRptgInd" type="RegulatoryReportingType1Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="Authrty" type="RegulatoryAuthority2"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Dtls" type="StructuredRegulatoryReporting3"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RegulatoryReportingType1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="CRED"/>
<xs:enumeration value="DEBT"/>
<xs:enumeration value="BOTH"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="RemittanceAmount2">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="DuePyblAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="DscntApldAmt" type="DiscountAmountAndType1"/>
<xs:element maxOccurs="1" minOccurs="0" name="CdtNoteAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="TaxAmt" type="TaxAmountAndType1"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="AdjstmntAmtAndRsn" type="DocumentAdjustment1"/>
<xs:element maxOccurs="1" minOccurs="0" name="RmtdAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RemittanceInformation7">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Ustrd" type="Max140Text"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Strd" type="StructuredRemittanceInformation9"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RemittanceLocation2">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="RmtId" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="RmtLctnMtd" type="RemittanceLocationMethod2Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="RmtLctnElctrncAdr" type="Max2048Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="RmtLctnPstlAdr" type="NameAndAddress10"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RemittanceLocationMethod2Code">
<xs:restriction base="xs:string">
<xs:enumeration value="FAXI"/>
<xs:enumeration value="EDIC"/>
<xs:enumeration value="URID"/>
<xs:enumeration value="EMAL"/>
<xs:enumeration value="POST"/>
<xs:enumeration value="SMSM"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ServiceLevel8Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalServiceLevel1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="StructuredRegulatoryReporting3">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Dt" type="ISODate"/>
<xs:element maxOccurs="1" minOccurs="0" name="Ctry" type="CountryCode"/>
<xs:element maxOccurs="1" minOccurs="0" name="Cd" type="Max10Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Amt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Inf" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="StructuredRemittanceInformation9">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="RfrdDocInf" type="ReferredDocumentInformation3"/>
<xs:element maxOccurs="1" minOccurs="0" name="RfrdDocAmt" type="RemittanceAmount2"/>
<xs:element maxOccurs="1" minOccurs="0" name="CdtrRefInf" type="CreditorReferenceInformation2"/>
<xs:element maxOccurs="1" minOccurs="0" name="Invcr" type="PartyIdentification43"/>
<xs:element maxOccurs="1" minOccurs="0" name="Invcee" type="PartyIdentification43"/>
<xs:element maxOccurs="3" minOccurs="0" name="AddtlRmtInf" type="Max140Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="SupplementaryData1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="PlcAndNm" type="Max350Text"/>
<xs:element name="Envlp" type="SupplementaryDataEnvelope1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="SupplementaryDataEnvelope1">
<xs:sequence>
<xs:any namespace="##any" processContents="lax"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxAmount1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Rate" type="PercentageRate"/>
<xs:element maxOccurs="1" minOccurs="0" name="TaxblBaseAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element maxOccurs="1" minOccurs="0" name="TtlAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Dtls" type="TaxRecordDetails1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxAmountAndType1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="TaxAmountType1Choice"/>
<xs:element name="Amt" type="ActiveOrHistoricCurrencyAndAmount"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxAmountType1Choice">
<xs:choice>
<xs:element name="Cd" type="ExternalTaxAmountType1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="TaxAuthorisation1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Titl" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Nm" type="Max140Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxInformation3">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Cdtr" type="TaxParty1"/>
<xs:element maxOccurs="1" minOccurs="0" name="Dbtr" type="TaxParty2"/>
<xs:element maxOccurs="1" minOccurs="0" name="AdmstnZn" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="RefNb" type="Max140Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Mtd" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="TtlTaxblBaseAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element maxOccurs="1" minOccurs="0" name="TtlTaxAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
<xs:element maxOccurs="1" minOccurs="0" name="Dt" type="ISODate"/>
<xs:element maxOccurs="1" minOccurs="0" name="SeqNb" type="Number"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Rcrd" type="TaxRecord1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxParty1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="TaxId" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="RegnId" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="TaxTp" type="Max35Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxParty2">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="TaxId" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="RegnId" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="TaxTp" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Authstn" type="TaxAuthorisation1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxPeriod1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Yr" type="ISODate"/>
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="TaxRecordPeriod1Code"/>
<xs:element maxOccurs="1" minOccurs="0" name="FrToDt" type="DatePeriodDetails"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxRecord1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Ctgy" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="CtgyDtls" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="DbtrSts" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="CertId" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="FrmsCd" type="Max35Text"/>
<xs:element maxOccurs="1" minOccurs="0" name="Prd" type="TaxPeriod1"/>
<xs:element maxOccurs="1" minOccurs="0" name="TaxAmt" type="TaxAmount1"/>
<xs:element maxOccurs="1" minOccurs="0" name="AddtlInf" type="Max140Text"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TaxRecordDetails1">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Prd" type="TaxPeriod1"/>
<xs:element name="Amt" type="ActiveOrHistoricCurrencyAndAmount"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="TaxRecordPeriod1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="MM01"/>
<xs:enumeration value="MM02"/>
<xs:enumeration value="MM03"/>
<xs:enumeration value="MM04"/>
<xs:enumeration value="MM05"/>
<xs:enumeration value="MM06"/>
<xs:enumeration value="MM07"/>
<xs:enumeration value="MM08"/>
<xs:enumeration value="MM09"/>
<xs:enumeration value="MM10"/>
<xs:enumeration value="MM11"/>
<xs:enumeration value="MM12"/>
<xs:enumeration value="QTR1"/>
<xs:enumeration value="QTR2"/>
<xs:enumeration value="QTR3"/>
<xs:enumeration value="QTR4"/>
<xs:enumeration value="HLF1"/>
<xs:enumeration value="HLF2"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

View File

@@ -4,20 +4,29 @@
<!-- TODO In the suitable_bank_types field, we should restrict to IBAN type and Rib+IBAN of l10n_fr_rib ? --> <!-- TODO In the suitable_bank_types field, we should restrict to IBAN type and Rib+IBAN of l10n_fr_rib ? -->
<record id="export_sepa_sct_001_001_05" model="payment.mode.type">
<field name="name">SEPA Credit Transfer v05</field>
<field name="code">pain.001.001.05</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sepa_wizard"/>
<field name="payment_order_type">payment</field>
</record>
<record id="export_sepa_sct_001_001_04" model="payment.mode.type"> <record id="export_sepa_sct_001_001_04" model="payment.mode.type">
<field name="name">SEPA Credit Transfer v04</field> <field name="name">SEPA Credit Transfer v04</field>
<field name="code">pain.001.001.04</field> <field name="code">pain.001.001.04</field>
<field name="suitable_bank_types" <field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" /> eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sepa_wizard"/> <field name="ir_model_id" ref="model_banking_export_sepa_wizard"/>
<field name="payment_order_type">payment</field> <field name="payment_order_type">payment</field>
</record> </record>
<record id="export_sepa_sct_001_001_03" model="payment.mode.type"> <record id="export_sepa_sct_001_001_03" model="payment.mode.type">
<field name="name">SEPA Credit Transfer v03</field> <field name="name">SEPA Credit Transfer v03 (recommended)</field>
<field name="code">pain.001.001.03</field> <field name="code">pain.001.001.03</field>
<field name="suitable_bank_types" <field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" /> eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sepa_wizard"/> <field name="ir_model_id" ref="model_banking_export_sepa_wizard"/>
<field name="payment_order_type">payment</field> <field name="payment_order_type">payment</field>
</record> </record>
@@ -26,7 +35,7 @@
<field name="name">SEPA Credit Transfer v02</field> <field name="name">SEPA Credit Transfer v02</field>
<field name="code">pain.001.001.02</field> <field name="code">pain.001.001.02</field>
<field name="suitable_bank_types" <field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" /> eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sepa_wizard"/> <field name="ir_model_id" ref="model_banking_export_sepa_wizard"/>
<field name="payment_order_type">payment</field> <field name="payment_order_type">payment</field>
</record> </record>

View File

@@ -23,11 +23,13 @@
from openerp.osv import orm, fields from openerp.osv import orm, fields
import base64 import base64
from datetime import datetime, timedelta from datetime import datetime
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval
from openerp import tools, netsvc from openerp import tools, netsvc
from lxml import etree from lxml import etree
import logging import logging
from unidecode import unidecode
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -35,36 +37,41 @@ _logger = logging.getLogger(__name__)
class banking_export_sepa_wizard(orm.TransientModel): class banking_export_sepa_wizard(orm.TransientModel):
_name = 'banking.export.sepa.wizard' _name = 'banking.export.sepa.wizard'
_description = 'Export SEPA Credit Transfer XML file' _description = 'Export SEPA Credit Transfer XML file'
_columns = { _columns = {
'state': fields.selection([('create', 'Create'), ('finish', 'Finish')], 'state': fields.selection([
'State', readonly=True), ('create', 'Create'),
'msg_identification': fields.char('Message identification', size=35, ('finish', 'Finish'),
# Can't set required=True on the field because it blocks ], 'State', readonly=True),
# the launch of the wizard -> I set it as required in the view 'batch_booking': fields.boolean(
help='This is the message identification of the entire SEPA XML file. 35 characters max.'), 'Batch Booking',
'batch_booking': fields.boolean('Batch booking',
help="If true, the bank statement will display only one debit line for all the wire transfers of the SEPA XML file ; if false, the bank statement will display one debit line per wire transfer of the SEPA XML file."), help="If true, the bank statement will display only one debit line for all the wire transfers of the SEPA XML file ; if false, the bank statement will display one debit line per wire transfer of the SEPA XML file."),
'prefered_exec_date': fields.date('Prefered execution date', 'prefered_exec_date': fields.date(
'Prefered Execution Date',
help='This is the date on which the file should be processed by the bank. Please keep in mind that banks only execute on working days and typically use a delay of two days between execution date and effective transfer date.'), help='This is the date on which the file should be processed by the bank. Please keep in mind that banks only execute on working days and typically use a delay of two days between execution date and effective transfer date.'),
'charge_bearer': fields.selection([ 'charge_bearer': fields.selection([
('SHAR', 'Shared'), ('SHAR', 'Shared'),
('CRED', 'Borne by creditor'), ('CRED', 'Borne by creditor'),
('DEBT', 'Borne by debtor'), ('DEBT', 'Borne by debtor'),
('SLEV', 'Following service level'), ('SLEV', 'Following service level'),
], 'Charge bearer', required=True, ], 'Charge Bearer', required=True,
help='Shared : transaction charges on the sender side are to be borne by the debtor, transaction charges on the receiver side are to be borne by the creditor (most transfers use this). 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. Following service level : transaction charges are to be applied following the rules agreed in the service level and/or scheme.'), help='Shared : transaction charges on the sender side are to be borne by the debtor, transaction charges on the receiver side are to be borne by the creditor (most transfers use this). 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. Following service level : transaction charges are to be applied following the rules agreed in the service level and/or scheme.'),
'nb_transactions': fields.related('file_id', 'nb_transactions', 'nb_transactions': fields.related(
type='integer', string='Number of transactions', readonly=True), 'file_id', 'nb_transactions', type='integer',
'total_amount': fields.related('file_id', 'total_amount', type='float', string='Number of Transactions', readonly=True),
string='Total amount', readonly=True), 'total_amount': fields.related(
'file_id': fields.many2one('banking.export.sepa', 'SEPA XML file', readonly=True), 'file_id', 'total_amount', type='float', string='Total Amount',
'file': fields.related('file_id', 'file', string="File", type='binary',
readonly=True), readonly=True),
'filename': fields.related('file_id', 'filename', string="Filename", 'file_id': fields.many2one(
type='char', size=256, readonly=True), 'banking.export.sepa', 'SEPA XML File', readonly=True),
'payment_order_ids': fields.many2many('payment.order', 'file': fields.related(
'wiz_sepa_payorders_rel', 'wizard_id', 'payment_order_id', 'file_id', 'file', string="File", type='binary', readonly=True),
'Payment orders', readonly=True), 'filename': fields.related(
'file_id', 'filename', string="Filename", type='char',
size=256, readonly=True),
'payment_order_ids': fields.many2many(
'payment.order', 'wiz_sepa_payorders_rel', 'wizard_id',
'payment_order_id', 'Payment Orders', readonly=True),
} }
_defaults = { _defaults = {
@@ -72,13 +79,6 @@ class banking_export_sepa_wizard(orm.TransientModel):
'state': 'create', 'state': 'create',
} }
def _limit_size(self, cr, uid, field, max_size, context=None):
'''Limit size of strings to respect the PAIN standard'''
max_size = int(max_size)
return field[0:max_size]
def _validate_iban(self, cr, uid, iban, context=None): def _validate_iban(self, cr, uid, iban, context=None):
'''if IBAN is valid, returns IBAN '''if IBAN is valid, returns IBAN
if IBAN is NOT valid, raises an error message''' if IBAN is NOT valid, raises an error message'''
@@ -86,29 +86,97 @@ class banking_export_sepa_wizard(orm.TransientModel):
if partner_bank_obj.is_iban_valid(cr, uid, iban, context=context): if partner_bank_obj.is_iban_valid(cr, uid, iban, context=context):
return iban.replace(' ', '') return iban.replace(' ', '')
else: else:
raise orm.except_orm(_('Error :'), _("This IBAN is not valid : %s") % iban) raise orm.except_orm(
_('Error :'), _("This IBAN is not valid : %s") % iban)
def create(self, cr, uid, vals, context=None): def create(self, cr, uid, vals, context=None):
payment_order_ids = context.get('active_ids', []) payment_order_ids = context.get('active_ids', [])
vals.update({ vals.update({
'payment_order_ids': [[6, 0, payment_order_ids]], 'payment_order_ids': [[6, 0, payment_order_ids]],
}) })
return super(banking_export_sepa_wizard, self).create(cr, uid, return super(banking_export_sepa_wizard, self).create(
vals, context=context) cr, uid, vals, context=context)
def _prepare_field(
self, cr, uid, field_name, field_value, max_size=0,
sepa_export=False, line=False, context=None):
'''This function is designed to be inherited !'''
eval_ctx = {
'sepa_export': sepa_export,
'line': line,
}
try:
# SEPA uses XML ; XML = UTF-8 ; UTF-8 = support for all characters
# But we are dealing with banks...
# and many banks don't want non-ASCCI characters !
# cf section 1.4 "Character set" of the SEPA Credit Transfer
# Scheme Customer-to-bank guidelines
value = unidecode(safe_eval(field_value, eval_ctx))
except:
if line:
raise orm.except_orm(
_('Error:'),
_("Cannot compute the '%s' of the Payment Line with Invoice Reference '%s'.")
% (field_name, self.pool['account.invoice'].name_get(cr, uid, [line.ml_inv_ref.id], context=context)[0][1]))
else:
raise orm.except_orm(
_('Error:'),
_("Cannot compute the '%s'.") % field_name)
if not isinstance(value, (str, unicode)):
raise orm.except_orm(
_('Field type error:'),
_("The type of the field '%s' is %s. It should be a string or unicode.")
% (field_name, type(value)))
if not value:
raise orm.except_orm(
_('Error:'),
_("The '%s' is empty or 0. It should have a non-null value.")
% field_name)
if max_size and len(value) > max_size:
value = value[0:max_size]
return value
def _prepare_export_sepa(
self, cr, uid, sepa_export, total_amount, transactions_count,
xml_string, context=None):
return {
'batch_booking': sepa_export.batch_booking,
'charge_bearer': sepa_export.charge_bearer,
'prefered_exec_date': sepa_export.prefered_exec_date,
'total_amount': total_amount,
'nb_transactions': transactions_count,
'file': base64.encodestring(xml_string),
'payment_order_ids': [
(6, 0, [x.id for x in sepa_export.payment_order_ids])
],
}
def _validate_xml(self, cr, uid, xml_string, pain_flavor):
xsd_etree_obj = etree.parse(
tools.file_open(
'account_banking_sepa_credit_transfer/data/%s.xsd'
% pain_flavor))
official_pain_schema = etree.XMLSchema(xsd_etree_obj)
try:
root_to_validate = etree.fromstring(xml_string)
official_pain_schema.assertValid(root_to_validate)
except Exception, e:
_logger.warning(
"The XML file is invalid against the XML Schema Definition")
_logger.warning(xml_string)
_logger.warning(e)
raise orm.except_orm(
_('Error :'),
_('The generated XML file is not valid against the official XML Schema Definition. The generated XML file and the full error have been written in the server logs. Here is the error, which may give you an idea on the cause of the problem : %s')
% str(e))
return True
def create_sepa(self, cr, uid, ids, context=None): def create_sepa(self, cr, uid, ids, context=None):
''' '''
Creates the SEPA Credit Transfer file. That's the important code ! Creates the SEPA Credit Transfer file. That's the important code !
''' '''
sepa_export = self.browse(cr, uid, ids[0], context=context) sepa_export = self.browse(cr, uid, ids[0], context=context)
my_company_name = sepa_export.payment_order_ids[0].mode.bank_id.partner_id.name
my_company_iban = self._validate_iban(cr, uid, sepa_export.payment_order_ids[0].mode.bank_id.acc_number, context=context)
my_company_bic = sepa_export.payment_order_ids[0].mode.bank_id.bank.bic
#my_company_country_code = sepa_export.payment_order_ids[0].mode.bank_id.partner_id.address[0].country_id.code
#my_company_city = sepa_export.payment_order_ids[0].mode.bank_id.partner_id.address[0].city
#my_company_street1 = sepa_export.payment_order_ids[0].mode.bank_id.partner_id.address[0].street
pain_flavor = sepa_export.payment_order_ids[0].mode.type.code pain_flavor = sepa_export.payment_order_ids[0].mode.type.code
if pain_flavor == 'pain.001.001.02': if pain_flavor == 'pain.001.001.02':
bic_xml_tag = 'BIC' bic_xml_tag = 'BIC'
@@ -118,23 +186,28 @@ class banking_export_sepa_wizard(orm.TransientModel):
bic_xml_tag = 'BIC' bic_xml_tag = 'BIC'
# size 70 -> 140 for <Nm> with pain.001.001.03 # size 70 -> 140 for <Nm> with pain.001.001.03
# BUT the European Payment Council, in the document # BUT the European Payment Council, in the document
# "SEPA Credit Transfer Scheme Customer-to-bank Implementation guidelines" v6.0 # "SEPA Credit Transfer Scheme Customer-to-bank
# available on http://www.europeanpaymentscouncil.eu/knowledge_bank.cfm # Implementation guidelines" v6.0 available on
# http://www.europeanpaymentscouncil.eu/knowledge_bank.cfm
# says that 'Nm' should be limited to 70 # says that 'Nm' should be limited to 70
# so we follow the "European Payment Council" and we put 70 and not 140 # so we follow the "European Payment Council"
# and we put 70 and not 140
name_maxsize = 70 name_maxsize = 70
root_xml_tag = 'CstmrCdtTrfInitn' root_xml_tag = 'CstmrCdtTrfInitn'
elif pain_flavor == 'pain.001.001.04': elif pain_flavor == 'pain.001.001.04':
bic_xml_tag = 'BICFI' bic_xml_tag = 'BICFI'
name_maxsize = 140 name_maxsize = 140
root_xml_tag = 'CstmrCdtTrfInitn' root_xml_tag = 'CstmrCdtTrfInitn'
elif pain_flavor == 'pain.001.001.05':
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrCdtTrfInitn'
else: else:
raise orm.except_orm(_('Error :'), _("Payment Type Code '%s' is not supported. The only Payment Type Codes supported for SEPA Credit Transfers are 'pain.001.001.02', 'pain.001.001.03' and 'pain.001.001.04'.") % pain_flavor) raise orm.except_orm(
if sepa_export.batch_booking: _('Error :'),
my_batch_booking = 'true' _("Payment Type Code '%s' is not supported. The only Payment Type Codes supported for SEPA Credit Transfers are 'pain.001.001.02', 'pain.001.001.03', 'pain.001.001.04' and 'pain.001.001.05'.")
else: % pain_flavor)
my_batch_booking = 'false'
my_msg_identification = sepa_export.msg_identification
if sepa_export.prefered_exec_date: if sepa_export.prefered_exec_date:
my_requested_exec_date = sepa_export.prefered_exec_date my_requested_exec_date = sepa_export.prefered_exec_date
else: else:
@@ -148,73 +221,99 @@ class banking_export_sepa_wizard(orm.TransientModel):
root = etree.Element('Document', nsmap=pain_ns) root = etree.Element('Document', nsmap=pain_ns)
pain_root = etree.SubElement(root, root_xml_tag) pain_root = etree.SubElement(root, root_xml_tag)
my_company_name = self._prepare_field(
cr, uid, 'Company Name',
'sepa_export.payment_order_ids[0].mode.bank_id.partner_id.name',
name_maxsize, sepa_export=sepa_export, context=context)
# A. Group header # A. Group header
group_header = etree.SubElement(pain_root, 'GrpHdr') group_header_1_0 = etree.SubElement(pain_root, 'GrpHdr')
message_identification = etree.SubElement(group_header, 'MsgId') message_identification_1_1 = etree.SubElement(
message_identification.text = self._limit_size(cr, uid, my_msg_identification, 35, context=context) group_header_1_0, 'MsgId')
creation_date_time = etree.SubElement(group_header, 'CreDtTm') message_identification_1_1.text = self._prepare_field(
creation_date_time.text = datetime.strftime(datetime.today(), '%Y-%m-%dT%H:%M:%S') cr, uid, 'Message Identification',
'sepa_export.payment_order_ids[0].reference', 35,
sepa_export=sepa_export, context=context)
creation_date_time_1_2 = etree.SubElement(group_header_1_0, 'CreDtTm')
creation_date_time_1_2.text = datetime.strftime(
datetime.today(), '%Y-%m-%dT%H:%M:%S')
if pain_flavor == 'pain.001.001.02': if pain_flavor == 'pain.001.001.02':
# batch_booking is in "Group header" with pain.001.001.02 # batch_booking is in "Group header" with pain.001.001.02
# and in "Payment info" in pain.001.001.03/04 # and in "Payment info" in pain.001.001.03/04
batch_booking = etree.SubElement(group_header, 'BtchBookg') batch_booking = etree.SubElement(group_header_1_0, 'BtchBookg')
batch_booking.text = my_batch_booking batch_booking.text = str(sepa_export.batch_booking).lower()
nb_of_transactions_grphdr = etree.SubElement(group_header, 'NbOfTxs') nb_of_transactions_1_6 = etree.SubElement(
control_sum_grphdr = etree.SubElement(group_header, 'CtrlSum') group_header_1_0, 'NbOfTxs')
control_sum_1_7 = etree.SubElement(group_header_1_0, 'CtrlSum')
# Grpg removed in pain.001.001.03 # Grpg removed in pain.001.001.03
if pain_flavor == 'pain.001.001.02': if pain_flavor == 'pain.001.001.02':
grouping = etree.SubElement(group_header, 'Grpg') grouping = etree.SubElement(group_header_1_0, 'Grpg')
grouping.text = 'GRPD' grouping.text = 'GRPD'
initiating_party = etree.SubElement(group_header, 'InitgPty') initiating_party_1_8 = etree.SubElement(group_header_1_0, 'InitgPty')
initiating_party_name = etree.SubElement(initiating_party, 'Nm') initiating_party_name = etree.SubElement(initiating_party_1_8, 'Nm')
initiating_party_name.text = self._limit_size(cr, uid, my_company_name, name_maxsize, context=context) initiating_party_name.text = my_company_name
# B. Payment info # B. Payment info
payment_info = etree.SubElement(pain_root, 'PmtInf') payment_info_2_0 = etree.SubElement(pain_root, 'PmtInf')
payment_info_identification = etree.SubElement(payment_info, 'PmtInfId') payment_info_identification_2_1 = etree.SubElement(
payment_info_identification.text = self._limit_size(cr, uid, my_msg_identification, 35, context=context) payment_info_2_0, 'PmtInfId')
payment_method = etree.SubElement(payment_info, 'PmtMtd') payment_info_identification_2_1.text = self._prepare_field(
payment_method.text = 'TRF' cr, uid, 'Payment Information Identification',
if pain_flavor in ['pain.001.001.03', 'pain.001.001.04']: "sepa_export.payment_order_ids[0].reference",
35, sepa_export=sepa_export, context=context)
payment_method_2_2 = etree.SubElement(payment_info_2_0, 'PmtMtd')
payment_method_2_2.text = 'TRF'
if pain_flavor in [
'pain.001.001.03', 'pain.001.001.04', 'pain.001.001.05']:
# batch_booking is in "Group header" with pain.001.001.02 # batch_booking is in "Group header" with pain.001.001.02
# and in "Payment info" in pain.001.001.03/04 # and in "Payment info" in pain.001.001.03/04
batch_booking = etree.SubElement(payment_info, 'BtchBookg') batch_booking_2_3 = etree.SubElement(payment_info_2_0, 'BtchBookg')
batch_booking.text = my_batch_booking batch_booking_2_3.text = str(sepa_export.batch_booking).lower()
# It may seem surprising, but the # It may seem surprising, but the
# "SEPA Credit Transfer Scheme Customer-to-bank Implementation guidelines" # "SEPA Credit Transfer Scheme Customer-to-bank Implementation
# v6.0 says that control sum and nb_of_transactions should be present # guidelines" v6.0 says that control sum and nb_of_transactions
# at both "group header" level and "payment info" level # should be present at both "group header" level and "payment info"
# This seems to be confirmed by the tests carried out at # level. This seems to be confirmed by the tests carried out at
# BNP Paribas in PAIN v001.001.03 # BNP Paribas in PAIN v001.001.03
if pain_flavor in ['pain.001.001.03', 'pain.001.001.04']: if pain_flavor in [
nb_of_transactions_pmtinf = etree.SubElement(payment_info, 'NbOfTxs') 'pain.001.001.03', 'pain.001.001.04', 'pain.001.001.05']:
control_sum_pmtinf = etree.SubElement(payment_info, 'CtrlSum') nb_of_transactions_2_4 = etree.SubElement(
payment_type_info = etree.SubElement(payment_info, 'PmtTpInf') payment_info_2_0, 'NbOfTxs')
service_level = etree.SubElement(payment_type_info, 'SvcLvl') control_sum_2_5 = etree.SubElement(payment_info_2_0, 'CtrlSum')
service_level_code = etree.SubElement(service_level, 'Cd') payment_type_info_2_6 = etree.SubElement(payment_info_2_0, 'PmtTpInf')
service_level_code.text = 'SEPA' service_level_2_8 = etree.SubElement(payment_type_info_2_6, 'SvcLvl')
requested_exec_date = etree.SubElement(payment_info, 'ReqdExctnDt') service_level_code_2_9 = etree.SubElement(service_level_2_8, 'Cd')
requested_exec_date.text = my_requested_exec_date service_level_code_2_9.text = 'SEPA'
debtor = etree.SubElement(payment_info, 'Dbtr') requested_exec_date_2_17 = etree.SubElement(
debtor_name = etree.SubElement(debtor, 'Nm') payment_info_2_0, 'ReqdExctnDt')
debtor_name.text = self._limit_size(cr, uid, my_company_name, name_maxsize, context=context) requested_exec_date_2_17.text = my_requested_exec_date
# debtor_address = etree.SubElement(debtor, 'PstlAdr') debtor_2_19 = etree.SubElement(payment_info_2_0, 'Dbtr')
# debtor_street = etree.SubElement(debtor_address, 'AdrLine') debtor_name = etree.SubElement(debtor_2_19, 'Nm')
# debtor_street.text = my_company_street1 debtor_name.text = my_company_name
# debtor_city = etree.SubElement(debtor_address, 'AdrLine') debtor_account_2_20 = etree.SubElement(payment_info_2_0, 'DbtrAcct')
# debtor_city.text = my_company_city debtor_account_id = etree.SubElement(debtor_account_2_20, 'Id')
# debtor_country = etree.SubElement(debtor_address, 'Ctry')
# debtor_country.text = my_company_country_code
debtor_account = etree.SubElement(payment_info, 'DbtrAcct')
debtor_account_id = etree.SubElement(debtor_account, 'Id')
debtor_account_iban = etree.SubElement(debtor_account_id, 'IBAN') debtor_account_iban = etree.SubElement(debtor_account_id, 'IBAN')
debtor_account_iban.text = my_company_iban debtor_account_iban.text = self._validate_iban(
debtor_agent = etree.SubElement(payment_info, 'DbtrAgt') cr, uid, self._prepare_field(
debtor_agent_institution = etree.SubElement(debtor_agent, 'FinInstnId') cr, uid, 'Company IBAN',
if my_company_bic: 'sepa_export.payment_order_ids[0].mode.bank_id.acc_number',
debtor_agent_bic = etree.SubElement(debtor_agent_institution, bic_xml_tag) sepa_export=sepa_export, context=context),
debtor_agent_bic.text = my_company_bic context=context)
charge_bearer = etree.SubElement(payment_info, 'ChrgBr') debtor_agent_2_21 = etree.SubElement(payment_info_2_0, 'DbtrAgt')
charge_bearer.text = sepa_export.charge_bearer debtor_agent_institution = etree.SubElement(
debtor_agent_2_21, 'FinInstnId')
debtor_agent_bic = etree.SubElement(
debtor_agent_institution, bic_xml_tag)
# TODO validate BIC with pattern
# [A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}
# because OpenERP doesn't have a constraint on BIC
debtor_agent_bic.text = self._prepare_field(
cr, uid, 'Company BIC',
'sepa_export.payment_order_ids[0].mode.bank_id.bank.bic',
sepa_export=sepa_export, context=context)
charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr')
charge_bearer_2_24.text = sepa_export.charge_bearer
transactions_count = 0 transactions_count = 0
total_amount = 0.0 total_amount = 0.0
@@ -226,88 +325,100 @@ class banking_export_sepa_wizard(orm.TransientModel):
for line in payment_order.line_ids: for line in payment_order.line_ids:
transactions_count += 1 transactions_count += 1
# C. Credit Transfer Transaction Info # C. Credit Transfer Transaction Info
credit_transfer_transaction_info = etree.SubElement(payment_info, 'CdtTrfTxInf') credit_transfer_transaction_info_2_27 = etree.SubElement(
payment_identification = etree.SubElement(credit_transfer_transaction_info, 'PmtId') payment_info_2_0, 'CdtTrfTxInf')
instruction_identification = etree.SubElement(payment_identification, 'InstrId') payment_identification_2_28 = etree.SubElement(
instruction_identification.text = self._limit_size(cr, uid, line.communication, 35, context=context) #otherwise, we can reach the invoice fields via ml_inv_ref credit_transfer_transaction_info_2_27, 'PmtId')
end2end_identification = etree.SubElement(payment_identification, 'EndToEndId') end2end_identification_2_30 = etree.SubElement(
end2end_identification.text = self._limit_size(cr, uid, line.communication, 35, context=context) payment_identification_2_28, 'EndToEndId')
amount = etree.SubElement(credit_transfer_transaction_info, 'Amt') end2end_identification_2_30.text = self._prepare_field(
instructed_amount = etree.SubElement(amount, 'InstdAmt', Ccy=line.currency.name) cr, uid, 'End to End Identification', 'line.name', 35,
instructed_amount.text = '%.2f' % line.amount_currency line=line, context=context)
currency_name = self._prepare_field(
cr, uid, 'Currency Code', 'line.currency.name', 3,
line=line, context=context)
amount_2_42 = etree.SubElement(
credit_transfer_transaction_info_2_27, 'Amt')
instructed_amount_2_43 = etree.SubElement(
amount_2_42, 'InstdAmt', Ccy=currency_name)
instructed_amount_2_43.text = '%.2f' % line.amount_currency
amount_control_sum += line.amount_currency amount_control_sum += line.amount_currency
creditor_agent = etree.SubElement(credit_transfer_transaction_info, 'CdtrAgt') creditor_agent_2_77 = etree.SubElement(
creditor_agent_institution = etree.SubElement(creditor_agent, 'FinInstnId') credit_transfer_transaction_info_2_27, 'CdtrAgt')
creditor_agent_institution = etree.SubElement(
creditor_agent_2_77, 'FinInstnId')
if not line.bank_id: if not line.bank_id:
raise orm.except_orm(_('Error :'), _("Missing Bank Account on invoice '%s' (payment order line reference '%s').") %(line.ml_inv_ref.number, line.name)) raise orm.except_orm(
if line.bank_id.bank.bic: _('Error :'),
creditor_agent_bic = etree.SubElement(creditor_agent_institution, bic_xml_tag) _("Missing Bank Account on invoice '%s' (payment order line reference '%s').")
creditor_agent_bic.text = line.bank_id.bank.bic % (line.ml_inv_ref.number, line.name))
creditor = etree.SubElement(credit_transfer_transaction_info, 'Cdtr') creditor_agent_bic = etree.SubElement(
creditor_name = etree.SubElement(creditor, 'Nm') creditor_agent_institution, bic_xml_tag)
creditor_name.text = self._limit_size(cr, uid, line.partner_id.name, name_maxsize, context=context) creditor_agent_bic.text = self._prepare_field(
# I don't think they want it cr, uid, 'Customer BIC', 'line.bank_id.bank.bic',
# If they want it, we need to implement full spec p26 appendix line=line, context=context)
# creditor_address = etree.SubElement(creditor, 'PstlAdr') creditor_2_79 = etree.SubElement(
# creditor_street = etree.SubElement(creditor_address, 'AdrLine') credit_transfer_transaction_info_2_27, 'Cdtr')
# creditor_street.text = line.partner_id.address[0].street creditor_name = etree.SubElement(creditor_2_79, 'Nm')
# creditor_city = etree.SubElement(creditor_address, 'AdrLine') creditor_name.text = self._prepare_field(
# creditor_city.text = line.partner_id.address[0].city cr, uid, 'Customer Name', 'line.partner_id.name',
# creditor_country = etree.SubElement(creditor_address, 'Ctry') name_maxsize, line=line, context=context)
# creditor_country.text = line.partner_id.address[0].country_id.code creditor_account_2_80 = etree.SubElement(
creditor_account = etree.SubElement(credit_transfer_transaction_info, 'CdtrAcct') credit_transfer_transaction_info_2_27, 'CdtrAcct')
creditor_account_id = etree.SubElement(creditor_account, 'Id') creditor_account_id = etree.SubElement(
creditor_account_iban = etree.SubElement(creditor_account_id, 'IBAN') creditor_account_2_80, 'Id')
creditor_account_iban.text = self._validate_iban(cr, uid, line.bank_id.acc_number, context=context) creditor_account_iban = etree.SubElement(
remittance_info = etree.SubElement(credit_transfer_transaction_info, 'RmtInf') creditor_account_id, 'IBAN')
# switch to Structured (Strdr) ? If we do it, beware that the format is not the same between pain 02 and pain 03 creditor_account_iban.text = self._validate_iban(
remittance_info_unstructured = etree.SubElement(remittance_info, 'Ustrd') cr, uid, self._prepare_field(
remittance_info_unstructured.text = self._limit_size(cr, uid, line.communication, 140, context=context) cr, uid, 'Customer IBAN',
'line.bank_id.acc_number', line=line,
context=context),
context=context)
remittance_info_2_91 = etree.SubElement(
credit_transfer_transaction_info_2_27, 'RmtInf')
# switch to Structured (Strdr) ?
# If we do it, beware that the format is not the same
# between pain 02 and pain 03
remittance_info_unstructured_2_99 = etree.SubElement(
remittance_info_2_91, 'Ustrd')
remittance_info_unstructured_2_99.text = self._prepare_field(
cr, uid, 'Remittance Information', 'line.communication',
140, line=line, context=context)
if pain_flavor in ['pain.001.001.03', 'pain.001.001.04']: if pain_flavor in [
nb_of_transactions_grphdr.text = nb_of_transactions_pmtinf.text = str(transactions_count) 'pain.001.001.03', 'pain.001.001.04', 'pain.001.001.05']:
control_sum_grphdr.text = control_sum_pmtinf.text = '%.2f' % amount_control_sum nb_of_transactions_1_6.text = nb_of_transactions_2_4.text = \
str(transactions_count)
control_sum_1_7.text = control_sum_2_5.text = \
'%.2f' % amount_control_sum
else: else:
nb_of_transactions_grphdr.text = str(transactions_count) nb_of_transactions_1_6.text = str(transactions_count)
control_sum_grphdr.text = '%.2f' % amount_control_sum control_sum_1_7.text = '%.2f' % amount_control_sum
xml_string = etree.tostring(
xml_string = etree.tostring(root, pretty_print=True, encoding='UTF-8', xml_declaration=True) root, pretty_print=True, encoding='UTF-8', xml_declaration=True)
_logger.debug("Generated SEPA XML file below") _logger.debug(
"Generated SEPA Credit Transfer XML file in format %s below"
% pain_flavor)
_logger.debug(xml_string) _logger.debug(xml_string)
official_pain_schema = etree.XMLSchema(etree.parse(tools.file_open('account_banking_sepa_credit_transfer/data/%s.xsd' % pain_flavor))) self._validate_xml(cr, uid, xml_string, pain_flavor)
try:
root_to_validate = etree.fromstring(xml_string)
official_pain_schema.assertValid(root_to_validate)
except Exception, e:
_logger.warning("The XML file is invalid against the XML Schema Definition")
_logger.warning(xml_string)
_logger.warning(e)
raise orm.except_orm(_('Error :'), _('The generated XML file is not valid against the official XML Schema Definition. The generated XML file and the full error have been written in the server logs. Here is the error, which may give you an idea on the cause of the problem : %s') % str(e))
# CREATE the banking.export.sepa record # CREATE the banking.export.sepa record
file_id = self.pool.get('banking.export.sepa').create(cr, uid, file_id = self.pool.get('banking.export.sepa').create(
{ cr, uid, self._prepare_export_sepa(
'msg_identification': my_msg_identification, cr, uid, sepa_export, total_amount, transactions_count,
'batch_booking': sepa_export.batch_booking, xml_string, context=context),
'charge_bearer': sepa_export.charge_bearer, context=context)
'prefered_exec_date': sepa_export.prefered_exec_date,
'total_amount': total_amount,
'nb_transactions': transactions_count,
'file': base64.encodestring(xml_string),
'payment_order_ids': [
(6, 0, [x.id for x in sepa_export.payment_order_ids])
],
}, context=context)
self.write(cr, uid, ids, { self.write(
'file_id': file_id, cr, uid, ids, {
'state': 'finish', 'file_id': file_id,
'state': 'finish',
}, context=context) }, context=context)
action = { action = {
'name': 'SEPA XML', 'name': 'SEPA Credit Transfer XML',
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'view_type': 'form', 'view_type': 'form',
'view_mode': 'form,tree', 'view_mode': 'form,tree',
@@ -317,26 +428,27 @@ class banking_export_sepa_wizard(orm.TransientModel):
} }
return action return action
def cancel_sepa(self, cr, uid, ids, context=None): def cancel_sepa(self, cr, uid, ids, context=None):
''' '''
Cancel the SEPA PAIN: just drop the file Cancel the SEPA PAIN: just drop the file
''' '''
sepa_export = self.browse(cr, uid, ids[0], context=context) sepa_export = self.browse(cr, uid, ids[0], context=context)
self.pool.get('banking.export.sepa').unlink(cr, uid, sepa_export.file_id.id, context=context) self.pool.get('banking.export.sepa').unlink(
cr, uid, sepa_export.file_id.id, context=context)
return {'type': 'ir.actions.act_window_close'} return {'type': 'ir.actions.act_window_close'}
def save_sepa(self, cr, uid, ids, context=None): def save_sepa(self, cr, uid, ids, context=None):
''' '''
Save the SEPA PAIN: send the done signal to all payment orders in the file. Save the SEPA PAIN: send the done signal to all payment
With the default workflow, they will transition to 'done', while with the orders in the file. With the default workflow, they will
advanced workflow in account_banking_payment they will transition to 'sent' transition to 'done', while with the advanced workflow in
waiting reconciliation. account_banking_payment they will transition to 'sent' waiting
reconciliation.
''' '''
sepa_export = self.browse(cr, uid, ids[0], context=context) sepa_export = self.browse(cr, uid, ids[0], context=context)
self.pool.get('banking.export.sepa').write(cr, uid, self.pool.get('banking.export.sepa').write(
sepa_export.file_id.id, {'state': 'sent'}, context=context) cr, uid, sepa_export.file_id.id, {'state': 'sent'},
context=context)
wf_service = netsvc.LocalService('workflow') wf_service = netsvc.LocalService('workflow')
for order in sepa_export.payment_order_ids: for order in sepa_export.payment_order_ids:
wf_service.trg_validate(uid, 'payment.order', order.id, 'done', cr) wf_service.trg_validate(uid, 'payment.order', order.id, 'done', cr)

View File

@@ -18,17 +18,10 @@
<field name="batch_booking" /> <field name="batch_booking" />
<field name="prefered_exec_date" /> <field name="prefered_exec_date" />
<field name="charge_bearer" /> <field name="charge_bearer" />
<separator colspan="4" string="Reference for further communication" />
<field name="msg_identification" required="True" />
</group> </group>
<group states="finish"> <group states="finish">
<field name="total_amount" /> <field name="total_amount" />
<!-- Don't display this field, to avoid this bug : <field name="nb_transactions" />
https://bugs.launchpad.net/openobject-client-web/+bug/718989
Bug desc : in the Gtk client, you have to clic twice on the
"Create" button.
<field name="nb_transactions" /> -->
<!-- <field name="file_id" /> -->
<field name="file" filename="filename" /> <field name="file" filename="filename" />
<field name="filename" invisible="True"/> <field name="filename" invisible="True"/>
</group> </group>