ch.dvbern.oss.lib.iso20022.pain001.v00103ch02.Pain001V00103CH02Service Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2017 DV Bern AG
*
* 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.
* limitations under the License.
*/
package ch.dvbern.oss.lib.iso20022.pain001.v00103ch02;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Optional;
import java.util.regex.Pattern;
import com.six_interbank_clearing.de.pain_001_001_03_ch_02.ClearingSystemIdentification2Choice;
import com.six_interbank_clearing.de.pain_001_001_03_ch_02.ClearingSystemMemberIdentification2;
import com.six_interbank_clearing.de.pain_001_001_03_ch_02.CreditTransferTransactionInformation10CH;
import com.six_interbank_clearing.de.pain_001_001_03_ch_02.Document;
import com.six_interbank_clearing.de.pain_001_001_03_ch_02.GroupHeader32CH;
import com.six_interbank_clearing.de.pain_001_001_03_ch_02.ObjectFactory;
import com.six_interbank_clearing.de.pain_001_001_03_ch_02.PaymentInstructionInformation3CH;
import com.six_interbank_clearing.de.pain_001_001_03_ch_02.PaymentMethod3Code;
import ch.dvbern.oss.lib.iso20022.Iso20022JaxbUtil;
import ch.dvbern.oss.lib.iso20022.Iso20022Util;
import ch.dvbern.oss.lib.iso20022.dtos.pain.AuszahlungDTO;
import ch.dvbern.oss.lib.iso20022.dtos.pain.Pain001DTO;
import ch.dvbern.oss.lib.iso20022.exceptions.Iso20022RuntimeException;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.enterprise.context.ApplicationScoped;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Contract;
import static ch.dvbern.oss.lib.iso20022.Iso2022ConstantsUtil.CCY;
import static ch.dvbern.oss.lib.iso20022.Iso2022ConstantsUtil.CTCTDTLS_OTHR;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.SPACE;
/**
* Service implementation to generate Payment-File Pain001 according to ISO20022 for a swiss bank
*/
@ApplicationScoped
public class Pain001V00103CH02Service implements Pain001Service {
private static final PaymentMethod3Code PAYMENT_METHOD_3_CODE = PaymentMethod3Code.TRA;
private static final Boolean BTCHBOOKG = true;
private static final String CLRSYS_CD = "CHBCC"; // Code swiss bank
private static final Pattern FIND_SPACES = Pattern.compile(SPACE);
private static final Pattern NON_ASCII = Pattern.compile("[^\\p{ASCII}]");
private static final int MAX_SIGNS = 35;
private static final int MAX_70_TEXT = 70;
@Override
public byte[] getPainFileContent(@Nonnull Pain001DTO pain001DTO) {
final Document document = createPain001Document(pain001DTO);
return Iso20022JaxbUtil.getXMLStringFromDocument(document, Document.class, SCHEMA_LOCATION, SCHEMA_NAME)
.getBytes(StandardCharsets.UTF_8);
}
/**
* Example:
*
* {@code
*
*
*
*
*
*
*
*
*
*
*
* }
*
*/
@Nonnull
private Document createPain001Document(@Nonnull Pain001DTO pain001DTO) {
String debtorName = pain001DTO.getSchuldnerName();
String debtorBic = pain001DTO.getSchuldnerBIC();
String debtorIban = pain001DTO.getSchuldnerIBAN();
if (debtorName == null) {
throw new Iso20022RuntimeException("Empty deptor name: debtor_name is required");
}
if (debtorBic == null) {
throw new Iso20022RuntimeException("Empty deptor Bank BIC Number: debtor_bic is required");
}
if (debtorIban == null) {
throw new Iso20022RuntimeException("Empty IBAN: debtor_iban is required");
}
ObjectFactory objectFactory = new ObjectFactory();
Document document = objectFactory.createDocument();
document.setCstmrCdtTrfInitn(objectFactory.createCustomerCreditTransferInitiationV03CH());
PaymentInstructionInformation3CH paymentInstructionInformation3CH = createPaymentInstructionInformation3CH(
pain001DTO,
objectFactory,
debtorName,
debtorIban,
debtorBic);
document.getCstmrCdtTrfInitn().getPmtInf().add(paymentInstructionInformation3CH);
document.getCstmrCdtTrfInitn().getPmtInf().get(0).getCdtTrfTxInf().clear();
int transaktion = 0;
BigDecimal ctrlSum = BigDecimal.ZERO;
for (AuszahlungDTO auszahlungDTO : pain001DTO.getAuszahlungen()) {
requireNonNull(auszahlungDTO.getBetragTotalZahlung(), "Amount is required");
transaktion++;
ctrlSum = ctrlSum.add(auszahlungDTO.getBetragTotalZahlung());
CreditTransferTransactionInformation10CH info = createCreditTransferTransactionInformation10CH(
objectFactory,
transaktion,
auszahlungDTO,
requireNonNull(pain001DTO.getAuszahlungsDatum()));
document.getCstmrCdtTrfInitn().getPmtInf().get(0).getCdtTrfTxInf().add(info);
}
GroupHeader32CH groupHeader = createGroupHeader(pain001DTO, objectFactory, transaktion, ctrlSum, debtorName);
document.getCstmrCdtTrfInitn().setGrpHdr(groupHeader);
return document;
}
/**
* Example:
*
* {@code
*
*
* 18
*
*
* 1175
*
*
*
*
*
* CHBCC
*
* 9000
*
*
*
*
* Tester-Ncbijgep Tim
*
* Thunstrasse 17
* 3000
* Bern
* CH
*
*
*
*
* CH3780817000000576623
*
*
*
* some text...
*
*
* }
*
*/
@Nonnull
private CreditTransferTransactionInformation10CH createCreditTransferTransactionInformation10CH(
@Nonnull ObjectFactory objectFactory,
int transaktion,
@Nonnull AuszahlungDTO auszahlungDTO,
@Nonnull LocalDate date) {
requireNonNull(auszahlungDTO.getZahlungsempfaengerIBAN(), "IBAN is required");
requireNonNull(auszahlungDTO.getBetragTotalZahlung(), "Amount is required");
CreditTransferTransactionInformation10CH cTTI10CH = objectFactory
.createCreditTransferTransactionInformation10CH();
// structure
cTTI10CH.setPmtId(objectFactory.createPaymentIdentification1());
cTTI10CH.setAmt(objectFactory.createAmountType3Choice());
cTTI10CH.getAmt().setInstdAmt(objectFactory.createActiveOrHistoricCurrencyAndAmount());
cTTI10CH.setCdtr(objectFactory.createPartyIdentification32CHName());
cTTI10CH.getCdtr().setPstlAdr(objectFactory.createPostalAddress6CH());
cTTI10CH.setRmtInf(objectFactory.createRemittanceInformation5CH());
cTTI10CH.setCdtrAgt(objectFactory.createBranchAndFinancialInstitutionIdentification4CH());
cTTI10CH.getCdtrAgt().setFinInstnId(objectFactory.createFinancialInstitutionIdentification7CH());
ClearingSystemMemberIdentification2 csmid2 = objectFactory.createClearingSystemMemberIdentification2();
cTTI10CH.getCdtrAgt().getFinInstnId().setClrSysMmbId(csmid2);
ClearingSystemIdentification2Choice csid2choice = objectFactory.createClearingSystemIdentification2Choice();
cTTI10CH.getCdtrAgt().getFinInstnId().getClrSysMmbId().setClrSysId(csid2choice);
// data
String transaktionStr = String.valueOf(transaktion);
cTTI10CH.getPmtId().setInstrId(Iso20022Util.replaceSwift(transaktionStr)); // 2.29 // SWIFT
String zahlungsempfaengerName = requireNonNull(auszahlungDTO.getZahlungsempfaengerName());
String endToEndId;
if (auszahlungDTO.getEndToEndId() == null) {
// "{id}/{month number}/{normalized without umlauts (öäü)} => "1/2/Brunnen
endToEndId = transaktionStr + '/' + date.getMonthValue() + '/' + zahlungsempfaengerName; // SWIFT
} else {
endToEndId = auszahlungDTO.getEndToEndId();
}
endToEndId = Iso20022Util.replaceSwift(endToEndId);
// 2.30 max 35 signs
cTTI10CH.getPmtId().setEndToEndId(endToEndId.substring(0, Math.min(endToEndId.length(), MAX_SIGNS)));
// value
cTTI10CH.getAmt().getInstdAmt().setCcy(CCY);// 2.43
cTTI10CH.getAmt().getInstdAmt().setValue(auszahlungDTO.getBetragTotalZahlung());// 2.43
if (auszahlungDTO.getZahlungsempfaengerBIC() != null) {
// BIC
cTTI10CH.getCdtrAgt().getFinInstnId().setBIC(auszahlungDTO.getZahlungsempfaengerBIC());
} else {
//ClrSysMmbId
requireNonNull(auszahlungDTO.getZahlungsempfaengerBankClearingNumber(), "Clearing number is required");
cTTI10CH.getCdtrAgt().getFinInstnId().getClrSysMmbId().getClrSysId().setCd(CLRSYS_CD);
String zempfBCN = auszahlungDTO.getZahlungsempfaengerBankClearingNumber();
cTTI10CH.getCdtrAgt().getFinInstnId().getClrSysMmbId().setMmbId(zempfBCN);
}
//IBAN
cTTI10CH.setCdtrAcct(objectFactory.createCashAccount16CHId());
cTTI10CH.getCdtrAcct().setId(objectFactory.createAccountIdentification4ChoiceCH()); // 2.80
String iban = FIND_SPACES.matcher(auszahlungDTO.getZahlungsempfaengerIBAN()).replaceAll(EMPTY);
cTTI10CH.getCdtrAcct().getId().setIBAN(iban); // 2.80
cTTI10CH.setCdtr(objectFactory.createPartyIdentification32CHName());
cTTI10CH.getCdtr().setNm(normalize(StringUtils.abbreviate(zahlungsempfaengerName, MAX_70_TEXT))); // 2.79
setPstlAdr(objectFactory, auszahlungDTO, cTTI10CH);
cTTI10CH.setRmtInf(objectFactory.createRemittanceInformation5CH());
if (auszahlungDTO.getZahlungText() == null) {
String monat = date.format(DateTimeFormatter.ofPattern("MMM.yyyy", Locale.GERMAN));
String ustrd = normalize(zahlungsempfaengerName + ", Monat " + monat);
cTTI10CH.getRmtInf().setUstrd(ustrd); // 2.99
} else {
cTTI10CH.getRmtInf().setUstrd(normalize(auszahlungDTO.getZahlungText()));
}
return cTTI10CH;
}
private void setPstlAdr(
@Nonnull ObjectFactory objectFactory,
@Nonnull AuszahlungDTO auszahlungDTO,
@Nonnull CreditTransferTransactionInformation10CH cTTI10CH) {
if (!hasAddressData(auszahlungDTO)) {
return;
}
cTTI10CH.getCdtr().setPstlAdr(objectFactory.createPostalAddress6CH());
if (auszahlungDTO.getZahlungsempfaengerStrasse() != null) {
cTTI10CH.getCdtr().getPstlAdr().setStrtNm(normalize(auszahlungDTO.getZahlungsempfaengerStrasse())); // 2.79
}
if (auszahlungDTO.getZahlungsempfaengerHausnummer() != null) {
cTTI10CH.getCdtr().getPstlAdr().setBldgNb(auszahlungDTO.getZahlungsempfaengerHausnummer()); // 2.79
}
if (auszahlungDTO.getZahlungsempfaengerPlz() != null) {
cTTI10CH.getCdtr().getPstlAdr().setPstCd(auszahlungDTO.getZahlungsempfaengerPlz());// 2.79
}
if (auszahlungDTO.getZahlungsempfaengerOrt() != null) {
cTTI10CH.getCdtr().getPstlAdr().setTwnNm(normalize(auszahlungDTO.getZahlungsempfaengerOrt()));// 2.79
}
if (auszahlungDTO.getZahlungsempfaengerLand() != null) {
cTTI10CH.getCdtr().getPstlAdr().setCtry(auszahlungDTO.getZahlungsempfaengerLand());// 2.79
}
}
@SuppressWarnings("checkstyle:BooleanExpressionComplexity")
private boolean hasAddressData(@Nonnull AuszahlungDTO auszahlungDTO) {
return auszahlungDTO.getZahlungsempfaengerStrasse() != null
|| auszahlungDTO.getZahlungsempfaengerHausnummer() != null
|| auszahlungDTO.getZahlungsempfaengerPlz() != null
|| auszahlungDTO.getZahlungsempfaengerOrt() != null
|| auszahlungDTO.getZahlungsempfaengerLand() != null;
}
@Contract("!null->!null; null->null;")
@Nullable
private String normalize(@Nullable String text) {
if (null == text) {
return null;
}
return NON_ASCII.matcher(Normalizer.normalize(text, Form.NFD)).replaceAll(EMPTY);
}
/**
* Example PaymentInformation:
*
* {@code
*
* 01-201611-01
* TRA
* true
*
*
* PENS
*
*
* 2017-01
*
* Name of Deptor
*
*
*
* CH0809000000300270001
*
*
*
*
* POFICHBEXXX
*
*
*
*
* CH4709000000300003131
*
*
*
*
*
*
* }
*
*/
@Nonnull
private PaymentInstructionInformation3CH createPaymentInstructionInformation3CH(
@Nonnull Pain001DTO pain001DTO,
@Nonnull ObjectFactory objectFactory,
@Nonnull String debtorName,
@Nonnull String debtorIban,
@Nonnull String debtorBic) {
PaymentInstructionInformation3CH paymentInstructionInformation3CH = objectFactory
.createPaymentInstructionInformation3CH();
String paymentInfoId = Optional.ofNullable(pain001DTO.getPmtInfId())
.orElse(pain001DTO.getMsgId());
paymentInstructionInformation3CH.setPmtInfId(Iso20022Util.replaceSwift(paymentInfoId)); // SWIFT
paymentInstructionInformation3CH.setPmtMtd(PAYMENT_METHOD_3_CODE);
paymentInstructionInformation3CH.setBtchBookg(BTCHBOOKG);
paymentInstructionInformation3CH.setPmtTpInf(objectFactory.createPaymentTypeInformation19CH());
paymentInstructionInformation3CH.setReqdExctnDt(pain001DTO.getAuszahlungsDatum());
// Debtor name
paymentInstructionInformation3CH.setDbtr(objectFactory.createPartyIdentification32CH());
paymentInstructionInformation3CH.getDbtr().setNm(StringUtils.abbreviate(debtorName, MAX_70_TEXT));
// Debtor Iban
paymentInstructionInformation3CH.setDbtrAcct(objectFactory.createCashAccount16CHIdTpCcy());
paymentInstructionInformation3CH.getDbtrAcct().setId(objectFactory.createAccountIdentification4ChoiceCH());
paymentInstructionInformation3CH.getDbtrAcct().getId().setIBAN(debtorIban);
// Debtor BIC
paymentInstructionInformation3CH.setDbtrAgt(objectFactory
.createBranchAndFinancialInstitutionIdentification4CHBicOrClrId());
paymentInstructionInformation3CH.getDbtrAgt().setFinInstnId(objectFactory
.createFinancialInstitutionIdentification7CHBicOrClrId());
paymentInstructionInformation3CH.getDbtrAgt().getFinInstnId().setBIC(debtorBic);
// Debtor charge Iban: Wird nur verwendet, wenn es tatsaechlich eine separate SchuldnerIBAN hat
String debtorIbanGebuehren = pain001DTO.getSchuldnerIBANGebuehren();
if (StringUtils.isNotEmpty(debtorIbanGebuehren)) {
paymentInstructionInformation3CH.setChrgsAcct(objectFactory.createCashAccount16CHIdAndCurrency());
paymentInstructionInformation3CH.getChrgsAcct().setId(objectFactory.createAccountIdentification4ChoiceCH());
paymentInstructionInformation3CH.getChrgsAcct().getId().setIBAN(debtorIbanGebuehren);
}
return paymentInstructionInformation3CH;
}
/**
* Example:
*
* {@code
* 01-201611-01
* 2016-10-28T00:00:00.000+02:00
* 130
* 204013
*
* Name of Deptor
*
* Kitac
* V01
*
*
*
* }
*
*/
@Nonnull
private GroupHeader32CH createGroupHeader(
@Nonnull Pain001DTO pain001DTO,
@Nonnull ObjectFactory objectFactory,
int transaktion,
@Nonnull BigDecimal ctrlSum,
@Nonnull String debtorName) {
// GroupHeader
// structure
GroupHeader32CH groupHeader32CH = objectFactory.createGroupHeader32CH();
groupHeader32CH.setInitgPty(objectFactory.createPartyIdentification32CHNameAndId());
groupHeader32CH.getInitgPty().setCtctDtls(objectFactory.createContactDetails2CH());
// data
groupHeader32CH.setMsgId(Iso20022Util.replaceSwift(pain001DTO.getMsgId())); // 1.1 // SWIFT
groupHeader32CH.setNbOfTxs(Integer.toString(transaktion)); // 1.6
groupHeader32CH.setCtrlSum(ctrlSum); // 1.7
groupHeader32CH.getInitgPty().setNm(debtorName); // 1.8
groupHeader32CH.getInitgPty().getCtctDtls().setNm(requireNonNull(pain001DTO.getSoftwareName())); // 1.8
String version = pain001DTO.getSoftwareVersion() == null ?
CTCTDTLS_OTHR :
Iso20022Util.replaceSwift(pain001DTO.getSoftwareVersion());
groupHeader32CH.getInitgPty().getCtctDtls().setOthr(version); // 1.8
LocalDateTime creationTime = requireNonNull(pain001DTO.getGenerierungsDatum());
groupHeader32CH.setCreDtTm(Iso20022Util.toXmlGregorianCalendar(creationTime)); // 1.2
return groupHeader32CH;
}
}