FIX bon prelevement SEPA generation (#38998)
* TEST : add test for bon_prelevement generation * TEST : add tests for bank transfer SEPA file generation * FIX WARNING : dol_syslog(array) outputs a warning. * FIX bon prelevement : non-default IBAN were not used in the generated XML. * BonPrelevement test : add validation against XSD * BonVirement test : add validation against XSD * FIX codesniffer * Enhance test error messages * Avoid PHP warning during tests * TEST FIX : one bank account was not created * TEST FIX : runnning all tests makes this one fail because is corrupted by a previous test. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
This commit is contained in:
parent
1e74fc3885
commit
5f2905d54a
5 changed files with 3180 additions and 4 deletions
|
|
@ -1099,7 +1099,7 @@ class BonPrelevement extends CommonObject
|
|||
// phpcs:enable
|
||||
global $conf, $langs, $user;
|
||||
|
||||
dol_syslog(__METHOD__ . " mode=".$mode." format=".$format." type=".$type." dids=".$dids." fk_bank_account=".$fk_bank_account." sourcetype=".$sourcetype, LOG_DEBUG);
|
||||
dol_syslog(__METHOD__ . " mode=".$mode." format=".$format." type=".$type." dids=".(is_array($dids) ? implode(',', $dids) : $dids)." fk_bank_account=".$fk_bank_account." sourcetype=".$sourcetype, LOG_DEBUG);
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT . "/compta/facture/class/facture.class.php";
|
||||
require_once DOL_DOCUMENT_ROOT . "/societe/class/societe.class.php";
|
||||
|
|
@ -1421,7 +1421,7 @@ class BonPrelevement extends CommonObject
|
|||
$row = $this->db->fetch_row($resql);
|
||||
|
||||
// Build the new ref
|
||||
$ref = $prefixt . $ref . sprintf("%03d", (intval($row[0]) + 1));
|
||||
$ref = $prefixt . $ref . sprintf("%03d", (intval($row[0] ?? 0) + 1));
|
||||
|
||||
// $conf->abc->dir_output may be:
|
||||
// /home/ldestailleur/git/dolibarr_15.0/documents/abc/
|
||||
|
|
@ -1986,10 +1986,39 @@ class BonPrelevement extends CommonObject
|
|||
}
|
||||
$cachearraytotestduplicate[$obj->idfac] = $obj->rowid;
|
||||
|
||||
// Get the default value
|
||||
$daterum = (!empty($obj->date_rum)) ? $this->db->jdate($obj->date_rum) : $this->db->jdate($obj->datec);
|
||||
$iban = dolDecrypt($obj->iban);
|
||||
$bic = $obj->bic;
|
||||
$drum = $obj->drum;
|
||||
$rum = $obj->rum;
|
||||
|
||||
$fileDebiteurSection .= $this->EnregDestinataireSEPA($obj->code, $obj->nom, $obj->address, $obj->zip, $obj->town, $obj->country_code, '', '', '', $obj->somme, $obj->reffac, $obj->idfac, $iban, $obj->bic, $daterum, (string) $obj->drum, $obj->rum, $type);
|
||||
// But if a force bank account is defined, we use it instead
|
||||
if (!empty($obj->fk_prelevement_demande)) {
|
||||
$companybankaccountid = 0;
|
||||
|
||||
$sqltmp = "SELECT fk_societe_rib FROM ".MAIN_DB_PREFIX."prelevement_demande";
|
||||
$sqltmp .= " WHERE rowid = ".((int) $obj->fk_prelevement_demande);
|
||||
|
||||
$resqltmp = $this->db->query($sqltmp);
|
||||
|
||||
$objtmp = $this->db->fetch_object($resqltmp);
|
||||
if ($objtmp) {
|
||||
$companybankaccountid = (int) $objtmp->fk_societe_rib;
|
||||
}
|
||||
|
||||
$bankaccount = new CompanyBankAccount($this->db);
|
||||
$bankaccount->fetch($companybankaccountid);
|
||||
if ($bankaccount->id > 0) {
|
||||
$daterum = $bankaccount->date_rum;
|
||||
$iban = $bankaccount->iban;
|
||||
$bic = $bankaccount->bic;
|
||||
$drum = $bankaccount->id;
|
||||
$rum = $bankaccount->rum;
|
||||
}
|
||||
}
|
||||
|
||||
$fileDebiteurSection .= $this->EnregDestinataireSEPA($obj->code, $obj->nom, $obj->address, $obj->zip, $obj->town, $obj->country_code, '', '', '', $obj->somme, $obj->reffac, $obj->idfac, $iban, $bic, $daterum, (string) $drum, $rum, $type);
|
||||
|
||||
$this->total += $obj->somme;
|
||||
$i++;
|
||||
|
|
|
|||
921
test/assets/xsd/pain.001.001.03.xsd
Normal file
921
test/assets/xsd/pain.001.001.03.xsd
Normal file
|
|
@ -0,0 +1,921 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!--Generated by SWIFTStandards Workstation (build:R6.1.0.2) on 2009 Jan 08 17:30:53-->
|
||||
<xs:schema xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
|
||||
<xs:element name="Document" type="Document"/>
|
||||
<xs:complexType name="AccountIdentification4Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="IBAN" type="IBAN2007Identifier"/>
|
||||
<xs:element name="Othr" type="GenericAccountIdentification1"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AccountSchemeName1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalAccountIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="ActiveOrHistoricCurrencyAndAmount_SimpleType">
|
||||
<xs:restriction base="xs:decimal">
|
||||
<xs:minInclusive value="0"/>
|
||||
<xs:fractionDigits value="5"/>
|
||||
<xs:totalDigits value="18"/>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="InstdAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<xs:element name="EqvtAmt" type="EquivalentAmount2"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="AnyBICIdentifier">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="Authorisation1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="Authorisation1Code"/>
|
||||
<xs:element name="Prtry" type="Max128Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="BICIdentifier">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="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="BranchAndFinancialInstitutionIdentification4">
|
||||
<xs:sequence>
|
||||
<xs:element name="FinInstnId" type="FinancialInstitutionIdentification7"/>
|
||||
<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="CashAccount16">
|
||||
<xs:sequence>
|
||||
<xs:element name="Id" type="AccountIdentification4Choice"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="CashAccountType2"/>
|
||||
<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="CashAccountType2">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="CashAccountType4Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="CashAccountType4Code">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="CASH"/>
|
||||
<xs:enumeration value="CHAR"/>
|
||||
<xs:enumeration value="COMM"/>
|
||||
<xs:enumeration value="TAXE"/>
|
||||
<xs:enumeration value="CISH"/>
|
||||
<xs:enumeration value="TRAS"/>
|
||||
<xs:enumeration value="SACC"/>
|
||||
<xs:enumeration value="CACC"/>
|
||||
<xs:enumeration value="SVGS"/>
|
||||
<xs:enumeration value="ONDP"/>
|
||||
<xs:enumeration value="MGLD"/>
|
||||
<xs:enumeration value="NREX"/>
|
||||
<xs:enumeration value="MOMA"/>
|
||||
<xs:enumeration value="LOAN"/>
|
||||
<xs:enumeration value="SLRY"/>
|
||||
<xs:enumeration value="ODFT"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="CategoryPurpose1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalCategoryPurpose1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="Cheque6">
|
||||
<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: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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ChequeDelivery1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalClearingSystemIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="CreditTransferTransactionInformation10">
|
||||
<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="ExchangeRateInformation1"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChrgBr" type="ChargeBearerType1Code"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChqInstr" type="Cheque6"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="UltmtDbtr" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt1" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt1Acct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt2" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt2Acct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt3" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="IntrmyAgt3Acct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtrAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtrAgtAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="Cdtr" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtrAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="UltmtCdtr" type="PartyIdentification32"/>
|
||||
<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="RemittanceInformation5"/>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="DocumentType3Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="CustomerCreditTransferInitiationV03">
|
||||
<xs:sequence>
|
||||
<xs:element name="GrpHdr" type="GroupHeader32"/>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="1" name="PmtInf" type="PaymentInstructionInformation3"/>
|
||||
</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="Document">
|
||||
<xs:sequence>
|
||||
<xs:element name="CstmrCdtTrfInitn" type="CustomerCreditTransferInitiationV03"/>
|
||||
</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="ExchangeRateInformation1">
|
||||
<xs:sequence>
|
||||
<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="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="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:complexType name="FinancialIdentificationSchemeName1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalFinancialInstitutionIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="FinancialInstitutionIdentification7">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="BIC" type="BICIdentifier"/>
|
||||
<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="GroupHeader32">
|
||||
<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="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="FwdgAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalLocalInstrument1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="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="OrganisationIdentification4">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="BICOrBEI" type="AnyBICIdentifier"/>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" name="Othr" type="GenericOrganisationIdentification1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="OrganisationIdentificationSchemeName1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalOrganisationIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Party6Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="OrgId" type="OrganisationIdentification4"/>
|
||||
<xs:element name="PrvtId" type="PersonIdentification5"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="PartyIdentification32">
|
||||
<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="Party6Choice"/>
|
||||
<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="PaymentInstructionInformation3">
|
||||
<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="PartyIdentification32"/>
|
||||
<xs:element name="DbtrAcct" type="CashAccount16"/>
|
||||
<xs:element name="DbtrAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="DbtrAgtAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="UltmtDbtr" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChrgBr" type="ChargeBearerType1Code"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChrgsAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChrgsAcctAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="1" name="CdtTrfTxInf" type="CreditTransferTransactionInformation10"/>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalPersonIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalPurpose1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="DocumentType5Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="RemittanceAmount1">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="DuePyblAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="DscntApldAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtNoteAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="TaxAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<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="RemittanceInformation5">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" name="Ustrd" type="Max140Text"/>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" name="Strd" type="StructuredRemittanceInformation7"/>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalServiceLevel1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="StructuredRemittanceInformation7">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" name="RfrdDocInf" type="ReferredDocumentInformation3"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="RfrdDocAmt" type="RemittanceAmount1"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtrRefInf" type="CreditorReferenceInformation2"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="Invcr" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="Invcee" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="3" minOccurs="0" name="AddtlRmtInf" type="Max140Text"/>
|
||||
</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="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>
|
||||
879
test/assets/xsd/pain.008.001.02.xsd
Normal file
879
test/assets/xsd/pain.008.001.02.xsd
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!--Generated by SWIFTStandards Workstation (build:R6.1.0.2) on 2009 Jan 08 17:30:53-->
|
||||
<xs:schema xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.001.02" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:iso:std:iso:20022:tech:xsd:pain.008.001.02">
|
||||
<xs:element name="Document" type="Document"/>
|
||||
<xs:complexType name="AccountIdentification4Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="IBAN" type="IBAN2007Identifier"/>
|
||||
<xs:element name="Othr" type="GenericAccountIdentification1"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AccountSchemeName1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalAccountIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="ActiveOrHistoricCurrencyAndAmount_SimpleType">
|
||||
<xs:restriction base="xs:decimal">
|
||||
<xs:minInclusive value="0"/>
|
||||
<xs:fractionDigits value="5"/>
|
||||
<xs:totalDigits value="18"/>
|
||||
</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="AmendmentInformationDetails6">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlMndtId" type="Max35Text"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlCdtrSchmeId" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlCdtrAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlCdtrAgtAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlDbtr" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlDbtrAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlDbtrAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlDbtrAgtAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlFnlColltnDt" type="ISODate"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="OrgnlFrqcy" type="Frequency1Code"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="AnyBICIdentifier">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="Authorisation1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="Authorisation1Code"/>
|
||||
<xs:element name="Prtry" type="Max128Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="BICIdentifier">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="BatchBookingIndicator">
|
||||
<xs:restriction base="xs:boolean"/>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="BranchAndFinancialInstitutionIdentification4">
|
||||
<xs:sequence>
|
||||
<xs:element name="FinInstnId" type="FinancialInstitutionIdentification7"/>
|
||||
<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="CashAccount16">
|
||||
<xs:sequence>
|
||||
<xs:element name="Id" type="AccountIdentification4Choice"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="Tp" type="CashAccountType2"/>
|
||||
<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="CashAccountType2">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="CashAccountType4Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="CashAccountType4Code">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="CASH"/>
|
||||
<xs:enumeration value="CHAR"/>
|
||||
<xs:enumeration value="COMM"/>
|
||||
<xs:enumeration value="TAXE"/>
|
||||
<xs:enumeration value="CISH"/>
|
||||
<xs:enumeration value="TRAS"/>
|
||||
<xs:enumeration value="SACC"/>
|
||||
<xs:enumeration value="CACC"/>
|
||||
<xs:enumeration value="SVGS"/>
|
||||
<xs:enumeration value="ONDP"/>
|
||||
<xs:enumeration value="MGLD"/>
|
||||
<xs:enumeration value="NREX"/>
|
||||
<xs:enumeration value="MOMA"/>
|
||||
<xs:enumeration value="LOAN"/>
|
||||
<xs:enumeration value="SLRY"/>
|
||||
<xs:enumeration value="ODFT"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="CategoryPurpose1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalCategoryPurpose1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="ClearingSystemIdentification2Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalClearingSystemIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="DocumentType3Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="CustomerDirectDebitInitiationV02">
|
||||
<xs:sequence>
|
||||
<xs:element name="GrpHdr" type="GroupHeader39"/>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="1" name="PmtInf" type="PaymentInstructionInformation4"/>
|
||||
</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="DirectDebitTransaction6">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="MndtRltdInf" type="MandateRelatedInformation6"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtrSchmeId" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="PreNtfctnId" type="Max35Text"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="PreNtfctnDt" type="ISODate"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="DirectDebitTransactionInformation9">
|
||||
<xs:sequence>
|
||||
<xs:element name="PmtId" type="PaymentIdentification1"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="PmtTpInf" type="PaymentTypeInformation20"/>
|
||||
<xs:element name="InstdAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChrgBr" type="ChargeBearerType1Code"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="DrctDbtTx" type="DirectDebitTransaction6"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="UltmtCdtr" type="PartyIdentification32"/>
|
||||
<xs:element name="DbtrAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="DbtrAgtAcct" type="CashAccount16"/>
|
||||
<xs:element name="Dbtr" type="PartyIdentification32"/>
|
||||
<xs:element name="DbtrAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="UltmtDbtr" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="InstrForCdtrAgt" 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="RemittanceInformation5"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Document">
|
||||
<xs:sequence>
|
||||
<xs:element name="CstmrDrctDbtInitn" type="CustomerDirectDebitInitiationV02"/>
|
||||
</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:simpleType name="ExternalAccountIdentification1Code">
|
||||
<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="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:complexType name="FinancialIdentificationSchemeName1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalFinancialInstitutionIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="FinancialInstitutionIdentification7">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="BIC" type="BICIdentifier"/>
|
||||
<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:simpleType name="Frequency1Code">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="YEAR"/>
|
||||
<xs:enumeration value="MNTH"/>
|
||||
<xs:enumeration value="QURT"/>
|
||||
<xs:enumeration value="MIAN"/>
|
||||
<xs:enumeration value="WEEK"/>
|
||||
<xs:enumeration value="DAIL"/>
|
||||
<xs:enumeration value="ADHO"/>
|
||||
<xs:enumeration value="INDA"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<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="GroupHeader39">
|
||||
<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="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="FwdgAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="IBAN2007Identifier">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="ISODate">
|
||||
<xs:restriction base="xs:date"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="ISODateTime">
|
||||
<xs:restriction base="xs:dateTime"/>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="LocalInstrument2Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalLocalInstrument1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="MandateRelatedInformation6">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="MndtId" type="Max35Text"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="DtOfSgntr" type="ISODate"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="AmdmntInd" type="TrueFalseIndicator"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="AmdmntInfDtls" type="AmendmentInformationDetails6"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ElctrncSgntr" type="Max1025Text"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="FrstColltnDt" type="ISODate"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="FnlColltnDt" type="ISODate"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="Frqcy" type="Frequency1Code"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="Max1025Text">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:minLength value="1"/>
|
||||
<xs:maxLength value="1025"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="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="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="OrganisationIdentification4">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="BICOrBEI" type="AnyBICIdentifier"/>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" name="Othr" type="GenericOrganisationIdentification1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="OrganisationIdentificationSchemeName1Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalOrganisationIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Party6Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="OrgId" type="OrganisationIdentification4"/>
|
||||
<xs:element name="PrvtId" type="PersonIdentification5"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="PartyIdentification32">
|
||||
<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="Party6Choice"/>
|
||||
<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="PaymentInstructionInformation4">
|
||||
<xs:sequence>
|
||||
<xs:element name="PmtInfId" type="Max35Text"/>
|
||||
<xs:element name="PmtMtd" type="PaymentMethod2Code"/>
|
||||
<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="PaymentTypeInformation20"/>
|
||||
<xs:element name="ReqdColltnDt" type="ISODate"/>
|
||||
<xs:element name="Cdtr" type="PartyIdentification32"/>
|
||||
<xs:element name="CdtrAcct" type="CashAccount16"/>
|
||||
<xs:element name="CdtrAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtrAgtAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="UltmtCdtr" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChrgBr" type="ChargeBearerType1Code"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChrgsAcct" type="CashAccount16"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="ChrgsAcctAgt" type="BranchAndFinancialInstitutionIdentification4"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtrSchmeId" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="1" name="DrctDbtTxInf" type="DirectDebitTransactionInformation9"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="PaymentMethod2Code">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="DD"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="PaymentTypeInformation20">
|
||||
<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="SeqTp" type="SequenceType1Code"/>
|
||||
<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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalPersonIdentification1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalPurpose1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="DocumentType5Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="RemittanceAmount1">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="DuePyblAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="DscntApldAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtNoteAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="TaxAmt" type="ActiveOrHistoricCurrencyAndAmount"/>
|
||||
<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="RemittanceInformation5">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" name="Ustrd" type="Max140Text"/>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" name="Strd" type="StructuredRemittanceInformation7"/>
|
||||
</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:simpleType name="SequenceType1Code">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="FRST"/>
|
||||
<xs:enumeration value="RCUR"/>
|
||||
<xs:enumeration value="FNAL"/>
|
||||
<xs:enumeration value="OOFF"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="ServiceLevel8Choice">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="Cd" type="ExternalServiceLevel1Code"/>
|
||||
<xs:element name="Prtry" type="Max35Text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</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="StructuredRemittanceInformation7">
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" name="RfrdDocInf" type="ReferredDocumentInformation3"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="RfrdDocAmt" type="RemittanceAmount1"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="CdtrRefInf" type="CreditorReferenceInformation2"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="Invcr" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="1" minOccurs="0" name="Invcee" type="PartyIdentification32"/>
|
||||
<xs:element maxOccurs="3" minOccurs="0" name="AddtlRmtInf" type="Max140Text"/>
|
||||
</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="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:simpleType name="TrueFalseIndicator">
|
||||
<xs:restriction base="xs:boolean"/>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
/* Copyright (C) 2010 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2023 Alexandre Janniaux <alexandre.janniaux@gmail.com>
|
||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2025 Thomas Negre <tnegre@open-dsi.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -29,7 +30,12 @@ global $conf,$user,$langs,$db;
|
|||
//define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver
|
||||
//require_once 'PHPUnit/Autoload.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/master.inc.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/core/lib/admin.lib.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/compta/prelevement/class/bonprelevement.class.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/compta/facture/class/facture.class.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/societe/class/societe.class.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/societe/class/companybankaccount.class.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/compta/bank/class/account.class.php';
|
||||
require_once dirname(__FILE__).'/CommonClassTest.class.php';
|
||||
|
||||
if (empty($user->id)) {
|
||||
|
|
@ -51,8 +57,188 @@ $langs->load("main");
|
|||
*/
|
||||
class BonPrelevementTest extends CommonClassTest
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test IBANs valid (mod97 check compliant).
|
||||
// Used to identify which bank account (RIB) was selected in the SEPA file.
|
||||
// ---------------------------------------------------------------------------
|
||||
const IBAN_A_DEFAULT = 'FR7630001007941234567890185'; // default bank account of COMPANY_A
|
||||
const IBAN_A_SPECIFIC = 'FR7630001007941234567890282'; // specific bank account of COMPANY_A
|
||||
const IBAN_B_DEFAULT = 'FR7630001007941234567890379'; // default bank account of COMPANY_B
|
||||
const IBAN_B_SPECIFIC = 'FR7630001007941234567890476'; // specific bank account of COMPANY_B
|
||||
const BIC = 'BNPAFRPPXXX';
|
||||
const XSD_PAIN_008 = __DIR__.'/../assets/xsd/pain.008.001.02.xsd';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared fixtures created once in setUpBeforeClass(),
|
||||
// inside the parent transaction (rolled back by tearDownAfterClass()).
|
||||
// ---------------------------------------------------------------------------
|
||||
/** @var int Row ID of COMPANY_A */
|
||||
protected static $socidA = 0;
|
||||
/** @var int Row ID of COMPANY_B */
|
||||
protected static $socidB = 0;
|
||||
/** @var int Row ID of the default bank account of COMPANY_A */
|
||||
protected static $ribADefaultId = 0;
|
||||
/** @var int Row ID of the specific bank account of COMPANY_A */
|
||||
protected static $ribASpecificId = 0;
|
||||
/** @var int Row ID of the default bank account of COMPANY_B */
|
||||
protected static $ribBDefaultId = 0;
|
||||
/** @var int Row ID of the specific bank account of COMPANY_B */
|
||||
protected static $ribBSpecificId = 0;
|
||||
/** @var int Row ID of the issuer bank account (llx_bank_account) */
|
||||
protected static $fkBankAccount = 0;
|
||||
/** @var string Error message collected in setUpBeforeClass() if a fixture failed to be created */
|
||||
protected static $setUpError = '';
|
||||
/** @var ?Societe Global $mysoc as it was before this test class forced it into a SEPA country */
|
||||
protected static $savmysoc;
|
||||
|
||||
/**
|
||||
* testBonPrevelementCreate
|
||||
* setUpBeforeClass
|
||||
*
|
||||
* Creates shared fixtures for all tests in this class:
|
||||
* - COMPANY_A and COMPANY_B (French customer third parties)
|
||||
* - Two bank accounts per company (one default, one specific)
|
||||
* - One issuer bank account used for BonPrelevement generation
|
||||
*
|
||||
* Everything is created inside the parent transaction;
|
||||
* the final rollback in tearDownAfterClass() removes all this data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
global $db, $user, $mysoc;
|
||||
parent::setUpBeforeClass(); // Opens the parent transaction ($db->begin())
|
||||
|
||||
// BonPrelevement::generate() requires $mysoc to be in a SEPA country (global $mysoc,
|
||||
// not restored between test classes by CommonClassTest). Some other test class run
|
||||
// earlier in the same PHPUnit process (e.g. PricesTest) may have left it on a
|
||||
// non-SEPA country, so force it here and restore it in tearDownAfterClass().
|
||||
self::$savmysoc = clone $mysoc;
|
||||
$mysoc->country_code = 'FR';
|
||||
$mysoc->country_id = 1;
|
||||
|
||||
// Enable the prelevement module if not already active
|
||||
if (!isModEnabled('prelevement')) {
|
||||
activateModule('modPrelevement', 1, 1);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// COMPANY_A: French customer third party
|
||||
// ------------------------------------------------------------------
|
||||
$socA = new Societe($db);
|
||||
$socA->name = 'BonPrelevTest CompanyA';
|
||||
$socA->client = 1;
|
||||
$socA->country_id = 1; // France (rowid=1 in c_country)
|
||||
$socA->code_client = -1; // -1 = auto-generate customer code
|
||||
self::$socidA = (int) $socA->create($user);
|
||||
if (self::$socidA <= 0) {
|
||||
self::$setUpError .= 'Societe::create() (COMPANY_A) failed: '.$socA->errorsToString().' ';
|
||||
}
|
||||
|
||||
// RIB_A_DEFAULT: first bank account for COMPANY_A, will be the default (default_rib=1)
|
||||
$ribADef = new CompanyBankAccount($db);
|
||||
$ribADef->socid = self::$socidA;
|
||||
$ribADef->type = 'ban';
|
||||
$ribADef->iban = self::IBAN_A_DEFAULT;
|
||||
$ribADef->bic = self::BIC;
|
||||
$ribADef->rum = 'RUM-A-DEF-01';
|
||||
$ribADef->date_rum = dol_now();
|
||||
$ribADef->frstrecur = 'RCUR';
|
||||
$ribADef->default_rib = 1;
|
||||
self::$ribADefaultId = (int) $ribADef->create($user); // minimal INSERT
|
||||
$ribADef->update($user); // persists iban, bic, rum, date_rum, default_rib
|
||||
|
||||
// RIB_A_SPECIFIC: second bank account for COMPANY_A, non-default
|
||||
$ribASpec = new CompanyBankAccount($db);
|
||||
$ribASpec->socid = self::$socidA;
|
||||
$ribASpec->type = 'ban';
|
||||
$ribASpec->iban = self::IBAN_A_SPECIFIC;
|
||||
$ribASpec->bic = self::BIC;
|
||||
$ribASpec->rum = 'RUM-A-SPEC-01';
|
||||
$ribASpec->date_rum = dol_now();
|
||||
$ribASpec->frstrecur = 'RCUR';
|
||||
$ribASpec->default_rib = 0;
|
||||
self::$ribASpecificId = (int) $ribASpec->create($user);
|
||||
$ribASpec->update($user);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// COMPANY_B: same structure, distinct IBANs
|
||||
// ------------------------------------------------------------------
|
||||
$socB = new Societe($db);
|
||||
$socB->name = 'BonPrelevTest CompanyB';
|
||||
$socB->client = 1;
|
||||
$socB->country_id = 1;
|
||||
$socB->code_client = -1; // -1 = auto-generate customer code
|
||||
self::$socidB = (int) $socB->create($user);
|
||||
if (self::$socidB <= 0) {
|
||||
self::$setUpError .= 'Societe::create() (COMPANY_B) failed: '.$socB->errorsToString().' ';
|
||||
}
|
||||
|
||||
$ribBDef = new CompanyBankAccount($db);
|
||||
$ribBDef->socid = self::$socidB;
|
||||
$ribBDef->type = 'ban';
|
||||
$ribBDef->iban = self::IBAN_B_DEFAULT;
|
||||
$ribBDef->bic = self::BIC;
|
||||
$ribBDef->rum = 'RUM-B-DEF-01';
|
||||
$ribBDef->date_rum = dol_now();
|
||||
$ribBDef->frstrecur = 'RCUR';
|
||||
$ribBDef->default_rib = 1;
|
||||
self::$ribBDefaultId = (int) $ribBDef->create($user);
|
||||
$ribBDef->update($user);
|
||||
|
||||
$ribBSpec = new CompanyBankAccount($db);
|
||||
$ribBSpec->socid = self::$socidB;
|
||||
$ribBSpec->type = 'ban';
|
||||
$ribBSpec->iban = self::IBAN_B_SPECIFIC;
|
||||
$ribBSpec->bic = self::BIC;
|
||||
$ribBSpec->rum = 'RUM-B-SPEC-01';
|
||||
$ribBSpec->date_rum = dol_now();
|
||||
$ribBSpec->frstrecur = 'RCUR';
|
||||
$ribBSpec->default_rib = 0;
|
||||
self::$ribBSpecificId = (int) $ribBSpec->create($user);
|
||||
$ribBSpec->update($user);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Issuer bank account (llx_bank_account) passed as fk_bank_account
|
||||
// ------------------------------------------------------------------
|
||||
$account = new Account($db);
|
||||
$account->ref = 'BONPRELTEST'; // max 12 chars (llx_bank_account.ref is varchar(12))
|
||||
$account->label = 'BonPrelevTest Issuer';
|
||||
$account->country_id = 1; // France
|
||||
$account->date_solde = dol_now();
|
||||
$account->iban = 'FR7630001007941234567890573'; // valid IBAN, not used in assertions
|
||||
$account->bic = self::BIC;
|
||||
$account->ics = 'FR77ZZZ123456789'; // SEPA Creditor Identifier (ICS)
|
||||
$account->owner_name = 'TestCorp';
|
||||
$account->currency_code = 'EUR';
|
||||
self::$fkBankAccount = (int) $account->create($user);
|
||||
if (self::$fkBankAccount <= 0) {
|
||||
self::$setUpError .= 'Account::create() (issuer) failed: '.$account->errorsToString().' ';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tearDownAfterClass
|
||||
*
|
||||
* Restores the global $mysoc forced in setUpBeforeClass() before rolling back
|
||||
* the parent transaction, so this class does not leak state to test classes
|
||||
* that run after it in the same PHPUnit process.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
global $mysoc;
|
||||
$mysoc = self::$savmysoc;
|
||||
|
||||
parent::tearDownAfterClass(); // Rolls back the parent transaction ($db->rollback())
|
||||
}
|
||||
|
||||
/**
|
||||
* testBonPrelevementCreate
|
||||
*
|
||||
* Non-regression test: verifies that create() in simulation mode
|
||||
* with no pending payment requests returns 0 (no requests processed, no error).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
|
|
@ -85,6 +271,472 @@ class BonPrelevementTest extends CommonClassTest
|
|||
// Test SEPA file
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* testTwoCompaniesSimpleRib
|
||||
*
|
||||
* Verifies that when two different companies each have one invoice with a
|
||||
* specific bank account forced in the direct debit request, the generated
|
||||
* SEPA file contains the forced IBAN for each transaction (not the
|
||||
* company's default bank account.)
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A (100) -> request with RIB_A_SPECIFIC -> expects IBAN_A_SPECIFIC
|
||||
* INV_B (300) -> request with RIB_B_SPECIFIC -> expects IBAN_B_SPECIFIC
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTwoCompaniesSimpleRib()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA): '.self::$setUpError);
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account: '.self::$setUpError);
|
||||
|
||||
// Create one invoice for COMPANY_A (100) and one for COMPANY_B (300)
|
||||
$facA = $this->createValidatedInvoice(self::$socidA, 100.0);
|
||||
$facB = $this->createValidatedInvoice(self::$socidB, 300.0);
|
||||
|
||||
// Link each invoice to its specific bank account (not the default)
|
||||
$demAId = $this->createPaymentRequest($facA, 100.0, self::$ribASpecificId);
|
||||
$demBId = $this->createPaymentRequest($facB, 300.0, self::$ribBSpecificId);
|
||||
|
||||
// Generate the direct debit order with both requests
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'RCUR', 0, 0, 'direct-debit',
|
||||
array($demAId, $demBId), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->errorsToString());
|
||||
|
||||
// Parse the SEPA file and extract IBAN -> amount pairs
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// Invoice A must use COMPANY_A's specific IBAN
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_SPECIFIC],
|
||||
'COMPANY_A invoice must use IBAN_A_SPECIFIC (forced bank account)');
|
||||
// Invoice B must use COMPANY_B's specific IBAN
|
||||
$this->assertEquals(300.0, $ibanAmounts[self::IBAN_B_SPECIFIC],
|
||||
'COMPANY_B invoice must use IBAN_B_SPECIFIC (forced bank account)');
|
||||
// Total must be 100 + 300
|
||||
$this->assertEquals(400.0, $bon->total,
|
||||
'Order total must equal the sum of both invoices');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testTwoCompaniesDefaultRib
|
||||
*
|
||||
* Verifies that when two different companies each have one invoice with no
|
||||
* forced bank account in the direct debit request (fk_societe_rib IS NULL),
|
||||
* the generated SEPA file contains each company's default IBAN (default_rib=1).
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A (100) -> request with no forced RIB -> expects IBAN_A_DEFAULT
|
||||
* INV_B (300) -> request with no forced RIB -> expects IBAN_B_DEFAULT
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTwoCompaniesDefaultRib()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA): '.self::$setUpError);
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account: '.self::$setUpError);
|
||||
|
||||
// Create one invoice for COMPANY_A (100) and one for COMPANY_B (300)
|
||||
$facA = $this->createValidatedInvoice(self::$socidA, 100.0);
|
||||
$facB = $this->createValidatedInvoice(self::$socidB, 300.0);
|
||||
|
||||
// No forced bank account: fk_societe_rib will be NULL -> default RIB used
|
||||
$demAId = $this->createPaymentRequest($facA, 100.0);
|
||||
$demBId = $this->createPaymentRequest($facB, 300.0);
|
||||
|
||||
// Generate the direct debit order with both requests
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'RCUR', 0, 0, 'direct-debit',
|
||||
array($demAId, $demBId), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->errorsToString());
|
||||
|
||||
// Parse the SEPA file and extract IBAN -> amount pairs
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// Invoice A must use COMPANY_A's default IBAN
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_DEFAULT],
|
||||
'COMPANY_A invoice must use IBAN_A_DEFAULT (default_rib=1, no forced account)');
|
||||
// Invoice B must use COMPANY_B's default IBAN
|
||||
$this->assertEquals(300.0, $ibanAmounts[self::IBAN_B_DEFAULT],
|
||||
'COMPANY_B invoice must use IBAN_B_DEFAULT (default_rib=1, no forced account)');
|
||||
// No specific IBAN must appear in this order
|
||||
$this->assertArrayNotHasKey(self::IBAN_A_SPECIFIC, $ibanAmounts,
|
||||
'IBAN_A_SPECIFIC must not appear when no RIB is forced for COMPANY_A');
|
||||
$this->assertArrayNotHasKey(self::IBAN_B_SPECIFIC, $ibanAmounts,
|
||||
'IBAN_B_SPECIFIC must not appear when no RIB is forced for COMPANY_B');
|
||||
// Total must be 100 + 300
|
||||
$this->assertEquals(400.0, $bon->total,
|
||||
'Order total must equal the sum of both invoices');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testTwoCompaniesDefaultRibFilteredToFirst
|
||||
*
|
||||
* Verifies that when $dids is limited to only the first payment request,
|
||||
* only COMPANY_A's invoice appears in the SEPA file (even though a second
|
||||
* request exists in the database for COMPANY_B.)
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A (100) -> request with no forced RIB -> expects IBAN_A_DEFAULT (in $dids)
|
||||
* INV_B (300) -> request with no forced RIB -> must NOT appear in file (not in $dids)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTwoCompaniesDefaultRibFilteredToFirst()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA): '.self::$setUpError);
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account: '.self::$setUpError);
|
||||
|
||||
// Create one invoice for each company
|
||||
$facA = $this->createValidatedInvoice(self::$socidA, 100.0);
|
||||
$facB = $this->createValidatedInvoice(self::$socidB, 300.0);
|
||||
|
||||
// Create both payment requests (no forced bank account for either)
|
||||
$demAId = $this->createPaymentRequest($facA, 100.0);
|
||||
$demBId = $this->createPaymentRequest($facB, 300.0); // exists in DB but excluded from $dids
|
||||
|
||||
// Generate the order with ONLY the first request ($demAId)
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'RCUR', 0, 0, 'direct-debit',
|
||||
array($demAId), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->errorsToString());
|
||||
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// COMPANY_A must appear with its default IBAN
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_DEFAULT],
|
||||
'COMPANY_A invoice must use IBAN_A_DEFAULT when no RIB is forced');
|
||||
// COMPANY_B must NOT appear since its request was not in $dids
|
||||
$this->assertArrayNotHasKey(self::IBAN_B_DEFAULT, $ibanAmounts,
|
||||
'COMPANY_B must not appear: its request was excluded from $dids');
|
||||
// Total must be 100 only
|
||||
$this->assertEquals(100.0, $bon->total,
|
||||
'Order total must equal only the included request');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testOneCompanyTwoRibs
|
||||
*
|
||||
* Verifies that for the same company with two invoices, the bank account
|
||||
* selection is correct based on whether a specific account is forced:
|
||||
* - A request with a forced bank account uses that specific IBAN.
|
||||
* - A request without a forced account (ribId=0) falls back to the
|
||||
* company's default bank account (default_rib=1).
|
||||
* No IBAN from COMPANY_B should appear in the file.
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A1 (100) -> request with RIB_A_SPECIFIC -> expects IBAN_A_SPECIFIC
|
||||
* INV_A2 (200) -> request without forced RIB -> expects IBAN_A_DEFAULT
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testOneCompanyTwoRibs()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA): '.self::$setUpError);
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account: '.self::$setUpError);
|
||||
|
||||
// Two invoices for COMPANY_A with different amounts
|
||||
$facA1 = $this->createValidatedInvoice(self::$socidA, 100.0);
|
||||
$facA2 = $this->createValidatedInvoice(self::$socidA, 200.0);
|
||||
|
||||
// First request: specific bank account forced (not the default)
|
||||
$demA1Id = $this->createPaymentRequest($facA1, 100.0, self::$ribASpecificId);
|
||||
// Second request: no forced bank account (ribId=0 -> default will be used)
|
||||
$demA2Id = $this->createPaymentRequest($facA2, 200.0);
|
||||
|
||||
// Generate the order with both COMPANY_A requests only
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'RCUR', 0, 0, 'direct-debit',
|
||||
array($demA1Id, $demA2Id), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->errorsToString());
|
||||
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// INV_A1: forced bank account -> must use IBAN_A_SPECIFIC
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_SPECIFIC],
|
||||
'INV_A1 with forced bank account must use IBAN_A_SPECIFIC');
|
||||
// INV_A2: no forced account -> must fall back to default IBAN_A_DEFAULT
|
||||
$this->assertEquals(200.0, $ibanAmounts[self::IBAN_A_DEFAULT],
|
||||
'INV_A2 without forced account must use IBAN_A_DEFAULT (default_rib=1)');
|
||||
// No IBAN from COMPANY_B must appear in this order
|
||||
$this->assertArrayNotHasKey(self::IBAN_B_DEFAULT, $ibanAmounts,
|
||||
'COMPANY_B bank account must not appear in a COMPANY_A-only order');
|
||||
$this->assertEquals(300.0, $bon->total);
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testOneCompanyTwoRibsSpecificOnly
|
||||
*
|
||||
* Verifies that when $dids contains only the request with the specific bank
|
||||
* account, only that invoice appears in the SEPA file (even though a second
|
||||
* request using the default RIB exists in the database.)
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A1 (100) -> request with RIB_A_SPECIFIC -> expects IBAN_A_SPECIFIC (in $dids)
|
||||
* INV_A2 (200) -> request without forced RIB -> must NOT appear in file (not in $dids)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testOneCompanyTwoRibsSpecificOnly()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA): '.self::$setUpError);
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account: '.self::$setUpError);
|
||||
|
||||
// Two invoices for COMPANY_A with different amounts
|
||||
$facA1 = $this->createValidatedInvoice(self::$socidA, 100.0);
|
||||
$facA2 = $this->createValidatedInvoice(self::$socidA, 200.0);
|
||||
|
||||
// First request: specific bank account forced
|
||||
$demA1Id = $this->createPaymentRequest($facA1, 100.0, self::$ribASpecificId);
|
||||
// Second request: no forced bank account (exists in DB but excluded from $dids)
|
||||
$demA2Id = $this->createPaymentRequest($facA2, 200.0);
|
||||
|
||||
// Generate the order with ONLY the specific-RIB request ($demA1Id)
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'RCUR', 0, 0, 'direct-debit',
|
||||
array($demA1Id), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->errorsToString());
|
||||
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// INV_A1 must appear with the forced specific IBAN
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_SPECIFIC],
|
||||
'INV_A1 with forced bank account must use IBAN_A_SPECIFIC');
|
||||
// INV_A2 must NOT appear since its request was not in $dids
|
||||
$this->assertArrayNotHasKey(self::IBAN_A_DEFAULT, $ibanAmounts,
|
||||
'INV_A2 must not appear: its request was excluded from $dids');
|
||||
// Total must be 100 only
|
||||
$this->assertEquals(100.0, $bon->total,
|
||||
'Order total must equal only the included request');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testMixedOrderFourInvoices
|
||||
*
|
||||
* Verifies that bank account assignment remains correct when direct debit
|
||||
* requests from two companies are interleaved (A1, B1, A2, B2).
|
||||
* The test guards against cross-contamination between companies or between
|
||||
* consecutive requests.
|
||||
*
|
||||
* Scenario (requests created in interleaved order):
|
||||
* INV_A1 (100) -> forced: RIB_A_SPECIFIC -> expects IBAN_A_SPECIFIC
|
||||
* INV_B1 (300) -> no forced RIB -> default -> expects IBAN_B_DEFAULT
|
||||
* INV_A2 (200) -> no forced RIB -> default -> expects IBAN_A_DEFAULT
|
||||
* INV_B2 (400) -> forced: RIB_B_SPECIFIC -> expects IBAN_B_SPECIFIC
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testMixedOrderFourInvoices()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA): '.self::$setUpError);
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account: '.self::$setUpError);
|
||||
|
||||
// Interleaved invoices: A1, B1, A2, B2
|
||||
$facA1 = $this->createValidatedInvoice(self::$socidA, 100.0);
|
||||
$facB1 = $this->createValidatedInvoice(self::$socidB, 300.0);
|
||||
$facA2 = $this->createValidatedInvoice(self::$socidA, 200.0);
|
||||
$facB2 = $this->createValidatedInvoice(self::$socidB, 400.0);
|
||||
|
||||
// Interleaved requests: forced, no RIB, no RIB, forced
|
||||
$demA1Id = $this->createPaymentRequest($facA1, 100.0, self::$ribASpecificId); // forced
|
||||
$demB1Id = $this->createPaymentRequest($facB1, 300.0); // -> RIB_B_DEFAULT
|
||||
$demA2Id = $this->createPaymentRequest($facA2, 200.0); // -> RIB_A_DEFAULT
|
||||
$demB2Id = $this->createPaymentRequest($facB2, 400.0, self::$ribBSpecificId); // forced
|
||||
|
||||
// Generate the order with all four requests in interleaved order
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'RCUR', 0, 0, 'direct-debit',
|
||||
array($demA1Id, $demB1Id, $demA2Id, $demB2Id), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->errorsToString());
|
||||
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// Verify each invoice is matched to the correct IBAN with no cross-contamination
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_SPECIFIC],
|
||||
'INV_A1 with forced bank account must use IBAN_A_SPECIFIC');
|
||||
$this->assertEquals(300.0, $ibanAmounts[self::IBAN_B_DEFAULT],
|
||||
'INV_B1 without forced account must use IBAN_B_DEFAULT (default_rib=1 of COMPANY_B)');
|
||||
$this->assertEquals(200.0, $ibanAmounts[self::IBAN_A_DEFAULT],
|
||||
'INV_A2 without forced account must use IBAN_A_DEFAULT (default_rib=1 of COMPANY_A)');
|
||||
$this->assertEquals(400.0, $ibanAmounts[self::IBAN_B_SPECIFIC],
|
||||
'INV_B2 with forced bank account must use IBAN_B_SPECIFIC');
|
||||
$this->assertEquals(1000.0, $bon->total,
|
||||
'Order total must equal the sum of all four invoices');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates and validates a customer invoice for a given company and amount.
|
||||
* The invoice contains a single service line at 0% VAT.
|
||||
*
|
||||
* @param int $socid Row ID of the third party
|
||||
* @param float $amount Pre-tax amount of the line (= total incl. tax with 0% VAT)
|
||||
* @return Facture
|
||||
*/
|
||||
private function createValidatedInvoice(int $socid, float $amount): Facture
|
||||
{
|
||||
global $user, $db;
|
||||
|
||||
$fac = new Facture($db);
|
||||
$fac->socid = $socid;
|
||||
$fac->date = dol_now();
|
||||
$fac->cond_reglement_code = 'RECEP';
|
||||
$fac->mode_reglement_code = 'PRE'; // direct debit payment mode
|
||||
|
||||
$result = $fac->create($user);
|
||||
$this->assertGreaterThan(0, $result, 'Facture::create() failed: '.$fac->errorsToString());
|
||||
|
||||
// Service line at 0% VAT so total_ttc = $amount
|
||||
$fac->addline('Service test', $amount, 1, 0);
|
||||
|
||||
$result = $fac->validate($user);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'Facture::validate() failed: '.$fac->errorsToString());
|
||||
|
||||
return $fac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a direct debit payment request (demande_prelevement) for a validated invoice.
|
||||
* If $ribId is provided, the request stores that specific bank account in fk_societe_rib.
|
||||
* Otherwise (ribId=0), the company's default bank account will be used.
|
||||
*
|
||||
* @param Facture $fac Validated invoice
|
||||
* @param float $amount Requested amount
|
||||
* @param int $ribId Row ID of the bank account to force (0 = use default)
|
||||
* @return int Row ID of the created entry in llx_prelevement_demande
|
||||
*/
|
||||
private function createPaymentRequest(Facture $fac, float $amount, int $ribId = 0): int
|
||||
{
|
||||
global $user, $db;
|
||||
|
||||
// Insert a row in llx_prelevement_demande with fk_societe_rib = $ribId
|
||||
$result = $fac->demande_prelevement($user, $amount, 'direct-debit', 'facture', 0, $ribId);
|
||||
$this->assertEquals(1, $result, 'demande_prelevement() failed for invoice #'.$fac->id.': '.$fac->errorsToString());
|
||||
|
||||
// Retrieve the row ID of the freshly inserted request
|
||||
$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."prelevement_demande WHERE fk_facture = ".((int) $fac->id)." AND traite = 0 ORDER BY rowid DESC LIMIT 1";
|
||||
$resql = $db->query($sql);
|
||||
$obj = $db->fetch_object($resql);
|
||||
$this->assertNotNull($obj, 'Payment request not found in DB for invoice #'.$fac->id);
|
||||
|
||||
return (int) $obj->rowid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a generated SEPA XML file validates against the pain.008.001.02 XSD schema.
|
||||
*
|
||||
* @param string $filename Path to the SEPA XML file
|
||||
* @return void
|
||||
*/
|
||||
private function assertSepaXmlValid(string $filename): void
|
||||
{
|
||||
$this->assertFileExists($filename, 'SEPA XML file does not exist: '.$filename);
|
||||
$this->assertFileExists(self::XSD_PAIN_008, 'XSD schema file not found: '.self::XSD_PAIN_008);
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$loaded = $dom->load($filename);
|
||||
$this->assertTrue($loaded, 'DOMDocument failed to load SEPA XML: '.$filename);
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
$valid = $dom->schemaValidate(self::XSD_PAIN_008);
|
||||
$errors = libxml_get_errors();
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors(false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($errors as $error) {
|
||||
$messages[] = trim($error->message).' (line '.$error->line.')';
|
||||
}
|
||||
$this->assertTrue($valid, 'SEPA XML does not validate against pain.008.001.02 XSD: '.implode('; ', $messages));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a generated SEPA pain.008 XML file and returns an associative
|
||||
* array of IBAN => amount for each DrctDbtTxInf transaction element.
|
||||
*
|
||||
* @param string $filename Path to the SEPA XML file
|
||||
* @return array<string,float> Array keyed by IBAN with transaction amount as value
|
||||
*/
|
||||
private function parseSepaIbanAmounts(string $filename): array
|
||||
{
|
||||
$this->assertFileExists($filename, 'SEPA XML file was not created: '.$filename);
|
||||
|
||||
$xml = simplexml_load_file($filename);
|
||||
$this->assertNotFalse($xml, 'Failed to parse SEPA XML file: '.$filename);
|
||||
|
||||
// Register the SEPA direct-debit namespace (pain.008.001.02)
|
||||
$ns = 'urn:iso:std:iso:20022:tech:xsd:pain.008.001.02';
|
||||
$xml->registerXPathNamespace('ns', $ns);
|
||||
|
||||
$result = array();
|
||||
foreach ($xml->xpath('//ns:DrctDbtTxInf') as $txInf) {
|
||||
$txInf->registerXPathNamespace('ns', $ns);
|
||||
// Debtor IBAN (the third party being debited)
|
||||
$ibanNodes = $txInf->xpath('ns:DbtrAcct/ns:Id/ns:IBAN');
|
||||
// Transaction amount
|
||||
$amountNodes = $txInf->xpath('ns:InstdAmt');
|
||||
|
||||
if (!empty($ibanNodes) && !empty($amountNodes)) {
|
||||
$iban = (string) $ibanNodes[0];
|
||||
$amount = (float) $amountNodes[0];
|
||||
$result[$iban] = $amount;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
695
test/phpunit/BonVirementTest.php
Normal file
695
test/phpunit/BonVirementTest.php
Normal file
|
|
@ -0,0 +1,695 @@
|
|||
<?php
|
||||
/* Copyright (C) 2025 Thomas Negre <tnegre@open-dsi.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* or see https://www.gnu.org/
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file test/phpunit/BonVirementTest.php
|
||||
* \ingroup test
|
||||
* \brief PHPUnit test
|
||||
* \remarks To run this script as CLI: phpunit filename.php
|
||||
*/
|
||||
|
||||
global $conf,$user,$langs,$db;
|
||||
require_once dirname(__FILE__).'/../../htdocs/master.inc.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/core/lib/admin.lib.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/compta/prelevement/class/bonprelevement.class.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/fourn/class/fournisseur.facture.class.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/societe/class/societe.class.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/societe/class/companybankaccount.class.php';
|
||||
require_once dirname(__FILE__).'/../../htdocs/compta/bank/class/account.class.php';
|
||||
require_once dirname(__FILE__).'/CommonClassTest.class.php';
|
||||
|
||||
if (empty($user->id)) {
|
||||
print "Load permissions for admin user nb 1\n";
|
||||
$user->fetch(1);
|
||||
$user->loadRights();
|
||||
}
|
||||
$conf->global->MAIN_DISABLE_ALL_MAILS = 1;
|
||||
|
||||
$langs->load("main");
|
||||
|
||||
|
||||
/**
|
||||
* Class for PHPUnit tests
|
||||
*
|
||||
* @backupGlobals disabled
|
||||
* @backupStaticAttributes enabled
|
||||
* @remarks backupGlobals must be disabled to have db,conf,user and lang not erased.
|
||||
*/
|
||||
class BonVirementTest extends CommonClassTest
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test IBANs valid (mod97 check compliant).
|
||||
// Distinct from BonPrelevementTest IBANs to avoid collisions when both
|
||||
// suites run against the same database instance.
|
||||
// ---------------------------------------------------------------------------
|
||||
const IBAN_A_DEFAULT = 'FR7630001007941234567890670'; // default bank account of COMPANY_A
|
||||
const IBAN_A_SPECIFIC = 'FR7630001007941234567890767'; // specific bank account of COMPANY_A
|
||||
const IBAN_B_DEFAULT = 'FR7630001007941234567890864'; // default bank account of COMPANY_B
|
||||
const IBAN_B_SPECIFIC = 'FR7630001007941234567890961'; // specific bank account of COMPANY_B
|
||||
const BIC = 'BNPAFRPPXXX';
|
||||
const XSD_PAIN_001 = __DIR__.'/../assets/xsd/pain.001.001.03.xsd';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared fixtures created once in setUpBeforeClass(),
|
||||
// inside the parent transaction (rolled back by tearDownAfterClass()).
|
||||
// ---------------------------------------------------------------------------
|
||||
/** @var int Row ID of COMPANY_A */
|
||||
protected static $socidA = 0;
|
||||
/** @var int Row ID of COMPANY_B */
|
||||
protected static $socidB = 0;
|
||||
/** @var int Row ID of the default bank account of COMPANY_A */
|
||||
protected static $ribADefaultId = 0;
|
||||
/** @var int Row ID of the specific bank account of COMPANY_A */
|
||||
protected static $ribASpecificId = 0;
|
||||
/** @var int Row ID of the default bank account of COMPANY_B */
|
||||
protected static $ribBDefaultId = 0;
|
||||
/** @var int Row ID of the specific bank account of COMPANY_B */
|
||||
protected static $ribBSpecificId = 0;
|
||||
/** @var int Row ID of the issuer bank account (llx_bank_account) */
|
||||
protected static $fkBankAccount = 0;
|
||||
|
||||
/**
|
||||
* setUpBeforeClass
|
||||
*
|
||||
* Creates shared fixtures for all tests in this class:
|
||||
* - COMPANY_A and COMPANY_B (French supplier third parties)
|
||||
* - Two bank accounts per company (one default, one specific)
|
||||
* - One issuer bank account used for BonPrelevement generation
|
||||
*
|
||||
* Everything is created inside the parent transaction;
|
||||
* the final rollback in tearDownAfterClass() removes all this data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
global $db, $user;
|
||||
parent::setUpBeforeClass(); // Opens the parent transaction ($db->begin())
|
||||
|
||||
// Enable required modules if not already active
|
||||
if (!isModEnabled('paymentbybanktransfer')) {
|
||||
activateModule('modPaymentByBankTransfer', 1, 1);
|
||||
}
|
||||
if (!isModEnabled('fournisseur')) {
|
||||
activateModule('modFournisseur', 1, 1);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// COMPANY_A: French supplier third party
|
||||
// ------------------------------------------------------------------
|
||||
$socA = new Societe($db);
|
||||
$socA->name = 'BonVirementTest CompanyA';
|
||||
$socA->fournisseur = 1;
|
||||
$socA->country_id = 1; // France (rowid=1 in c_country)
|
||||
$socA->code_fournisseur = -1; // -1 = auto-generate supplier code
|
||||
self::$socidA = (int) $socA->create($user);
|
||||
|
||||
// RIB_A_DEFAULT: first bank account for COMPANY_A, will be the default (default_rib=1)
|
||||
$ribADef = new CompanyBankAccount($db);
|
||||
$ribADef->socid = self::$socidA;
|
||||
$ribADef->type = 'ban';
|
||||
$ribADef->iban = self::IBAN_A_DEFAULT;
|
||||
$ribADef->bic = self::BIC;
|
||||
$ribADef->default_rib = 1;
|
||||
self::$ribADefaultId = (int) $ribADef->create($user);
|
||||
$ribADef->update($user);
|
||||
|
||||
// RIB_A_SPECIFIC: second bank account for COMPANY_A, non-default
|
||||
$ribASpec = new CompanyBankAccount($db);
|
||||
$ribASpec->socid = self::$socidA;
|
||||
$ribASpec->type = 'ban';
|
||||
$ribASpec->iban = self::IBAN_A_SPECIFIC;
|
||||
$ribASpec->bic = self::BIC;
|
||||
$ribASpec->default_rib = 0;
|
||||
self::$ribASpecificId = (int) $ribASpec->create($user);
|
||||
$ribASpec->update($user);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// COMPANY_B: same structure, distinct IBANs
|
||||
// ------------------------------------------------------------------
|
||||
$socB = new Societe($db);
|
||||
$socB->name = 'BonVirementTest CompanyB';
|
||||
$socB->fournisseur = 1;
|
||||
$socB->country_id = 1;
|
||||
$socB->code_fournisseur = -1;
|
||||
self::$socidB = (int) $socB->create($user);
|
||||
|
||||
$ribBDef = new CompanyBankAccount($db);
|
||||
$ribBDef->socid = self::$socidB;
|
||||
$ribBDef->type = 'ban';
|
||||
$ribBDef->iban = self::IBAN_B_DEFAULT;
|
||||
$ribBDef->bic = self::BIC;
|
||||
$ribBDef->default_rib = 1;
|
||||
self::$ribBDefaultId = (int) $ribBDef->create($user);
|
||||
$ribBDef->update($user);
|
||||
|
||||
$ribBSpec = new CompanyBankAccount($db);
|
||||
$ribBSpec->socid = self::$socidB;
|
||||
$ribBSpec->type = 'ban';
|
||||
$ribBSpec->iban = self::IBAN_B_SPECIFIC;
|
||||
$ribBSpec->bic = self::BIC;
|
||||
$ribBSpec->default_rib = 0;
|
||||
self::$ribBSpecificId = (int) $ribBSpec->create($user);
|
||||
$ribBSpec->update($user);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Issuer bank account (llx_bank_account) passed as fk_bank_account
|
||||
// ------------------------------------------------------------------
|
||||
$account = new Account($db);
|
||||
$account->ref = 'BONVIR-TEST';
|
||||
$account->label = 'BonVirementTest Issuer';
|
||||
$account->country_id = 1; // France
|
||||
$account->date_solde = dol_now();
|
||||
$account->iban = 'FR7630001007941234567891058'; // valid IBAN, not used in assertions
|
||||
$account->bic = self::BIC;
|
||||
$account->ics = 'FR77ZZZ123456789'; // SEPA identifier (also used for bank-transfer)
|
||||
$account->ics_transfer = 'FR77ZZZ123456789'; // used when SEPA_USE_IDS is enabled
|
||||
$account->owner_name = 'TestCorp';
|
||||
$account->currency_code = 'EUR';
|
||||
self::$fkBankAccount = (int) $account->create($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* We save global variables into local variables
|
||||
*
|
||||
* @param string $name Name
|
||||
* @param array<mixed> $data Test data
|
||||
* @param string $dataName Test data name.
|
||||
*/
|
||||
public function __construct($name = null, array $data = array(), $dataName = '')
|
||||
{
|
||||
parent::__construct($name, $data, $dataName);
|
||||
|
||||
// This const is tested in prelevement_check_config(), it SHOULD be set.
|
||||
$this->savconf->global->PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT = self::$fkBankAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* testBonVirementCreate
|
||||
*
|
||||
* Non-regression test: verifies that create() in simulation mode
|
||||
* with no pending payment requests returns 0 (no requests processed, no error).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function testBonVirementCreate()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$localobject = new BonPrelevement($db);
|
||||
$result = $localobject->Create(0, 0, 'simu');
|
||||
|
||||
print __METHOD__." result=".$result."\n";
|
||||
$this->assertEquals($result, 0);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* testTwoSuppliersSpecificRib
|
||||
*
|
||||
* Verifies that when two different suppliers each have one invoice with a
|
||||
* specific bank account forced in the bank transfer request, the generated
|
||||
* SEPA file contains the forced IBAN for each transaction (not the
|
||||
* supplier's default bank account.)
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A (100) -> request with RIB_A_SPECIFIC -> expects IBAN_A_SPECIFIC
|
||||
* INV_B (300) -> request with RIB_B_SPECIFIC -> expects IBAN_B_SPECIFIC
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTwoSuppliersSpecificRib()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA)');
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account');
|
||||
|
||||
// Create one invoice for COMPANY_A (100) and one for COMPANY_B (300)
|
||||
$facA = $this->createValidatedSupplierInvoice(self::$socidA, 100.0);
|
||||
$facB = $this->createValidatedSupplierInvoice(self::$socidB, 300.0);
|
||||
|
||||
// Link each invoice to its specific bank account (not the default)
|
||||
$demAId = $this->createPaymentRequest($facA, 100.0, self::$ribASpecificId);
|
||||
$demBId = $this->createPaymentRequest($facB, 300.0, self::$ribBSpecificId);
|
||||
|
||||
// Generate the bank transfer order with both requests
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'ALL', 0, 0, 'bank-transfer',
|
||||
array($demAId, $demBId), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->error);
|
||||
|
||||
// Parse the SEPA file and extract IBAN -> amount pairs
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// Invoice A must use COMPANY_A's specific IBAN
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_SPECIFIC],
|
||||
'COMPANY_A invoice must use IBAN_A_SPECIFIC (forced bank account)');
|
||||
// Invoice B must use COMPANY_B's specific IBAN
|
||||
$this->assertEquals(300.0, $ibanAmounts[self::IBAN_B_SPECIFIC],
|
||||
'COMPANY_B invoice must use IBAN_B_SPECIFIC (forced bank account)');
|
||||
// Total must be 100 + 300
|
||||
$this->assertEquals(400.0, $bon->total,
|
||||
'Order total must equal the sum of both invoices');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testTwoSuppliersDefaultRib
|
||||
*
|
||||
* Verifies that when two different suppliers each have one invoice with no
|
||||
* forced bank account in the bank transfer request (fk_societe_rib IS NULL),
|
||||
* the generated SEPA file contains each supplier's default IBAN (default_rib=1).
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A (100) -> request with no forced RIB -> expects IBAN_A_DEFAULT
|
||||
* INV_B (300) -> request with no forced RIB -> expects IBAN_B_DEFAULT
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTwoSuppliersDefaultRib()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA)');
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account');
|
||||
|
||||
// Create one invoice for COMPANY_A (100) and one for COMPANY_B (300)
|
||||
$facA = $this->createValidatedSupplierInvoice(self::$socidA, 100.0);
|
||||
$facB = $this->createValidatedSupplierInvoice(self::$socidB, 300.0);
|
||||
|
||||
// No forced bank account: fk_societe_rib will be NULL -> default RIB used
|
||||
$demAId = $this->createPaymentRequest($facA, 100.0);
|
||||
$demBId = $this->createPaymentRequest($facB, 300.0);
|
||||
|
||||
// Generate the bank transfer order with both requests
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'ALL', 0, 0, 'bank-transfer',
|
||||
array($demAId, $demBId), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->error);
|
||||
|
||||
// Parse the SEPA file and extract IBAN -> amount pairs
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// Invoice A must use COMPANY_A's default IBAN
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_DEFAULT],
|
||||
'COMPANY_A invoice must use IBAN_A_DEFAULT (default_rib=1, no forced account)');
|
||||
// Invoice B must use COMPANY_B's default IBAN
|
||||
$this->assertEquals(300.0, $ibanAmounts[self::IBAN_B_DEFAULT],
|
||||
'COMPANY_B invoice must use IBAN_B_DEFAULT (default_rib=1, no forced account)');
|
||||
// No specific IBAN must appear in this order
|
||||
$this->assertArrayNotHasKey(self::IBAN_A_SPECIFIC, $ibanAmounts,
|
||||
'IBAN_A_SPECIFIC must not appear when no RIB is forced for COMPANY_A');
|
||||
$this->assertArrayNotHasKey(self::IBAN_B_SPECIFIC, $ibanAmounts,
|
||||
'IBAN_B_SPECIFIC must not appear when no RIB is forced for COMPANY_B');
|
||||
// Total must be 100 + 300
|
||||
$this->assertEquals(400.0, $bon->total,
|
||||
'Order total must equal the sum of both invoices');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testTwoSuppliersDefaultRibFilteredToFirst
|
||||
*
|
||||
* Verifies that when $dids is limited to only the first payment request,
|
||||
* only COMPANY_A's invoice appears in the SEPA file (even though a second
|
||||
* request exists in the database for COMPANY_B.)
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A (100) -> request with no forced RIB -> expects IBAN_A_DEFAULT (in $dids)
|
||||
* INV_B (300) -> request with no forced RIB -> must NOT appear in file (not in $dids)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTwoSuppliersDefaultRibFilteredToFirst()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA)');
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account');
|
||||
|
||||
// Create one invoice for each supplier
|
||||
$facA = $this->createValidatedSupplierInvoice(self::$socidA, 100.0);
|
||||
$facB = $this->createValidatedSupplierInvoice(self::$socidB, 300.0);
|
||||
|
||||
// Create both payment requests (no forced bank account for either)
|
||||
$demAId = $this->createPaymentRequest($facA, 100.0);
|
||||
$demBId = $this->createPaymentRequest($facB, 300.0); // exists in DB but excluded from $dids
|
||||
|
||||
// Generate the order with ONLY the first request ($demAId)
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'ALL', 0, 0, 'bank-transfer',
|
||||
array($demAId), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->error);
|
||||
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// COMPANY_A must appear with its default IBAN
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_DEFAULT],
|
||||
'COMPANY_A invoice must use IBAN_A_DEFAULT when no RIB is forced');
|
||||
// COMPANY_B must NOT appear since its request was not in $dids
|
||||
$this->assertArrayNotHasKey(self::IBAN_B_DEFAULT, $ibanAmounts,
|
||||
'COMPANY_B must not appear: its request was excluded from $dids');
|
||||
// Total must be 100 only
|
||||
$this->assertEquals(100.0, $bon->total,
|
||||
'Order total must equal only the included request');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testOneSupplierTwoRibs
|
||||
*
|
||||
* Verifies that for the same supplier with two invoices, the bank account
|
||||
* selection is correct based on whether a specific account is forced:
|
||||
* - A request with a forced bank account uses that specific IBAN.
|
||||
* - A request without a forced account (ribId=0) falls back to the
|
||||
* supplier's default bank account (default_rib=1).
|
||||
* No IBAN from COMPANY_B should appear in the file.
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A1 (100) -> request with RIB_A_SPECIFIC -> expects IBAN_A_SPECIFIC
|
||||
* INV_A2 (200) -> request without forced RIB -> expects IBAN_A_DEFAULT
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testOneSupplierTwoRibs()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA)');
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account');
|
||||
|
||||
// Two invoices for COMPANY_A with different amounts
|
||||
$facA1 = $this->createValidatedSupplierInvoice(self::$socidA, 100.0);
|
||||
$facA2 = $this->createValidatedSupplierInvoice(self::$socidA, 200.0);
|
||||
|
||||
// First request: specific bank account forced (not the default)
|
||||
$demA1Id = $this->createPaymentRequest($facA1, 100.0, self::$ribASpecificId);
|
||||
// Second request: no forced bank account (ribId=0 -> default will be used)
|
||||
$demA2Id = $this->createPaymentRequest($facA2, 200.0);
|
||||
|
||||
// Generate the order with both COMPANY_A requests only
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'ALL', 0, 0, 'bank-transfer',
|
||||
array($demA1Id, $demA2Id), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->error);
|
||||
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// INV_A1: forced bank account -> must use IBAN_A_SPECIFIC
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_SPECIFIC],
|
||||
'INV_A1 with forced bank account must use IBAN_A_SPECIFIC');
|
||||
// INV_A2: no forced account -> must fall back to default IBAN_A_DEFAULT
|
||||
$this->assertEquals(200.0, $ibanAmounts[self::IBAN_A_DEFAULT],
|
||||
'INV_A2 without forced account must use IBAN_A_DEFAULT (default_rib=1)');
|
||||
// No IBAN from COMPANY_B must appear in this order
|
||||
$this->assertArrayNotHasKey(self::IBAN_B_DEFAULT, $ibanAmounts,
|
||||
'COMPANY_B bank account must not appear in a COMPANY_A-only order');
|
||||
$this->assertEquals(300.0, $bon->total);
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testOneSupplierTwoRibsSpecificOnly
|
||||
*
|
||||
* Verifies that when $dids contains only the request with the specific bank
|
||||
* account, only that invoice appears in the SEPA file (even though a second
|
||||
* request using the default RIB exists in the database.)
|
||||
*
|
||||
* Scenario:
|
||||
* INV_A1 (100) -> request with RIB_A_SPECIFIC -> expects IBAN_A_SPECIFIC (in $dids)
|
||||
* INV_A2 (200) -> request without forced RIB -> must NOT appear in file (not in $dids)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testOneSupplierTwoRibsSpecificOnly()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA)');
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account');
|
||||
|
||||
// Two invoices for COMPANY_A with different amounts
|
||||
$facA1 = $this->createValidatedSupplierInvoice(self::$socidA, 100.0);
|
||||
$facA2 = $this->createValidatedSupplierInvoice(self::$socidA, 200.0);
|
||||
|
||||
// First request: specific bank account forced
|
||||
$demA1Id = $this->createPaymentRequest($facA1, 100.0, self::$ribASpecificId);
|
||||
// Second request: no forced bank account (exists in DB but excluded from $dids)
|
||||
$demA2Id = $this->createPaymentRequest($facA2, 200.0);
|
||||
|
||||
// Generate the order with ONLY the specific-RIB request ($demA1Id)
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'ALL', 0, 0, 'bank-transfer',
|
||||
array($demA1Id), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->error);
|
||||
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// INV_A1 must appear with the forced specific IBAN
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_SPECIFIC],
|
||||
'INV_A1 with forced bank account must use IBAN_A_SPECIFIC');
|
||||
// INV_A2 must NOT appear since its request was not in $dids
|
||||
$this->assertArrayNotHasKey(self::IBAN_A_DEFAULT, $ibanAmounts,
|
||||
'INV_A2 must not appear: its request was excluded from $dids');
|
||||
// Total must be 100 only
|
||||
$this->assertEquals(100.0, $bon->total,
|
||||
'Order total must equal only the included request');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* testMixedOrderFourInvoices
|
||||
*
|
||||
* Verifies that bank account assignment remains correct when bank transfer
|
||||
* requests from two suppliers are interleaved (A1, B1, A2, B2).
|
||||
* The test guards against cross-contamination between suppliers or between
|
||||
* consecutive requests.
|
||||
*
|
||||
* Scenario (requests created in interleaved order):
|
||||
* INV_A1 (100) -> forced: RIB_A_SPECIFIC -> expects IBAN_A_SPECIFIC
|
||||
* INV_B1 (300) -> no forced RIB -> default -> expects IBAN_B_DEFAULT
|
||||
* INV_A2 (200) -> no forced RIB -> default -> expects IBAN_A_DEFAULT
|
||||
* INV_B2 (400) -> forced: RIB_B_SPECIFIC -> expects IBAN_B_SPECIFIC
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testMixedOrderFourInvoices()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf = $this->savconf;
|
||||
$user = $this->savuser;
|
||||
$langs = $this->savlangs;
|
||||
$db = $this->savdb;
|
||||
|
||||
$this->assertGreaterThan(0, self::$socidA, 'setUpBeforeClass() did not create fixtures (socidA)');
|
||||
$this->assertGreaterThan(0, self::$fkBankAccount, 'setUpBeforeClass() did not create issuer account');
|
||||
|
||||
// Interleaved invoices: A1, B1, A2, B2
|
||||
$facA1 = $this->createValidatedSupplierInvoice(self::$socidA, 100.0);
|
||||
$facB1 = $this->createValidatedSupplierInvoice(self::$socidB, 300.0);
|
||||
$facA2 = $this->createValidatedSupplierInvoice(self::$socidA, 200.0);
|
||||
$facB2 = $this->createValidatedSupplierInvoice(self::$socidB, 400.0);
|
||||
|
||||
// Interleaved requests: forced, no RIB, no RIB, forced
|
||||
$demA1Id = $this->createPaymentRequest($facA1, 100.0, self::$ribASpecificId); // forced
|
||||
$demB1Id = $this->createPaymentRequest($facB1, 300.0); // -> RIB_B_DEFAULT
|
||||
$demA2Id = $this->createPaymentRequest($facA2, 200.0); // -> RIB_A_DEFAULT
|
||||
$demB2Id = $this->createPaymentRequest($facB2, 400.0, self::$ribBSpecificId); // forced
|
||||
|
||||
// Generate the order with all four requests in interleaved order
|
||||
$bon = new BonPrelevement($db);
|
||||
$result = $bon->create('', '', 'real', 'ALL', 0, 0, 'bank-transfer',
|
||||
array($demA1Id, $demB1Id, $demA2Id, $demB2Id), self::$fkBankAccount);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'BonPrelevement::create() failed: '.$bon->error);
|
||||
|
||||
$ibanAmounts = $this->parseSepaIbanAmounts($bon->filename);
|
||||
|
||||
// Verify each invoice is matched to the correct IBAN with no cross-contamination
|
||||
$this->assertEquals(100.0, $ibanAmounts[self::IBAN_A_SPECIFIC],
|
||||
'INV_A1 with forced bank account must use IBAN_A_SPECIFIC');
|
||||
$this->assertEquals(300.0, $ibanAmounts[self::IBAN_B_DEFAULT],
|
||||
'INV_B1 without forced account must use IBAN_B_DEFAULT (default_rib=1 of COMPANY_B)');
|
||||
$this->assertEquals(200.0, $ibanAmounts[self::IBAN_A_DEFAULT],
|
||||
'INV_A2 without forced account must use IBAN_A_DEFAULT (default_rib=1 of COMPANY_A)');
|
||||
$this->assertEquals(400.0, $ibanAmounts[self::IBAN_B_SPECIFIC],
|
||||
'INV_B2 with forced bank account must use IBAN_B_SPECIFIC');
|
||||
$this->assertEquals(1000.0, $bon->total,
|
||||
'Order total must equal the sum of all four invoices');
|
||||
|
||||
$this->assertSepaXmlValid($bon->filename);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates and validates a supplier invoice for a given company and amount.
|
||||
* The invoice contains a single service line at 0% VAT.
|
||||
*
|
||||
* @param int $socid Row ID of the third party
|
||||
* @param float $amount Pre-tax amount of the line (= total incl. tax with 0% VAT)
|
||||
* @return FactureFournisseur
|
||||
*/
|
||||
private function createValidatedSupplierInvoice(int $socid, float $amount): FactureFournisseur
|
||||
{
|
||||
global $user, $db;
|
||||
|
||||
$fac = new FactureFournisseur($db);
|
||||
$fac->socid = $socid;
|
||||
$fac->date = dol_now();
|
||||
$fac->cond_reglement_code = 'RECEP';
|
||||
$fac->mode_reglement_code = 'VIR'; // bank transfer payment mode
|
||||
$fac->ref_supplier = 'SUPP-TEST-'.$socid.'-'.$amount.'-'.uniqid();
|
||||
|
||||
$result = $fac->create($user);
|
||||
$this->assertGreaterThan(0, $result, 'FactureFournisseur::create() failed: '.$fac->error);
|
||||
|
||||
// Service line at 0% VAT so total_ttc = $amount
|
||||
// Signature: addline(desc, pu, txtva, txlocaltax1, txlocaltax2, qty, ...)
|
||||
$fac->addline('Service test', $amount, 0, 0, 0, 1);
|
||||
|
||||
$result = $fac->validate($user);
|
||||
$this->assertGreaterThanOrEqual(0, $result, 'FactureFournisseur::validate() failed: '.$fac->error);
|
||||
|
||||
return $fac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bank transfer payment request (demande_prelevement) for a validated supplier invoice.
|
||||
* If $ribId is provided, the request stores that specific bank account in fk_societe_rib.
|
||||
* Otherwise (ribId=0), the supplier's default bank account will be used.
|
||||
*
|
||||
* @param FactureFournisseur $fac Validated supplier invoice
|
||||
* @param float $amount Requested amount
|
||||
* @param int $ribId Row ID of the bank account to force (0 = use default)
|
||||
* @return int Row ID of the created entry in llx_prelevement_demande
|
||||
*/
|
||||
private function createPaymentRequest(FactureFournisseur $fac, float $amount, int $ribId = 0): int
|
||||
{
|
||||
global $user, $db;
|
||||
|
||||
// Insert a row in llx_prelevement_demande with fk_facture_fourn and fk_societe_rib = $ribId
|
||||
$result = $fac->demande_prelevement($user, $amount, 'bank-transfer', 'supplier_invoice', 0, $ribId);
|
||||
$this->assertEquals(1, $result, 'demande_prelevement() failed for invoice #'.$fac->id.': '.$fac->error);
|
||||
|
||||
// Retrieve the row ID of the freshly inserted request
|
||||
$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."prelevement_demande WHERE fk_facture_fourn = ".((int) $fac->id)." AND traite = 0 ORDER BY rowid DESC LIMIT 1";
|
||||
$resql = $db->query($sql);
|
||||
$obj = $db->fetch_object($resql);
|
||||
$this->assertNotNull($obj, 'Payment request not found in DB for invoice #'.$fac->id);
|
||||
|
||||
return (int) $obj->rowid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a generated SEPA XML file validates against the pain.001.001.03 XSD schema.
|
||||
*
|
||||
* @param string $filename Path to the SEPA XML file
|
||||
* @return void
|
||||
*/
|
||||
private function assertSepaXmlValid(string $filename): void
|
||||
{
|
||||
$this->assertFileExists($filename, 'SEPA XML file does not exist: '.$filename);
|
||||
$this->assertFileExists(self::XSD_PAIN_001, 'XSD schema file not found: '.self::XSD_PAIN_001);
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$loaded = $dom->load($filename);
|
||||
$this->assertTrue($loaded, 'DOMDocument failed to load SEPA XML: '.$filename);
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
$valid = $dom->schemaValidate(self::XSD_PAIN_001);
|
||||
$errors = libxml_get_errors();
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors(false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($errors as $error) {
|
||||
$messages[] = trim($error->message).' (line '.$error->line.')';
|
||||
}
|
||||
$this->assertTrue($valid, 'SEPA XML does not validate against pain.001.001.03 XSD: '.implode('; ', $messages));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a generated SEPA pain.001 XML file and returns an associative
|
||||
* array of IBAN => amount for each CdtTrfTxInf transaction element.
|
||||
*
|
||||
* @param string $filename Path to the SEPA XML file
|
||||
* @return array<string,float> Array keyed by IBAN with transaction amount as value
|
||||
*/
|
||||
private function parseSepaIbanAmounts(string $filename): array
|
||||
{
|
||||
$this->assertFileExists($filename, 'SEPA XML file was not created: '.$filename);
|
||||
|
||||
$xml = simplexml_load_file($filename);
|
||||
$this->assertNotFalse($xml, 'Failed to parse SEPA XML file: '.$filename);
|
||||
|
||||
// Register the SEPA credit-transfer namespace (pain.001.001.03)
|
||||
$ns = 'urn:iso:std:iso:20022:tech:xsd:pain.001.001.03';
|
||||
$xml->registerXPathNamespace('ns', $ns);
|
||||
|
||||
$result = array();
|
||||
foreach ($xml->xpath('//ns:CdtTrfTxInf') as $txInf) {
|
||||
$txInf->registerXPathNamespace('ns', $ns);
|
||||
// Creditor IBAN (the supplier being paid)
|
||||
$ibanNodes = $txInf->xpath('ns:CdtrAcct/ns:Id/ns:IBAN');
|
||||
// Transaction amount (nested under Amt element in pain.001)
|
||||
$amountNodes = $txInf->xpath('ns:Amt/ns:InstdAmt');
|
||||
|
||||
if (!empty($ibanNodes) && !empty($amountNodes)) {
|
||||
$iban = (string) $ibanNodes[0];
|
||||
$amount = (float) $amountNodes[0];
|
||||
$result[$iban] = $amount;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue