/**
* Copyright © 2015 Pablo Grela Palleiro (pablogp_9@hotmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cuacfm.members.model.bankremittanceservice;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import org.cuacfm.members.model.bankremittance.BankRemittance;
import org.cuacfm.members.model.directdebit.DirectDebit;
import org.cuacfm.members.model.util.DateUtils;
import org.cuacfm.members.model.util.sepa.BankRemittanceUtils;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.AccountIdentification4Choice;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.ActiveOrHistoricCurrencyAndAmount;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.BranchAndFinancialInstitutionIdentification4;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.CashAccount16;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.CategoryPurpose1Choice;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.ChargeBearerType1Code;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.CustomerDirectDebitInitiationV02;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.DirectDebitTransaction6;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.DirectDebitTransactionInformation9;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.Document;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.FinancialInstitutionIdentification7;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.GenericOrganisationIdentification1;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.GenericPersonIdentification1;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.GroupHeader39;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.LocalInstrument2Choice;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.MandateRelatedInformation6;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.ObjectFactory;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.OrganisationIdentification4;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.OrganisationIdentificationSchemeName1Choice;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.Party6Choice;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.PartyIdentification32;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.PaymentIdentification1;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.PaymentInstructionInformation4;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.PaymentMethod2Code;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.PaymentTypeInformation20;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.PersonIdentification5;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.PersonIdentificationSchemeName1Choice;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.Purpose2Choice;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.RemittanceInformation5;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.SequenceType1Code;
import org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation.ServiceLevel8Choice;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* Representa un fichero especificado por el CSB19 para la realización de adeudos por domiciliación en soporte magnético.
*/
@Service("bankRemittanceServiceSEPAXML")
public class BankRemittanceSEPAXML {
@Value("${country}")
private String country;
@Value("${currency}")
private String currency;
@Value("${suffix}")
private String suffix;
@Value("${presenterName}")
private String presenterName;
@Value("${presenterNif}")
private String presenterNif;
@Value("${creditorBIC}")
private String creditorBIC;
@Value("${creditorIBAN}")
private String creditorIBAN;
@Value("${debtorCategory}")
private String debtorCategory;
@Value("${debtorPurpose}")
private String debtorPurpose;
// The max decimals in SEPA
private static final int DECIMALS = 2;
private static final RoundingMode ROUNDING = RoundingMode.HALF_UP;
private ObjectFactory factory = new ObjectFactory();
private int totalReceiptsFile;
private BigDecimal totalImportFile;
private int totalReceiptsDebtor;
private BigDecimal totalImportDebtor;
/**
* Instantiates a new bank remittance SEPAXML.
*/
public BankRemittanceSEPAXML() {
super();
}
/**
* Instantiates a new Bank Remittance SEPAXML.
*
* @param path the path
* @param bankRemittance the bank remittance
* @param directDebits the direct debits
* @throws IOException Signals that an I/O exception has occurred.
* @throws JAXBException the JAXB exception
* @throws DatatypeConfigurationException the datatype configuration exception
*/
public void create(String path, BankRemittance bankRemittance, List<DirectDebit> directDebits)
throws IOException, JAXBException, DatatypeConfigurationException {
FileOutputStream file = new FileOutputStream(path);
JAXBElement<Document> element = factory.createDocument(generateDocument(bankRemittance, directDebits));
JAXBContext context = JAXBContext.newInstance("org.cuacfm.members.model.util.sepa.customerdirectdebitinitiation");
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
marshaller.marshal(element, file);
file.close();
}
/**
* Generate group header.
*
* @return the group header 39
* @throws DatatypeConfigurationException the datatype configuration exception
*/
// Cabecera
private GroupHeader39 generateGroupHeader() throws DatatypeConfigurationException {
GroupHeader39 groupHeader = factory.createGroupHeader39();
groupHeader.setMsgId("CUACFM" + DateUtils.format(new Date(), "yyyyMMdd"));
GregorianCalendar today = new GregorianCalendar();
XMLGregorianCalendar todayXML = DatatypeFactory.newInstance().newXMLGregorianCalendar(today);
todayXML.setMillisecond(DatatypeConstants.FIELD_UNDEFINED);
todayXML.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
groupHeader.setCreDtTm(todayXML);
groupHeader.setNbOfTxs(Integer.toString(totalReceiptsFile));
groupHeader.setCtrlSum(totalImportFile.setScale(DECIMALS, ROUNDING));
groupHeader.setInitgPty(generatePresentadorPartyIdentification());
return groupHeader;
}
/**
* Generate presenter party identification.
*
* @return the party identification 32
*/
private PartyIdentification32 generatePresentadorPartyIdentification() {
GenericOrganisationIdentification1 otherorginfo = factory.createGenericOrganisationIdentification1();
// The suffix is necessary for SEPA
otherorginfo.setId(BankRemittanceUtils.calculateSEPACreditorID(country, suffix, presenterNif));
OrganisationIdentificationSchemeName1Choice schemeOrganization = factory.createOrganisationIdentificationSchemeName1Choice();
schemeOrganization.setCd("COR1");
otherorginfo.setSchmeNm(schemeOrganization);
OrganisationIdentification4 organizationPresenter = factory.createOrganisationIdentification4();
List<GenericOrganisationIdentification1> listotherorganization = organizationPresenter.getOthr();
listotherorganization.add(otherorginfo);
Party6Choice partyChoicePresenter = factory.createParty6Choice();
partyChoicePresenter.setOrgId(organizationPresenter);
PartyIdentification32 partyIdentification = factory.createPartyIdentification32();
partyIdentification.setNm(presenterName);
partyIdentification.setId(partyChoicePresenter);
return partyIdentification;
}
/**
* Generate debtor() party identification.
*
* @param directDebit the direct debit
* @return the party identification 32
*/
// Cabecera del ordenante, debtors
private PartyIdentification32 generateOrdenantePartyIdentification(DirectDebit directDebit) {
// Nombre del que paga
PartyIdentification32 partyIdentification = factory.createPartyIdentification32();
partyIdentification.setNm(directDebit.getAccount().getName() + " " + directDebit.getAccount().getSurname());
return partyIdentification;
}
/**
* Generate ordenante domiciliacion party identification.
*
* @param directDebit the direct debit
* @return the party identification 32
*/
//Datos del presenter, creditor
private PartyIdentification32 generateOrdenanteDomiciliacionPartyIdentification(DirectDebit directDebit) {
GenericPersonIdentification1 otherpersoninfo = factory.createGenericPersonIdentification1();
// The suffix is necessary for SEPA
otherpersoninfo.setId(BankRemittanceUtils.calculateSEPACreditorID(country, suffix, presenterNif));
// field 04: Referencia del adeudo (AT-10), 35 ch.
String debtorId = String.valueOf(directDebit.getAccount().getId());
otherpersoninfo.setId(String.format("%1$-35s", debtorId));
PersonIdentificationSchemeName1Choice schemePerson = factory.createPersonIdentificationSchemeName1Choice();
schemePerson.setPrtry("SEPA");
otherpersoninfo.setSchmeNm(schemePerson);
PersonIdentification5 personIdentification = factory.createPersonIdentification5();
List<GenericPersonIdentification1> listOtherPersonIdentification = personIdentification.getOthr();
listOtherPersonIdentification.add(otherpersoninfo);
Party6Choice partyChoicePresenter = factory.createParty6Choice();
partyChoicePresenter.setPrvtId(personIdentification);
PartyIdentification32 partyIdentification = factory.createPartyIdentification32();
partyIdentification.setId(partyChoicePresenter);
return partyIdentification;
}
/**
* Presenter, Fill data payment intructions information.
*
* @param paymentInstructionsInformation the payment instructions information
* @param directDebit the direct debit
* @param bankRemittance the bank remittance
* @return the payment instruction information 4
* @throws DatatypeConfigurationException the datatype configuration exception
*/
private PaymentInstructionInformation4 fillDataPaymentIntructionsInformation(PaymentInstructionInformation4 paymentInstructionsInformation,
DirectDebit directDebit, BankRemittance bankRemittance) throws DatatypeConfigurationException {
// Identificación de información de pago
// paymentInstructionsInformation.setPmtInfId(fileId + "-" + ordenante.getSepaSeqType());
paymentInstructionsInformation.setPmtInfId(directDebit.getId());
paymentInstructionsInformation.setPmtMtd(PaymentMethod2Code.DD);
paymentInstructionsInformation.setNbOfTxs(Integer.toString(totalReceiptsDebtor));
paymentInstructionsInformation.setCtrlSum(totalImportDebtor.setScale(DECIMALS, ROUNDING));
PaymentTypeInformation20 paymentTypeInformation = factory.createPaymentTypeInformation20();
ServiceLevel8Choice serviceLevel = factory.createServiceLevel8Choice();
serviceLevel.setCd("SEPA");
paymentTypeInformation.setSvcLvl(serviceLevel);
LocalInstrument2Choice localInstrument = factory.createLocalInstrument2Choice();
localInstrument.setCd("COR1");
paymentTypeInformation.setLclInstrm(localInstrument);
//FRST(first) o RCUR(Recursive)
paymentTypeInformation.setSeqTp(SequenceType1Code.fromValue(directDebit.getSecuence()));
CategoryPurpose1Choice categoryPurpose = factory.createCategoryPurpose1Choice();
categoryPurpose.setCd(debtorCategory);
paymentTypeInformation.setCtgyPurp(categoryPurpose);
paymentInstructionsInformation.setPmtTpInf(paymentTypeInformation);
GregorianCalendar fechaCobro = new GregorianCalendar();
fechaCobro.setTime(bankRemittance.getDateCharge());
// ReqdColltnDt: Convert Date to gregorian Calendar
paymentInstructionsInformation.setReqdColltnDt(DatatypeFactory.newInstance().newXMLGregorianCalendarDate(fechaCobro.get(Calendar.YEAR),
fechaCobro.get(Calendar.MONTH) + 1, fechaCobro.get(Calendar.DAY_OF_MONTH), DatatypeConstants.FIELD_UNDEFINED));
//Head, Cabecera
paymentInstructionsInformation.setCdtr(generateOrdenantePartyIdentification(directDebit));
CashAccount16 cashAccount = factory.createCashAccount16();
AccountIdentification4Choice accountIdentification = factory.createAccountIdentification4Choice();
accountIdentification.setIBAN(creditorIBAN);
cashAccount.setId(accountIdentification);
paymentInstructionsInformation.setCdtrAcct(cashAccount);
BranchAndFinancialInstitutionIdentification4 financialIdentification = factory.createBranchAndFinancialInstitutionIdentification4();
FinancialInstitutionIdentification7 financialId = factory.createFinancialInstitutionIdentification7();
financialId.setBIC(creditorBIC);
financialIdentification.setFinInstnId(financialId);
paymentInstructionsInformation.setCdtrAgt(financialIdentification);
paymentInstructionsInformation.setChrgBr(ChargeBearerType1Code.SLEV);
return paymentInstructionsInformation;
}
/**
* Generate direct debit information, debtor.
*
* @param directDebit the direct debit
* @return the direct debit transaction information 9
* @throws DatatypeConfigurationException the datatype configuration exception
*/
private DirectDebitTransactionInformation9 generateDirectDebitInformation(DirectDebit directDebit) throws DatatypeConfigurationException {
DirectDebitTransactionInformation9 directDebitInformation = factory.createDirectDebitTransactionInformation9();
PaymentIdentification1 paymentIdentification = factory.createPaymentIdentification1();
//Campo de 35 caracteres, identificador del pago, para saber lo que se devolvio
paymentIdentification.setEndToEndId(String.valueOf(directDebit.getId()));
directDebitInformation.setPmtId(paymentIdentification);
ActiveOrHistoricCurrencyAndAmount currencyAndAmount = factory.createActiveOrHistoricCurrencyAndAmount();
currencyAndAmount.setCcy(currency);
BigDecimal importe = new BigDecimal(directDebit.getPrice()).setScale(DECIMALS, ROUNDING);
// Antes se dividia por 100 ¿Por que se divide?
currencyAndAmount.setValue(importe.setScale(DECIMALS, ROUNDING));
directDebitInformation.setInstdAmt(currencyAndAmount);
DirectDebitTransaction6 directDebitTransaction = factory.createDirectDebitTransaction6();
MandateRelatedInformation6 mandateInformation = factory.createMandateRelatedInformation6();
mandateInformation.setMndtId(directDebit.getAccount().activeBankAccount().getMandate());
// Date Mandante
GregorianCalendar fechaFirmaMandato = new GregorianCalendar();
fechaFirmaMandato.setTime(directDebit.getAccount().activeBankAccount().getDateMandate());
mandateInformation.setDtOfSgntr(DatatypeFactory.newInstance().newXMLGregorianCalendarDate(fechaFirmaMandato.get(Calendar.YEAR),
fechaFirmaMandato.get(Calendar.MONTH) + 1, fechaFirmaMandato.get(Calendar.DAY_OF_MONTH), DatatypeConstants.FIELD_UNDEFINED));
directDebitTransaction.setMndtRltdInf(mandateInformation);
directDebitTransaction.setCdtrSchmeId(generateOrdenanteDomiciliacionPartyIdentification(directDebit));
directDebitInformation.setDrctDbtTx(directDebitTransaction);
BranchAndFinancialInstitutionIdentification4 financialIdentification = factory.createBranchAndFinancialInstitutionIdentification4();
FinancialInstitutionIdentification7 financialId = factory.createFinancialInstitutionIdentification7();
// BIC, si esta vacio poner notprovided
if (directDebit.getAccount().activeBankAccount().getBic() != null || !directDebit.getAccount().activeBankAccount().getBic().isEmpty()) {
financialId.setBIC(directDebit.getAccount().activeBankAccount().getBic());
} else {
financialId.setBIC("NOTPROVIDED");
}
financialIdentification.setFinInstnId(financialId);
directDebitInformation.setDbtrAgt(financialIdentification);
PartyIdentification32 partyDeudor = factory.createPartyIdentification32();
partyDeudor.setNm(directDebit.getAccount().getName() + " " + directDebit.getAccount().getSurname());
directDebitInformation.setDbtr(partyDeudor);
CashAccount16 cashAccount = factory.createCashAccount16();
AccountIdentification4Choice accountIdentification = factory.createAccountIdentification4Choice();
accountIdentification.setIBAN(directDebit.getAccount().activeBankAccount().getIban());
cashAccount.setId(accountIdentification);
directDebitInformation.setDbtrAcct(cashAccount);
Purpose2Choice purpose = factory.createPurpose2Choice();
purpose.setCd(debtorPurpose);
directDebitInformation.setPurp(purpose);
RemittanceInformation5 informacionConcepto = factory.createRemittanceInformation5();
informacionConcepto.getUstrd().add(directDebit.getConcept());
directDebitInformation.setRmtInf(informacionConcepto);
return directDebitInformation;
}
/**
* Generate document.
*
* @param bankRemittance the bank remittance
* @param directDebits the direct debits
* @return the document
* @throws DatatypeConfigurationException the datatype configuration exception
*/
private Document generateDocument(BankRemittance bankRemittance, List<DirectDebit> directDebits) throws DatatypeConfigurationException {
totalReceiptsFile = 0;
totalImportFile = new BigDecimal("0.00");
CustomerDirectDebitInitiationV02 customerDirectDebitInitiation = factory.createCustomerDirectDebitInitiationV02();
List<PaymentInstructionInformation4> listadoOrdenantesRemesa = customerDirectDebitInitiation.getPmtInf();
// Una remesa tiene pagos directos, cada pago engloba varias cuaotas de un mismo usuario
for (DirectDebit directDebit : directDebits) {
totalReceiptsDebtor = 0;
totalImportDebtor = new BigDecimal("0.00");
PaymentInstructionInformation4 paymentInstructionsInformation = factory.createPaymentInstructionInformation4();
List<DirectDebitTransactionInformation9> listadoAdeudosOrdenante = paymentInstructionsInformation.getDrctDbtTxInf();
// Collection<Domiciliacion> domiciliaciones = domiciliacionesOrdenante.get(clienteOrdenante);
// for (Domiciliacion domiciliacion : domiciliaciones) {
DirectDebitTransactionInformation9 directDebitInformation = generateDirectDebitInformation(directDebit);
listadoAdeudosOrdenante.add(directDebitInformation);
totalReceiptsDebtor++;
BigDecimal importe = new BigDecimal(directDebit.getPrice());
totalImportDebtor = totalImportDebtor.add(importe.setScale(DECIMALS, ROUNDING));
// }
fillDataPaymentIntructionsInformation(paymentInstructionsInformation, directDebit, bankRemittance);
listadoOrdenantesRemesa.add(paymentInstructionsInformation);
totalReceiptsFile += totalReceiptsDebtor;
totalImportFile = totalImportFile.add(totalImportDebtor.setScale(DECIMALS, ROUNDING));
}
customerDirectDebitInitiation.setGrpHdr(generateGroupHeader());
Document doc = factory.createDocument();
doc.setCstmrDrctDbtInitn(customerDirectDebitInitiation);
return doc;
}
}