@@ -23,6 +23,7 @@
from openerp . osv import orm , fields
from openerp . tools . translate import _
from openerp . tools . safe_eval import safe_eval
from openerp import tools , netsvc
import base64
from datetime import datetime , timedelta
@@ -35,33 +36,41 @@ _logger = logging.getLogger(__name__)
class banking_export_sdd_wizard ( orm . TransientModel ) :
_name = ' banking.export.sdd.wizard '
_description = ' Export SEPA Direct Debit XML f ile '
_description = ' Export SEPA Direct Debit F ile '
_columns = {
' state ' : fields . selection ( [ ( ' create ' , ' Create ' ) , ( ' finish ' , ' Finish ' ) ] ,
' St ate' , readonly = True ) ,
' batch_booking ' : fields . boolean ( ' Batch booking ' ,
' state ' : fields . selection ( [
( ' cre ate' , ' Create ' ) ,
( ' finish ' , ' Finish ' ) ,
] , ' State ' , readonly = True ) ,
' 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. " ) ,
' requested_collec_date ' : fields . date ( ' Requested collection date ' ,
' requested_collec_date ' : fields . date (
' Requested Collection Date ' ,
help = ' This is the date on which the collection should be made by the bank. Please keep in mind that banks only execute on working days. ' ) ,
' charge_bearer ' : fields . selection ( [
( ' SHAR ' , ' Shared ' ) ,
( ' CRED ' , ' Borne by c reditor ' ) ,
( ' DEBT ' , ' Borne by d ebtor ' ) ,
( ' SLEV ' , ' Following s ervice l evel ' ) ,
] , ' Charge b earer ' , required = True ,
( ' CRED ' , ' Borne by C reditor ' ) ,
( ' DEBT ' , ' Borne by D ebtor ' ) ,
( ' SLEV ' , ' Following S ervice L evel ' ) ,
] , ' Charge B earer ' , 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. ' ) ,
' nb_transactions ' : fields . related ( ' file_id ' , ' nb_transactions ' ,
type = ' integer ', string = ' Number of transactions ' , readonly = True ) ,
' total_amount ' : fields . related ( ' file_id ' , ' total_amount ' , type = ' float ' ,
string = ' T otal amount' , readonly = True ) ,
' file_id ' : fields . many2one ( ' banking.export.sdd ' , ' SDD XML file ' , readonly = True ) ,
' file ' : fields . related ( ' file_id ' , ' file ' , string = " File " , type = ' binary ' ,
' nb_transactions ' : fields . related (
' file_id ' , ' nb_transactions ' , type = ' integer ' ,
string = ' Number of Transactions ' , readonly = True ) ,
' t otal_ amount' : fields . related (
' file_id ' , ' total_amount ' , type = ' float ' , string = ' Total Amount ' ,
readonly = True ) ,
' filename ' : fields . related ( ' file_id ' , ' filename ' , string = " Filename " ,
type = ' char ' , size = 256 , readonly = True ) ,
' payment_order_ids ' : fields . many2many ( ' payment.order ' ,
' wiz_sdd_payorders_rel ' , ' wizard_id ' , ' payment_order_id ' ,
' Payment orders ' , readonly = True ) ,
' file_id ' : fields . many2one (
' banking.export.sdd ' , ' SDD XML File ' , readonly = True ) ,
' file ' : fields . related (
' file_id ' , ' file ' , string = " File " , type = ' binary ' , readonly = True ) ,
' filename ' : fields . related (
' file_id ' , ' filename ' , string = " Filename " , type = ' char ' , size = 256 ,
readonly = True ) ,
' payment_order_ids ' : fields . many2many (
' payment.order ' , ' wiz_sdd_payorders_rel ' , ' wizard_id ' ,
' payment_order_id ' , ' Payment Orders ' , readonly = True ) ,
}
_defaults = {
@@ -76,37 +85,94 @@ class banking_export_sdd_wizard(orm.TransientModel):
if partner_bank_obj . is_iban_valid ( cr , uid , iban , context = context ) :
return iban . replace ( ' ' , ' ' )
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 ) :
payment_order_ids = context . get ( ' active_ids ' , [ ] )
vals . update ( {
' payment_order_ids ' : [ [ 6 , 0 , payment_order_ids ] ] ,
} )
return super ( banking_export_sdd_wizard , self ) . create ( cr , uid ,
vals , context = context )
return super ( banking_export_sdd_wizard , self ) . create (
cr , uid , vals, context = context )
def _prepare_field ( self , cr , uid , field_name , field_value , max_size = 0 , sepa_export = False , line = False , sequence_type = False , context = None ) :
def _prepare_field (
self , cr , uid , field_name , field_value , max_size = 0 ,
sepa_export = False , line = False , sequence_type = False , context = None ) :
''' This function is designed to be inherited ! '''
eval_ctx = {
' sepa_export ' : sepa_export ,
' line ' : line ,
' sequence_type ' : sequence_type ,
}
try :
# SEPA uses XML, and XML = UTF-8 with support for all characters...
# But we are dealing with banks
# 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 Core Direct Debit
# Customer-to-bank implementation guidelines version 6.0
value = unidecode ( eval( field_value ) )
# Customer-to-bank implementation 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 ] ) )
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 )
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 ) ) )
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 )
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 ,
' requested_collec_date ' : sepa_export . requested_collec_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_direct_debit/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 ) :
'''
Creates the SEPA Direct Debit file. That ' s the important code !
@@ -131,7 +197,8 @@ class banking_export_sdd_wizard(orm.TransientModel):
if sepa_export . requested_collec_date :
my_requested_collec_date = sepa_export . requested_collec_date
else :
my_requested_collec_date = datetime . strftime ( datetime . today ( ) + timedelta ( days = 1 ) , ' % Y- % m- %d ' )
my_requested_collec_date = datetime . strftime (
datetime . today ( ) + timedelta ( days = 1 ) , ' % Y- % m- %d ' )
pain_ns = {
' xsi ' : ' http://www.w3.org/2001/XMLSchema-instance ' ,
@@ -141,19 +208,22 @@ class banking_export_sdd_wizard(orm.TransientModel):
root = etree . Element ( ' Document ' , nsmap = pain_ns )
pain_root = etree . SubElement ( root , root_xml_tag )
my_company_name = self . _prepare_field ( cr , uid , ' Company Name ' ,
' sepa_export.payment_order_ids[0].company_id.partner_id.n ame' ,
name_maxsize , sepa_export = sepa_export , context = context )
my_company_name = self . _prepare_field (
cr , uid , ' Company N ame ' ,
' sepa_export.payment_order_ids[0].company_id.partner_id.name ' ,
name_maxsize , sepa_export = sepa_export , context = context )
# A. Group header
group_header_1_0 = etree . SubElement ( pain_root , ' GrpHdr ' )
message_identification_1_1 = etree . SubElement ( group_header_1_0 , ' MsgId ' )
message_identification_1_1 . text = self . _prepare_field ( cr , uid ,
' M essage I dentification' ,
message_identification_1_1 = etree . SubElement (
group_header_1_0 , ' MsgId ' )
m essage_i dentification_1_1 . text = self . _prepare_field (
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- %d T % H: % M: % S ' )
creation_date_time_1_2 . text = datetime . strftime (
datetime . today ( ) , ' % Y- % m- %d T % H: % M: % S ' )
nb_of_transactions_1_6 = etree . SubElement ( group_header_1_0 , ' NbOfTxs ' )
control_sum_1_7 = etree . SubElement ( group_header_1_0 , ' CtrlSum ' )
initiating_party_1_8 = etree . SubElement ( group_header_1_0 , ' InitgPty ' )
@@ -176,7 +246,7 @@ class banking_export_sdd_wizard(orm.TransientModel):
_ ( ' Error: ' ) ,
_ ( " Missing SEPA Direct Debit mandate on the payment line with partner ' %s ' and Invoice ref ' %s ' . " )
% ( line . partner_id . name ,
line . ml_inv_ref . number ) )
line . ml_inv_ref . number ) )
if line . sdd_mandate_id . state != ' valid ' :
raise orm . except_orm (
_ ( ' Error: ' ) ,
@@ -190,7 +260,8 @@ class banking_export_sdd_wizard(orm.TransientModel):
_ ( " Missing signature date on SEPA Direct Debit mandate with reference ' %s ' for partner ' %s ' . " )
% ( line . sdd_mandate_id . unique_mandate_reference ,
line . sdd_mandate_id . partner_id . name ) )
elif line . sdd_mandate_id . signature_date > datetime . today ( ) . strftime ( ' % Y- % m- %d ' ) :
elif ( line . sdd_mandate_id . signature_date >
datetime . today ( ) . strftime ( ' % Y- % m- %d ' ) ) :
raise orm . except_orm (
_ ( ' Error: ' ) ,
_ ( " The signature date on SEPA Direct Debit mandate with reference ' %s ' for partner ' %s ' is ' %s ' , which is in the future ! " )
@@ -206,11 +277,11 @@ class banking_export_sdd_wizard(orm.TransientModel):
first_recur_lines [ ' OOFF ' ] = [ line ]
else :
raise orm . except_orm (
_ ( ' Error: ' ) ,
_ ( " 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 . sdd_mandate_id . unique_mandate_reference ,
line . sdd_mandate_id . partner_id . name ,
line . sdd_mandate_id . last_debit_date ) )
_ ( ' Error: ' ) ,
_ ( " 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 . sdd_mandate_id . unique_mandate_reference ,
line . sdd_mandate_id . partner_id . name ,
line . sdd_mandate_id . last_debit_date ) )
elif line . sdd_mandate_id . type == ' recurrent ' :
if line . sdd_mandate_id . last_debit_date :
if first_recur_lines . get ( ' RCUR ' ) :
@@ -226,7 +297,8 @@ class banking_export_sdd_wizard(orm.TransientModel):
for sequence_type , lines in first_recur_lines . items ( ) :
# B. Payment info
payment_info_2_0 = etree . SubElement ( pain_root , ' PmtInf ' )
payment_info_identification_2_1 = etree . SubElement ( payment_info_2_0 , ' PmtInfId ' )
payment_info_identification_2_1 = etree . SubElement (
payment_info_2_0 , ' PmtInfId ' )
payment_info_identification_2_1 . text = self . _prepare_field (
cr , uid , ' Payment Information Identification ' ,
" sequence_type + ' - ' + sepa_export.payment_order_ids[0].reference " ,
@@ -241,57 +313,73 @@ class banking_export_sdd_wizard(orm.TransientModel):
# Implementation guidelines" v6.0 says that control sum
# and nb_of_transactions should be present
# at both "group header" level and "payment info" level
nb_of_transactions_2_4 = etree . SubElement ( payment_info_2_0 , ' NbOfTxs ' )
nb_of_transactions_2_4 = etree . SubElement (
payment_info_2_0 , ' NbOfTxs ' )
control_sum_2_5 = etree . SubElement ( payment_info_2_0 , ' CtrlSum ' )
payment_type_info_2_6 = etree . SubElement ( payment_info_2_0 , ' PmtTpInf ' )
service_level_2_8 = etree . SubElement ( payment_type_ info_2_6 , ' SvcLvl ' )
payment_type_info_2_6 = etree . SubElement (
payment_info_2_0 , ' PmtTpInf ' )
service_level_2_8 = etree . SubElement (
payment_type_info_2_6 , ' SvcLvl ' )
service_level_code_2_9 = etree . SubElement ( service_level_2_8 , ' Cd ' )
service_level_code_2_9 . text = ' SEPA '
local_instrument_2_11 = etree . SubElement ( payment_type_info_2_6 , ' LclInstrm ' )
local_instr_code_2_12 = etree . SubElement ( local_instrument _2_11 , ' Cd ' )
local_instrument_2_11 = etree . SubElement (
payment_type_info _2_6 , ' LclInstrm ' )
local_instr_code_2_12 = etree . SubElement (
local_instrument_2_11 , ' Cd ' )
local_instr_code_2_12 . text = ' CORE '
# 2.14 Sequence Type MANDATORY
# this message element must indicate ‘ FRST
# 'FRST' = First ; 'OOFF' = One Off ; 'RCUR' : Recurring
# 'FNAL' = Final
sequence_type_2_14 = etree . SubElement ( payment_type_info_2_6 , ' SeqTp ' )
sequence_type_2_14 = etree . SubElement (
payment_type_info_2_6 , ' SeqTp ' )
sequence_type_2_14 . text = sequence_type
requested_collec_date_2_18 = etree . SubElement ( payment_info_2_0 , ' ReqdColltnDt ' )
requested_collec_date_2_18 = etree . SubElement (
payment_info_2_0 , ' ReqdColltnDt ' )
requested_collec_date_2_18 . text = my_requested_collec_date
creditor_2_19 = etree . SubElement ( payment_info_2_0 , ' Cdtr ' )
creditor_name = etree . SubElement ( creditor_2_19 , ' Nm ' )
creditor_name . text = my_company_name
creditor_account_2_20 = etree . SubElement ( payment_info_2_0 , ' CdtrAcct ' )
creditor_account_2_20 = etree . SubElement (
payment_info_2_0 , ' CdtrAcct ' )
creditor_account_id = etree . SubElement ( creditor_account_2_20 , ' Id ' )
creditor_account_iban = etree . SubElement ( creditor_account_id , ' IBAN ' )
creditor_account_iban . text = self . _validate_iban ( cr , uid ,
self . _prepare_field ( cr , uid , ' Company IBAN ' ,
creditor_account_iban = etree . SubElement (
creditor_account_id , ' IBAN ' )
creditor_account_iban . text = self . _validate_iban (
cr , uid , self . _prepare_field (
cr , uid , ' Company IBAN ' ,
' sepa_export.payment_order_ids[0].mode.bank_id.acc_number ' ,
sepa_export = sepa_export , context = context ) ,
context = context )
creditor_agent_2_21 = etree . SubElement ( payment_info_2_0 , ' CdtrAgt ' )
creditor_agent_institution = etree . SubElement ( creditor_agent_2_21 , ' FinInstnId ' )
creditor_agent_bic = etree . SubElement ( creditor_agent_institution , bic_xml_tag )
creditor_agent_bic . text = self . _prepare_field ( cr , uid , ' Company BIC ' ,
creditor_agent_institution = etree . SubElement (
creditor_agent_2_21 , ' FinInstnId ' )
creditor_agent_bic = etree . SubElement (
creditor_agent_institution , bic_xml_tag )
creditor_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
creditor_scheme_identification_2_27 = etree . SubElement ( payment_info_2_0 , ' CdtrSchmeId ' )
csi_id = etree . SubElement ( creditor_scheme_identification _2_27 , ' Id ' )
creditor_scheme_identification_2_27 = etree . SubElement (
payment_info _2_0 , ' CdtrSchme Id' )
csi_id = etree . SubElement (
creditor_scheme_identification_2_27 , ' Id ' )
csi_privateid = csi_id = etree . SubElement ( csi_id , ' PrvtId ' )
csi_other = etree . SubElement ( csi_privateid , ' Othr ' )
csi_other_id = etree . SubElement ( csi_other , ' Id ' )
csi_other_id . text = self . _prepare_field ( cr , uid ,
' SEPA Creditor Identifier ' ,
csi_other_id . text = self . _prepare_field (
cr , uid , ' SEPA Creditor Identifier ' ,
' sepa_export.payment_order_ids[0].company_id.sepa_creditor_identifier ' ,
sepa_export = sepa_export , context = context )
csi_scheme_name = etree . SubElement ( csi_other , ' SchmeNm ' )
csi_scheme_name_proprietary = etree . SubElement ( csi_scheme_name , ' Prtry ' )
csi_scheme_name_proprietary = etree . SubElement (
csi_scheme_name , ' Prtry ' )
csi_scheme_name_proprietary . text = ' SEPA '
transactions_count_2_4 = 0
@@ -299,22 +387,29 @@ class banking_export_sdd_wizard(orm.TransientModel):
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 ' )
# Instruction identification (2.30) is not mandatory, so we don't use it
end2end_identification_2_31 = etree . SubElement ( payment_identification _2_29 , ' EndToEnd Id' )
end2end_identification_2_31 . text = self . _prepare_field ( cr , uid ,
' End to End I dentification' , ' line.name ' , 35 ,
dd_transaction_info_2_28 = etree . SubElement (
payment_info_2_0 , ' DrctDbtTxInf ' )
payment_identification_2_29 = etree . SubElement (
dd_transaction_info _2_28 , ' Pmt Id' )
end2end_identification_2_31 = etree . SubElement (
payment_i dentification_2_29 , ' EndToEndId ' )
end2end_identification_2_31 . text = self . _prepare_field (
cr , uid , ' End to End Identification ' , ' line.name ' , 35 ,
line = line , context = context )
currency_name = self . _prepare_field ( cr , uid , ' Currency Code ' ,
' line.c urrency.nam e' , 3 , line = line , context = context )
instructed_amount_2_44 = etree . SubElement ( dd_transaction_info_2_28 , ' InstdAmt ' , Ccy = currency_name )
currency_name = self . _prepare_field (
cr , uid , ' C urrency Cod e' , ' line.currency.name ' , 3 ,
line = line , context = context )
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 ' )
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 (
cr , uid , ' Unique Mandate Reference ' ,
' line.sdd_mandate_id.unique_mandate_reference ' ,
@@ -327,81 +422,67 @@ class banking_export_sdd_wizard(orm.TransientModel):
line = line , context = context )
# TODO look at 2.50 "Amendment Indicator
debtor_agent_2_70 = etree . SubElement ( dd_transaction_info_2_28 , ' DbtrAgt ' )
debtor_agent_institution = etree . SubElement ( debtor_agent_2_70 , ' FinInstnId ' )
debtor_agent_bic = etree . SubElement ( debtor_agent_institution , bic_xml_tag )
debtor_agent_bic . text = self . _prepare_field ( cr , uid ,
' Customer BIC ' , ' line.bank_id.bank.bic ' ,
debtor_agent_2_70 = etree . SubElement (
dd_transaction_info_2_28 , ' DbtrAgt ' )
debtor_agent_institution = etree . SubElement (
debtor_agent_2_70 , ' FinInstnId ' )
debtor_agent_bic = etree . SubElement (
debtor_agent_institution , bic_xml_tag )
debtor_agent_bic . text = self . _prepare_field (
cr , uid , ' Customer BIC ' , ' line.bank_id.bank.bic ' ,
line = line , context = context )
debtor_2_72 = etree . SubElement ( dd_transaction_info_2_28 , ' Dbtr ' )
debtor_2_72 = etree . SubElement (
dd_transaction_info_2_28 , ' Dbtr ' )
debtor_name = etree . SubElement ( debtor_2_72 , ' Nm ' )
debtor_name . text = self . _prepare_field ( cr , uid ,
' Customer Name ' , ' line.partner_id.name ' ,
debtor_name . text = self . _prepare_field (
cr , uid , ' Customer Name ' , ' line.partner_id.name ' ,
name_maxsize , line = line , context = context )
debtor_account_2_73 = etree . SubElement ( dd_transaction_info_2_28 , ' DbtrAcct ' )
debtor_account_2_73 = etree . SubElement (
dd_transaction_info_2_28 , ' DbtrAcct ' )
debtor_account_id = etree . SubElement ( debtor_account_2_73 , ' Id ' )
debtor_account_iban = etree . SubElement ( debtor_account_id , ' IBAN ' )
debtor_account_iban . text = self . _validate_iban ( cr , uid ,
self . _prepare_field ( cr , uid , ' Customer IBAN ' ,
debtor_account_iban = etree . SubElement (
debtor_account_id , ' IBAN ' )
debtor_account_iban . text = self . _validate_iban (
cr , uid , self . _prepare_field (
cr , uid , ' Customer IBAN ' ,
' line.bank_id.acc_number ' , line = line ,
context = context ) ,
context = context )
remittance_info_2_88 = etree . SubElement ( dd_transaction_info_2_28 , ' RmtInf ' )
context = context ) ,
context = context )
remittance_info_2_88 = etree . SubElement (
dd_transaction_info_2_28 , ' 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_89 = etree . SubElement ( remittance_info_2_88 , ' Ustrd ' )
remittance_info_unstructured_2_89 . text = self . _prepare_field ( cr , uid ,
' R emittance I nformation ' , ' line.communication ' ,
remittance_info_unstructured_2_89 = etree . SubElement (
remittance_info_2_88 , ' Ustrd ' )
r emittance_i nfo_unstructured_2_89 . text = self . _prepare_field (
cr , uid , ' Remittance Information ' , ' line.communication ' ,
140 , line = line , context = context )
nb_of_transactions_2_4 . text = str ( transactions_count_2_4 )
control_sum_2_5 . text = ' %.2f ' % amount_control_sum_2_5
nb_of_transactions_1_6 . text = str ( transactions_count_1_6 )
control_sum_1_7 . text = ' %.2f ' % amount_control_sum_1_7
xml_string = etree . tostring ( root , pretty_print = True , encoding = ' UTF-8 ' , xml_declaration = True )
_logger . debug ( " Generated SDD XML file in format %s below " % pain_flavor )
xml_string = etree . tostring (
root , pretty_print = True , encoding = ' UTF-8 ' , xml_declaration = True )
_logger . debug (
" Generated SDD XML file in format %s below " % pain_flavor )
_logger . debug ( xml_string )
xsd_etree_obj = etree . parse ( tools . file_open ( ' account_banking_sepa_direct_debit/data/ %s .xsd ' % pain_flavor ) )
official_pain_schema = etree . XMLSchema ( xsd_etree_obj )
_logger . debug ( " Printing %s XML Schema definition: " % pain_flavor )
_logger . debug ( etree . tostring ( xsd_etree_obj , pretty_print = True , encoding = ' UTF-8 ' , xml_declaration = True ) )
try :
# If I do official_pain_schema.assertValid(root), then I get this
# error msg in the exception :
# 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 : Element 'Document': No matching global declaration available for the validation root.
# So I re-import the SEPA XML from the string, and give this
# so validation
# If you know how I can avoid that, please tell me -- Alexis
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 ) )
self . _validate_xml ( cr , uid , xml_string , pain_flavor )
# CREATE the banking.export.sepa record
file_id = self . pool . get ( ' banking.export.sdd ' ) . create ( cr , uid ,
{
' batch_booking ' : sepa_export . batch_booking ,
' charge_bearer ' : sepa_expor t. charge_bearer ,
' requested_collec_date ' : sepa_expor t. requested_collec_date ,
' total_amount ' : total_amount ,
' nb_transactions ' : transactions_count_1_6 ,
' file ' : base64 . encodestring ( xml_string ) ,
' payment_order_ids ' : [
( 6 , 0 , [ x . id for x in sepa_export . payment_order_ids ] )
] ,
} , context = context )
file_id = self . pool . get ( ' banking.export.sdd ' ) . create (
cr , uid , self . _prepare_export_sepa (
cr , uid , sepa_export , total_amount , transactions_count_1_6 ,
xml_string , contex t= context ) ,
contex t= context )
self . write ( cr , uid , ids , {
' file_id ' : file_ id,
' state ' : ' finish ' ,
self . write (
cr , uid , ids , {
' file_id ' : file_id ,
' state ' : ' finish ' ,
} , context = context )
action = {
' name ' : ' SEPA Direct Debit XML ' ,
' name ' : ' SEPA Direct Debit File ' ,
' type ' : ' ir.actions.act_window ' ,
' view_type ' : ' form ' ,
' view_mode ' : ' form,tree ' ,
@@ -422,12 +503,14 @@ class banking_export_sdd_wizard(orm.TransientModel):
def save_sepa ( self , cr , uid , ids , context = None ) :
'''
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
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
'''
sepa_export = self . browse ( cr , uid , ids [ 0 ] , context = context )
self . pool . get ( ' banking.export.sdd ' ) . write ( cr , uid ,
sepa_export . file_id . id , { ' state ' : ' sent ' } , context = context )
self . pool . get ( ' banking.export.sdd ' ) . write (
cr , uid , sepa_export. file_id . id , { ' state ' : ' sent ' } ,
context = context )
wf_service = netsvc . LocalService ( ' workflow ' )
for order in sepa_export . payment_order_ids :
wf_service . trg_validate ( uid , ' payment.order ' , order . id , ' done ' , cr )
@@ -437,7 +520,9 @@ class banking_export_sdd_wizard(orm.TransientModel):
' last_debit_date ' : datetime . today ( ) . strftime ( ' % Y- % m- %d ' )
} ,
context = context )
oneoff_mandate_ids = [ line . sdd_mandate_id . id for line in order . line_ids if line . sdd_mandate_id . type == ' oneoff ' ]
oneoff_mandate_ids = \
[ line . sdd_mandate_id . id for line in order . line_ids
if line . sdd_mandate_id . type == ' oneoff ' ]
self . pool [ ' sdd.mandate ' ] . write (
cr , uid , oneoff_mandate_ids , { ' state ' : ' expired ' } ,
context = context )