First commit waiting for Budget Alert

This commit is contained in:
2025-09-04 13:37:35 +01:00
commit 2d681f27f5
4563 changed files with 1061534 additions and 0 deletions

View File

@ -0,0 +1,57 @@
apply plugin: "com.axelor.app-module"
apply from: "../version.gradle"
apply {
version = openSuiteVersion
}
axelor {
title "Axelor Bank Payment"
description "Axelor Bank Payment Module"
}
configurations {
jaxb
}
dependencies {
compile project(":modules:axelor-account")
jaxb 'com.sun.xml.bind:jaxb-xjc:2.2.2',
'com.sun.xml.bind:jaxb-impl:2.2.2',
'javax.xml.bind:jaxb-api:2.2.2'
compile files("src/main/lib/ebics-1.0.2.jar")
compile "org.bouncycastle:bcprov-jdk15on:1.62"
compile "org.apache.santuario:xmlsec:1.4.3"
compile "commons-codec:commons-codec:1.11"
}
license {
// Don't generate license header on java classes generated from xsd
// exclude "**/xsd/pain_001_001_*/**"
exclude "**/xsd/sepa/**"
}
task xjc () {
def source = "${projectDir}/src/main/resources/xsd/sepa"
def target = "${buildDir}/src-gen/java"
def xsdToGenerate = [
['package': 'com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02', 'destdir': "$target", 'schema': "$source/pain.001.001.02.xsd"],
['package': 'com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03', 'destdir': "$target", 'schema': "$source/pain.001.001.03.xsd"],
['package': 'com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01', 'destdir': "$target", 'schema': "$source/pain.008.001.01.xsd"], // SEPA Direct Debit v01
['package': 'com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02', 'destdir': "$target", 'schema': "$source/pain.008.001.02.xsd"] // SEPA Direct Debit v02
]
doLast {
ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.jaxb.asPath)
xsdToGenerate.each { argsin ->
ant.xjc ( argsin )
}
}
}
compileJava.dependsOn xjc
generateCode.finalizedBy xjc

View File

@ -0,0 +1,75 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.db.repo;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.db.BankOrderLineOrigin;
import com.axelor.apps.tool.StringTool;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Map;
public class BankOrderLineManagementRepository extends BankOrderLineRepository {
@Override
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
Long bankOrderLineId = (Long) json.get("id");
BankOrderLine bankOrderLine = find(bankOrderLineId);
String pieceReferenceList = "";
String pieceDateList = "";
String pieceDueDateList = "";
boolean bFirst = true;
for (BankOrderLineOrigin bankOrderLineOrigin : bankOrderLine.getBankOrderLineOriginList()) {
if (bFirst) {
pieceReferenceList += changeNullToEmptyString(bankOrderLineOrigin.getRelatedToSelectName());
pieceDateList += changeDateToString(bankOrderLineOrigin.getRelatedToSelectDate());
pieceDueDateList += changeDateToString(bankOrderLineOrigin.getRelatedToSelectDueDate());
bFirst = false;
} else {
pieceReferenceList +=
"," + changeNullToEmptyString(bankOrderLineOrigin.getRelatedToSelectName());
pieceDateList += "," + changeDateToString(bankOrderLineOrigin.getRelatedToSelectDate());
pieceDueDateList +=
"," + changeDateToString(bankOrderLineOrigin.getRelatedToSelectDueDate());
}
}
json.put("$pieceReferenceList", StringTool.cutTooLongString(pieceReferenceList));
json.put("$pieceDateList", StringTool.cutTooLongString(pieceDateList));
json.put("$pieceDueDateList", StringTool.cutTooLongString(pieceDueDateList));
return super.populate(json, context);
}
protected String changeNullToEmptyString(Object object) {
if (object == null) {
return "";
} else {
return object.toString();
}
}
protected String changeDateToString(LocalDate date) {
if (date == null) {
return "";
} else {
return date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
}
}

View File

@ -0,0 +1,73 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.db.repo;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import javax.persistence.PersistenceException;
public class BankOrderManagementRepository extends BankOrderRepository {
@Override
public BankOrder save(BankOrder entity) {
try {
BankOrderService bankOrderService = Beans.get(BankOrderService.class);
bankOrderService.generateSequence(entity);
bankOrderService.setSequenceOnBankOrderLines(entity);
if (entity.getStatusSelect() == BankOrderRepository.STATUS_DRAFT) {
bankOrderService.updateTotalAmounts(entity);
}
return super.save(entity);
} catch (Exception e) {
throw new PersistenceException(e.getLocalizedMessage());
}
}
@Override
public BankOrder copy(BankOrder entity, boolean deep) {
BankOrder copy = super.copy(entity, deep);
copy.setStatusSelect(STATUS_DRAFT);
copy.setGeneratedMetaFile(null);
copy.setSignedMetaFile(null);
copy.setConfirmationDateTime(null);
copy.setFileGenerationDateTime(null);
copy.setValidationDateTime(null);
copy.setSendingDateTime(null);
copy.setBankOrderSeq(null);
return copy;
}
@Override
public void remove(BankOrder entity) {
if (entity.getStatusSelect() == BankOrderRepository.STATUS_DRAFT
|| entity.getStatusSelect() == BankOrderRepository.STATUS_CANCELED) {
super.remove(entity);
return;
}
throw new PersistenceException(I18n.get(IExceptionMessage.BANK_ORDER_CANNOT_REMOVE));
}
}

View File

@ -0,0 +1,50 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.db.repo;
import com.axelor.apps.bankpayment.db.BankReconciliation;
import com.axelor.apps.bankpayment.service.bankreconciliation.BankReconciliationCreateService;
import com.axelor.inject.Beans;
import com.google.common.base.Strings;
import java.math.BigDecimal;
public class BankReconciliationManagementRepository extends BankReconciliationRepository {
@Override
public BankReconciliation copy(BankReconciliation entity, boolean deep) {
entity.setStatusSelect(STATUS_DRAFT);
entity.setStartingBalance(BigDecimal.ZERO);
entity.setEndingBalance(BigDecimal.ZERO);
entity.setComputedBalance(BigDecimal.ZERO);
entity.setAccountBalance(BigDecimal.ZERO);
entity.setTotalCashed(BigDecimal.ZERO);
entity.setTotalPaid(BigDecimal.ZERO);
entity.setBankReconciliationLineList(null);
return super.copy(entity, deep);
}
@Override
public BankReconciliation save(BankReconciliation entity) {
if (Strings.isNullOrEmpty(entity.getName())) {
entity.setName(Beans.get(BankReconciliationCreateService.class).computeName(entity));
}
return super.save(entity);
}
}

View File

@ -0,0 +1,34 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.db.repo;
import com.axelor.apps.bankpayment.db.EbicsBank;
import com.axelor.apps.bankpayment.ebics.service.EbicsBankService;
import com.google.inject.Inject;
public class EbicsBankAccountRepository extends EbicsBankRepository {
@Inject EbicsBankService ebicsBankService;
@Override
public EbicsBank save(EbicsBank ebicsBank) {
ebicsBankService.computeFullName(ebicsBank);
return super.save(ebicsBank);
}
}

View File

@ -0,0 +1,35 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.db.repo;
import com.axelor.apps.bankpayment.db.EbicsCertificate;
import com.axelor.apps.bankpayment.ebics.service.EbicsCertificateService;
import com.google.inject.Inject;
public class EbicsCertificateAccountRepository extends EbicsCertificateRepository {
@Inject private EbicsCertificateService certificateService;
@Override
public EbicsCertificate save(EbicsCertificate entity) {
certificateService.computeFullName(entity);
return super.save(entity);
}
}

View File

@ -0,0 +1,38 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.db.repo;
import com.axelor.apps.bankpayment.db.EbicsRequestLog;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.inject.Beans;
import java.util.List;
public class EbicsUserManagementRepository extends EbicsUserRepository {
@Override
public void remove(EbicsUser entity) {
EbicsRequestLogRepository ebicsRequestLogRepository =
Beans.get(EbicsRequestLogRepository.class);
List<EbicsRequestLog> ebicsRequestLogList =
ebicsRequestLogRepository.all().filter("self.ebicsUser = ?1", entity).fetch();
for (EbicsRequestLog ebicsRequestLog : ebicsRequestLogList) {
ebicsRequestLogRepository.remove(ebicsRequestLog);
}
super.remove(entity);
}
}

View File

@ -0,0 +1,40 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.db.repo;
import com.axelor.apps.account.db.Move;
import com.axelor.apps.account.db.MoveLine;
import com.axelor.apps.account.db.repo.MoveManagementRepository;
import java.math.BigDecimal;
import java.util.List;
public class MoveBankPaymentRepository extends MoveManagementRepository {
@Override
public Move copy(Move entity, boolean deep) {
Move copy = super.copy(entity, deep);
List<MoveLine> moveLineList = copy.getMoveLineList();
if (moveLineList != null) {
moveLineList.forEach(moveLine -> moveLine.setBankReconciledAmount(BigDecimal.ZERO));
}
return copy;
}
}

View File

@ -0,0 +1,312 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.certificate;
import com.axelor.apps.bankpayment.db.EbicsCertificate;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.EbicsCertificateRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsUserRepository;
import com.axelor.apps.bankpayment.ebics.service.EbicsCertificateService;
import com.axelor.inject.Beans;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* Simple manager for EBICS certificates.
*
* @author hacheni
*/
public class CertificateManager {
public CertificateManager(EbicsUser user) {
this.user = user;
generator = new X509Generator();
}
/**
* Creates the certificates for the user
*
* @throws GeneralSecurityException
* @throws IOException
*/
public void create() throws GeneralSecurityException, IOException {
Calendar calendar;
calendar = Calendar.getInstance();
calendar.add(
Calendar.YEAR, user.getEbicsPartner().getEbicsBank().getCertValidityPeriodSelect());
org.apache.xml.security.Init.init();
java.security.Security.addProvider(new BouncyCastleProvider());
EbicsPartner ebicsPartner = user.getEbicsPartner();
if ((user.getUserTypeSelect() == EbicsUserRepository.USER_TYPE_TRANSPORT
&& ebicsPartner.getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_TS)
|| ebicsPartner.getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_T) {
createA005Certificate(new Date(calendar.getTimeInMillis()));
}
createX002Certificate(new Date(calendar.getTimeInMillis()));
createE002Certificate(new Date(calendar.getTimeInMillis()));
setUserCertificates();
}
/**
* Sets the user certificates
*
* @throws IOException
* @throws CertificateEncodingException
*/
private void setUserCertificates() throws IOException, CertificateEncodingException {
EbicsPartner ebicsPartner = user.getEbicsPartner();
if ((user.getUserTypeSelect() == EbicsUserRepository.USER_TYPE_TRANSPORT
&& ebicsPartner.getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_TS)
|| ebicsPartner.getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_T) {
user.setA005Certificate(
updateCertificate(
a005Certificate,
user.getA005Certificate(),
a005PrivateKey.getEncoded(),
EbicsCertificateRepository.TYPE_SIGNATURE));
}
user.setX002Certificate(
updateCertificate(
x002Certificate,
user.getX002Certificate(),
x002PrivateKey.getEncoded(),
EbicsCertificateRepository.TYPE_AUTHENTICATION));
user.setE002Certificate(
updateCertificate(
e002Certificate,
user.getE002Certificate(),
e002PrivateKey.getEncoded(),
EbicsCertificateRepository.TYPE_ENCRYPTION));
}
private EbicsCertificate updateCertificate(
X509Certificate certificate, EbicsCertificate cert, byte[] privateKey, String type)
throws CertificateEncodingException, IOException {
if (cert == null) {
cert = new EbicsCertificate();
cert.setTypeSelect(type);
}
EbicsCertificateService certificateService = Beans.get(EbicsCertificateService.class);
cert = certificateService.updateCertificate(certificate, cert, true);
cert.setPrivateKey(privateKey);
return cert;
}
/**
* Creates the signature certificate.
*
* @param the expiration date of a the certificate.
* @throws GeneralSecurityException
* @throws IOException
*/
public void createA005Certificate(Date end) throws GeneralSecurityException, IOException {
KeyPair keypair;
keypair = KeyUtil.makeKeyPair(X509Constants.EBICS_KEY_SIZE);
a005Certificate =
generator.generateA005Certificate(
keypair,
user.getDn(),
new Date(),
end,
user.getEbicsPartner().getEbicsBank().getUseX509ExtentionForAutoSignedCert());
a005PrivateKey = keypair.getPrivate();
}
/**
* Creates the authentication certificate.
*
* @param the expiration date of a the certificate.
* @throws GeneralSecurityException
* @throws IOException
*/
public void createX002Certificate(Date end) throws GeneralSecurityException, IOException {
KeyPair keypair;
keypair = KeyUtil.makeKeyPair(X509Constants.EBICS_KEY_SIZE);
x002Certificate =
generator.generateX002Certificate(
keypair,
user.getDn(),
new Date(),
end,
user.getEbicsPartner().getEbicsBank().getUseX509ExtentionForAutoSignedCert());
x002PrivateKey = keypair.getPrivate();
}
/**
* Creates the encryption certificate.
*
* @param the expiration date of a the certificate.
* @throws GeneralSecurityException
* @throws IOException
*/
public void createE002Certificate(Date end) throws GeneralSecurityException, IOException {
KeyPair keypair;
keypair = KeyUtil.makeKeyPair(X509Constants.EBICS_KEY_SIZE);
e002Certificate =
generator.generateE002Certificate(
keypair,
user.getDn(),
new Date(),
end,
user.getEbicsPartner().getEbicsBank().getUseX509ExtentionForAutoSignedCert());
e002PrivateKey = keypair.getPrivate();
}
/**
* Saves the certificates in PKCS12 format
*
* @param path the certificates path
* @param pwdCallBack the password call back
* @throws GeneralSecurityException
* @throws IOException
*/
public void save(String path) throws GeneralSecurityException, IOException {
char[] pwd = null;
if (user.getPassword() != null) {
pwd = user.getPassword().toCharArray();
}
writePKCS12Certificate(path + "/" + user.getUserId(), pwd);
}
/**
* Loads user certificates from a given key store
*
* @param path the key store path
* @param pwdCallBack the password call back
* @throws GeneralSecurityException
* @throws IOException
*/
public void load(String path) throws GeneralSecurityException, IOException {
KeyStoreManager loader;
loader = new KeyStoreManager();
if (user.getPassword() != null) {
loader.load(path, user.getPassword().toCharArray());
} else {
loader.load(path, null);
}
a005Certificate = loader.getCertificate(user.getUserId() + "-A005");
x002Certificate = loader.getCertificate(user.getUserId() + "-X002");
e002Certificate = loader.getCertificate(user.getUserId() + "-E002");
a005PrivateKey = loader.getPrivateKey(user.getUserId() + "-A005");
x002PrivateKey = loader.getPrivateKey(user.getUserId() + "-X002");
e002PrivateKey = loader.getPrivateKey(user.getUserId() + "-E002");
setUserCertificates();
}
/**
* Writes a the generated certificates into a PKCS12 key store.
*
* @param filename the key store file name
* @param password the key password
* @throws IOException
*/
public void writePKCS12Certificate(String filename, char[] password)
throws GeneralSecurityException, IOException {
if (filename == null || "".equals(filename)) {
throw new IOException("The file name cannot be empty");
}
if (!filename.toLowerCase().endsWith(".p12")) {
filename += ".p12";
}
FileOutputStream fos = new FileOutputStream(filename);
writePKCS12Certificate(password, fos);
fos.close();
}
/**
* Writes a the generated certificates into a PKCS12 key store.
*
* @param password the key store password
* @param fos the output stream
* @throws GeneralSecurityException
* @throws IOException
*/
public void writePKCS12Certificate(char[] password, OutputStream fos)
throws GeneralSecurityException, IOException {
KeyStore keystore;
keystore = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
keystore.load(null, null);
keystore.setKeyEntry(
user.getUserId() + "-A005",
a005PrivateKey,
password,
new X509Certificate[] {a005Certificate});
keystore.setKeyEntry(
user.getUserId() + "-X002",
x002PrivateKey,
password,
new X509Certificate[] {x002Certificate});
keystore.setKeyEntry(
user.getUserId() + "-E002",
e002PrivateKey,
password,
new X509Certificate[] {e002Certificate});
keystore.store(fos, password);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private X509Generator generator;
private EbicsUser user;
private X509Certificate a005Certificate;
private X509Certificate e002Certificate;
private X509Certificate x002Certificate;
private PrivateKey a005PrivateKey;
private PrivateKey x002PrivateKey;
private PrivateKey e002PrivateKey;
}

View File

@ -0,0 +1,218 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.certificate;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMParser;
/**
* Key store loader. This class loads a key store from a given path and allow to get private keys
* and certificates for a given alias. The PKCS12 key store type is recommended to be used
*
* @author hachani
*/
public class KeyStoreManager {
/**
* Loads a certificate for a given alias
*
* @param alias the certificate alias
* @return the certificate
* @throws KeyStoreException
*/
public final X509Certificate getCertificate(String alias) throws KeyStoreException {
X509Certificate cert;
cert = (X509Certificate) keyStore.getCertificate(alias);
if (cert == null) {
throw new IllegalArgumentException("alias " + alias + " not found in the KeyStore");
}
return cert;
}
/**
* Loads a private key for a given alias
*
* @param alias the certificate alias
* @return the private key
* @throws GeneralSecurityException
*/
public final PrivateKey getPrivateKey(String alias) throws GeneralSecurityException {
PrivateKey key;
key = (PrivateKey) keyStore.getKey(alias, password);
if (key == null) {
throw new IllegalArgumentException("private key not found for alias " + alias);
}
return key;
}
/**
* Loads a key store from a given path and password
*
* @param path the key store path
* @param password the key store password
* @throws GeneralSecurityException
* @throws IOException
*/
public void load(String path, char[] password) throws GeneralSecurityException, IOException {
keyStore = KeyStore.getInstance("PKCS12", "BC");
this.password = password;
load(path);
}
/**
* Loads a key store and cache the loaded one
*
* @param path the key store path.
* @throws GeneralSecurityException
* @throws IOException
*/
private void load(String path) throws GeneralSecurityException, IOException {
if (path.equals("")) {
this.keyStore.load(null, null);
} else {
this.keyStore.load(new FileInputStream(path), password);
this.certs = read(this.keyStore);
}
}
/**
* Reads a certificate from an input stream for a given provider
*
* @param input the input stream
* @param provider the certificate provider
* @return the certificate
* @throws CertificateException
* @throws IOException
*/
public X509Certificate read(InputStream input, Provider provider)
throws CertificateException, IOException {
X509Certificate certificate;
certificate =
(X509Certificate)
CertificateFactory.getInstance("X.509", provider).generateCertificate(input);
if (certificate == null) {
try (final PEMParser reader = new PEMParser(new InputStreamReader(input))) {
final X509CertificateHolder certificateHolder = (X509CertificateHolder) reader.readObject();
certificate = new JcaX509CertificateConverter().getCertificate(certificateHolder);
}
}
return certificate;
}
/**
* Returns the public key of a given certificate.
*
* @param input the given certificate
* @return The RSA public key of the given certificate
* @throws GeneralSecurityException
* @throws IOException
*/
public RSAPublicKey getPublicKey(InputStream input) throws GeneralSecurityException, IOException {
X509Certificate cert;
cert = read(input, keyStore.getProvider());
return (RSAPublicKey) cert.getPublicKey();
}
/**
* Writes the given certificate into the key store.
*
* @param alias the certificate alias
* @param input the given certificate.
* @throws GeneralSecurityException
* @throws IOException
*/
public void setCertificateEntry(String alias, InputStream input)
throws GeneralSecurityException, IOException {
keyStore.setCertificateEntry(alias, read(input, keyStore.getProvider()));
}
/**
* Saves the key store to a given output stream.
*
* @param output the output stream.
*/
public void save(OutputStream output) throws GeneralSecurityException, IOException {
keyStore.store(output, password);
}
/**
* Returns the certificates contained in the key store.
*
* @return the certificates contained in the key store.
*/
public Map<String, X509Certificate> getCertificates() {
return certs;
}
/**
* Reads all certificate existing in a given key store
*
* @param keyStore the key store
* @return A <code>Map</code> of certificate, the key of the map is the certificate alias
* @throws KeyStoreException
*/
public Map<String, X509Certificate> read(KeyStore keyStore) throws KeyStoreException {
Map<String, X509Certificate> certificates;
Enumeration<String> enumeration;
certificates = new HashMap<String, X509Certificate>();
enumeration = keyStore.aliases();
while (enumeration.hasMoreElements()) {
String alias;
alias = enumeration.nextElement();
certificates.put(alias, (X509Certificate) keyStore.getCertificate(alias));
}
return certificates;
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private KeyStore keyStore;
private char[] password;
private Map<String, X509Certificate> certs;
}

View File

@ -0,0 +1,125 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.certificate;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
/**
* Some key utilities
*
* @author hachani
*/
public class KeyUtil {
/**
* Generates a <code>KeyPair</code> in RSA format.
*
* @param keyLen - key size
* @return KeyPair the key pair
* @throws NoSuchAlgorithmException
*/
public static KeyPair makeKeyPair(int keyLen) throws NoSuchAlgorithmException {
KeyPairGenerator keyGen;
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keyLen, new SecureRandom());
KeyPair keypair = keyGen.generateKeyPair();
return keypair;
}
/**
* Generates a random password
*
* @return the password
*/
public static String generatePassword() {
SecureRandom random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
String pwd = Base64.encodeBase64String(random.generateSeed(5));
return pwd.substring(0, pwd.length() - 2);
} catch (NoSuchAlgorithmException e) {
return "changeit";
}
}
/**
* Returns the digest value of a given public key.
*
* <p>In Version “H003” of the EBICS protocol the ES of the financial:
*
* <p>The SHA-256 hash values of the financial institution's public keys for X002 and E002 are
* composed by concatenating the exponent with a blank character and the modulus in hexadecimal
* representation (using lower case letters) without leading zero (as to the hexadecimal
* representation). The resulting string has to be converted into a byte array based on US ASCII
* code.
*
* @param publicKey the public key
* @return the digest value
* @throws EbicsException
*/
public static byte[] getKeyDigest(RSAPublicKey publicKey) throws AxelorException {
String modulus;
String exponent;
String hash;
byte[] digest;
exponent = Hex.encodeHexString(publicKey.getPublicExponent().toByteArray());
modulus = Hex.encodeHexString(removeFirstByte(publicKey.getModulus().toByteArray()));
hash = exponent + " " + modulus;
if (hash.charAt(0) == '0') {
hash = hash.substring(1);
}
try {
digest = MessageDigest.getInstance("SHA-256", "BC").digest(hash.getBytes("US-ASCII"));
} catch (GeneralSecurityException | UnsupportedEncodingException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
return new String(Hex.encodeHex(digest, false)).getBytes();
}
/**
* Remove the first byte of an byte array
*
* @return the array
*/
private static byte[] removeFirstByte(byte[] byteArray) {
byte[] b = new byte[byteArray.length - 1];
System.arraycopy(byteArray, 1, b, 0, b.length);
return b;
}
}

View File

@ -0,0 +1,38 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.certificate;
/**
* X509 certificate constants
*
* @author hachani
*/
public interface X509Constants {
/** Certificates key usage */
int SIGNATURE_KEY_USAGE = 1;
int AUTHENTICATION_KEY_USAGE = 2;
int ENCRYPTION_KEY_USAGE = 3;
/** Certificate signature algorithm */
String SIGNATURE_ALGORITHM = "SHA256WithRSAEncryption";
/** EBICS key size */
int EBICS_KEY_SIZE = 2048;
}

View File

@ -0,0 +1,317 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.certificate;
/*
* Copyright (c) 1990-2012 kopiLeft Development SARL, Bizerte, Tunisia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id$
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.x509.X509V3CertificateGenerator;
/**
* An X509 certificate generator for EBICS protocol. Generated certificates are self signed
* certificates.
*
* @author hachani
*/
@SuppressWarnings("deprecation")
public class X509Generator {
/**
* Generates the signature certificate for the EBICS protocol
*
* @param keypair the key pair
* @param issuer the certificate issuer
* @param notBefore the begin validity date
* @param notAfter the end validity date
* @return the signature certificate
* @throws GeneralSecurityException
* @throws IOException
*/
public X509Certificate generateA005Certificate(
KeyPair keypair,
String issuer,
Date notBefore,
Date notAfter,
boolean useX509ExtentionForAutoSignedCert)
throws GeneralSecurityException, IOException {
return generate(
keypair,
issuer,
notBefore,
notAfter,
X509Constants.SIGNATURE_KEY_USAGE,
useX509ExtentionForAutoSignedCert);
}
/**
* Generates the authentication certificate for the EBICS protocol
*
* @param keypair the key pair
* @param issuer the certificate issuer
* @param notBefore the begin validity date
* @param notAfter the end validity date
* @return the authentication certificate
* @throws GeneralSecurityException
* @throws IOException
*/
public X509Certificate generateX002Certificate(
KeyPair keypair,
String issuer,
Date notBefore,
Date notAfter,
boolean useX509ExtentionForAutoSignedCert)
throws GeneralSecurityException, IOException {
return generate(
keypair,
issuer,
notBefore,
notAfter,
X509Constants.AUTHENTICATION_KEY_USAGE,
useX509ExtentionForAutoSignedCert);
}
/**
* Generates the encryption certificate for the EBICS protocol
*
* @param keypair the key pair
* @param issuer the certificate issuer
* @param notBefore the begin validity date
* @param notAfter the end validity date
* @return the encryption certificate
* @throws GeneralSecurityException
* @throws IOException
*/
public X509Certificate generateE002Certificate(
KeyPair keypair,
String issuer,
Date notBefore,
Date notAfter,
boolean useX509ExtentionForAutoSignedCert)
throws GeneralSecurityException, IOException {
return generate(
keypair,
issuer,
notBefore,
notAfter,
X509Constants.ENCRYPTION_KEY_USAGE,
useX509ExtentionForAutoSignedCert);
}
/**
* Returns an <code>X509Certificate</code> from a given <code>KeyPair</code> and limit dates
* validations
*
* @param keypair the given key pair
* @param issuer the certificate issuer
* @param notBefore the begin validity date
* @param notAfter the end validity date
* @param keyusage the certificate key usage
* @return the X509 certificate
* @throws GeneralSecurityException
* @throws IOException
*/
public X509Certificate generate(
KeyPair keypair,
String issuer,
Date notBefore,
Date notAfter,
int keyusage,
boolean useX509ExtentionForAutoSignedCert)
throws GeneralSecurityException, IOException {
X509V3CertificateGenerator generator;
BigInteger serial;
X509Certificate certificate;
ASN1EncodableVector vector;
serial = BigInteger.valueOf(generateSerial());
generator = new X509V3CertificateGenerator();
generator.setSerialNumber(serial);
generator.setIssuerDN(new X509Principal(issuer));
generator.setNotBefore(notBefore);
generator.setNotAfter(notAfter);
generator.setSubjectDN(new X509Principal(issuer));
generator.setPublicKey(keypair.getPublic());
generator.setSignatureAlgorithm(X509Constants.SIGNATURE_ALGORITHM);
if (useX509ExtentionForAutoSignedCert) {
generator.addExtension(X509Extensions.BasicConstraints, false, new BasicConstraints(true));
generator.addExtension(
X509Extensions.SubjectKeyIdentifier, false, getSubjectKeyIdentifier(keypair.getPublic()));
generator.addExtension(
X509Extensions.AuthorityKeyIdentifier,
false,
getAuthorityKeyIdentifier(keypair.getPublic(), issuer, serial));
vector = new ASN1EncodableVector();
vector.add(KeyPurposeId.id_kp_emailProtection);
generator.addExtension(
X509Extensions.ExtendedKeyUsage,
false,
ExtendedKeyUsage.getInstance(new DERSequence(vector)));
}
switch (keyusage) {
case X509Constants.SIGNATURE_KEY_USAGE:
generator.addExtension(
X509Extensions.KeyUsage, false, new KeyUsage(KeyUsage.nonRepudiation));
break;
case X509Constants.AUTHENTICATION_KEY_USAGE:
generator.addExtension(
X509Extensions.KeyUsage, false, new KeyUsage(KeyUsage.digitalSignature));
break;
case X509Constants.ENCRYPTION_KEY_USAGE:
generator.addExtension(X509Extensions.KeyUsage, false, new KeyUsage(KeyUsage.keyAgreement));
break;
default:
generator.addExtension(
X509Extensions.KeyUsage,
false,
new KeyUsage(KeyUsage.keyEncipherment | KeyUsage.digitalSignature));
break;
}
certificate = generator.generate(keypair.getPrivate(), "BC", new SecureRandom());
certificate.checkValidity(new Date());
certificate.verify(keypair.getPublic());
return certificate;
}
/**
* Returns the <code>AuthorityKeyIdentifier</code> corresponding to a given <code>PublicKey</code>
*
* @param publicKey the given public key
* @param issuer the certificate issuer
* @param serial the certificate serial number
* @return the authority key identifier of the public key
* @throws IOException
*/
private AuthorityKeyIdentifier getAuthorityKeyIdentifier(
PublicKey publicKey, String issuer, BigInteger serial) throws IOException {
InputStream input;
SubjectPublicKeyInfo keyInfo;
ASN1EncodableVector vector;
input = new ByteArrayInputStream(publicKey.getEncoded());
try (final ASN1InputStream is = new ASN1InputStream(input)) {
keyInfo = SubjectPublicKeyInfo.getInstance((ASN1Sequence) is.readObject());
}
vector = new ASN1EncodableVector();
vector.add(new GeneralName(new X509Name(issuer)));
return new AuthorityKeyIdentifier(
keyInfo, GeneralNames.getInstance(new DERSequence(vector)), serial);
}
/**
* Returns the <code>SubjectKeyIdentifier</code> corresponding to a given <code>PublicKey</code>
*
* @param publicKey the given public key
* @return the subject key identifier
* @throws IOException
* @throws NoSuchAlgorithmException
*/
private SubjectKeyIdentifier getSubjectKeyIdentifier(PublicKey publicKey)
throws IOException, NoSuchAlgorithmException {
InputStream input;
SubjectPublicKeyInfo keyInfo;
input = new ByteArrayInputStream(publicKey.getEncoded());
try (final ASN1InputStream is = new ASN1InputStream(input)) {
keyInfo = SubjectPublicKeyInfo.getInstance((ASN1Sequence) is.readObject());
}
final JcaX509ExtensionUtils jcaX509ExtensionUtils = new JcaX509ExtensionUtils();
return jcaX509ExtensionUtils.createSubjectKeyIdentifier(keyInfo);
}
/**
* Generates a random serial number
*
* @return the serial number
*/
private long generateSerial() {
Date now;
now = new Date();
String sNow = sdfSerial.format(now);
return Long.valueOf(sNow).longValue();
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private static SimpleDateFormat sdfSerial;
static {
sdfSerial = new SimpleDateFormat("yyyyMMddHHmmssSSS");
TimeZone tz = TimeZone.getTimeZone("UTC");
sdfSerial.setTimeZone(tz);
}
}

View File

@ -0,0 +1,45 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.exception.AxelorException;
import java.io.PrintStream;
public interface EbicsElement {
/**
* Returns the name of this <code>EbicsElement</code>
*
* @return the name of the element
*/
public String getName();
/**
* Builds the <code>EbicsElement</code> XML fragment
*
* @throws EbicsException
*/
public void build() throws AxelorException;
/**
* Prints the <code>EbicsElement</code> into the given stream.
*
* @param stream the print stream
*/
public void print(PrintStream stream);
}

View File

@ -0,0 +1,88 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import java.io.Serializable;
import java.util.Locale;
/**
* Optional information about the client product.
*
* @author Hachani
*/
public class EbicsProduct implements Serializable {
/**
* Creates a new product information element.
*
* @param name this is the name of the product. It is a mandatory field.
* @param language this is the language. If you use null, the language of the default locale is
* used.
* @param instituteID the institute, this is an optional value, you can leave this parameter
* empty.
*/
public EbicsProduct(String name, String language, String instituteID) {
this.name = name;
if (language == null) {
language = Locale.getDefault().getLanguage();
} else {
this.language = language;
}
this.instituteID = instituteID;
}
/** @return the name */
public String getName() {
return name;
}
/** @param name the name to set */
public void setName(String name) {
this.name = name;
}
/** @return the language */
public String getLanguage() {
return language;
}
/** @param language the language to set */
public void setLanguage(String language) {
this.language = language;
}
/** @return the instituteID */
public String getInstituteID() {
return instituteID;
}
/** @param instituteID the instituteID to set */
public void setInstituteID(String instituteID) {
this.instituteID = instituteID;
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private transient String name;
private String language;
private String instituteID;
private static final long serialVersionUID = 6400195827756653241L;
}

View File

@ -0,0 +1,60 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.exception.AxelorException;
import java.io.OutputStream;
import org.jdom.JDOMException;
/**
* An Ebics root element knows its name.
*
* @author hachani
*/
public interface EbicsRootElement extends EbicsElement {
/**
* Converts the <code>EbicsElement</code> to a byte array
*
* @return the equivalent byte array of this <code>EbicsElement</code>
*/
public byte[] toByteArray();
/**
* Validates the request element according to the EBICS XML schema specification
*
* @throws EbicsException throws an EbicsException when validation fails
*/
public void validate() throws AxelorException;
/**
* Adds a namespace declaration for the <code>EbicsRootElement</code>
*
* @param prefix namespace prefix
* @param uri namespace uri
*/
public void addNamespaceDecl(String prefix, String uri);
/**
* Saves the <code>EbicsElement</code> into a given output stream.
*
* @param out the output stream
* @throws EbicsException the save operation fails
*/
public void save(OutputStream out) throws AxelorException, JDOMException;
}

View File

@ -0,0 +1,156 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.EbicsCertificateRepository;
import com.axelor.apps.bankpayment.ebics.service.EbicsCertificateService;
import com.axelor.exception.AxelorException;
import java.io.IOException;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
public class EbicsSession {
private EbicsUser user;
private EbicsUser signatoryUser;
private EbicsProduct product;
private Map<String, String> parameters;
/**
* Constructs a new ebics session
*
* @param user the ebics user
* @param the ebics client configuration
*/
public EbicsSession(EbicsUser user) {
this.user = user;
parameters = new HashMap<String, String>();
}
/**
* Constructs a new ebics session
*
* @param user the ebics user
* @param the ebics client configuration
*/
public EbicsSession(EbicsUser user, EbicsUser signatoryUser) {
this.user = user;
this.signatoryUser = signatoryUser;
parameters = new HashMap<String, String>();
}
/**
* Returns the banks encryption key. The key will be fetched automatically form the bank if
* needed.
*
* @return the banks encryption key.
* @throws IOException Communication error during key retrieval.
* @throws EbicsException Server error message generated during key retrieval.
*/
public RSAPublicKey getBankE002Key() throws AxelorException {
return (RSAPublicKey)
EbicsCertificateService.getBankCertificate(
user.getEbicsPartner().getEbicsBank(), EbicsCertificateRepository.TYPE_ENCRYPTION)
.getPublicKey();
}
/**
* Returns the banks authentication key. The key will be fetched automatically form the bank if
* needed.
*
* @return the banks authentication key.
* @throws IOException Communication error during key retrieval.
* @throws EbicsException Server error message generated during key retrieval.
*/
public RSAPublicKey getBankX002Key() throws AxelorException {
return (RSAPublicKey)
EbicsCertificateService.getBankCertificate(
user.getEbicsPartner().getEbicsBank(),
EbicsCertificateRepository.TYPE_AUTHENTICATION)
.getPublicKey();
}
/**
* Returns the bank id.
*
* @return the bank id.
* @throws EbicsException
*/
public String getBankID() throws AxelorException {
return user.getEbicsPartner().getEbicsBank().getHostId();
}
/**
* Return the session user.
*
* @return the session user.
*/
public EbicsUser getUser() {
return user;
}
/**
* Return the session signatory user.
*
* @return the session signatory user.
*/
public EbicsUser getSignatoryUser() {
return signatoryUser;
}
/**
* Sets the optional product identification that will be sent to the bank during each request.
*
* @param product Product description
*/
public void setProduct(EbicsProduct product) {
this.product = product;
}
/** @return the product */
public EbicsProduct getProduct() {
return product;
}
/**
* Adds a session parameter to use it in the transfer process.
*
* @param key the parameter key
* @param value the parameter value
*/
public void addSessionParam(String key, String value) {
parameters.put(key, value);
}
/**
* Retrieves a session parameter using its key.
*
* @param key the parameter key
* @return the session parameter
*/
public String getSessionParam(String key) {
if (key == null) {
return null;
}
return parameters.get(key);
}
}

View File

@ -0,0 +1,322 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.app.AppSettings;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.utils.IgnoreAllErrorHandler;
import org.apache.xpath.XPathAPI;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
/**
* Some utilities for EBICS request creation and reception
*
* @author hachani
*/
public class EbicsUtils {
/**
* Compresses an input of byte array
*
* <p>The Decompression is ensured via Universal compression algorithm (RFC 1950, RFC 1951) As
* specified in the EBICS specification (16 Appendix: Standards and references)
*
* @param toZip the input to be compressed
* @return the compressed input data
* @throws IOException compression failed
*/
public static byte[] zip(byte[] toZip) throws AxelorException {
if (toZip == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get("The input to be zipped cannot be null"));
}
Deflater compressor;
ByteArrayOutputStream output;
byte[] buffer;
output = new ByteArrayOutputStream(toZip.length);
buffer = new byte[1024];
compressor = new Deflater(Deflater.BEST_COMPRESSION);
compressor.setInput(toZip);
compressor.finish();
while (!compressor.finished()) {
int count = compressor.deflate(buffer);
output.write(buffer, 0, count);
}
try {
output.close();
} catch (IOException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
compressor.end();
return output.toByteArray();
}
/**
* Generates a random nonce.
*
* <p>EBICS Specification 2.4.2 - 11.6 Generation of the transaction IDs:
*
* <p>Transaction IDs are cryptographically-strong random numbers with a length of 128 bits. This
* means that the likelihood of any two bank systems using the same transaction ID at the same
* time is sufficiently small.
*
* <p>Transaction IDs are generated by cryptographic pseudo-random number generators (PRNG) that
* have been initialized with a real random number (seed). The entropy of the seed should be at
* least 100 bits.
*
* @return a random nonce.
* @throws EbicsException nonce generation fails.
*/
public static byte[] generateNonce() throws AxelorException {
SecureRandom secureRandom;
try {
secureRandom = SecureRandom.getInstance("SHA1PRNG");
return secureRandom.generateSeed(16);
} catch (NoSuchAlgorithmException e) {
throw new AxelorException(e.getCause(), TraceBackRepository.TYPE_FUNCTIONNAL, e.getMessage());
}
}
/**
* Uncompresses a given byte array input.
*
* <p>The Decompression is ensured via Universal compression algorithm (RFC 1950, RFC 1951) As
* specified in the EBICS specification (16 Appendix: Standards and references)
*
* @param zip the zipped input.
* @return the uncompressed data.
*/
public static byte[] unzip(byte[] zip) throws AxelorException {
Inflater decompressor;
ByteArrayOutputStream output;
byte[] buf;
decompressor = new Inflater();
output = new ByteArrayOutputStream(zip.length);
decompressor.setInput(zip);
buf = new byte[1024];
while (!decompressor.finished()) {
int count;
try {
count = decompressor.inflate(buf);
} catch (DataFormatException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.TYPE_FUNCTIONNAL, e.getMessage());
}
output.write(buf, 0, count);
}
try {
output.close();
} catch (IOException e) {
throw new AxelorException(e.getCause(), TraceBackRepository.TYPE_FUNCTIONNAL, e.getMessage());
}
decompressor.end();
return output.toByteArray();
}
/**
* Canonizes an input with inclusive c14n without comments algorithm.
*
* <p>EBICS Specification 2.4.2 - 5.5.1.1.1 EBICS messages in transaction initialization:
*
* <p>The identification and authentication signature includes all XML elements of the EBICS
* request whose attribute value for @authenticate is equal to “true”. The definition of the XML
* schema “ebics_request.xsd“ guarantees that the value of the attribute @authenticate is equal to
* “true” for precisely those elements that also need to be signed.
*
* <p>Thus, All the Elements with the attribute authenticate = true and their sub elements are
* considered for the canonization process. This is performed via the {@link
* XPathAPI#selectNodeIterator(Node, String) selectNodeIterator(Node, String)}.
*
* @param input the byte array XML input.
* @return the canonized form of the given XML
* @throws EbicsException
*/
public static byte[] canonize(byte[] input) throws AxelorException {
DocumentBuilderFactory factory;
DocumentBuilder builder;
Document document;
NodeIterator iter;
ByteArrayOutputStream output;
Node node;
try {
factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
builder = factory.newDocumentBuilder();
builder.setErrorHandler(new IgnoreAllErrorHandler());
document = builder.parse(new ByteArrayInputStream(input));
iter = XPathAPI.selectNodeIterator(document, "//*[@authenticate='true']");
output = new ByteArrayOutputStream();
while ((node = iter.nextNode()) != null) {
Canonicalizer canonicalizer;
canonicalizer = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
output.write(canonicalizer.canonicalizeSubtree(node));
}
return output.toByteArray();
} catch (Exception e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Encrypts an input with a given key spec.
*
* <p>EBICS Specification 2.4.2 - 15.1 Workflows at the senders end:
*
* <p><b>Preparation for DEK encryption</b>
*
* <p>The 128 bit DEK that is interpreted as a natural number is filled out with null bits to 768
* bits in front of the highest-value bit. The result is called PDEK.
*
* <p><b>Encryption of the secret DES key</b>
*
* <p>PDEK is then encrypted with the recipients public key of the RSA key system and is then
* expanded with leading null bits to 1024 bits.
*
* <p>The result is called EDEK. It must be ensured that EDEK is not equal to DEK.
*
* <p><b>Encryption of the messages</b>
*
* <p><U>Padding of the message:</U>
*
* <p>The method Padding with Octets in accordance with ANSI X9.23 is used for padding the
* message, i.e. in all cases, data is appended to the message that is to be encrypted.
*
* <p><U>Application of the encryption algorithm:</U>
*
* <p>The message is encrypted in CBC mode in accordance with ANSI X3.106 with the secret key DEK
* according to the 2-key triple DES process as specified in ANSI X3.92-1981.
*
* <p>In doing this, the following initialization value “ICV” is used: X 00 00 00 00 00 00 00
* 00.
*
* @param input the input to encrypt
* @param keySpec the key spec
* @return the encrypted input
* @throws EbicsException
*/
public static byte[] encrypt(byte[] input, SecretKeySpec keySpec) throws AxelorException {
return encryptOrDecrypt(Cipher.ENCRYPT_MODE, input, keySpec);
}
/**
* Decrypts the given input according to key spec.
*
* @param input the input to decrypt
* @param keySpec the key spec
* @return the decrypted input
* @throws EbicsException
*/
public static byte[] decrypt(byte[] input, SecretKeySpec keySpec) throws AxelorException {
return encryptOrDecrypt(Cipher.DECRYPT_MODE, input, keySpec);
}
/**
* Encrypts or decrypts the given input according to key spec.
*
* @param mode the encryption-decryption mode.
* @param input the input to encrypt or decrypt.
* @param keySpec the key spec.
* @return the encrypted or decrypted data.
* @throws GeneralSecurityException
*/
private static byte[] encryptOrDecrypt(int mode, byte[] input, SecretKeySpec keySpec)
throws AxelorException {
IvParameterSpec iv;
Cipher cipher;
iv = new IvParameterSpec(new byte[16]);
try {
cipher = Cipher.getInstance("AES/CBC/ISO10126Padding", BouncyCastleProvider.PROVIDER_NAME);
cipher.init(mode, keySpec, iv);
return cipher.doFinal(input);
} catch (GeneralSecurityException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Parses a string date
*
* @param date the given string date
* @return the date value
*/
public static Date parse(String date) throws AxelorException {
try {
return new SimpleDateFormat(AppSettings.get().get("date.format")).parse(date);
} catch (Exception e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Checks for the returned http code
*
* @param httpCode the http code
* @throws EbicsException
*/
public static void checkHttpCode(int httpCode) throws AxelorException {
if (httpCode != 200) {
throw new AxelorException(
TraceBackRepository.TYPE_FUNCTIONNAL, "http.code.error[Code:%s]", httpCode);
}
}
}

View File

@ -0,0 +1,301 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.apps.bankpayment.db.EbicsTransferState;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.apps.bankpayment.ebics.io.ByteArrayContentFactory;
import com.axelor.apps.bankpayment.ebics.io.Joiner;
import com.axelor.apps.bankpayment.ebics.xml.DInitializationRequestElement;
import com.axelor.apps.bankpayment.ebics.xml.DInitializationResponseElement;
import com.axelor.apps.bankpayment.ebics.xml.DTransferRequestElement;
import com.axelor.apps.bankpayment.ebics.xml.DTransferResponseElement;
import com.axelor.apps.bankpayment.ebics.xml.DefaultEbicsRootElement;
import com.axelor.apps.bankpayment.ebics.xml.InitializationResponseElement;
import com.axelor.apps.bankpayment.ebics.xml.ReceiptRequestElement;
import com.axelor.apps.bankpayment.ebics.xml.ReceiptResponseElement;
import com.axelor.apps.bankpayment.ebics.xml.TransferResponseElement;
import com.axelor.apps.bankpayment.ebics.xml.UInitializationRequestElement;
import com.axelor.apps.bankpayment.ebics.xml.UTransferRequestElement;
import com.axelor.exception.AxelorException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import org.jdom.JDOMException;
/**
* Handling of file transfers. Files can be transferred to and fetched from the bank. Every transfer
* may be performed in a recoverable way. For convenience and performance reasons there are also
* methods that do the whole transfer in one method call. To use the recoverable transfer mode, you
* may set a working directory for temporarily created files.
*
* <p>EBICS specification 2.4.2 - 6.2 Encryption at application level
*
* <p>In the event of an upload transaction, a random symmetrical key is generated in the customer
* system that is used exclusively within the framework of this transaction both for encryption of
* the ESs and for encryption of the order data. This key is encrypted asymmetrically with the
* financial institutions public encryption key and is transmitted by the customer system to the
* bank system during the initialization phase of the transaction.
*
* <p>Analogously, in the case of a download transaction a random symmetrical key is generated in
* the bank system that is used for encryption of the order data that is to be downloaded and for
* encryption of the bank-technical signature that has been provided by the financial institution.
* This key is asymmetrically encrypted and is transmitted by the bank system to the customer system
* during the initialization phase of the transaction. The asymmetrical encryption takes place with
* the technical subscribers public encryption key if the transactions EBICS messages are sent by
* a technical subscriber. Otherwise the asymmetrical encryption takes place with the public
* encryption key of the non-technical subscriber, i.e. the submitter of the order.
*
* @author Hachani
*/
public class FileTransfer {
/**
* Constructs a new FileTransfer session
*
* @param session the user session
*/
public FileTransfer(EbicsSession session) {
this.session = session;
}
/**
* Initiates a file transfer to the bank.
*
* @param content The bytes you want to send.
* @param orderType As which order type
* @throws IOException
* @throws EbicsException
*/
public void sendFile(byte[] content, OrderType orderType, byte[] signature)
throws IOException, AxelorException {
HttpRequestSender sender;
UInitializationRequestElement initializer;
InitializationResponseElement response;
int httpCode;
EbicsTransferState state;
sender = new HttpRequestSender(session);
initializer = new UInitializationRequestElement(session, orderType, content, signature);
initializer.build();
initializer.validate();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try {
initializer.save(bout);
} catch (JDOMException e) {
// TODO Bloc catch généré automatiquement
e.printStackTrace();
}
System.out.println(
"Requete ----------------------------------------------------------------------------");
System.out.println(bout.toString());
httpCode = sender.send(new ByteArrayContentFactory(initializer.prettyPrint()));
EbicsUtils.checkHttpCode(httpCode);
response =
new InitializationResponseElement(
sender.getResponseBody(),
orderType,
DefaultEbicsRootElement.generateName(orderType),
session.getUser());
response.build();
response.report(new EbicsRootElement[] {initializer, response});
state = new EbicsTransferState();
state.setNumSegments(initializer.getSegmentNumber());
state.setTransactionId(response.getTransactionId());
int segNumber = 1;
while (segNumber <= state.getNumSegments()) {
state.setSegmentNumber(segNumber);
if (segNumber == state.getSegmentNumber()) {
state.setLastSegment(true);
}
sendFile(
initializer.getContent(segNumber),
segNumber,
state.getLastSegment(),
state.getTransactionId(),
orderType);
segNumber++;
}
}
/**
* Sends a segment to the ebics bank server.
*
* @param factory the content factory that contain the segment data.
* @param segmentNumber the segment number
* @param lastSegment is it the last segment?
* @param transactionId the transaction Id
* @param orderType the order type
* @throws IOException
* @throws EbicsException
*/
public void sendFile(
ContentFactory factory,
int segmentNumber,
boolean lastSegment,
byte[] transactionId,
OrderType orderType)
throws IOException, AxelorException {
UTransferRequestElement uploader;
HttpRequestSender sender;
TransferResponseElement response;
int httpCode;
uploader =
new UTransferRequestElement(
session, orderType, segmentNumber, lastSegment, transactionId, factory);
sender = new HttpRequestSender(session);
uploader.build();
uploader.validate();
httpCode = sender.send(new ByteArrayContentFactory(uploader.prettyPrint()));
EbicsUtils.checkHttpCode(httpCode);
response =
new TransferResponseElement(
sender.getResponseBody(),
DefaultEbicsRootElement.generateName(orderType),
session.getUser());
response.build();
response.report(new EbicsRootElement[] {uploader, response});
}
/**
* Fetches a file of the given order type from the bank. You may give an optional start and end
* date. This type of transfer will run until everything is processed. No transaction recovery is
* possible.
*
* @param orderType type of file to fetch
* @param start optional begin of fetch term
* @param end optional end of fetch term
* @param dest where to put the data
* @throws IOException communication error
* @throws EbicsException server generated error
*/
public void fetchFile(OrderType orderType, Date start, Date end, OutputStream dest)
throws IOException, AxelorException {
HttpRequestSender sender;
DInitializationRequestElement initializer;
DInitializationResponseElement response;
ReceiptRequestElement receipt;
ReceiptResponseElement receiptResponse;
int httpCode;
EbicsTransferState state;
Joiner joiner;
sender = new HttpRequestSender(session);
initializer = new DInitializationRequestElement(session, orderType, start, end);
initializer.build();
initializer.validate();
httpCode = sender.send(new ByteArrayContentFactory(initializer.prettyPrint()));
EbicsUtils.checkHttpCode(httpCode);
response =
new DInitializationResponseElement(
sender.getResponseBody(),
orderType,
DefaultEbicsRootElement.generateName(orderType),
session.getUser());
response.build();
response.report(new EbicsRootElement[] {initializer, response});
state = new EbicsTransferState();
state.setSegmentNumber(response.getSegmentsNumber());
state.setTransactionId(response.getTransactionId());
state.setSegmentNumber(response.getSegmentNumber());
joiner = new Joiner(session.getUser());
joiner.append(response.getOrderData());
while (state.getSegmentNumber() <= state.getNumSegments()) {
if (state.getSegmentNumber() == state.getNumSegments()) {
state.setLastSegment(true);
}
fetchFile(
orderType,
state.getSegmentNumber(),
state.getLastSegment(),
state.getTransactionId(),
joiner);
state.setSegmentNumber(state.getSegmentNumber() + 1);
}
joiner.writeTo(dest, response.getTransactionKey());
receipt =
new ReceiptRequestElement(
session, state.getTransactionId(), DefaultEbicsRootElement.generateName(orderType));
receipt.build();
receipt.validate();
httpCode = sender.send(new ByteArrayContentFactory(receipt.prettyPrint()));
EbicsUtils.checkHttpCode(httpCode);
receiptResponse =
new ReceiptResponseElement(
sender.getResponseBody(),
DefaultEbicsRootElement.generateName(orderType),
session.getUser());
receiptResponse.build();
receiptResponse.report(new EbicsRootElement[] {receipt, receiptResponse});
}
/**
* Fetches a given portion of a file.
*
* @param orderType the order type
* @param segmentNumber the segment number
* @param lastSegment is it the last segment?
* @param transactionId the transaction ID
* @param joiner the portions joiner
* @throws IOException communication error
* @throws EbicsException server generated error
*/
public void fetchFile(
OrderType orderType,
int segmentNumber,
boolean lastSegment,
byte[] transactionId,
Joiner joiner)
throws IOException, AxelorException {
DTransferRequestElement downloader;
HttpRequestSender sender;
DTransferResponseElement response;
int httpCode;
sender = new HttpRequestSender(session);
downloader =
new DTransferRequestElement(session, orderType, segmentNumber, lastSegment, transactionId);
downloader.build();
downloader.validate();
httpCode = sender.send(new ByteArrayContentFactory(downloader.prettyPrint()));
EbicsUtils.checkHttpCode(httpCode);
response =
new DTransferResponseElement(
sender.getResponseBody(),
orderType,
DefaultEbicsRootElement.generateName(orderType),
session.getUser());
response.build();
response.report(new EbicsRootElement[] {downloader, response});
joiner.append(response.getOrderData());
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private EbicsSession session;
}

View File

@ -0,0 +1,262 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.app.AppSettings;
import com.axelor.apps.bankpayment.db.EbicsBank;
import com.axelor.apps.bankpayment.db.repo.EbicsCertificateRepository;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.apps.bankpayment.ebics.service.EbicsCertificateService;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLException;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple HTTP request sender and receiver. The send returns a HTTP code that should be analyzed
* before proceeding ebics request response parse.
*
* @author hachani
*/
public class HttpRequestSender {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Constructs a new <code>HttpRequestSender</code> with a given ebics session.
*
* @param session the ebics session
*/
public HttpRequestSender(EbicsSession session) {
this.session = session;
}
/**
* Sends the request contained in the <code>ContentFactory</code>. The <code>ContentFactory</code>
* will deliver the request as an <code>InputStream</code>.
*
* @param request the ebics request
* @return the HTTP return code
* @throws AxelorException
*/
public final int send(ContentFactory request) throws IOException, AxelorException {
EbicsBank bank = session.getUser().getEbicsPartner().getEbicsBank();
String url = bank.getUrl();
if (url == null || !url.startsWith("http://") && !url.startsWith("https://")) {
throw new AxelorException(
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.EBICS_INVALID_BANK_URL));
}
if (bank.getProtocolSelect().equals("ssl")) {
return sendSSL(request, bank);
} else {
return sendTLS(request, bank);
}
}
public final int sendSSL(ContentFactory request, EbicsBank bank)
throws AxelorException, IOException {
String url = bank.getUrl();
X509Certificate certificate =
EbicsCertificateService.getBankCertificate(bank, EbicsCertificateRepository.TYPE_SSL);
DefaultHttpClient client = getSecuredHttpClient(certificate, url);
String proxyConfiguration = AppSettings.get().get("http.proxy.host");
if (proxyConfiguration != null && !proxyConfiguration.equals("")) {
setProxy(client);
}
InputStream input = request.getContent();
int retCode = -1;
log.debug("Bank url: {}", url);
HttpPost post = new HttpPost(url);
ContentType type = ContentType.TEXT_XML;
HttpEntity entity = new InputStreamEntity(input, retCode, type);
post.setEntity(entity);
try {
HttpResponse responseHttp = client.execute(post);
retCode = responseHttp.getStatusLine().getStatusCode();
log.debug("Http reason phrase: {}", responseHttp.getStatusLine().getReasonPhrase());
response = new InputStreamContentFactory(responseHttp.getEntity().getContent());
} catch (IOException e) {
e.printStackTrace();
throw new AxelorException(
e.getCause(),
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get("Connection error: %s"),
e.getMessage());
}
return retCode;
}
public final int sendTLS(ContentFactory request, EbicsBank bank) throws IOException {
HttpClient httpClient;
PostMethod method;
RequestEntity requestEntity;
InputStream input;
int retCode;
httpClient = new HttpClient();
String proxyConfiguration = AppSettings.get().get("http.proxy.host");
if (proxyConfiguration != null && !proxyConfiguration.equals("")) {
setProxy(httpClient);
}
input = request.getContent();
method = new PostMethod(bank.getUrl());
method.getParams().setSoTimeout(30000);
requestEntity = new InputStreamRequestEntity(input);
method.setRequestEntity(requestEntity);
method.setRequestHeader("Content-type", "text/xml; charset=ISO-8859-1");
retCode = -1;
retCode = httpClient.executeMethod(method);
response = new InputStreamContentFactory(method.getResponseBodyAsStream());
return retCode;
}
private void setProxy(DefaultHttpClient client) {
String proxyHost;
int proxyPort;
proxyHost = AppSettings.get().get("http.proxy.host").trim();
proxyPort = Integer.parseInt(AppSettings.get().get("http.proxy.port").trim());
if (!AppSettings.get().get("http.proxy.user").equals("")) {
String user;
String pwd;
Credentials credentials;
AuthScope authscope;
user = AppSettings.get().get("http.proxy.user").trim();
pwd = AppSettings.get().get("http.proxy.password").trim();
credentials = new UsernamePasswordCredentials(user, pwd);
authscope = new AuthScope(proxyHost, proxyPort);
client.getCredentialsProvider().setCredentials(authscope, credentials);
}
}
private void setProxy(HttpClient httpClient) {
String proxyHost = AppSettings.get().get("http.proxy.host").trim();
Integer proxyPort = Integer.parseInt(AppSettings.get().get("http.proxy.port").trim());
HostConfiguration hostConfig = httpClient.getHostConfiguration();
hostConfig.setProxy(proxyHost, proxyPort);
if (!AppSettings.get().get("http.proxy.user").equals("")) {
String user;
String pwd;
org.apache.commons.httpclient.UsernamePasswordCredentials credentials;
org.apache.commons.httpclient.auth.AuthScope authscope;
user = AppSettings.get().get("http.proxy.user").trim();
pwd = AppSettings.get().get("http.proxy.password").trim();
credentials = new org.apache.commons.httpclient.UsernamePasswordCredentials(user, pwd);
authscope = new org.apache.commons.httpclient.auth.AuthScope(proxyHost, proxyPort);
httpClient.getState().setProxyCredentials(authscope, credentials);
}
}
private DefaultHttpClient getSecuredHttpClient(Certificate cert, String bankURL)
throws AxelorException {
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 30000);
HttpConnectionParams.setSoTimeout(httpParams, 30000);
DefaultHttpClient client = new DefaultHttpClient(httpParams);
try {
Scheme https = null;
if (cert != null) {
log.debug("SSL certificate exist");
URL url = new URL(bankURL);
log.debug("Url host: {}", url.getHost());
KeyStore keystore = KeyStore.getInstance("jks");
char[] password = "NoPassword".toCharArray();
keystore.load(null, password);
keystore.setCertificateEntry(url.getHost(), cert);
SSLSocketFactory factory = new SSLSocketFactory(keystore);
try {
factory.getHostnameVerifier().verify(url.getHost(), (X509Certificate) cert);
https = new Scheme("https", 443, new SSLSocketFactory(keystore));
} catch (SSLException e) {
log.debug("Error in ssl certifcate host name verification");
https = new Scheme("https", 443, SSLSocketFactory.getSocketFactory());
}
} else {
log.debug("SSL certificate not exist");
https = new Scheme("https", 443, SSLSocketFactory.getSocketFactory());
}
client.getConnectionManager().getSchemeRegistry().register(https);
} catch (Exception e) {
e.printStackTrace();
throw new AxelorException(
e.getCause(), TraceBackRepository.TYPE_TECHNICAL, I18n.get("Error adding certificate"));
}
return client;
}
/**
* Returns the content factory of the response body
*
* @return the content factory of the response.
*/
public ContentFactory getResponseBody() {
return response;
}
//////////////////////////////////////////////////////////////////
// DATA MEMBERS
//////////////////////////////////////////////////////////////////
private EbicsSession session;
private ContentFactory response;
}

View File

@ -0,0 +1,70 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
/*
* Copyright (c) 1990-2012 kopiLeft Development SARL, Bizerte, Tunisia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id$
*/
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import java.io.IOException;
import java.io.InputStream;
/**
* Input stream content factory that delivers the its content as an <code>InputStream</code> This
* object is serializable in a way to recover interrupted file transfers.
*
* @author hachani
*/
public class InputStreamContentFactory implements ContentFactory {
/**
* Creates a new <code>ContentFactory</code> from an input stream
*
* @param input the given input stream.
*/
public InputStreamContentFactory(InputStream input) {
this.input = input;
}
@Override
public InputStream getContent() throws IOException {
return input;
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private InputStream input;
}

View File

@ -0,0 +1,228 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.apps.bankpayment.ebics.certificate.KeyStoreManager;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.apps.bankpayment.ebics.io.ByteArrayContentFactory;
import com.axelor.apps.bankpayment.ebics.service.EbicsUserService;
import com.axelor.apps.bankpayment.ebics.xml.HIARequestElement;
import com.axelor.apps.bankpayment.ebics.xml.HPBRequestElement;
import com.axelor.apps.bankpayment.ebics.xml.HPBResponseOrderDataElement;
import com.axelor.apps.bankpayment.ebics.xml.INIRequestElement;
import com.axelor.apps.bankpayment.ebics.xml.KeyManagementResponseElement;
import com.axelor.apps.bankpayment.ebics.xml.SPRRequestElement;
import com.axelor.apps.bankpayment.ebics.xml.SPRResponseElement;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import org.jdom.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Everything that has to do with key handling. If you have a totally new account use <code>
* sendINI()</code> and <code>sendHIA()</code> to send you newly created keys to the bank. Then wait
* until the bank activated your keys. If you are migrating from FTAM. Just send HPB, your EBICS
* account should be usable without delay.
*
* @author Hachani
*/
public class KeyManagement {
/**
* Constructs a new <code>KeyManagement</code> instance with a given ebics session
*
* @param session the ebics session
*/
public KeyManagement(EbicsSession session) {
this.session = session;
}
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Sends the user's signature key (A005) to the bank. After successful operation the user is in
* state "initialized".
*
* @param orderId the order ID. Let it null to generate a random one.
* @throws EbicsException server generated error message
* @throws IOException communication error
* @throws AxelorException
* @throws JDOMException
*/
public void sendINI() throws IOException, AxelorException, JDOMException {
INIRequestElement request;
KeyManagementResponseElement response;
HttpRequestSender sender;
int httpCode;
sender = new HttpRequestSender(session);
log.debug("HttpRequestSender OK");
request = new INIRequestElement(session);
log.debug("INIRequestElement OK");
request.build();
log.debug("build OK");
request.validate();
log.debug("validate OK");
// session.getConfiguration().getTraceManager().trace(request);
httpCode = sender.send(new ByteArrayContentFactory(request.prettyPrint()));
log.debug("send OK");
EbicsUtils.checkHttpCode(httpCode);
log.debug("checkHttpCode OK");
response =
new KeyManagementResponseElement(
sender.getResponseBody(), "INIResponse", session.getUser());
log.debug("KeyManagementResponseElement OK");
response.build();
log.debug("build OK");
// session.getConfiguration().getTraceManager().trace(response);
response.report(new EbicsRootElement[] {request, response});
log.debug("report OK");
}
/**
* Sends the public part of the protocol keys to the bank.
*
* @param orderId the order ID. Let it null to generate a random one.
* @throws IOException communication error
* @throws JDOMException
* @throws EbicsException server generated error message
*/
public void sendHIA() throws IOException, AxelorException, JDOMException {
HIARequestElement request;
KeyManagementResponseElement response;
HttpRequestSender sender;
int httpCode;
sender = new HttpRequestSender(session);
request = new HIARequestElement(session);
request.build();
request.validate();
// session.getConfiguration().getTraceManager().trace(request);
httpCode = sender.send(new ByteArrayContentFactory(request.prettyPrint()));
EbicsUtils.checkHttpCode(httpCode);
response =
new KeyManagementResponseElement(
sender.getResponseBody(), "HIAResponse", session.getUser());
response.build();
// session.getConfiguration().getTraceManager().trace(response);
response.report(new EbicsRootElement[] {request, response});
}
/**
* Sends encryption and authentication keys to the bank. This order is only allowed for a new user
* at the bank side that has been created by copying the A005 key. The keys will be activated
* immediately after successful completion of the transfer.
*
* @param orderId the order ID. Let it null to generate a random one.
* @throws IOException communication error
* @throws GeneralSecurityException data decryption error
* @throws AxelorException
* @throws JDOMException
* @throws EbicsException server generated error message
*/
public X509Certificate[] sendHPB()
throws IOException, GeneralSecurityException, AxelorException, JDOMException {
HPBRequestElement request;
KeyManagementResponseElement response;
HttpRequestSender sender;
HPBResponseOrderDataElement orderData;
ContentFactory factory;
int httpCode;
sender = new HttpRequestSender(session);
request = new HPBRequestElement(session);
request.build();
request.validate();
httpCode = sender.send(new ByteArrayContentFactory(request.prettyPrint()));
EbicsUtils.checkHttpCode(httpCode);
response =
new KeyManagementResponseElement(
sender.getResponseBody(), "HBPResponse", session.getUser());
response.build();
response.report(new EbicsRootElement[] {request, response});
EbicsUserService userService = Beans.get(EbicsUserService.class);
factory =
new ByteArrayContentFactory(
EbicsUtils.unzip(
userService.decrypt(
session.getUser(), response.getOrderData(), response.getTransactionKey())));
orderData = new HPBResponseOrderDataElement(factory, session.getUser());
orderData.build();
return createCertificates(orderData);
}
private X509Certificate[] createCertificates(HPBResponseOrderDataElement orderData)
throws AxelorException, GeneralSecurityException, IOException {
KeyStoreManager keystoreManager = new KeyStoreManager();
if (session.getUser().getPassword() != null) {
keystoreManager.load("", session.getUser().getPassword().toCharArray());
} else {
keystoreManager.load("", null);
}
String certId = session.getBankID() + "-E002";
keystoreManager.setCertificateEntry(
certId, new ByteArrayInputStream(orderData.getBankE002Certificate()));
X509Certificate certificateE002 = keystoreManager.getCertificate(certId);
certId = session.getBankID() + "-X002";
keystoreManager.setCertificateEntry(
certId, new ByteArrayInputStream(orderData.getBankX002Certificate()));
X509Certificate certificateX002 = keystoreManager.getCertificate(certId);
return new X509Certificate[] {certificateE002, certificateX002};
}
/**
* Sends the SPR order to the bank. After that you have to start over with sending INI and HIA.
*
* @throws IOException Communication exception
* @throws AxelorException
* @throws JDOMException
* @throws EbicsException Error message generated by the bank.
*/
public void lockAccess() throws IOException, AxelorException, JDOMException {
HttpRequestSender sender;
SPRRequestElement request;
SPRResponseElement response;
int httpCode;
sender = new HttpRequestSender(session);
request = new SPRRequestElement(session);
request.build();
request.validate();
httpCode = sender.send(new ByteArrayContentFactory(request.prettyPrint()));
EbicsUtils.checkHttpCode(httpCode);
response = new SPRResponseElement(sender.getResponseBody(), session.getUser());
response.build();
response.report(new EbicsRootElement[] {request, response});
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private EbicsSession session;
}

View File

@ -0,0 +1,167 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
/**
* 12.3 Order attributes The following settings are permissible for the order attribute (5 bytes
* alphanumeric) in EBICS
*/
public class OrderAttribute {
/**
* Type of transmitted data O = order data and ESs U = bank-technical ESs D = order data and
* transport ES (D is also used for HIA, INI, HPB)
*/
private char transmittedDataType;
/** O = order data and ESs */
public static final char TRANSMITTED_DATA_TYPE_O = 'O';
/** U = bank-technical ESs */
public static final char TRANSMITTED_DATA_TYPE_U = 'U';
/** D = order data and transport ES (D is also used for HIA, INI, HPB) */
public static final char TRANSMITTED_DATA_TYPE_D = 'D';
/** Compression type for order data and/or ESs Z = ZIP compression */
private char compressionType;
/** Z = ZIP compression */
public static final char COMPRESSION_TYPE_Z = 'Z';
/** Encryption type for order data and/or ESs N = no encryption H = hybrid process AES/RSA */
private char encryptionType;
/** N = no encryption */
public static final char ENCRYPTION_TYPE_N = 'N';
/** H = hybrid process AES/RSA */
public static final char ENCRYPTION_TYPE_H = 'H';
public static final char RESERVED_POSITION = 'N';
private String orderAttributes;
public char computeTransmittedDataType(OrderType orderType, int ebicsTypeSelect) {
if (orderType.equals(OrderType.INI)) {
return TRANSMITTED_DATA_TYPE_D;
} else if (orderType.equals(OrderType.HIA)) {
return TRANSMITTED_DATA_TYPE_D;
} else if (orderType.equals(OrderType.HPB)) {
return TRANSMITTED_DATA_TYPE_D;
} else if (orderType.equals(OrderType.FUL)) {
if (ebicsTypeSelect == EbicsPartnerRepository.EBICS_TYPE_T) {
return TRANSMITTED_DATA_TYPE_D;
} else if (ebicsTypeSelect == EbicsPartnerRepository.EBICS_TYPE_TS) {
return TRANSMITTED_DATA_TYPE_O;
}
} else if (orderType.equals(OrderType.FDL)
|| orderType.equals(OrderType.HTD)
|| orderType.equals(OrderType.HPD)
|| orderType.equals(OrderType.PTK)) {
return TRANSMITTED_DATA_TYPE_D;
// "O" if the bank use financial institutions bank-technical ES
} else if (orderType.equals(OrderType.SPR)) {
return TRANSMITTED_DATA_TYPE_U;
}
throw new IllegalArgumentException("NOT SUPPORTED ORDER TYPE OR EBICS MODE");
}
public char computeCompressionType() {
return COMPRESSION_TYPE_Z;
}
public char computeEncryptionType(OrderType orderType) {
if (orderType.equals(OrderType.INI)) {
return ENCRYPTION_TYPE_N;
} else if (orderType.equals(OrderType.HIA)) {
return ENCRYPTION_TYPE_N;
} else if (orderType.equals(OrderType.HPB)) {
return ENCRYPTION_TYPE_H;
} else if (orderType.equals(OrderType.FUL)) {
return ENCRYPTION_TYPE_H;
} else if (orderType.equals(OrderType.FDL)
|| orderType.equals(OrderType.HTD)
|| orderType.equals(OrderType.HPD)
|| orderType.equals(OrderType.PTK)) {
return ENCRYPTION_TYPE_H;
} else if (orderType.equals(OrderType.SPR)) {
return ENCRYPTION_TYPE_H;
}
throw new IllegalArgumentException("NOT SUPPORTED ORDER TYPE OR EBICS MODE");
}
/**
* Depending on order type and possible further marginal conditions, an EBICS client MUST enter
* the following order attributes in the control data of the first EBICS request of an EBICS
* transaction (ebicsRequest/header/static/OrderDetails/OrderAttribute):
*
* <p>Order type | Marginal conditions | Order attributes
* --------------------------------------------------------------------------------------------
* INI   | - | DZNNN HIA | - | DZNNN HSA | - | OZNNN HPB | - | DZHNN PUB | - | OZHNN HCA | - |
* OZHNN HCS | - | OZHNN SPR | - | UZHNN HVE | - | UZHNN HVS | - | UZHNN other upload order types
* | order data and ES(s) | OZHNN other upload order types | only bank-technical ES(s), no order
* data | UZHNN other upload order types | order data with transport signature | DZHNN | (release
* of the order via accompanying | | note instead of bank-technical ES) | other download order
* types | download data request with financial | OZHNN | institutions banktechnical ES | other
* download order types | download data request without financial | DZHNN | institutions
* banktechnical ES |
*
* @param orderType
* @param ebicsTypeSelect
*/
public OrderAttribute(OrderType orderType, int ebicsTypeSelect) {
this.transmittedDataType = computeTransmittedDataType(orderType, ebicsTypeSelect);
this.compressionType = computeCompressionType();
this.encryptionType = computeEncryptionType(orderType);
}
public OrderAttribute(char transmittedDataType, char compressionType, char encryptionType) {
this.transmittedDataType = transmittedDataType;
this.compressionType = compressionType;
this.encryptionType = encryptionType;
}
public OrderAttribute build() {
orderAttributes =
new StringBuilder()
.append(transmittedDataType)
.append(compressionType)
.append(encryptionType)
.append(RESERVED_POSITION)
.append(RESERVED_POSITION)
.toString();
return this;
}
/** @return the orderType */
public String getOrderAttributes() {
return orderAttributes;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof OrderAttribute) {
return orderAttributes.equals(((OrderAttribute) obj).getOrderAttributes());
}
return false;
}
}

View File

@ -0,0 +1,128 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
/**
* A BCS order type.
*
* @author Hachani
*/
public class OrderType {
/**
* Constructs new Order type instance
*
* @param orderType the order type
*/
public OrderType(String orderType) {
this.orderType = orderType;
}
/** @return the orderType */
public String getOrderType() {
return orderType;
}
/** @param orderType the orderType to set */
public void setOrderType(String orderType) {
this.orderType = orderType;
}
/**
* Returns the corresponding <code>OrderType</code> to a given string order type.
*
* @param orderType the given order type.
* @return the corresponding <code>OrderType</code>
*/
public static OrderType toOrderType(String orderType) {
if (orderType.equals("INI")) {
return INI;
} else if (orderType.equals("HIA")) {
return HIA;
} else if (orderType.equals("HPB")) {
return HPB;
} else if (orderType.equals("FUL")) {
return FUL;
} else if (orderType.equals("FDL")) {
return FDL;
} else if (orderType.equals("HTD")) {
return HTD;
} else if (orderType.equals("HPD")) {
return HPD;
} else if (orderType.equals("PTK")) {
return PTK;
} else if (orderType.equals("SPR")) {
return SPR;
} else {
throw new IllegalArgumentException("NOT SUPPORTED ORDER TYPE");
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof OrderType) {
return orderType.equals(((OrderType) obj).getOrderType());
}
return false;
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private String orderType;
/** Send the users protocol keys. */
public static final OrderType HIA;
/** Fetch the banks protocol keys. */
public static final OrderType HPB;
/** Return bank parameters. */
public static final OrderType HPD;
/** Fetch user informations. */
public static final OrderType HTD;
/** Send the first signature key. */
public static final OrderType INI;
/** File upload */
public static final OrderType FUL;
/** File download */
public static final OrderType FDL;
/** Lock the channel. */
public static final OrderType SPR;
public static final OrderType PTK;
static {
HIA = new OrderType("HIA");
HPB = new OrderType("HPB");
HPD = new OrderType("HPD");
HTD = new OrderType("HTD");
INI = new OrderType("INI");
FUL = new OrderType("FUL");
FDL = new OrderType("FDL");
SPR = new OrderType("SPR");
PTK = new OrderType("PTK");
}
}

View File

@ -0,0 +1,110 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.client;
import com.axelor.apps.account.ebics.schema.h003.EbicsUnsecuredRequestDocument.EbicsUnsecuredRequest;
import com.axelor.apps.account.ebics.schema.h003.EbicsUnsecuredRequestDocument.EbicsUnsecuredRequest.Body;
import com.axelor.apps.account.ebics.schema.h003.EbicsUnsecuredRequestDocument.EbicsUnsecuredRequest.Body.DataTransfer;
import com.axelor.apps.account.ebics.schema.h003.EbicsUnsecuredRequestDocument.EbicsUnsecuredRequest.Body.DataTransfer.OrderData;
import com.axelor.apps.account.ebics.schema.h003.EbicsUnsecuredRequestDocument.EbicsUnsecuredRequest.Header;
import com.axelor.apps.account.ebics.schema.h003.EmptyMutableHeaderType;
import com.axelor.apps.account.ebics.schema.h003.OrderDetailsType;
import com.axelor.apps.account.ebics.schema.h003.ProductElementType;
import com.axelor.apps.account.ebics.schema.h003.UnsecuredRequestStaticHeaderType;
import com.axelor.apps.bankpayment.ebics.xml.DefaultEbicsRootElement;
import com.axelor.apps.bankpayment.ebics.xml.EbicsXmlFactory;
import com.axelor.auth.AuthUtils;
import com.axelor.exception.AxelorException;
/**
* The <code>UnsecuredRequestElement</code> is the common element used for key management requests.
*
* @author hachani
*/
public class UnsecuredRequestElement extends DefaultEbicsRootElement {
/**
* Constructs a Unsecured Request Element.
*
* @param session the ebics session.
* @param orderType the order type (INI | HIA).
* @param orderId the order id, if null a random one is generated.
*/
public UnsecuredRequestElement(EbicsSession session, OrderType orderType, byte[] orderData) {
super(session);
this.orderType = orderType;
this.orderData = orderData;
}
@Override
public void build() throws AxelorException {
Header header;
Body body;
EmptyMutableHeaderType mutable;
UnsecuredRequestStaticHeaderType xstatic;
ProductElementType productType;
OrderDetailsType orderDetails;
DataTransfer dataTransfer;
OrderData orderData;
EbicsUnsecuredRequest request;
orderDetails =
EbicsXmlFactory.createOrderDetailsType(
"DZNNN", session.getUser().getNextOrderId(), orderType.getOrderType());
productType =
EbicsXmlFactory.creatProductElementType(
AuthUtils.getUser().getLanguage(), session.getProduct().getName());
try {
xstatic =
EbicsXmlFactory.createUnsecuredRequestStaticHeaderType(
session.getBankID(),
session.getUser().getEbicsPartner().getPartnerId(),
session.getUser().getUserId(),
productType,
orderDetails,
session.getUser().getSecurityMedium());
mutable = EbicsXmlFactory.createEmptyMutableHeaderType();
header = EbicsXmlFactory.createHeader(true, mutable, xstatic);
orderData = EbicsXmlFactory.createOrderData(this.orderData);
dataTransfer = EbicsXmlFactory.createDataTransfer(orderData);
body = EbicsXmlFactory.createBody(dataTransfer);
request = EbicsXmlFactory.createEbicsUnsecuredRequest(header, body, 1, "H003");
document = EbicsXmlFactory.createEbicsUnsecuredRequestDocument(request);
} catch (AxelorException e) {
e.printStackTrace();
}
}
@Override
public String getName() {
return "UnsecuredRequest.xml";
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private OrderType orderType;
private byte[] orderData;
}

View File

@ -0,0 +1,287 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.exception;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
/**
* Representation of EBICS return codes. The return codes are described in chapter 13 of EBICS
* specification.
*
* @author hachani
*/
public class ReturnCode {
/**
* Constructs a new <code>ReturnCode</code> with a given standard code, symbolic name and text
*
* @param code the given standard code.
* @param symbolicName the symbolic name.
* @param the code text
*/
public ReturnCode(String code, String symbolicName, String text) {
this.code = code;
this.symbolicName = symbolicName;
this.text = text;
}
/**
* Throws an equivalent <code>EbicsException</code>
*
* @throws EbicsException
*/
public void throwException() throws AxelorException {
throw new AxelorException(TraceBackRepository.TYPE_FUNCTIONNAL, I18n.get(text));
}
/**
* Tells if the return code is an OK one.
*
* @return True if the return code is OK one.
*/
public boolean isOk() {
return equals(EBICS_OK);
}
/**
* Returns a slightly more human readable version of this return code.
*
* @return a slightly more human readable version of this return code.
*/
public String getSymbolicName() {
return symbolicName;
}
/**
* Returns a display text for the default locale.
*
* @return a text that can be displayed.
*/
public String getText() {
return text;
}
/**
* Returns the code.
*
* @return the code.
*/
public int getCode() {
return Integer.parseInt(code);
}
/**
* Returns the equivalent <code>ReturnCode</code> of a given code
*
* @param code the given code
* @param text the given code text
* @return the equivalent <code>ReturnCode</code>
*/
public static ReturnCode toReturnCode(String code, String text) {
if (code.equals(EBICS_OK.code)) {
return EBICS_OK;
} else if (code.equals(EBICS_DOWNLOAD_POSTPROCESS_DONE.code)) {
return EBICS_DOWNLOAD_POSTPROCESS_DONE;
} else if (code.equals(EBICS_DOWNLOAD_POSTPROCESS_SKIPPED.code)) {
return EBICS_DOWNLOAD_POSTPROCESS_SKIPPED;
} else if (code.equals(EBICS_TX_SEGMENT_NUMBER_UNDERRUN.code)) {
return EBICS_DOWNLOAD_POSTPROCESS_SKIPPED;
} else if (code.equals(EBICS_AUTHENTICATION_FAILED.code)) {
return EBICS_AUTHENTICATION_FAILED;
} else if (code.equals(EBICS_INVALID_REQUEST.code)) {
return EBICS_INVALID_REQUEST;
} else if (code.equals(EBICS_INTERNAL_ERROR.code)) {
return EBICS_INTERNAL_ERROR;
} else if (code.equals(EBICS_TX_RECOVERY_SYNC.code)) {
return EBICS_TX_RECOVERY_SYNC;
} else if (code.equals(EBICS_INVALID_USER_OR_USER_STATE.code)) {
return EBICS_INVALID_USER_OR_USER_STATE;
} else if (code.equals(EBICS_USER_UNKNOWN.code)) {
return EBICS_USER_UNKNOWN;
} else if (code.equals(EBICS_INVALID_USER_STATE.code)) {
return EBICS_INVALID_USER_STATE;
} else if (code.equals(EBICS_INVALID_ORDER_TYPE.code)) {
return EBICS_INVALID_ORDER_TYPE;
} else if (code.equals(EBICS_UNSUPPORTED_ORDER_TYPE.code)) {
return EBICS_UNSUPPORTED_ORDER_TYPE;
} else if (code.equals(EBICS_USER_AUTHENTICATION_REQUIRED.code)) {
return EBICS_USER_AUTHENTICATION_REQUIRED;
} else if (code.equals(EBICS_BANK_PUBKEY_UPDATE_REQUIRED.code)) {
return EBICS_BANK_PUBKEY_UPDATE_REQUIRED;
} else if (code.equals(EBICS_SEGMENT_SIZE_EXCEEDED.code)) {
return EBICS_SEGMENT_SIZE_EXCEEDED;
} else if (code.equals(EBICS_TX_UNKNOWN_TXID.code)) {
return EBICS_TX_UNKNOWN_TXID;
} else if (code.equals(EBICS_TX_ABORT.code)) {
return EBICS_TX_ABORT;
} else if (code.equals(EBICS_TX_MESSAGE_REPLAY.code)) {
return EBICS_TX_MESSAGE_REPLAY;
} else if (code.equals(EBICS_TX_SEGMENT_NUMBER_EXCEEDED.code)) {
return EBICS_TX_SEGMENT_NUMBER_EXCEEDED;
} else if (code.equals(EBICS_X509_CERTIFICATE_NOT_VALID_YET.code)) {
return EBICS_X509_CERTIFICATE_NOT_VALID_YET;
} else if (code.equals(EBICS_MAX_TRANSACTIONS_EXCEEDED.code)) {
return EBICS_MAX_TRANSACTIONS_EXCEEDED;
} else if (code.equals(EBICS_SIGNATURE_VERIFICATION_FAILED.code)) {
return EBICS_SIGNATURE_VERIFICATION_FAILED;
} else if (code.equals(EBICS_NO_DOWNLOAD_DATA_AVAILABLE.code)) {
return EBICS_NO_DOWNLOAD_DATA_AVAILABLE;
} else if (code.equals(EBICS_ORDER_PARAMS_IGNORED.code)) {
return EBICS_ORDER_PARAMS_IGNORED;
} else if (code.equals(EBICS_INVALID_XML.code)) {
return EBICS_INVALID_XML;
} else if (code.equals(EBICS_INVALID_HOST_ID.code)) {
return EBICS_INVALID_HOST_ID;
} else if (code.equals(EBICS_INVALID_ORDER_PARAMS.code)) {
return EBICS_INVALID_ORDER_PARAMS;
} else if (code.equals(EBICS_INVALID_REQUEST_CONTENT.code)) {
return EBICS_INVALID_REQUEST_CONTENT;
} else if (code.equals(EBICS_MAX_ORDER_DATA_SIZE_EXCEEDED.code)) {
return EBICS_MAX_ORDER_DATA_SIZE_EXCEEDED;
} else if (code.equals(EBICS_MAX_SEGMENTS_EXCEEDED.code)) {
return EBICS_MAX_SEGMENTS_EXCEEDED;
} else if (code.equals(EBICS_PARTNER_ID_MISMATCH.code)) {
return EBICS_PARTNER_ID_MISMATCH;
} else if (code.equals(EBICS_INCOMPATIBLE_ORDER_ATTRIBUTE.code)) {
return EBICS_INCOMPATIBLE_ORDER_ATTRIBUTE;
} else {
return new ReturnCode(code, text, text);
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ReturnCode) {
return this.code.equals(((ReturnCode) obj).code);
}
return false;
}
@Override
public int hashCode() {
return Integer.parseInt(code);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private String code;
private String symbolicName;
private String text;
public static final ReturnCode EBICS_OK;
public static final ReturnCode EBICS_DOWNLOAD_POSTPROCESS_DONE;
public static final ReturnCode EBICS_DOWNLOAD_POSTPROCESS_SKIPPED;
public static final ReturnCode EBICS_TX_SEGMENT_NUMBER_UNDERRUN;
public static final ReturnCode EBICS_AUTHENTICATION_FAILED;
public static final ReturnCode EBICS_INVALID_REQUEST;
public static final ReturnCode EBICS_INTERNAL_ERROR;
public static final ReturnCode EBICS_TX_RECOVERY_SYNC;
public static final ReturnCode EBICS_INVALID_USER_OR_USER_STATE;
public static final ReturnCode EBICS_USER_UNKNOWN;
public static final ReturnCode EBICS_INVALID_USER_STATE;
public static final ReturnCode EBICS_INVALID_ORDER_TYPE;
public static final ReturnCode EBICS_UNSUPPORTED_ORDER_TYPE;
public static final ReturnCode EBICS_USER_AUTHENTICATION_REQUIRED;
public static final ReturnCode EBICS_BANK_PUBKEY_UPDATE_REQUIRED;
public static final ReturnCode EBICS_SEGMENT_SIZE_EXCEEDED;
public static final ReturnCode EBICS_TX_UNKNOWN_TXID;
public static final ReturnCode EBICS_TX_ABORT;
public static final ReturnCode EBICS_TX_MESSAGE_REPLAY;
public static final ReturnCode EBICS_TX_SEGMENT_NUMBER_EXCEEDED;
public static final ReturnCode EBICS_X509_CERTIFICATE_NOT_VALID_YET;
public static final ReturnCode EBICS_MAX_TRANSACTIONS_EXCEEDED;
public static final ReturnCode EBICS_SIGNATURE_VERIFICATION_FAILED;
public static final ReturnCode EBICS_NO_DOWNLOAD_DATA_AVAILABLE;
public static final ReturnCode EBICS_ORDER_PARAMS_IGNORED;
public static final ReturnCode EBICS_INVALID_XML;
public static final ReturnCode EBICS_INVALID_HOST_ID;
public static final ReturnCode EBICS_INVALID_ORDER_PARAMS;
public static final ReturnCode EBICS_INVALID_REQUEST_CONTENT;
public static final ReturnCode EBICS_MAX_ORDER_DATA_SIZE_EXCEEDED;
public static final ReturnCode EBICS_MAX_SEGMENTS_EXCEEDED;
public static final ReturnCode EBICS_PARTNER_ID_MISMATCH;
public static final ReturnCode EBICS_INCOMPATIBLE_ORDER_ATTRIBUTE;
static {
EBICS_OK = new ReturnCode("000000", "EBICS_OK", /*$$(*/ "000000" /*)*/);
EBICS_DOWNLOAD_POSTPROCESS_DONE =
new ReturnCode("011000", "EBICS_DOWNLOAD_POSTPROCESS_DONE", /*$$(*/ "011000" /*)*/);
EBICS_DOWNLOAD_POSTPROCESS_SKIPPED =
new ReturnCode("011001", "EBICS_DOWNLOAD_POSTPROCESS_SKIPPED", /*$$(*/ "011001" /*)*/);
EBICS_TX_SEGMENT_NUMBER_UNDERRUN =
new ReturnCode("011101", "EBICS_TX_SEGMENT_NUMBER_UNDERRUN", /*$$(*/ "011101" /*)*/);
EBICS_ORDER_PARAMS_IGNORED =
new ReturnCode("031001", "EBICS_ORDER_PARAMS_IGNORED", /*$$(*/ "031001" /*)*/);
EBICS_AUTHENTICATION_FAILED =
new ReturnCode("061001", "EBICS_AUTHENTICATION_FAILED", /*$$(*/ "061001" /*)*/);
EBICS_INVALID_REQUEST =
new ReturnCode("061002", "EBICS_INVALID_REQUEST", /*$$(*/ "061002" /*)*/);
EBICS_INTERNAL_ERROR = new ReturnCode("061099", "EBICS_INTERNAL_ERROR", /*$$(*/ "061099" /*)*/);
EBICS_TX_RECOVERY_SYNC =
new ReturnCode("061101", "EBICS_TX_RECOVERY_SYNC", /*$$(*/ "061101" /*)*/);
EBICS_NO_DOWNLOAD_DATA_AVAILABLE =
new ReturnCode("090005", "EBICS_NO_DOWNLOAD_DATA_AVAILABLE", /*$$(*/ "090005" /*)*/);
EBICS_INVALID_USER_OR_USER_STATE =
new ReturnCode("091002", "EBICS_INVALID_USER_OR_USER_STATE", /*$$(*/ "091002" /*)*/);
EBICS_USER_UNKNOWN = new ReturnCode("091003", "EBICS_USER_UNKNOWN", /*$$(*/ "091003" /*)*/);
EBICS_INVALID_USER_STATE =
new ReturnCode("091004", "EBICS_INVALID_USER_STATE", /*$$(*/ "091004" /*)*/);
EBICS_INVALID_ORDER_TYPE =
new ReturnCode("091005", "EBICS_INVALID_ORDER_TYPE", /*$$(*/ "091005" /*)*/);
EBICS_UNSUPPORTED_ORDER_TYPE =
new ReturnCode("091006", "EBICS_UNSUPPORTED_ORDER_TYPE", /*$$(*/ "091006" /*)*/);
EBICS_USER_AUTHENTICATION_REQUIRED =
new ReturnCode("091007", "EBICS_USER_AUTHENTICATION_REQUIRED", /*$$(*/ "091007" /*)*/);
EBICS_BANK_PUBKEY_UPDATE_REQUIRED =
new ReturnCode("091008", "EBICS_BANK_PUBKEY_UPDATE_REQUIRED", /*$$(*/ "091008" /*)*/);
EBICS_SEGMENT_SIZE_EXCEEDED =
new ReturnCode("091009", "EBICS_SEGMENT_SIZE_EXCEEDED", /*$$(*/ "091009" /*)*/);
EBICS_INVALID_XML = new ReturnCode("091010", "EBICS_INVALID_XML", /*$$(*/ "091010" /*)*/);
EBICS_INVALID_HOST_ID =
new ReturnCode("091011", "EBICS_INVALID_HOST_ID", /*$$(*/ "091011" /*)*/);
EBICS_TX_UNKNOWN_TXID =
new ReturnCode("091101", "EBICS_TX_UNKNOWN_TXID", /*$$(*/ "091101" /*)*/);
EBICS_TX_ABORT = new ReturnCode("091102", "EBICS_TX_ABORT", /*$$(*/ "091102" /*)*/);
EBICS_TX_MESSAGE_REPLAY =
new ReturnCode("091103", "EBICS_TX_MESSAGE_REPLAY", /*$$(*/ "091103" /*)*/);
EBICS_TX_SEGMENT_NUMBER_EXCEEDED =
new ReturnCode("091104", "EBICS_TX_SEGMENT_NUMBER_EXCEEDED", /*$$(*/ "091104" /*)*/);
EBICS_INVALID_ORDER_PARAMS =
new ReturnCode("091112", "EBICS_INVALID_ORDER_PARAMS", /*$$(*/ "091112" /*)*/);
EBICS_INVALID_REQUEST_CONTENT =
new ReturnCode("091113", "EBICS_INVALID_REQUEST_CONTENT", /*$$(*/ "091113" /*)*/);
EBICS_MAX_ORDER_DATA_SIZE_EXCEEDED =
new ReturnCode("091117", "EBICS_MAX_ORDER_DATA_SIZE_EXCEEDED", /*$$(*/ "091117" /*)*/);
EBICS_MAX_SEGMENTS_EXCEEDED =
new ReturnCode("091118", "EBICS_MAX_SEGMENTS_EXCEEDED", /*$$(*/ "091118" /*)*/);
EBICS_MAX_TRANSACTIONS_EXCEEDED =
new ReturnCode("091119", "EBICS_MAX_TRANSACTIONS_EXCEEDED", /*$$(*/ "091119" /*)*/);
EBICS_PARTNER_ID_MISMATCH =
new ReturnCode("091120", "EBICS_PARTNER_ID_MISMATCH", /*$$(*/ "091120" /*)*/);
EBICS_INCOMPATIBLE_ORDER_ATTRIBUTE =
new ReturnCode("091121", "EBICS_INCOMPATIBLE_ORDER_ATTRIBUTE", /*$$(*/ "091121" /*)*/);
EBICS_X509_CERTIFICATE_NOT_VALID_YET =
new ReturnCode("091209", "EBICS_X509_CERTIFICATE_NOT_VALID_YET", /*$$(*/ "091209" /*)*/);
EBICS_SIGNATURE_VERIFICATION_FAILED =
new ReturnCode("091301", "EBICS_SIGNATURE_VERIFICATION_FAILED", /*$$(*/ "091301" /*)*/);
}
}

View File

@ -0,0 +1,53 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.interfaces;
/*
* Copyright (c) 1990-2012 kopiLeft Development SARL, Bizerte, Tunisia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id$
*/
import java.io.IOException;
import java.io.InputStream;
public interface ContentFactory {
/**
* Returns a new data source of the data to be sent. The instance must ensure that the returned
* stream will deliver the identical data during the lifetime of this instance. Nevertheless how
* often the method will be called.
*
* @return a new data source of the data to be sent.
* @throws IOException
*/
public InputStream getContent() throws IOException;
}

View File

@ -0,0 +1,52 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.io;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Byte array content factory that delivers the file content as a <code>ByteArrayInputStream</code>.
* This object is serializable in a way to recover interrupted file transfers.
*
* @author hachani
*/
public class ByteArrayContentFactory implements ContentFactory {
/**
* Constructs a new <code>ByteArrayContentFactory</code> with a given byte array content.
*
* @param content the byte array content
*/
public ByteArrayContentFactory(byte[] content) {
this.content = content;
}
@Override
public InputStream getContent() throws IOException {
return new ByteArrayInputStream(content);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private byte[] content;
}

View File

@ -0,0 +1,99 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.io;
import java.io.File;
import java.util.Hashtable;
import java.util.Map;
/**
* A simple mean to cache created files.
*
* @author hachani
*/
public class FileCache {
/**
* Constructs a new <code>FileCache</code> object
*
* @param isTraceEnabled is trace enabled?
*/
public FileCache(boolean isTraceEnabled) {
this.isTraceEnabled = isTraceEnabled;
cache = new Hashtable<String, File>();
}
/**
* Cache a new <code>java.io.File</code> in the cache buffer
*
* @param file the file to cache
* @return True if the file is cached
*/
public boolean add(File file) {
if (cache.containsKey(file.getName())) {
return false;
}
cache.put(file.getName(), file);
return true;
}
/**
* Removes the given <code>java.io.file</code> from the cache.
*
* @param filename the file to remove
* @return True if the file is removed
*/
public boolean remove(String filename) {
if (!cache.containsKey(filename)) {
return false;
}
cache.remove(filename);
return true;
}
/** Clears the cache buffer */
public void clear() {
if (isTraceEnabled) {
for (File file : cache.values()) {
file.delete();
}
}
cache.clear();
}
/**
* Sets the trace ability.
*
* @param enabled is trace enabled?
*/
public void setTraceEnabled(boolean enabled) {
this.isTraceEnabled = enabled;
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private Map<String, File> cache;
private boolean isTraceEnabled;
}

View File

@ -0,0 +1,194 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.io;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Some IO utilities for EBICS files management. EBICS server
*
* @author hachani
*/
public class IOUtils {
/**
* Creates a directory from a root one
*
* @param parent the parent directory
* @param child the directory name
* @return The created directory
*/
public static File createDirectory(File parent, String child) {
File directory;
directory = new File(parent, child);
directory.mkdir();
return directory;
}
/**
* Creates a directory from a root one
*
* @param parent the parent directory
* @param child the directory name
* @return The created directory
*/
public static File createDirectory(String parent, String child) {
File directory;
directory = new File(parent, child);
directory.mkdir();
return directory;
}
/**
* Creates a directory from a directory name
*
* @param name the absolute directory name
* @return The created directory
*/
public static File createDirectory(String name) {
File directory;
directory = new File(name);
directory.mkdir();
return directory;
}
/**
* Creates many directories from a given full path. Path should use default separator like '/' for
* UNIX systems
*
* @param fullName the full absolute path of the directories
* @return The created directory
*/
public static File createDirectories(String fullName) {
File directory;
directory = new File(fullName);
directory.mkdirs();
return directory;
}
/**
* Creates a new <code>java.io.File</code> from a given root.
*
* @param parent the parent of the file.
* @param name the file name.
* @return the created file.
*/
public static File createFile(String parent, String name) {
File file;
file = new File(parent, name);
return file;
}
/**
* Creates a new <code>java.io.File</code> from a given root.
*
* @param parent the parent of the file.
* @param name the file name.
* @return the created file.
*/
public static File createFile(File parent, String name) {
File file;
file = new File(parent, name);
return file;
}
/**
* Creates a file from its name. The name can be absolute if only the directory tree is created
*
* @param name the file name
* @return the created file
*/
public static File createFile(String name) {
File file;
file = new File(name);
return file;
}
/**
* Returns the content of a file as byte array.
*
* @param path the file path
* @return the byte array content of the file
* @throws EbicsException
*/
public static byte[] getFileContent(String path) throws AxelorException {
try {
InputStream input;
byte[] content;
input = new FileInputStream(path);
content = new byte[input.available()];
input.read(content);
input.close();
return content;
} catch (IOException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Returns the content of a <code>ContentFactory</code> as a byte array
*
* @param content
* @return
* @throws EbicsException
*/
public static byte[] getFactoryContent(ContentFactory content) throws AxelorException {
try {
byte[] buffer;
ByteArrayOutputStream out;
InputStream in;
int len = -1;
out = new ByteArrayOutputStream();
in = content.getContent();
buffer = new byte[1024];
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
in.close();
out.close();
return out.toByteArray();
} catch (IOException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
}

View File

@ -0,0 +1,87 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.io;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.service.EbicsUserService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.inject.Beans;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
/**
* A simple mean to join downloaded segments from the bank ebics server.
*
* @author Hachani
*/
public class Joiner {
/**
* Constructs a new <code>Joiner</code> object.
*
* @param user the ebics user.
*/
public Joiner(EbicsUser user) {
this.user = user;
buffer = new ByteArrayOutputStream();
}
public void append(byte[] data) throws AxelorException {
try {
buffer.write(data);
buffer.flush();
} catch (IOException e) {
throw new AxelorException(e.getCause(), TraceBackRepository.TYPE_TECHNICAL, e.getMessage());
}
}
/**
* Writes the joined part to an output stream.
*
* @param output the output stream.
* @param transactionKey the transaction key
* @throws EbicsException
*/
public void writeTo(OutputStream output, byte[] transactionKey) throws AxelorException {
try {
byte[] decrypted;
buffer.close();
decrypted =
Beans.get(EbicsUserService.class).decrypt(user, buffer.toByteArray(), transactionKey);
output.write(EbicsUtils.unzip(decrypted));
output.close();
} catch (GeneralSecurityException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_INCONSISTENCY, e.getMessage());
} catch (IOException e) {
throw new AxelorException(e.getCause(), TraceBackRepository.TYPE_TECHNICAL, e.getMessage());
}
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private EbicsUser user;
private ByteArrayOutputStream buffer;
}

View File

@ -0,0 +1,147 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.io;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import javax.crypto.spec.SecretKeySpec;
/**
* A mean to split a given input file to 1MB portions. this i useful to handle big file uploading.
*
* @author Hachani
*/
public class Splitter {
/**
* Constructs a new <code>FileSplitter</code> with a given file.
*
* @param input the input byte array
*/
public Splitter(byte[] input) {
this.input = input;
}
/**
* Reads the input stream and splits it to segments of 1MB size.
*
* <p>EBICS Specification 2.4.2 - 7 Segmentation of the order data:
*
* <p>The following procedure is to be followed with segmentation:
*
* <ol>
* <li>The order data is ZIP compressed
* <li>The compressed order data is encrypted in accordance with Chapter 6.2
* <li>The compressed, encrypted order data is base64-coded.
* <li>The result is to be verified with regard to the data volume:
* <ol>
* <li>If the resulting data volume is below the threshold of 1 MB = 1,048,576 bytes, the
* order data can be sent complete as a data segment within one transmission step
* <li>If the resulting data volume exceeds 1,048,576 bytes the data is to be separated
* sequentially and in a base64-conformant manner into segments that each have a
* maximum of 1,048,576 bytes.
* </ol>
*
* @param isCompressionEnabled enable compression?
* @param keySpec the secret key spec
* @throws EbicsException
*/
public final void readInput(boolean isCompressionEnabled, SecretKeySpec keySpec)
throws AxelorException {
try {
if (isCompressionEnabled) {
input = EbicsUtils.zip(input);
}
content = EbicsUtils.encrypt(input, keySpec);
segmentation();
} catch (Exception e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Slits the input into 1MB portions.
*
* <p>EBICS Specification 2.4.2 - 7 Segmentation of the order data:
*
* <p>In Version H003 of the EBICS standard, order data that requires more than 1 MB of storage
* space in compressed, encrypted and base64-coded form MUST be segmented before transmission,
* irrespective of the transfer direction (upload/download).
*/
private void segmentation() {
numSegments = content.length / 1048576; // (1024 * 1024)
if (content.length % 1048576 != 0) {
numSegments++;
}
segmentSize = content.length / numSegments;
}
/**
* Returns the content of a data segment according to a given segment number.
*
* @param segmentNumber the segment number
* @return
*/
public ContentFactory getContent(int segmentNumber) {
byte[] segment;
int offset;
offset = segmentSize * (segmentNumber - 1);
if (content.length < segmentSize + offset) {
segment = new byte[content.length - offset];
} else {
segment = new byte[segmentSize];
}
System.arraycopy(content, offset, segment, 0, segment.length);
return new ByteArrayContentFactory(segment);
}
/**
* Returns the hole content.
*
* @return the input content.
*/
public byte[] getContent() {
return content;
}
/**
* Returns the total segment number.
*
* @return the total segment number.
*/
public int getSegmentNumber() {
return numSegments;
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private byte[] input;
private byte[] content;
private int segmentSize;
private int numSegments;
}

View File

@ -0,0 +1,85 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.script;
import com.axelor.apps.bankpayment.db.EbicsBank;
import com.axelor.apps.bankpayment.db.EbicsCertificate;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.service.EbicsCertificateService;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.service.BankService;
import com.google.inject.Inject;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
public class EbicsUserImport {
@Inject private EbicsCertificateService certificateService;
@Inject private BankService bankService;
public Object importEbicsUser(Object bean, Map<String, Object> context) {
assert bean instanceof EbicsUser;
EbicsUser user = (EbicsUser) bean;
updateCertificate(user.getA005Certificate());
updateCertificate(user.getE002Certificate());
updateCertificate(user.getX002Certificate());
EbicsPartner partner = user.getEbicsPartner();
if (partner != null) {
EbicsBank ebicsBank = partner.getEbicsBank();
if (ebicsBank.getVersion() == 0) {
for (EbicsCertificate cert : ebicsBank.getEbicsCertificateList()) {
updateCertificate(cert);
}
Bank bank = ebicsBank.getBank();
if (bank.getVersion() == 0) {
bankService.computeFullName(bank);
bankService.splitBic(bank);
}
}
}
return user;
}
private void updateCertificate(EbicsCertificate cert) {
if (cert == null) {
return;
}
String pem = cert.getPemString();
if (pem == null) {
return;
}
try {
X509Certificate certificate = certificateService.convertToCertificate(pem);
certificateService.updateCertificate(certificate, cert, false);
} catch (IOException | CertificateException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.service;
import com.axelor.apps.bankpayment.db.EbicsBank;
public interface EbicsBankService {
public void computeFullName(EbicsBank ebicsBank);
}

View File

@ -0,0 +1,29 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.service;
import com.axelor.apps.bankpayment.db.EbicsBank;
public class EbicsBankServiceImpl implements EbicsBankService {
@Override
public void computeFullName(EbicsBank ebicsBank) {
ebicsBank.setFullName(ebicsBank.getBank().getFullName());
ebicsBank.setName(ebicsBank.getBank().getBankName());
}
}

View File

@ -0,0 +1,348 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.service;
import com.axelor.apps.bankpayment.db.EbicsBank;
import com.axelor.apps.bankpayment.db.EbicsCertificate;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.EbicsCertificateRepository;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.tool.date.DateTool;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaStore;
import com.axelor.meta.schema.views.Selection.Option;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.invoke.MethodHandles;
import java.net.URL;
import java.security.NoSuchProviderException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PEMWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EbicsCertificateService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Inject private EbicsCertificateRepository certRepo;
@Inject private AppBaseService appBaseService;
public static byte[] getCertificateContent(EbicsBank bank, String type) throws AxelorException {
EbicsCertificate cert = getEbicsCertificate(bank, type);
if (cert != null) {
return cert.getCertificate();
}
if (bank.getUrl() != null && type.equals(EbicsCertificateRepository.TYPE_SSL)) {
return Beans.get(EbicsCertificateService.class).getSSLCertificate(bank);
}
throw new AxelorException(
bank,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get("No bank certificate of type %s found"),
type);
}
public static X509Certificate getCertificate(byte[] certificate, String type)
throws AxelorException {
ByteArrayInputStream instream = new ByteArrayInputStream(certificate);
X509Certificate cert;
try {
cert =
(X509Certificate)
CertificateFactory.getInstance("X.509", "BC").generateCertificate(instream);
} catch (CertificateException | NoSuchProviderException e) {
throw new AxelorException(
e.getCause(),
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get("Error in bank certificate of type %s"),
type);
}
return cert;
}
public static X509Certificate getBankCertificate(EbicsBank bank, String type)
throws AxelorException {
byte[] certificate = getCertificateContent(bank, type);
if (certificate == null) {
return null;
}
return getCertificate(certificate, type);
}
private byte[] getSSLCertificate(EbicsBank bank) throws AxelorException {
try {
final URL bankUrl = new URL(bank.getUrl());
log.debug("Bank url protocol: {}", bankUrl.getProtocol());
log.debug("Bank url host: {}", bankUrl.getHost());
log.debug("Bank url port: {}", bankUrl.getPort());
String urlStr = bankUrl.getProtocol() + "://" + bankUrl.getHost();
if (bankUrl.getPort() > -1) {
urlStr += ":" + bankUrl.getPort();
}
final URL url = new URL(urlStr);
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(
null,
new TrustManager[] {
new X509TrustManager() {
private X509Certificate[] accepted;
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
accepted = arg0;
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return accepted;
}
}
},
null);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
HttpsURLConnection.setDefaultHostnameVerifier(
new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
connection.setSSLSocketFactory(sslCtx.getSocketFactory());
log.debug("SSL connection response code: {}", connection.getResponseCode());
log.debug("SSL connection response message: {}", connection.getResponseMessage());
if (connection.getResponseCode() == 200) {
Certificate[] certificates = connection.getServerCertificates();
for (int i = 0; i < certificates.length; i++) {
Certificate certificate = certificates[i];
if (certificate instanceof X509Certificate) {
X509Certificate cert = (X509Certificate) certificate;
createCertificate(cert, bank, EbicsCertificateRepository.TYPE_SSL);
return certificate.getEncoded();
}
}
}
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return null;
// throw new AxelorException(I18n.get("Error in getting ssl certificate"),
// TraceBackRepository.CATEGORY_CONFIGURATION_ERROR);
}
public EbicsCertificate updateCertificate(
X509Certificate certificate, EbicsCertificate cert, boolean cleanPrivateKey)
throws CertificateEncodingException, IOException {
String sha = DigestUtils.sha256Hex(certificate.getEncoded());
log.debug("sha256 HEX : {}", sha);
log.debug("certificat : {}", new String(certificate.getEncoded()));
log.debug("certificat size : {}", certificate.getEncoded().length);
cert.setValidFrom(DateTool.toLocalDate(certificate.getNotBefore()));
cert.setValidTo(DateTool.toLocalDate(certificate.getNotAfter()));
cert.setIssuer(certificate.getIssuerDN().getName());
cert.setSubject(certificate.getSubjectDN().getName());
cert.setCertificate(certificate.getEncoded());
RSAPublicKey publicKey = (RSAPublicKey) certificate.getPublicKey();
cert.setPublicKeyExponent(publicKey.getPublicExponent().toString(16));
cert.setPublicKeyModulus(publicKey.getModulus().toString(16));
cert.setSerial(certificate.getSerialNumber().toString(16));
cert.setPemString(convertToPEMString(certificate));
if (cleanPrivateKey) {
cert.setPrivateKey(null);
}
sha = sha.toUpperCase();
cert.setSha2has(sha);
computeFullName(cert);
return cert;
}
@Transactional
public EbicsCertificate createCertificate(
X509Certificate certificate, EbicsBank bank, String type)
throws CertificateEncodingException, IOException {
EbicsCertificate cert = getEbicsCertificate(bank, type);
if (cert == null) {
log.debug("Creating bank certicate for bank: {}, type: {}", bank.getName(), type);
cert = new EbicsCertificate();
cert.setEbicsBank(bank);
cert.setTypeSelect(type);
}
cert = updateCertificate(certificate, cert, true);
return certRepo.save(cert);
}
private static EbicsCertificate getEbicsCertificate(EbicsBank bank, String type) {
if (bank == null) {
return null;
}
for (EbicsCertificate cert : bank.getEbicsCertificateList()) {
if (cert.getTypeSelect().equals(type)) {
return cert;
}
}
return null;
}
public void computeFullName(EbicsCertificate entity) {
StringBuilder fullName = new StringBuilder();
Option item =
MetaStore.getSelectionItem(
"bankpayment.ebics.certificate.type.select", entity.getTypeSelect());
if (item != null) {
fullName.append(I18n.get(item.getTitle()));
}
LocalDate date = entity.getValidFrom();
if (date != null) {
fullName.append(":" + date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
date = entity.getValidTo();
if (date != null) {
fullName.append("-" + date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
}
}
String issuer = entity.getIssuer();
if (issuer != null) {
fullName.append(":" + issuer);
}
entity.setFullName(fullName.toString());
}
public String convertToPEMString(X509Certificate x509Cert) throws IOException {
StringWriter sw = new StringWriter();
try (PEMWriter pw = new PEMWriter(sw)) {
pw.writeObject(x509Cert);
}
return sw.toString();
}
public X509Certificate convertToCertificate(String pemString)
throws IOException, CertificateException {
X509Certificate certificate;
StringReader reader = new StringReader(pemString);
try (final PEMParser pr = new PEMParser(reader)) {
final X509CertificateHolder certificateHolder = (X509CertificateHolder) pr.readObject();
certificate = new JcaX509CertificateConverter().getCertificate(certificateHolder);
}
return certificate;
}
public byte[] convertToDER(String pemString) throws IOException, CertificateException {
X509Certificate cert = convertToCertificate(pemString);
return cert.getEncoded();
}
@Transactional
public void updateEditionDate(EbicsUser user) {
LocalDateTime now = appBaseService.getTodayDateTime().toLocalDateTime();
EbicsCertificate certificate = user.getA005Certificate();
if (certificate != null) {
certificate.setInitLetterEditionDate(now);
certRepo.save(certificate);
}
certificate = user.getE002Certificate();
if (certificate != null) {
certificate.setInitLetterEditionDate(now);
certRepo.save(certificate);
}
certificate = user.getX002Certificate();
if (certificate != null) {
certificate.setInitLetterEditionDate(now);
certRepo.save(certificate);
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.service;
import com.axelor.apps.bankpayment.db.BankStatement;
import com.axelor.apps.bankpayment.db.BankStatementFileFormat;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.exception.AxelorException;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
public interface EbicsPartnerService {
List<BankStatement> getBankStatements(EbicsPartner ebicsPartner)
throws AxelorException, IOException;
List<BankStatement> getBankStatements(
EbicsPartner ebicsPartner,
Collection<BankStatementFileFormat> bankStatementFileFormatCollection)
throws AxelorException, IOException;
/**
* Check if bank details miss mandatory currency
*
* @param ebicsPartner
* @throws AxelorException with the name of the bank details missing currency if the currency is
* mandatory
*/
void checkBankDetailsMissingCurrency(EbicsPartner ebicsPartner) throws AxelorException;
}

View File

@ -0,0 +1,212 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.service;
import com.axelor.apps.bankpayment.db.BankStatement;
import com.axelor.apps.bankpayment.db.BankStatementFileFormat;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.BankStatementRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.bankpayment.service.bankstatement.BankStatementCreateService;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.tool.date.DateTool;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
public class EbicsPartnerServiceImpl implements EbicsPartnerService {
protected BankStatementCreateService bankStatementCreateService;
protected EbicsService ebicsService;
protected BankStatementRepository bankStatementRepository;
@Inject
public EbicsPartnerServiceImpl(
BankStatementCreateService bankStatementCreateService,
EbicsService ebicsService,
BankStatementRepository bankStatementRepository) {
this.bankStatementCreateService = bankStatementCreateService;
this.ebicsService = ebicsService;
this.bankStatementRepository = bankStatementRepository;
}
@Transactional
public List<BankStatement> getBankStatements(EbicsPartner ebicsPartner)
throws AxelorException, IOException {
return getBankStatements(ebicsPartner, null);
}
@Transactional
public List<BankStatement> getBankStatements(
EbicsPartner ebicsPartner,
Collection<BankStatementFileFormat> bankStatementFileFormatCollection)
throws AxelorException, IOException {
List<BankStatement> bankStatementList = Lists.newArrayList();
EbicsUser transportEbicsUser = ebicsPartner.getTransportEbicsUser();
if (ebicsPartner.getBsEbicsPartnerServiceList() == null
|| ebicsPartner.getBsEbicsPartnerServiceList().isEmpty()
|| transportEbicsUser == null) {
return bankStatementList;
}
LocalDateTime executionDateTime = LocalDateTime.now();
Date startDate = null;
Date endDate = null;
LocalDate bankStatementStartDate = null;
LocalDate bankStatementToDate = null;
if (ebicsPartner.getBankStatementGetModeSelect() == EbicsPartnerRepository.GET_MODE_PERIOD) {
bankStatementStartDate = ebicsPartner.getBankStatementStartDate();
if (bankStatementStartDate != null) {
startDate = DateTool.toDate(bankStatementStartDate);
}
bankStatementToDate = ebicsPartner.getBankStatementEndDate();
if (bankStatementToDate != null) {
endDate = DateTool.toDate(bankStatementToDate);
}
} else {
if (ebicsPartner.getBankStatementLastExeDateT() != null) {
bankStatementStartDate = ebicsPartner.getBankStatementLastExeDateT().toLocalDate();
}
bankStatementToDate = executionDateTime.toLocalDate();
}
for (com.axelor.apps.bankpayment.db.EbicsPartnerService bsEbicsPartnerService :
ebicsPartner.getBsEbicsPartnerServiceList()) {
BankStatementFileFormat bankStatementFileFormat =
bsEbicsPartnerService.getBankStatementFileFormat();
if (bankStatementFileFormatCollection != null
&& !bankStatementFileFormatCollection.isEmpty()
&& !bankStatementFileFormatCollection.contains(bankStatementFileFormat)) {
continue;
}
try {
File file =
ebicsService.sendFDLRequest(
transportEbicsUser,
null,
startDate,
endDate,
bsEbicsPartnerService.getEbicsCodification());
BankStatement bankStatement =
bankStatementCreateService.createBankStatement(
file,
bankStatementStartDate,
bankStatementToDate,
bankStatementFileFormat,
ebicsPartner,
executionDateTime);
bankStatementRepository.save(bankStatement);
bankStatementList.add(bankStatement);
} catch (Exception e) {
TraceBackService.trace(e);
}
}
ebicsPartner.setBankStatementLastExeDateT(executionDateTime);
Beans.get(EbicsPartnerRepository.class).save(ebicsPartner);
return bankStatementList;
}
public void checkBankDetailsMissingCurrency(EbicsPartner ebicsPartner) throws AxelorException {
List<com.axelor.apps.bankpayment.db.EbicsPartnerService> ebicsPartnerServiceSet =
ebicsPartner.getBoEbicsPartnerServiceList();
if (ebicsPartnerServiceSet == null) {
return;
}
boolean allowOrderCurrDiffFromBankDetails = false;
for (com.axelor.apps.bankpayment.db.EbicsPartnerService ebicsPartnerService :
ebicsPartnerServiceSet) {
allowOrderCurrDiffFromBankDetails =
allowOrderCurrDiffFromBankDetails
|| ebicsPartnerService
.getBankOrderFileFormat()
.getAllowOrderCurrDiffFromBankDetails();
if (allowOrderCurrDiffFromBankDetails) {
break;
}
}
if (!allowOrderCurrDiffFromBankDetails) {
return;
}
Set<BankDetails> bankDetailsSet = ebicsPartner.getBankDetailsSet();
if (bankDetailsSet == null) {
return;
}
List<String> bankDetailsWithoutCurrency = new ArrayList<>();
for (BankDetails bankDetails : bankDetailsSet) {
if (bankDetails.getCurrency() == null) {
bankDetailsWithoutCurrency.add(bankDetails.getFullName());
}
}
if (!bankDetailsWithoutCurrency.isEmpty()) {
Function<String, String> addLi =
new Function<String, String>() {
@Override
public String apply(String s) {
return "<li>".concat(s).concat("</li>").toString();
}
};
throw new AxelorException(
String.format(
I18n.get(IExceptionMessage.EBICS_PARTNER_BANK_DETAILS_WARNING),
"<ul>"
+ Joiner.on("").join(Iterables.transform(bankDetailsWithoutCurrency, addLi))
+ "<ul>"),
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
ebicsPartner);
}
}
}

View File

@ -0,0 +1,489 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.service;
import com.axelor.app.AppSettings;
import com.axelor.apps.bankpayment.db.BankOrderFileFormat;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsPartnerService;
import com.axelor.apps.bankpayment.db.EbicsRequestLog;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsRequestLogRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsUserRepository;
import com.axelor.apps.bankpayment.ebics.client.EbicsProduct;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.FileTransfer;
import com.axelor.apps.bankpayment.ebics.client.KeyManagement;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.apps.bankpayment.ebics.io.IOUtils;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.meta.MetaFiles;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Date;
import java.util.List;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jdom.JDOMException;
public class EbicsService {
@Inject private EbicsUserRepository userRepo;
@Inject private EbicsRequestLogRepository logRepo;
@Inject private EbicsUserService userService;
@Inject private MetaFiles metaFiles;
private EbicsProduct defaultProduct;
static {
org.apache.xml.security.Init.init();
java.security.Security.addProvider(new BouncyCastleProvider());
}
@Inject
public EbicsService() {
AppSettings settings = AppSettings.get();
String name = settings.get("application.name") + " " + settings.get("application.version");
String language = settings.get("application.locale");
String instituteID = settings.get("application.author");
defaultProduct = new EbicsProduct(name, language, instituteID);
}
public String makeDN(EbicsUser ebicsUser) {
String email = null;
String companyName = defaultProduct.getInstituteID();
User user = ebicsUser.getAssociatedUser();
if (user != null) {
email = user.getEmail();
if (user.getActiveCompany() != null) {
companyName = user.getActiveCompany().getName();
}
}
return makeDN(ebicsUser.getName(), email, "FR", companyName);
}
private String makeDN(String name, String email, String country, String organization) {
StringBuffer buffer = new StringBuffer();
buffer.append("CN=" + name);
if (country != null) {
buffer.append(", " + "C=" + country.toUpperCase());
}
if (organization != null) {
buffer.append(", " + "O=" + organization);
}
if (email != null) {
buffer.append(", " + "E=" + email);
}
return buffer.toString();
}
public RSAPublicKey getPublicKey(String modulus, String exponent)
throws NoSuchAlgorithmException, InvalidKeySpecException {
RSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));
KeyFactory factory = KeyFactory.getInstance("RSA");
RSAPublicKey pub = (RSAPublicKey) factory.generatePublic(spec);
/*Signature verifier = Signature.getInstance("SHA1withRSA");
verifier.initVerify(pub);
boolean okay = verifier.verify(signature);*/
return pub;
}
public RSAPrivateKey getPrivateKey(byte[] encoded)
throws NoSuchAlgorithmException, InvalidKeySpecException {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) kf.generatePrivate(keySpec);
}
/**
* Sends an INI request to the ebics bank server
*
* @param userId the user ID
* @param product the application product
* @throws AxelorException
* @throws JDOMException
* @throws IOException
*/
@Transactional
public void sendINIRequest(EbicsUser ebicsUser, EbicsProduct product) throws AxelorException {
if (ebicsUser.getStatusSelect()
!= EbicsUserRepository.STATUS_WAITING_SENDING_SIGNATURE_CERTIFICATE) {
return;
}
try {
userService.getNextOrderId(ebicsUser);
EbicsSession session = new EbicsSession(ebicsUser);
if (product == null) {
product = defaultProduct;
}
session.setProduct(product);
KeyManagement keyManager = new KeyManagement(session);
keyManager.sendINI();
ebicsUser.setStatusSelect(EbicsUserRepository.STATUS_WAITING_AUTH_AND_ENCRYPT_CERTIFICATES);
userRepo.save(ebicsUser);
} catch (Exception e) {
TraceBackService.trace(e);
throw new AxelorException(e, TraceBackRepository.TYPE_TECHNICAL);
}
}
/**
* Sends a HIA request to the ebics server.
*
* @param userId the user ID.
* @param product the application product.
* @throws AxelorException
*/
@Transactional
public void sendHIARequest(EbicsUser ebicsUser, EbicsProduct product) throws AxelorException {
if (ebicsUser.getStatusSelect()
!= EbicsUserRepository.STATUS_WAITING_AUTH_AND_ENCRYPT_CERTIFICATES) {
return;
}
userService.getNextOrderId(ebicsUser);
EbicsSession session = new EbicsSession(ebicsUser);
if (product == null) {
product = defaultProduct;
}
session.setProduct(product);
KeyManagement keyManager = new KeyManagement(session);
try {
keyManager.sendHIA();
ebicsUser.setStatusSelect(EbicsUserRepository.STATUS_ACTIVE_CONNECTION);
userRepo.save(ebicsUser);
} catch (IOException | AxelorException | JDOMException e) {
TraceBackService.trace(e);
throw new AxelorException(e, TraceBackRepository.TYPE_TECHNICAL);
}
}
/**
* Sends a HPB request to the ebics server.
*
* @param userId the user ID.
* @param product the application product.
* @throws AxelorException
*/
@Transactional
public X509Certificate[] sendHPBRequest(EbicsUser user, EbicsProduct product)
throws AxelorException {
EbicsSession session = new EbicsSession(user);
if (product == null) {
product = defaultProduct;
}
session.setProduct(product);
KeyManagement keyManager = new KeyManagement(session);
try {
return keyManager.sendHPB();
} catch (Exception e) {
TraceBackService.trace(e);
throw new AxelorException(e, TraceBackRepository.TYPE_TECHNICAL);
}
}
/**
* Sends the SPR order to the bank.
*
* @param userId the user ID
* @param product the session product
* @throws AxelorException
*/
@Transactional
public void sendSPRRequest(EbicsUser ebicsUser, EbicsProduct product) throws AxelorException {
EbicsSession session = new EbicsSession(ebicsUser);
if (product == null) {
product = defaultProduct;
}
session.setProduct(product);
KeyManagement keyManager = new KeyManagement(session);
try {
keyManager.lockAccess();
ebicsUser.setStatusSelect(EbicsUserRepository.STATUS_WAITING_SENDING_SIGNATURE_CERTIFICATE);
userService.getNextOrderId(ebicsUser);
userRepo.save(ebicsUser);
} catch (Exception e) {
TraceBackService.trace(e);
throw new AxelorException(e, TraceBackRepository.TYPE_TECHNICAL);
}
}
/**
* Send a file to the EBICS bank server.
*
* @param transportUser
* @param signatoryUser
* @param product
* @param file
* @param format
* @param signature
* @throws AxelorException
*/
public void sendFULRequest(
EbicsUser transportUser,
EbicsUser signatoryUser,
EbicsProduct product,
File file,
BankOrderFileFormat format,
File signature)
throws AxelorException {
Preconditions.checkNotNull(transportUser);
Preconditions.checkNotNull(transportUser.getEbicsPartner());
Preconditions.checkNotNull(format);
List<EbicsPartnerService> ebicsPartnerServiceList =
transportUser.getEbicsPartner().getBoEbicsPartnerServiceList();
String ebicsCodification;
if (ebicsPartnerServiceList == null || ebicsPartnerServiceList.isEmpty()) {
ebicsCodification = format.getOrderFileFormatSelect();
} else {
ebicsCodification = findEbicsCodification(transportUser.getEbicsPartner(), format);
}
sendFULRequest(transportUser, signatoryUser, product, file, ebicsCodification, signature);
}
private String findEbicsCodification(EbicsPartner ebicsPartner, BankOrderFileFormat format)
throws AxelorException {
Preconditions.checkNotNull(ebicsPartner);
Preconditions.checkNotNull(format);
if (ebicsPartner.getBoEbicsPartnerServiceList() != null) {
for (EbicsPartnerService service : ebicsPartner.getBoEbicsPartnerServiceList()) {
if (format.equals(service.getBankOrderFileFormat())) {
return service.getEbicsCodification();
}
}
}
throw new AxelorException(
I18n.get(IExceptionMessage.EBICS_NO_SERVICE_CONFIGURED),
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
ebicsPartner.getPartnerId(),
format.getName());
}
/**
* Sends a file to the ebics bank sever
*
* @param path the file path to send
* @param userId the user ID that sends the file.
* @param product the application product.
* @throws AxelorException
*/
private void sendFULRequest(
EbicsUser transportUser,
EbicsUser signatoryUser,
EbicsProduct product,
File file,
String format,
File signature)
throws AxelorException {
EbicsSession session = new EbicsSession(transportUser, signatoryUser);
boolean test = isTest(transportUser);
if (test) {
session.addSessionParam("TEST", "true");
}
if (file == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, "File is required to send FUL request");
}
EbicsPartner ebicsPartner = transportUser.getEbicsPartner();
if (ebicsPartner.getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_TS) {
if (signature == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
"Signature file is required to send FUL request");
}
if (signatoryUser == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
"Signatory user is required to send FUL request");
}
}
session.addSessionParam("EBCDIC", "false");
session.addSessionParam("FORMAT", format);
if (product == null) {
product = defaultProduct;
}
session.setProduct(product);
FileTransfer transferManager = new FileTransfer(session);
try {
if (ebicsPartner.getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_TS) {
transferManager.sendFile(
IOUtils.getFileContent(file.getAbsolutePath()),
OrderType.FUL,
IOUtils.getFileContent(signature.getAbsolutePath()));
} else {
transferManager.sendFile(
IOUtils.getFileContent(file.getAbsolutePath()), OrderType.FUL, null);
}
userService.getNextOrderId(transportUser);
} catch (IOException | AxelorException e) {
TraceBackService.trace(e);
throw new AxelorException(e, TraceBackRepository.TYPE_TECHNICAL);
}
try {
if (ebicsPartner.getUsePSR()) {
sendFDLRequest(
transportUser,
product,
null,
null,
ebicsPartner.getpSRBankStatementFileFormat().getStatementFileFormatSelect());
}
} catch (AxelorException e) {
TraceBackService.trace(e);
}
}
public File sendFDLRequest(
EbicsUser user, EbicsProduct product, Date start, Date end, String fileFormat)
throws AxelorException {
return fetchFile(OrderType.FDL, user, product, start, end, fileFormat);
}
public File sendHTDRequest(EbicsUser user, EbicsProduct product, Date start, Date end)
throws AxelorException {
return fetchFile(OrderType.HTD, user, product, start, end, null);
}
public File sendPTKRequest(EbicsUser user, EbicsProduct product, Date start, Date end)
throws AxelorException {
return fetchFile(OrderType.PTK, user, product, start, end, null);
}
public File sendHPDRequest(EbicsUser user, EbicsProduct product, Date start, Date end)
throws AxelorException {
return fetchFile(OrderType.HPD, user, product, start, end, null);
}
private File fetchFile(
OrderType orderType,
EbicsUser user,
EbicsProduct product,
Date start,
Date end,
String fileFormat)
throws AxelorException {
EbicsSession session = new EbicsSession(user);
File file = null;
try {
boolean test = isTest(user);
if (test) {
session.addSessionParam("TEST", "true");
}
if (fileFormat != null) {
session.addSessionParam("FORMAT", fileFormat);
}
if (product == null) {
product = defaultProduct;
}
session.setProduct(product);
FileTransfer transferManager = new FileTransfer(session);
file = File.createTempFile(user.getName(), "." + orderType.getOrderType());
transferManager.fetchFile(orderType, start, end, new FileOutputStream(file));
addResponseFile(user, file);
userService.getNextOrderId(user);
} catch (AxelorException e) {
TraceBackService.trace(e);
throw e;
} catch (IOException e) {
TraceBackService.trace(e);
throw new AxelorException(e, TraceBackRepository.TYPE_TECHNICAL);
}
return file;
}
private boolean isTest(EbicsUser user) throws AxelorException {
EbicsPartner partner = user.getEbicsPartner();
return partner.getTestMode();
}
@Transactional
public void addResponseFile(EbicsUser user, File file) throws IOException {
EbicsRequestLog requestLog =
logRepo.all().filter("self.ebicsUser = ?1", user).order("-id").fetchOne();
if (requestLog != null && file != null && file.length() > 0) {
requestLog.setResponseFile(metaFiles.upload(file));
logRepo.save(requestLog);
}
}
}

View File

@ -0,0 +1,342 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.service;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsRequestLog;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.EbicsRequestLogRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsUserRepository;
import com.axelor.apps.bankpayment.ebics.client.EbicsRootElement;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.Signature;
import java.time.LocalDateTime;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jdom.JDOMException;
public class EbicsUserService {
@Inject private EbicsService ebicsService;
@Inject private EbicsRequestLogRepository requestLogRepo;
@Inject private EbicsUserRepository ebicsUserRepo;
/**
* EBICS Specification 2.4.2 - 14.1 Version A005/A006 of the electronic signature:
*
* <p>For the signature processes A005 an interval of 1536 bit (minimum) and 4096 bit (maximum) is
* defined for the key length.
*
* <p>The digital signature mechanisms A005 is both based on the industry standard [PKCS1] using
* the hash algorithm SHA-256. They are both signature mechanisms without message recovery.
*
* <p>A hash algorithm maps bit sequences of arbitrary length (input bit sequences) to byte
* sequences of a fixed length, determined by the Hash algorithm. The result of the execution of a
* Hash algorithm to a bit sequence is defined as hash value.
*
* <p>The hash algorithm SHA-256 is specified in [FIPS H2]. SHA-256 maps input bit sequences of
* arbitrary length to byte sequences of 32 byte length. The padding of input bit sequences to a
* length being a multiple of 64 byte is part of the hash algorithm. The padding even is applied
* if the input bit sequence already has a length that is a multiple of 64 byte.
*
* <p>SHA-256 processes the input bit sequences in blocks of 64 byte length. The hash value of a
* bit sequence x under the hash algorithm SHA-256 is referred to as follows: SHA-256(x).
*
* <p>The digital signature mechanism A005 is identical to EMSA-PKCS1-v1_5 using the hash
* algorithm SHA-256. The byte length H of the hash value is 32.
*
* <p>According [PKCS1] (using the method EMSA-PKCS1-v1_5) the following steps shall be performed
* for the computation of a signature for message M with bit length m.
*
* <ol>
* <li>The hash value HASH(M) of the byte length H shall be computed. In the case of A005
* SHA-256(M) with a length of 32 bytes.
* <li>The DSI for the signature algorithm shall be generated.
* <li>A signature shall be computed using the DSI with the standard algorithm for the signature
* generation described in section 14.1.3.1 of the EBICS specification (V 2.4.2).
* </ol>
*
* <p>The {@link Signature} is a digital signature scheme with appendix (SSA) combining the RSA
* algorithm with the EMSA-PKCS1-v1_5 encoding method.
*
* <p>The {@code digest} will be signed with the RSA user signature key using the {@link
* Signature} that will be instantiated with the <b>SHA-256</b> algorithm. This signature is then
* put in a {@link UserSignature} XML object that will be sent to the EBICS server.
*/
public byte[] sign(EbicsUser ebicsUser, byte[] digest)
throws IOException, GeneralSecurityException {
Signature signature =
Signature.getInstance("SHA256WithRSA", BouncyCastleProvider.PROVIDER_NAME);
signature.initSign(ebicsService.getPrivateKey(ebicsUser.getA005Certificate().getPrivateKey()));
signature.update(removeOSSpecificChars(digest));
return signature.sign();
}
/**
* EBICS Specification 2.4.2 - 11.1.1 Process:
*
* <p>Identification and authentication signatures are based on the RSA signature process. The
* following parameters determine the identification and authentication signature process:
*
* <ol>
* <li>Length of the (secret) RSA key
* <li>Hash algorithm
* <li>Padding process
* <li>Canonisation process.
* </ol>
*
* <p>For the identification and authentication process, EBICS defines the process “X002” with the
* following parameters:
*
* <ol>
* <li>Key length in Kbit >=1Kbit (1024 bit) and lesser than 16Kbit
* <li>Hash algorithm SHA-256
* <li>Padding process: PKCS#1
* <li>Canonisation process: http://www.w3.org/TR/2001/REC-xml-c14n-20010315
* </ol>
*
* <p>From EBICS 2.4 on, the customer system must use the hash value of the public bank key X002
* in a request.
*
* <p>Notes:
*
* <ol>
* <li>The key length is defined else where.
* <li>The padding is performed by the {@link Signature} class.
* <li>The digest is already canonized in the {@link SignedInfo#sign(byte[]) sign(byte[])}
* </ol>
*/
public byte[] authenticate(EbicsUser ebicsUser, byte[] digest) throws GeneralSecurityException {
Signature signature;
signature = Signature.getInstance("SHA256WithRSA", BouncyCastleProvider.PROVIDER_NAME);
signature.initSign(ebicsService.getPrivateKey(ebicsUser.getX002Certificate().getPrivateKey()));
signature.update(digest);
return signature.sign();
}
/**
* EBICS Specification 2.4.2 - 7.1 Process description:
*
* <p>In particular, so-called “white-space characters” such as spaces, tabs, carriage returns and
* line feeds (“CR/LF”) are not permitted.
*
* <p>All white-space characters should be removed from entry buffer {@code buf}.
*
* @param buf the given byte buffer
* @param offset the offset
* @param length the length
* @return The byte buffer portion corresponding to the given length and offset
*/
public static byte[] removeOSSpecificChars(byte[] content) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
for (byte b : content) {
switch (b) {
case '\r':
case '\n':
case 0x1A: // CTRL-Z / EOF
// ignore this characters
break;
default:
output.write(b);
}
}
return output.toByteArray();
}
/**
* EBICS IG CFONB VF 2.1.4 2012 02 24 - 2.1.3.2 Calcul de la signature:
*
* <p>Il convient dutiliser PKCS1 V1.5 pour chiffrer la clé de chiffrement.
*
* <p>EBICS Specification 2.4.2 - 15.2 Workflows at the recipients end:
*
* <p><b>Decryption of the DES key</b>
*
* <p>The leading 256 null bits of the EDEK are removed and the remaining 768 bits are decrypted
* with the recipients secret key of the RSA key system. PDEK is then present. The secret DES key
* DEK is obtained from the lowest-value 128 bits of PDEK, this is split into the individual keys
* DEK<SUB>left</SUB> and DEK<SUB>right</SUB>.
*/
public byte[] decrypt(EbicsUser user, byte[] encryptedData, byte[] transactionKey)
throws AxelorException, GeneralSecurityException, IOException {
Cipher cipher;
int blockSize;
ByteArrayOutputStream outputStream;
cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", BouncyCastleProvider.PROVIDER_NAME);
cipher.init(
Cipher.DECRYPT_MODE, ebicsService.getPrivateKey(user.getE002Certificate().getPrivateKey()));
blockSize = cipher.getBlockSize();
outputStream = new ByteArrayOutputStream();
for (int j = 0; j * blockSize < transactionKey.length; j++) {
outputStream.write(cipher.doFinal(transactionKey, j * blockSize, blockSize));
}
return decryptData(encryptedData, outputStream.toByteArray());
}
/**
* Decrypts the <code>encryptedData</code> using the decoded transaction key.
*
* <p>EBICS Specification 2.4.2 - 15.2 Workflows at the recipients end:
*
* <p><b>Decryption of the message</b>
*
* <p>The encrypted original message is decrypted in CBC mode in accordance with the 2-key triple
* DES process via the secret DES key (comprising DEK<SUB>left</SUB> and DEK<SUP>right<SUB>). In
* doing this, the following initialization value ICV is again used.
*
* <p><b>Removal of the padding information</b>
*
* <p>The method “Padding with Octets” according to ANSI X9.23 is used to remove the padding
* information from the decrypted message. The original message is then available in decrypted
* form.
*
* @param input The encrypted data
* @param key The secret key.
* @return The decrypted data sent from the EBICS bank.
* @throws GeneralSecurityException
* @throws IOException
*/
private byte[] decryptData(byte[] input, byte[] key) throws AxelorException {
return EbicsUtils.decrypt(input, new SecretKeySpec(key, "EAS"));
}
@Transactional
public void logRequest(
long ebicsUserId, String requestType, String responseCode, EbicsRootElement[] rootElements) {
EbicsRequestLog requestLog = new EbicsRequestLog();
requestLog.setEbicsUser(ebicsUserRepo.find(ebicsUserId));
LocalDateTime time = LocalDateTime.now();
requestLog.setRequestTime(time);
requestLog.setRequestType(requestType);
requestLog.setResponseCode(responseCode);
try {
trace(requestLog, rootElements);
} catch (Exception e) {
e.printStackTrace();
}
requestLogRepo.save(requestLog);
}
@Transactional
public String getNextOrderId(EbicsUser user) throws AxelorException {
String orderId = user.getNextOrderId();
if (orderId == null) {
EbicsPartner partner = user.getEbicsPartner();
EbicsUser otherUser =
ebicsUserRepo
.all()
.filter(
"self.ebicsPartner = ?1 and self.id != ?2 and self.nextOrderId != null",
partner,
user.getId())
.order("-nextOrderId")
.fetchOne();
char firstLetter = 'A';
if (otherUser != null) {
String otherOrderId = otherUser.getNextOrderId();
firstLetter = otherOrderId.charAt(0);
firstLetter++;
}
orderId = String.valueOf(firstLetter) + "000";
user.setNextOrderId(orderId);
ebicsUserRepo.save(user);
} else {
orderId = getNextOrderNumber(orderId);
user.setNextOrderId(orderId);
ebicsUserRepo.save(user);
}
return orderId;
}
public String getNextOrderNumber(String orderId) throws AxelorException {
if (Strings.isNullOrEmpty(orderId) || orderId.matches("[^a-z0-9 ]") || orderId.length() != 4) {
throw new AxelorException(
TraceBackRepository.CATEGORY_MISSING_FIELD, I18n.get("Invalid order id \"%s\""), orderId);
}
if (orderId.substring(1).equals("ZZZ")) {
throw new AxelorException(
TraceBackRepository.CATEGORY_MISSING_FIELD, I18n.get("Maximum order limit reach"));
}
char[] orderIds = orderId.toCharArray();
if (orderIds[3] != 'Z') {
orderIds[3] = getNextChar(orderIds[3]);
} else {
orderIds[3] = '0';
if (orderIds[2] != 'Z') {
orderIds[2] = getNextChar(orderIds[2]);
} else {
orderIds[2] = '0';
if (orderIds[1] != 'Z') {
orderIds[1] = getNextChar(orderIds[1]);
}
}
}
return new String(orderIds);
}
private char getNextChar(char c) {
if (c == '9') {
return 'A';
}
return (char) ((int) c + 1);
}
private void trace(EbicsRequestLog requestLog, EbicsRootElement[] rootElements)
throws AxelorException, JDOMException, IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
rootElements[0].save(bout);
requestLog.setRequestTraceText(bout.toString());
bout.close();
bout = new ByteArrayOutputStream();
rootElements[1].save(bout);
requestLog.setResponseTraceText(bout.toString());
bout.close();
}
}

View File

@ -0,0 +1,519 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.web;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.bankpayment.db.BankOrderFileFormat;
import com.axelor.apps.bankpayment.db.BankStatementFileFormat;
import com.axelor.apps.bankpayment.db.EbicsBank;
import com.axelor.apps.bankpayment.db.EbicsCertificate;
import com.axelor.apps.bankpayment.db.EbicsRequestLog;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.EbicsBankRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsCertificateRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsRequestLogRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsUserRepository;
import com.axelor.apps.bankpayment.ebics.certificate.CertificateManager;
import com.axelor.apps.bankpayment.ebics.service.EbicsCertificateService;
import com.axelor.apps.bankpayment.ebics.service.EbicsService;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.bankpayment.report.IReport;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.data.Listener;
import com.axelor.data.xml.XMLImporter;
import com.axelor.db.Model;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.axelor.rpc.Context;
import com.google.common.base.Joiner;
import com.google.common.io.Files;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.xmlbeans.impl.common.IOUtil;
@Singleton
public class EbicsController {
private static int totalRecord = 0, importedRecord = 0;
@Transactional
public void generateCertificate(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
if (ebicsUser.getStatusSelect() != EbicsUserRepository.STATUS_WAITING_CERTIFICATE_CONFIG
&& ebicsUser.getStatusSelect()
!= EbicsUserRepository.STATUS_CERTIFICATES_SHOULD_BE_RENEWED) {
return;
}
CertificateManager cm = new CertificateManager(ebicsUser);
try {
cm.create();
ebicsUser.setStatusSelect(EbicsUserRepository.STATUS_WAITING_SENDING_SIGNATURE_CERTIFICATE);
Beans.get(EbicsUserRepository.class).save(ebicsUser);
} catch (GeneralSecurityException | IOException e) {
e.printStackTrace();
}
response.setReload(true);
}
public void generateDn(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
response.setValue("dn", Beans.get(EbicsService.class).makeDN(ebicsUser));
}
public void sendINIRequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
Beans.get(EbicsService.class).sendINIRequest(ebicsUser, null);
} catch (Exception e) {
e.printStackTrace();
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
public void sendHIARequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
Beans.get(EbicsService.class).sendHIARequest(ebicsUser, null);
} catch (Exception e) {
e.printStackTrace();
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
public void sendHPBRequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
X509Certificate[] certificates =
Beans.get(EbicsService.class).sendHPBRequest(ebicsUser, null);
confirmCertificates(ebicsUser, certificates, response);
} catch (Exception e) {
e.printStackTrace();
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
private void confirmCertificates(
EbicsUser user, X509Certificate[] certificates, ActionResponse response) {
try {
EbicsBank bank = user.getEbicsPartner().getEbicsBank();
response.setView(
ActionView.define("Confirm certificates")
.model("com.axelor.apps.bankpayment.db.EbicsCertificate")
.add("form", "ebics-certificate-confirmation-form")
.param("show-toolbar", "false")
.param("show-confirm", "false")
.param("popup-save", "false")
.param("popup", "true")
.context("ebicsBank", bank)
.context("url", bank.getUrl())
.context("hostId", bank.getHostId())
.context(
"e002Hash", DigestUtils.sha256Hex(certificates[0].getEncoded()).toUpperCase())
.context(
"x002Hash", DigestUtils.sha256Hex(certificates[1].getEncoded()).toUpperCase())
.context(
"certificateE002",
Beans.get(EbicsCertificateService.class).convertToPEMString(certificates[0]))
.context(
"certificateX002",
Beans.get(EbicsCertificateService.class).convertToPEMString(certificates[1]))
.map());
} catch (Exception e) {
response.setFlash("Error in certificate confirmation ");
}
}
public void sendSPRRequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
Beans.get(EbicsService.class).sendSPRRequest(ebicsUser, null);
} catch (Exception e) {
e.printStackTrace();
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
public void sendFULRequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
MetaFile testDataMetaFile = ebicsUser.getTestDataFile();
MetaFile testSignatureMetaFile = ebicsUser.getTestSignatureFile();
BankOrderFileFormat bankOrderFileFormat = ebicsUser.getTestBankOrderFileFormat();
if (testDataMetaFile != null && bankOrderFileFormat != null) {
File testSignatureFile = null;
if (ebicsUser.getEbicsPartner().getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_TS
&& testSignatureMetaFile != null) {
testSignatureFile = MetaFiles.getPath(testSignatureMetaFile).toFile();
}
Beans.get(EbicsService.class)
.sendFULRequest(
ebicsUser,
ebicsUser.getTestSignatoryEbicsUser(),
null,
MetaFiles.getPath(testDataMetaFile).toFile(),
bankOrderFileFormat,
testSignatureFile);
} else {
response.setFlash(I18n.get(IExceptionMessage.EBICS_TEST_MODE_NOT_ENABLED));
}
} catch (Exception e) {
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
public void sendFDLRequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
BankStatementFileFormat bankStatementFileFormat = ebicsUser.getTestBankStatementFileFormat();
if (ebicsUser.getEbicsPartner().getTestMode() && bankStatementFileFormat != null) {
Beans.get(EbicsService.class)
.sendFDLRequest(
ebicsUser,
null,
null,
null,
bankStatementFileFormat.getStatementFileFormatSelect());
downloadFile(response, ebicsUser);
} else {
response.setFlash(I18n.get(IExceptionMessage.EBICS_TEST_MODE_NOT_ENABLED));
}
} catch (Exception e) {
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
public void sendHTDRequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
Beans.get(EbicsService.class).sendHTDRequest(ebicsUser, null, null, null);
downloadFile(response, ebicsUser);
} catch (Exception e) {
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
public void sendPTKRequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
Beans.get(EbicsService.class).sendPTKRequest(ebicsUser, null, null, null);
downloadFile(response, ebicsUser);
} catch (Exception e) {
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
public void sendHPDRequest(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser =
Beans.get(EbicsUserRepository.class)
.find(request.getContext().asType(EbicsUser.class).getId());
try {
Beans.get(EbicsService.class).sendHPDRequest(ebicsUser, null, null, null);
downloadFile(response, ebicsUser);
} catch (Exception e) {
response.setFlash(stripClass(e.getLocalizedMessage()));
}
response.setReload(true);
}
private String stripClass(String msg) {
return msg.replace(AxelorException.class.getName() + ":", "");
}
public void addCertificates(ActionRequest request, ActionResponse response)
throws AxelorException {
Context context = request.getContext();
EbicsBank ebicsBank = (EbicsBank) context.get("ebicsBank");
ebicsBank = Beans.get(EbicsBankRepository.class).find(ebicsBank.getId());
try {
X509Certificate certificate =
Beans.get(EbicsCertificateService.class)
.convertToCertificate((String) context.get("certificateE002"));
Beans.get(EbicsCertificateService.class)
.createCertificate(certificate, ebicsBank, EbicsCertificateRepository.TYPE_ENCRYPTION);
certificate =
Beans.get(EbicsCertificateService.class)
.convertToCertificate((String) context.get("certificateX002"));
Beans.get(EbicsCertificateService.class)
.createCertificate(
certificate, ebicsBank, EbicsCertificateRepository.TYPE_AUTHENTICATION);
} catch (CertificateException | IOException e) {
e.printStackTrace();
throw new AxelorException(
e,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get("Error in adding bank certificate"));
}
response.setCanClose(true);
}
public void loadCertificate(ActionRequest request, ActionResponse response)
throws AxelorException, CertificateEncodingException, IOException {
EbicsCertificate ebicsCertificate = request.getContext().asType(EbicsCertificate.class);
ebicsCertificate = Beans.get(EbicsCertificateRepository.class).find(ebicsCertificate.getId());
byte[] certs = ebicsCertificate.getCertificate();
if (certs != null && certs.length > 0) {
X509Certificate certificate =
EbicsCertificateService.getCertificate(certs, ebicsCertificate.getTypeSelect());
ebicsCertificate =
Beans.get(EbicsCertificateService.class)
.updateCertificate(certificate, ebicsCertificate, true);
response.setValue("validFrom", ebicsCertificate.getValidFrom());
response.setValue("validTo", ebicsCertificate.getValidTo());
response.setValue("issuer", ebicsCertificate.getIssuer());
response.setValue("subject", ebicsCertificate.getSubject());
response.setValue("publicKeyModulus", ebicsCertificate.getPublicKeyModulus());
response.setValue("publicKeyExponent", ebicsCertificate.getPublicKeyExponent());
response.setValue("fullName", ebicsCertificate.getFullName());
response.setValue("pemString", ebicsCertificate.getPemString());
response.setValue("sha2has", ebicsCertificate.getSha2has());
}
}
public void updateEditionDate(ActionRequest request, ActionResponse response) {
EbicsUser ebicsUser = request.getContext().asType(EbicsUser.class);
ebicsUser = Beans.get(EbicsUserRepository.class).find(ebicsUser.getId());
Beans.get(EbicsCertificateService.class).updateEditionDate(ebicsUser);
response.setReload(true);
}
@Transactional
public void importEbicsUsers(ActionRequest request, ActionResponse response) {
String config = "/data-import/import-ebics-user-config.xml";
try {
InputStream inputStream = this.getClass().getResourceAsStream(config);
File configFile = File.createTempFile("config", ".xml");
FileOutputStream fout = new FileOutputStream(configFile);
IOUtil.copyCompletely(inputStream, fout);
Path path =
MetaFiles.getPath((String) ((Map) request.getContext().get("dataFile")).get("filePath"));
File tempDir = Files.createTempDir();
File importFile = new File(tempDir, "ebics-user.xml");
Files.copy(path.toFile(), importFile);
totalRecord = importedRecord = 0;
XMLImporter importer =
new XMLImporter(configFile.getAbsolutePath(), tempDir.getAbsolutePath());
final StringBuilder log = new StringBuilder();
Listener listner =
new Listener() {
@Override
public void imported(Integer imported, Integer total) {}
@Override
public void imported(Model arg0) {
if (arg0.getClass().isAssignableFrom(EbicsUser.class)) {
totalRecord++;
importedRecord++;
}
}
@Override
public void handle(Model arg0, Exception err) {
TraceBackService.trace(err);
if (arg0.getClass().isAssignableFrom(EbicsUser.class)) {
totalRecord++;
}
}
};
importer.addListener(listner);
importer.run();
FileUtils.forceDelete(configFile);
FileUtils.forceDelete(tempDir);
log.append("Total records: " + totalRecord + ", Total imported: " + importedRecord);
response.setValue("importLog", log.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
public void printCertificates(ActionRequest request, ActionResponse response)
throws AxelorException {
EbicsUser ebicsUser = request.getContext().asType(EbicsUser.class);
ArrayList<Long> certIds = new ArrayList<Long>();
if (ebicsUser.getA005Certificate() != null) {
certIds.add(ebicsUser.getA005Certificate().getId());
}
if (ebicsUser.getE002Certificate() != null) {
certIds.add(ebicsUser.getE002Certificate().getId());
}
if (ebicsUser.getX002Certificate() != null) {
certIds.add(ebicsUser.getX002Certificate().getId());
}
if (certIds.isEmpty()) {
throw new AxelorException(
ebicsUser,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.EBICS_MISSING_CERTIFICATES));
}
String title = I18n.get("EbicsCertificate");
ReportSettings report =
ReportFactory.createReport(IReport.EBICS_CERTIFICATE, title + "-${date}${time}");
report.addParam("CertificateId", Joiner.on(",").join(certIds));
report.addParam("EbicsUserId", ebicsUser.getId());
report.toAttach(ebicsUser);
report.generate();
response.setView(ActionView.define(title).add("html", report.getFileLink()).map());
}
private void downloadFile(ActionResponse response, EbicsUser user) {
EbicsRequestLog requestLog =
Beans.get(EbicsRequestLogRepository.class)
.all()
.filter("self.ebicsUser = ?1", user)
.order("-id")
.fetchOne();
if (requestLog != null && requestLog.getResponseFile() != null) {
response.setView(
ActionView.define(requestLog.getRequestType())
.add(
"html",
"ws/rest/"
+ EbicsRequestLog.class.getCanonicalName()
+ "/"
+ requestLog.getId()
+ "/responseFile/download")
.param("download", "dtrue")
.map());
}
}
}

View File

@ -0,0 +1,59 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.web;
import com.axelor.apps.bankpayment.db.BankStatement;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.ebics.service.EbicsPartnerService;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
import java.util.List;
@Singleton
public class EbicsPartnerController {
public void getBankStatement(ActionRequest request, ActionResponse response) {
try {
EbicsPartner ebicsPartner = request.getContext().asType(EbicsPartner.class);
List<BankStatement> bankStatementList =
Beans.get(EbicsPartnerService.class)
.getBankStatements(
Beans.get(EbicsPartnerRepository.class).find(ebicsPartner.getId()));
response.setFlash(
String.format(I18n.get("%s bank statements get."), bankStatementList.size()));
} catch (Exception e) {
TraceBackService.trace(response, e);
}
response.setReload(true);
}
public void checkBankDetailsSet(ActionRequest request, ActionResponse response) {
EbicsPartner ebicsPartner = request.getContext().asType(EbicsPartner.class);
try {
Beans.get(EbicsPartnerService.class).checkBankDetailsMissingCurrency(ebicsPartner);
} catch (Exception e) {
response.setFlash(e.getMessage());
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.web;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.bankpayment.db.EbicsRequestLog;
import com.axelor.apps.bankpayment.db.repo.EbicsRequestLogRepository;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
public class EbicsRequestLogController {
public void print(ActionRequest request, ActionResponse response) {
try {
EbicsRequestLog ebicsRequestLog = request.getContext().asType(EbicsRequestLog.class);
ebicsRequestLog = Beans.get(EbicsRequestLogRepository.class).find(ebicsRequestLog.getId());
String name = "Ebics report log " + ebicsRequestLog.getRequestType();
String fileLink =
ReportFactory.createReport("EbicsReportLog.rptdesign", name + "-${date}")
.addParam("EbicsReportLogId", ebicsRequestLog.getId())
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addFormat("pdf")
.toAttach(ebicsRequestLog)
.generate()
.getFileLink();
response.setView(ActionView.define(name).add("html", fileLink).map());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,177 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Body;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Header;
import com.axelor.apps.account.ebics.schema.h003.FDLOrderParamsType;
import com.axelor.apps.account.ebics.schema.h003.FDLOrderParamsType.DateRange;
import com.axelor.apps.account.ebics.schema.h003.FileFormatType;
import com.axelor.apps.account.ebics.schema.h003.MutableHeaderType;
import com.axelor.apps.account.ebics.schema.h003.ParameterDocument.Parameter;
import com.axelor.apps.account.ebics.schema.h003.ParameterDocument.Parameter.Value;
import com.axelor.apps.account.ebics.schema.h003.StandardOrderParamsType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderOrderDetailsType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderOrderDetailsType.OrderType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests.Authentication;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests.Encryption;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.Product;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.certificate.KeyUtil;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.OrderAttribute;
import com.axelor.exception.AxelorException;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
/**
* The <code>DInitializationRequestElement</code> is the common initialization for all ebics
* downloads.
*
* @author Hachani
*/
public class DInitializationRequestElement extends InitializationRequestElement {
/**
* Constructs a new <code>DInitializationRequestElement</code> for downloads initializations.
*
* @param session the current ebics session
* @param type the download order type (FDL, HTD, HPD)
* @param startRange the start range download
* @param endRange the end range download
* @throws EbicsException
*/
public DInitializationRequestElement(
EbicsSession session,
com.axelor.apps.bankpayment.ebics.client.OrderType type,
Date startRange,
Date endRange)
throws AxelorException {
super(session, type, generateName(type));
this.startRange = startRange;
this.endRange = endRange;
}
@Override
public void buildInitialization() throws AxelorException {
EbicsRequest request;
Header header;
Body body;
MutableHeaderType mutable;
StaticHeaderType xstatic;
Product product;
BankPubKeyDigests bankPubKeyDigests;
Authentication authentication;
Encryption encryption;
OrderType orderType;
StaticHeaderOrderDetailsType orderDetails;
mutable = EbicsXmlFactory.createMutableHeaderType("Initialisation", null);
product =
EbicsXmlFactory.createProduct(
session.getProduct().getLanguage(), session.getProduct().getName());
authentication =
EbicsXmlFactory.createAuthentication(
"X002",
"http://www.w3.org/2001/04/xmlenc#sha256",
decodeHex(KeyUtil.getKeyDigest(session.getBankX002Key())));
encryption =
EbicsXmlFactory.createEncryption(
"E002",
"http://www.w3.org/2001/04/xmlenc#sha256",
decodeHex(KeyUtil.getKeyDigest(session.getBankE002Key())));
bankPubKeyDigests = EbicsXmlFactory.createBankPubKeyDigests(authentication, encryption);
orderType = EbicsXmlFactory.createOrderType(type.getOrderType());
EbicsUser ebicsUser = session.getUser();
EbicsPartner ebicsPartner = ebicsUser.getEbicsPartner();
OrderAttribute orderAttribute = new OrderAttribute(type, ebicsPartner.getEbicsTypeSelect());
orderAttribute.build();
if (type.equals(com.axelor.apps.bankpayment.ebics.client.OrderType.FDL)) {
FDLOrderParamsType fDLOrderParamsType;
FileFormatType fileFormat;
fileFormat =
EbicsXmlFactory.createFileFormatType(
Locale.FRANCE.getCountry().toUpperCase(), session.getSessionParam("FORMAT"));
fDLOrderParamsType = EbicsXmlFactory.createFDLOrderParamsType(fileFormat);
if (startRange != null && endRange != null) {
DateRange range;
range = EbicsXmlFactory.createDateRange(startRange, endRange);
fDLOrderParamsType.setDateRange(range);
}
if (Boolean.getBoolean(session.getSessionParam("TEST"))) {
Parameter parameter;
Value value;
value = EbicsXmlFactory.createValue("String", "TRUE");
parameter = EbicsXmlFactory.createParameter("TEST", value);
fDLOrderParamsType.setParameterArray(new Parameter[] {parameter});
}
orderDetails =
EbicsXmlFactory.createStaticHeaderOrderDetailsType(
ebicsUser.getNextOrderId(),
orderAttribute.getOrderAttributes(),
orderType,
fDLOrderParamsType);
} else {
StandardOrderParamsType standardOrderParamsType;
standardOrderParamsType = EbicsXmlFactory.createStandardOrderParamsType();
// FIXME Some banks cannot handle OrderID element in download process. Add parameter in
// configuration!!!
orderDetails =
EbicsXmlFactory.createStaticHeaderOrderDetailsType(
ebicsUser.getNextOrderId(), // session.getUser().getPartner().nextOrderId(),
orderAttribute.getOrderAttributes(),
orderType,
standardOrderParamsType);
}
xstatic =
EbicsXmlFactory.createStaticHeaderType(
session.getBankID(),
nonce,
ebicsPartner.getPartnerId(),
product,
ebicsUser.getSecurityMedium(),
ebicsUser.getUserId(),
Calendar.getInstance(),
orderDetails,
bankPubKeyDigests);
header = EbicsXmlFactory.createEbicsRequestHeader(true, mutable, xstatic);
body = EbicsXmlFactory.createEbicsRequestBody();
request = EbicsXmlFactory.createEbicsRequest(1, "H003", header, body);
document = EbicsXmlFactory.createEbicsRequestDocument(request);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private Date startRange;
private Date endRange;
}

View File

@ -0,0 +1,120 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.apps.bankpayment.ebics.exception.ReturnCode;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
/**
* The <code>DInitializationResponseElement</code> is the response element for ebics downloads
* initializations.
*
* @author Hachani
*/
public class DInitializationResponseElement extends InitializationResponseElement {
/**
* Constructs a new <code>DInitializationResponseElement</code> object
*
* @param factory the content factory
* @param orderType the order type
* @param name the element name
*/
public DInitializationResponseElement(
ContentFactory factory, OrderType orderType, String name, EbicsUser ebicsUser) {
super(factory, orderType, name, ebicsUser);
}
@Override
public void build() throws AxelorException {
String bodyRetCode;
super.build();
if (!returnCode.isOk()) {
return;
}
bodyRetCode = response.getBody().getReturnCode().getStringValue();
returnCode = ReturnCode.toReturnCode(bodyRetCode, "");
numSegments = (int) response.getHeader().getStatic().getNumSegments();
if (numSegments > 0) {
segmentNumber = (int) response.getHeader().getMutable().getSegmentNumber().getLongValue();
lastSegment = response.getHeader().getMutable().getSegmentNumber().getLastSegment();
transactionKey =
response.getBody().getDataTransfer().getDataEncryptionInfo().getTransactionKey();
orderData = response.getBody().getDataTransfer().getOrderData().getByteArrayValue();
}
}
/**
* Returns the total segments number.
*
* @return the total segments number.
*/
public int getSegmentsNumber() {
return numSegments;
}
/**
* Returns The current segment number.
*
* @return the segment number.
*/
public int getSegmentNumber() {
return segmentNumber;
}
/**
* Checks if it is the last segment.
*
* @return True is it is the last segment.
*/
public boolean isLastSegment() {
return lastSegment;
}
/**
* Returns the transaction key.
*
* @return the transaction key.
*/
public byte[] getTransactionKey() {
return transactionKey;
}
/**
* Returns the order data.
*
* @return the order data.
*/
public byte[] getOrderData() {
return orderData;
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private int numSegments;
private int segmentNumber;
private boolean lastSegment;
private byte[] transactionKey;
private byte[] orderData;
}

View File

@ -0,0 +1,77 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Body;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Header;
import com.axelor.apps.account.ebics.schema.h003.MutableHeaderType;
import com.axelor.apps.account.ebics.schema.h003.MutableHeaderType.SegmentNumber;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.exception.AxelorException;
/**
* The <code>DTransferRequestElement</code> is the common elements for all ebics downloads.
*
* @author Hachani
*/
public class DTransferRequestElement extends TransferRequestElement {
/**
* Constructs a new <code>DTransferRequestElement</code> element.
*
* @param session the current ebics session
* @param type the order type
* @param segmentNumber the segment number
* @param lastSegment is it the last segment?
* @param transactionId the transaction ID
*/
public DTransferRequestElement(
EbicsSession session,
OrderType type,
int segmentNumber,
boolean lastSegment,
byte[] transactionId) {
super(session, generateName(type), type, segmentNumber, lastSegment, transactionId);
}
@Override
public void buildTransfer() throws AxelorException {
EbicsRequest request;
Header header;
Body body;
MutableHeaderType mutable;
SegmentNumber segmentNumber;
StaticHeaderType xstatic;
segmentNumber = EbicsXmlFactory.createSegmentNumber(this.segmentNumber, lastSegment);
mutable = EbicsXmlFactory.createMutableHeaderType("Transfer", segmentNumber);
xstatic = EbicsXmlFactory.createStaticHeaderType(session.getBankID(), transactionId);
header = EbicsXmlFactory.createEbicsRequestHeader(true, mutable, xstatic);
body = EbicsXmlFactory.createEbicsRequestBody();
request = EbicsXmlFactory.createEbicsRequest(1, "H003", header, body);
document = EbicsXmlFactory.createEbicsRequestDocument(request);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
}

View File

@ -0,0 +1,66 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
/**
* The <code>DTransferResponseElement</code> is the response element for all ebics downloads
* transfers.
*
* @author Hachani
*/
public class DTransferResponseElement extends TransferResponseElement {
/**
* Constructs a new <code>DTransferResponseElement</code> object.
*
* @param factory the content factory
* @param orderType the order type
* @param name the element name.
*/
public DTransferResponseElement(
ContentFactory factory, OrderType orderType, String name, EbicsUser ebicsUser) {
super(factory, name, ebicsUser);
}
@Override
public void build() throws AxelorException {
super.build();
orderData = response.getBody().getDataTransfer().getOrderData().getByteArrayValue();
}
/**
* Returns the order data.
*
* @return the order data.
*/
public byte[] getOrderData() {
return orderData;
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private byte[] orderData;
}

View File

@ -0,0 +1,231 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.bankpayment.ebics.client.EbicsRootElement;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.namespace.QName;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
public abstract class DefaultEbicsRootElement implements EbicsRootElement {
/**
* Constructs a new default <code>EbicsRootElement</code>
*
* @param session the current ebics session
*/
public DefaultEbicsRootElement(EbicsSession session) {
this.session = session;
suggestedPrefixes = new HashMap<String, String>();
}
/** Constructs a new default <code>EbicsRootElement</code> */
public DefaultEbicsRootElement() {
this(null);
}
/**
* Saves the Suggested Prefixes when the XML is printed
*
* @param uri the namespace URI
* @param prefix the namespace URI prefix
*/
protected static void setSaveSuggestedPrefixes(String uri, String prefix) {
suggestedPrefixes.put(uri, prefix);
}
/**
* Prints a pretty XML document using jdom framework.
*
* @param input the XML input
* @return the pretty XML document.
* @throws JDOMException
* @throws EbicsException pretty print fails
*/
public byte[] prettyPrint() throws AxelorException {
Document document;
XMLOutputter xmlOutputter;
SAXBuilder sxb;
ByteArrayOutputStream output;
sxb = new SAXBuilder();
output = new ByteArrayOutputStream();
xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
try {
document = sxb.build(new InputStreamReader(new ByteArrayInputStream(toByteArray()), "UTF-8"));
xmlOutputter.output(document, output);
} catch (IOException | JDOMException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
return output.toByteArray();
}
/**
* Inserts a schema location to the current ebics root element.
*
* @param namespaceURI the name space URI
* @param localPart the local part
* @param prefix the prefix
* @param value the value
*/
public void insertSchemaLocation(
String namespaceURI, String localPart, String prefix, String value) {
XmlCursor cursor;
cursor = document.newCursor();
while (cursor.hasNextToken()) {
if (cursor.isStart()) {
cursor.toNextToken();
cursor.insertAttributeWithValue(new QName(namespaceURI, localPart, prefix), value);
break;
} else {
cursor.toNextToken();
}
}
}
/**
* Generates a random file name with a prefix.
*
* @param type the order type.
* @return the generated file name.
*/
public static String generateName(OrderType type) {
return type.getOrderType() + new BigInteger(130, new SecureRandom()).toString(32);
}
/**
* Generates a random file name with a prefix.
*
* @param type the prefix to use.
* @return the generated file name.
*/
public static String generateName(String prefix) {
return prefix + new BigInteger(130, new SecureRandom()).toString(32);
}
@Override
public String toString() {
return new String(toByteArray());
}
@Override
public byte[] toByteArray() {
XmlOptions options;
options = new XmlOptions();
options.setSavePrettyPrint();
options.setSaveSuggestedPrefixes(suggestedPrefixes);
return document.xmlText(options).getBytes();
}
@Override
public void addNamespaceDecl(String prefix, String uri) {
XmlCursor cursor;
cursor = document.newCursor();
while (cursor.hasNextToken()) {
if (cursor.isStart()) {
cursor.toNextToken();
cursor.insertNamespace(prefix, uri);
break;
} else {
cursor.toNextToken();
}
}
}
@Override
public void validate() throws AxelorException {
ArrayList<XmlError> validationMessages;
boolean isValid;
validationMessages = new ArrayList<XmlError>();
isValid = document.validate(new XmlOptions().setErrorListener(validationMessages));
if (!isValid) {
String message;
Iterator<XmlError> iter;
iter = validationMessages.iterator();
message = "";
while (iter.hasNext()) {
if (!message.equals("")) {
message += ";";
}
message += iter.next().getMessage();
}
throw new AxelorException(TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, message);
}
}
@Override
public void save(OutputStream out) throws AxelorException, JDOMException {
try {
byte[] element;
element = prettyPrint();
out.write(element);
out.flush();
out.close();
} catch (IOException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
@Override
public void print(PrintStream stream) {
stream.println(document.toString());
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
protected XmlObject document;
protected EbicsSession session;
private static Map<String, String> suggestedPrefixes;
}

View File

@ -0,0 +1,102 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.client.EbicsRootElement;
import com.axelor.apps.bankpayment.ebics.exception.ReturnCode;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.apps.bankpayment.ebics.service.EbicsUserService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.inject.Beans;
import java.io.IOException;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
/**
* The <code>DefaultResponseElement</code> is the common element for all ebics server responses.
*
* @author Hachani
*/
public abstract class DefaultResponseElement extends DefaultEbicsRootElement {
/**
* Constructs a new ebics response element.
*
* @param factory the content factory containing the response.
* @param name the element name
*/
public DefaultResponseElement(ContentFactory factory, String name, EbicsUser ebicsUser) {
this.factory = factory;
this.name = name;
this.ebicsUser = ebicsUser;
}
/**
* Parses the content of a <code>ContentFactory</code>
*
* @param factory the content factory
* @throws EbicsException parse error
*/
protected void parse(ContentFactory factory) throws AxelorException {
try {
document = XmlObject.Factory.parse(factory.getContent());
} catch (XmlException | IOException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Reports the return code to the user.
*
* @throws EbicsException request fails.
*/
public void report(EbicsRootElement[] rootElements) throws AxelorException {
log(rootElements);
if (!returnCode.isOk()) {
returnCode.throwException();
}
}
protected void log(EbicsRootElement[] rootElements) {
if (ebicsUser != null && name != null && returnCode != null) {
Beans.get(EbicsUserService.class)
.logRequest(
ebicsUser.getId(), name.substring(0, 3), returnCode.getSymbolicName(), rootElements);
ebicsUser = null; // Prevent further log on same request
}
}
@Override
public String getName() {
return name + ".xml";
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private String name;
private EbicsUser ebicsUser;
protected ContentFactory factory;
protected ReturnCode returnCode;
}

View File

@ -0,0 +1,85 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.apps.bankpayment.ebics.client.UnsecuredRequestElement;
import com.axelor.exception.AxelorException;
import javax.xml.XMLConstants;
/**
* The <code>HIARequestElement</code> is the root element used to send the authentication and
* encryption keys to the ebics bank server
*
* @author hachani
*/
public class HIARequestElement extends DefaultEbicsRootElement {
/**
* Constructs a new HIA Request root element
*
* @param session the current ebics session
* @param orderId the order id, if null a random one is generated.
*/
public HIARequestElement(EbicsSession session) {
super(session);
}
@Override
public String getName() {
return "HIARequest.xml";
}
@Override
public void build() {
HIARequestOrderDataElement requestOrderData;
requestOrderData = new HIARequestOrderDataElement(session);
try {
requestOrderData.build();
unsecuredRequest =
new UnsecuredRequestElement(
session, OrderType.HIA, EbicsUtils.zip(requestOrderData.prettyPrint()));
unsecuredRequest.build();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public byte[] toByteArray() {
setSaveSuggestedPrefixes("http://www.ebics.org/H003", XMLConstants.DEFAULT_NS_PREFIX);
return unsecuredRequest.toByteArray();
}
@Override
public void validate() throws AxelorException {
unsecuredRequest.validate();
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private UnsecuredRequestElement unsecuredRequest;
}

View File

@ -0,0 +1,127 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.AuthenticationPubKeyInfoType;
import com.axelor.apps.account.ebics.schema.h003.EncryptionPubKeyInfoType;
import com.axelor.apps.account.ebics.schema.h003.HIARequestOrderDataType;
import com.axelor.apps.account.ebics.schema.h003.PubKeyValueType;
import com.axelor.apps.account.ebics.schema.xmldsig.RSAKeyValueType;
import com.axelor.apps.account.ebics.schema.xmldsig.X509DataType;
import com.axelor.apps.bankpayment.db.EbicsCertificate;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import java.math.BigInteger;
import java.util.Calendar;
import javax.xml.XMLConstants;
/**
* The <code>HIARequestOrderDataElement</code> is the element that contains X002 and E002 keys
* information needed for a HIA request in order to send the authentication and encryption user keys
* to the bank server.
*
* @author hachani
*/
public class HIARequestOrderDataElement extends DefaultEbicsRootElement {
/**
* Constructs a new HIA Request Order Data element
*
* @param session the current ebics session
*/
public HIARequestOrderDataElement(EbicsSession session) {
super(session);
}
@Override
public void build() {
HIARequestOrderDataType request;
AuthenticationPubKeyInfoType authenticationPubKeyInfo;
EncryptionPubKeyInfoType encryptionPubKeyInfo;
PubKeyValueType encryptionPubKeyValue;
X509DataType encryptionX509Data = null;
RSAKeyValueType encryptionRsaKeyValue;
PubKeyValueType authPubKeyValue;
X509DataType authX509Data = null;
RSAKeyValueType authRsaKeyValue;
EbicsCertificate certificate = session.getUser().getE002Certificate();
encryptionX509Data =
EbicsXmlFactory.createX509DataType(session.getUser().getDn(), certificate.getCertificate());
// Include Certificate issuer and serial ?
// encryptionX509Data = EbicsXmlFactory.createX509DataType(session.getUser().getDn(),
// certificate.getCertificate(), certificate.getIssuer(), new
// BigInteger(certificate.getSerial(), 16));
encryptionRsaKeyValue =
EbicsXmlFactory.createRSAKeyValueType(
new BigInteger(certificate.getPublicKeyExponent(), 16).toByteArray(),
new BigInteger(certificate.getPublicKeyModulus(), 16).toByteArray());
encryptionPubKeyValue =
EbicsXmlFactory.createH003PubKeyValueType(encryptionRsaKeyValue, Calendar.getInstance());
encryptionPubKeyInfo =
EbicsXmlFactory.createEncryptionPubKeyInfoType(
"E002", encryptionPubKeyValue, encryptionX509Data);
certificate = session.getUser().getX002Certificate();
authX509Data =
EbicsXmlFactory.createX509DataType(session.getUser().getDn(), certificate.getCertificate());
// Include Certificate issuer and serial ?
// authX509Data = EbicsXmlFactory.createX509DataType(session.getUser().getDn(),
// certificate.getCertificate(), certificate.getIssuer(), new
// BigInteger(certificate.getSerial(), 16));
authRsaKeyValue =
EbicsXmlFactory.createRSAKeyValueType(
new BigInteger(certificate.getPublicKeyExponent(), 16).toByteArray(),
new BigInteger(certificate.getPublicKeyModulus(), 16).toByteArray());
authPubKeyValue =
EbicsXmlFactory.createH003PubKeyValueType(authRsaKeyValue, Calendar.getInstance());
authenticationPubKeyInfo =
EbicsXmlFactory.createAuthenticationPubKeyInfoType("X002", authPubKeyValue, authX509Data);
request =
EbicsXmlFactory.createHIARequestOrderDataType(
authenticationPubKeyInfo,
encryptionPubKeyInfo,
session.getUser().getEbicsPartner().getPartnerId(),
session.getUser().getUserId());
document = EbicsXmlFactory.createHIARequestOrderDataDocument(request);
}
@Override
public String getName() {
return "HIARequestOrderData.xml";
}
@Override
public byte[] toByteArray() {
addNamespaceDecl("ds", "http://www.w3.org/2000/09/xmldsig#");
setSaveSuggestedPrefixes("http://www.ebics.org/S001", XMLConstants.DEFAULT_NS_PREFIX);
return super.toByteArray();
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
}

View File

@ -0,0 +1,74 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.exception.AxelorException;
/**
* The <code>HPBRequestElement</code> is the element to be sent when a HPB request is needed to
* retrieve the bank public keys
*
* @author hachani
*/
public class HPBRequestElement extends DefaultEbicsRootElement {
/**
* Constructs a new HPB Request element.
*
* @param session the current ebics session.
*/
public HPBRequestElement(EbicsSession session) {
super(session);
}
@Override
public String getName() {
return "HPBRequest.xml";
}
@Override
public void build() throws AxelorException {
SignedInfo signedInfo;
byte[] signature;
noPubKeyDigestsRequest = new NoPubKeyDigestsRequestElement(session);
noPubKeyDigestsRequest.build();
signedInfo = new SignedInfo(session.getUser(), noPubKeyDigestsRequest.getDigest());
signedInfo.build();
noPubKeyDigestsRequest.setAuthSignature(signedInfo.getSignatureType());
signature = signedInfo.sign(noPubKeyDigestsRequest.toByteArray());
noPubKeyDigestsRequest.setSignatureValue(signature);
}
@Override
public byte[] toByteArray() {
return noPubKeyDigestsRequest.toByteArray();
}
@Override
public void validate() throws AxelorException {
noPubKeyDigestsRequest.validate();
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private NoPubKeyDigestsRequestElement noPubKeyDigestsRequest;
}

View File

@ -0,0 +1,77 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.HPBResponseOrderDataDocument;
import com.axelor.apps.account.ebics.schema.h003.HPBResponseOrderDataType;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
/**
* The <code>HPBResponseOrderDataElement</code> contains the public bank keys in encrypted mode. The
* user should decrypt with his encryption key to have the bank public keys.
*
* @author hachani
*/
public class HPBResponseOrderDataElement extends DefaultResponseElement {
/**
* Creates a new <code>HPBResponseOrderDataElement</code> from a given content factory.
*
* @param factory the content factory.
*/
public HPBResponseOrderDataElement(ContentFactory factory, EbicsUser ebicsUser) {
super(factory, "HPBData", ebicsUser);
}
/**
* Returns the authentication bank certificate.
*
* @return the authentication bank certificate.
*/
public byte[] getBankX002Certificate() {
return response.getAuthenticationPubKeyInfo().getX509Data().getX509CertificateArray(0);
}
/**
* Returns the encryption bank certificate.
*
* @return the encryption bank certificate.
*/
public byte[] getBankE002Certificate() {
return response.getEncryptionPubKeyInfo().getX509Data().getX509CertificateArray(0);
}
@Override
public void build() throws AxelorException {
parse(factory);
response = ((HPBResponseOrderDataDocument) document).getHPBResponseOrderData();
}
@Override
public String getName() {
return "HPBData.xml";
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private HPBResponseOrderDataType response;
}

View File

@ -0,0 +1,89 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.apps.bankpayment.ebics.client.UnsecuredRequestElement;
import com.axelor.exception.AxelorException;
import java.lang.invoke.MethodHandles;
import javax.xml.XMLConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The INI request XML element. This root element is to be sent to the ebics server to initiate the
* signature certificate.
*
* @author hachani
*/
public class INIRequestElement extends DefaultEbicsRootElement {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Constructs a new INI request element.
*
* @param session the ebics session.
* @param orderId the order id, if null a random one is generated.
*/
public INIRequestElement(EbicsSession session) {
super(session);
}
@Override
public String getName() {
return "INIRequest.xml";
}
@Override
public void build() throws AxelorException {
SignaturePubKeyOrderDataElement signaturePubKey;
signaturePubKey = new SignaturePubKeyOrderDataElement(session);
log.debug("SignaturePubKeyOrderDataElement OK");
signaturePubKey.build();
log.debug("signaturePubKey.build OK");
unsecuredRequest =
new UnsecuredRequestElement(
session, OrderType.INI, EbicsUtils.zip(signaturePubKey.prettyPrint()));
log.debug("UnsecuredRequestElement OK");
unsecuredRequest.build();
log.debug("unsecuredRequest.build OK");
}
@Override
public byte[] toByteArray() {
setSaveSuggestedPrefixes("http://www.ebics.org/H003", XMLConstants.DEFAULT_NS_PREFIX);
return unsecuredRequest.toByteArray();
}
@Override
public void validate() throws AxelorException {
unsecuredRequest.validate();
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private UnsecuredRequestElement unsecuredRequest;
}

View File

@ -0,0 +1,175 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument;
import com.axelor.apps.account.ebics.schema.xmldsig.SignatureType;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import javax.crypto.Cipher;
import javax.xml.XMLConstants;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* The <code>InitializationRequestElement</code> is the root element for ebics uploads and downloads
* requests. The response of this element is then used either to upload or download files from the
* ebics server.
*
* @author Hachani
*/
public abstract class InitializationRequestElement extends DefaultEbicsRootElement {
/**
* Construct a new <code>InitializationRequestElement</code> root element.
*
* @param session the current ebics session.
* @param type the initialization type (UPLOAD, DOWNLOAD).
* @param name the element name.
* @throws EbicsException
*/
public InitializationRequestElement(EbicsSession session, OrderType type, String name)
throws AxelorException {
super(session);
this.type = type;
this.name = name;
nonce = EbicsUtils.generateNonce();
}
@Override
public void build() throws AxelorException {
SignedInfo signedInfo;
buildInitialization();
signedInfo = new SignedInfo(session.getUser(), getDigest());
signedInfo.build();
((EbicsRequestDocument) document)
.getEbicsRequest()
.setAuthSignature((SignatureType) signedInfo.getSignatureType());
((EbicsRequestDocument) document)
.getEbicsRequest()
.getAuthSignature()
.setSignatureValue(
EbicsXmlFactory.createSignatureValueType(signedInfo.sign(toByteArray())));
}
@Override
public String getName() {
return name + ".xml";
}
@Override
public byte[] toByteArray() {
setSaveSuggestedPrefixes("http://www.ebics.org/H003", XMLConstants.DEFAULT_NS_PREFIX);
return super.toByteArray();
}
/**
* Returns the digest value of the authenticated XML portions.
*
* @return the digest value.
* @throws EbicsException Failed to retrieve the digest value.
*/
public byte[] getDigest() throws AxelorException {
addNamespaceDecl("ds", "http://www.w3.org/2000/09/xmldsig#");
try {
return MessageDigest.getInstance("SHA-256", "BC").digest(EbicsUtils.canonize(toByteArray()));
} catch (NoSuchAlgorithmException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
} catch (NoSuchProviderException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Returns the element type.
*
* @return the element type.
*/
public String getType() {
return type.getOrderType();
}
/**
* Decodes an hexadecimal input.
*
* @param hex the hexadecimal input
* @return the decoded hexadecimal value
* @throws EbicsException
*/
protected byte[] decodeHex(byte[] hex) throws AxelorException {
if (hex == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
"Bank digest is empty, HPB request must be performed before");
}
try {
return Hex.decodeHex((new String(hex)).toCharArray());
} catch (DecoderException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Generates the upload transaction key
*
* @return the transaction key
*/
protected byte[] generateTransactionKey() throws AxelorException {
try {
Cipher cipher;
cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", BouncyCastleProvider.PROVIDER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, session.getBankE002Key());
return cipher.doFinal(nonce);
} catch (Exception e) {
e.printStackTrace();
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Builds the initialization request according to the element type.
*
* @throws EbicsException build fails
*/
public abstract void buildInitialization() throws AxelorException;
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private String name;
protected OrderType type;
protected byte[] nonce;
}

View File

@ -0,0 +1,87 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsResponseDocument;
import com.axelor.apps.account.ebics.schema.h003.EbicsResponseDocument.EbicsResponse;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.apps.bankpayment.ebics.exception.ReturnCode;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
/**
* The <code>InitializationResponseElement</code> is the common element for transfer initialization
* responses.
*
* @author Hachani
*/
public class InitializationResponseElement extends DefaultResponseElement {
/**
* Constructs a new <code>InitializationResponseElement</code> element.
*
* @param factory the content factory
* @param orderType the order type
* @param name the element name
*/
public InitializationResponseElement(
ContentFactory factory, OrderType orderType, String name, EbicsUser ebicsUser) {
super(factory, name, ebicsUser);
this.orderType = orderType;
}
@Override
public void build() throws AxelorException {
String code;
String text;
parse(factory);
response = ((EbicsResponseDocument) document).getEbicsResponse();
code = response.getHeader().getMutable().getReturnCode();
text = response.getHeader().getMutable().getReportText();
returnCode = ReturnCode.toReturnCode(code, text);
transactionId = response.getHeader().getStatic().getTransactionID();
}
/**
* Returns the transaction ID.
*
* @return the transaction ID.
*/
public byte[] getTransactionId() {
return transactionId;
}
/**
* Returns the order type.
*
* @return the order type.
*/
public String getOrderType() {
return orderType.getOrderType();
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
protected EbicsResponse response;
private OrderType orderType;
private byte[] transactionId;
}

View File

@ -0,0 +1,83 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsKeyManagementResponseDocument;
import com.axelor.apps.account.ebics.schema.h003.EbicsKeyManagementResponseDocument.EbicsKeyManagementResponse;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.exception.ReturnCode;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
/**
* The <code>KeyManagementResponseElement</code> is the common element for ebics key management
* requests. This element aims to control the returned code from the ebics server and throw an
* exception if it is not an EBICS_OK code.
*
* @author hachani
*/
public class KeyManagementResponseElement extends DefaultResponseElement {
/**
* Creates a new <code>KeyManagementResponseElement</code> from a given <code>ContentFactory
* </code>
*
* @param factory the content factory enclosing the ebics response
* @param name the element name
*/
public KeyManagementResponseElement(ContentFactory factory, String name, EbicsUser ebicsUser) {
super(factory, name, ebicsUser);
}
/**
* Returns the transaction key of the response.
*
* @return the transaction key.
*/
public byte[] getTransactionKey() {
return response.getBody().getDataTransfer().getDataEncryptionInfo().getTransactionKey();
}
/**
* Returns the order data of the response.
*
* @return the order data.
*/
@SuppressWarnings("deprecation")
public byte[] getOrderData() {
return response.getBody().getDataTransfer().getOrderData().byteArrayValue();
}
@Override
public void build() throws AxelorException {
String code;
String text;
parse(factory);
response = ((EbicsKeyManagementResponseDocument) document).getEbicsKeyManagementResponse();
code = response.getHeader().getMutable().getReturnCode();
text = response.getHeader().getMutable().getReportText();
returnCode = ReturnCode.toReturnCode(code, text);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private EbicsKeyManagementResponse response;
}

View File

@ -0,0 +1,162 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsNoPubKeyDigestsRequestDocument;
import com.axelor.apps.account.ebics.schema.h003.EbicsNoPubKeyDigestsRequestDocument.EbicsNoPubKeyDigestsRequest;
import com.axelor.apps.account.ebics.schema.h003.EbicsNoPubKeyDigestsRequestDocument.EbicsNoPubKeyDigestsRequest.Body;
import com.axelor.apps.account.ebics.schema.h003.EbicsNoPubKeyDigestsRequestDocument.EbicsNoPubKeyDigestsRequest.Header;
import com.axelor.apps.account.ebics.schema.h003.EmptyMutableHeaderType;
import com.axelor.apps.account.ebics.schema.h003.NoPubKeyDigestsRequestStaticHeaderType;
import com.axelor.apps.account.ebics.schema.h003.OrderDetailsType;
import com.axelor.apps.account.ebics.schema.h003.ProductElementType;
import com.axelor.apps.account.ebics.schema.xmldsig.SignatureType;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.client.OrderAttribute;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Calendar;
/**
* The <code>NoPubKeyDigestsRequestElement</code> is the root element for a HPB ebics server
* request.
*
* @author hachani
*/
public class NoPubKeyDigestsRequestElement extends DefaultEbicsRootElement {
/**
* Construct a new No Public Key Digests Request element.
*
* @param session the current ebics session.
*/
public NoPubKeyDigestsRequestElement(EbicsSession session) {
super(session);
}
/**
* Returns the digest value of the authenticated XML portions.
*
* @return the digest value.
* @throws EbicsException Failed to retrieve the digest value.
*/
public byte[] getDigest() throws AxelorException {
addNamespaceDecl("ds", "http://www.w3.org/2000/09/xmldsig#");
try {
return MessageDigest.getInstance("SHA-256", "BC").digest(EbicsUtils.canonize(toByteArray()));
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new AxelorException(e.getCause(), TraceBackRepository.TYPE_TECHNICAL, e.getMessage());
}
}
/**
* Sets the authentication signature of the <code>NoPubKeyDigestsRequestElement</code>
*
* @param authSignature the the authentication signature.
*/
public void setAuthSignature(SignatureType authSignature) {
((EbicsNoPubKeyDigestsRequestDocument) document)
.getEbicsNoPubKeyDigestsRequest()
.setAuthSignature(authSignature);
}
/**
* Sets the signature value of the request.
*
* @param signature the signature value
*/
public void setSignatureValue(byte[] signature) {
((EbicsNoPubKeyDigestsRequestDocument) document)
.getEbicsNoPubKeyDigestsRequest()
.getAuthSignature()
.setSignatureValue(EbicsXmlFactory.createSignatureValueType(signature));
}
@Override
public void build() throws AxelorException {
EbicsNoPubKeyDigestsRequest request;
Body body;
Header header;
EmptyMutableHeaderType mutable;
NoPubKeyDigestsRequestStaticHeaderType xstatic;
ProductElementType product;
OrderDetailsType orderDetails;
EbicsUser ebicsUser = session.getUser();
EbicsPartner ebicsPartner = ebicsUser.getEbicsPartner();
OrderAttribute orderAttribute =
new OrderAttribute(OrderType.HPB, ebicsPartner.getEbicsTypeSelect());
orderAttribute.build();
product =
EbicsXmlFactory.creatProductElementType(
session.getProduct().getLanguage(), session.getProduct().getName());
orderDetails =
EbicsXmlFactory.createOrderDetailsType(
orderAttribute.getOrderAttributes(), null, OrderType.HPB.getOrderType());
xstatic =
EbicsXmlFactory.createNoPubKeyDigestsRequestStaticHeaderType(
session.getBankID(),
EbicsUtils.generateNonce(),
Calendar.getInstance(),
ebicsPartner.getPartnerId(),
ebicsUser.getUserId(),
product,
orderDetails,
ebicsUser.getSecurityMedium());
mutable = EbicsXmlFactory.createEmptyMutableHeaderType();
header = EbicsXmlFactory.createDigestsRequestHeader(true, mutable, xstatic);
body = EbicsXmlFactory.createDigestsRequestBody();
request = EbicsXmlFactory.createEbicsNoPubKeyDigestsRequest(1, "H003", header, body);
document = EbicsXmlFactory.createEbicsNoPubKeyDigestsRequestDocument(request);
}
@Override
public byte[] toByteArray() {
// setSaveSuggestedPrefixes("http://www.w3.org/2000/09/xmldsig#", "ds"); //TODO TO CHECK
setSaveSuggestedPrefixes("http://www.w3.org/2000/09/xmldsig#", "ds");
setSaveSuggestedPrefixes("http://www.ebics.org/H003", "");
//
// addNamespaceDecl("ds", "http://www.w3.org/2000/09/xmldsig#");
//
// setSaveSuggestedPrefixes("http://www.ebics.org/H003", XMLConstants.DEFAULT_NS_PREFIX);
return super.toByteArray();
}
@Override
public String getName() {
return "NoPubKeyDigestsRequest.xml";
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
}

View File

@ -0,0 +1,120 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Body;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Body.TransferReceipt;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Header;
import com.axelor.apps.account.ebics.schema.h003.MutableHeaderType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import javax.xml.XMLConstants;
/**
* The <code>ReceiptRequestElement</code> is the element containing the receipt request to tell the
* server bank that all segments are received.
*
* @author Hachani
*/
public class ReceiptRequestElement extends DefaultEbicsRootElement {
/**
* Construct a new <code>ReceiptRequestElement</code> element.
*
* @param session the current ebics session
* @param name the element name
*/
public ReceiptRequestElement(EbicsSession session, byte[] transactionId, String name) {
super(session);
this.transactionId = transactionId;
this.name = name;
}
@Override
public void build() throws AxelorException {
EbicsRequest request;
Header header;
Body body;
MutableHeaderType mutable;
StaticHeaderType xstatic;
TransferReceipt transferReceipt;
SignedInfo signedInfo;
mutable = EbicsXmlFactory.createMutableHeaderType("Receipt", null);
xstatic = EbicsXmlFactory.createStaticHeaderType(session.getBankID(), transactionId);
header = EbicsXmlFactory.createEbicsRequestHeader(true, mutable, xstatic);
transferReceipt = EbicsXmlFactory.createTransferReceipt(true, 0);
body = EbicsXmlFactory.createEbicsRequestBody(transferReceipt);
request = EbicsXmlFactory.createEbicsRequest(1, "H003", header, body);
document = EbicsXmlFactory.createEbicsRequestDocument(request);
signedInfo = new SignedInfo(session.getUser(), getDigest());
signedInfo.build();
((EbicsRequestDocument) document)
.getEbicsRequest()
.setAuthSignature(signedInfo.getSignatureType());
((EbicsRequestDocument) document)
.getEbicsRequest()
.getAuthSignature()
.setSignatureValue(
EbicsXmlFactory.createSignatureValueType(signedInfo.sign(toByteArray())));
}
@Override
public byte[] toByteArray() {
setSaveSuggestedPrefixes("http://www.ebics.org/H003", XMLConstants.DEFAULT_NS_PREFIX);
return super.toByteArray();
}
@Override
public String getName() {
return name + ".xml";
}
/**
* Returns the digest value of the authenticated XML portions.
*
* @return the digest value.
* @throws EbicsException Failed to retrieve the digest value.
*/
public byte[] getDigest() throws AxelorException {
addNamespaceDecl("ds", "http://www.w3.org/2000/09/xmldsig#");
try {
return MessageDigest.getInstance("SHA-256", "BC").digest(EbicsUtils.canonize(toByteArray()));
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private byte[] transactionId;
private String name;
}

View File

@ -0,0 +1,72 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsResponseDocument;
import com.axelor.apps.account.ebics.schema.h003.EbicsResponseDocument.EbicsResponse;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.client.EbicsRootElement;
import com.axelor.apps.bankpayment.ebics.exception.ReturnCode;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
/**
* The <code>ReceiptResponseElement</code> is the response element for ebics receipt request.
*
* @author Hachani
*/
public class ReceiptResponseElement extends DefaultResponseElement {
/**
* Constructs a new <code>ReceiptResponseElement</code> object
*
* @param factory the content factory
* @param name the element name
*/
public ReceiptResponseElement(ContentFactory factory, String name, EbicsUser ebicsUser) {
super(factory, name, ebicsUser);
}
@Override
public void build() throws AxelorException {
String code;
String text;
EbicsResponse response;
parse(factory);
response = ((EbicsResponseDocument) document).getEbicsResponse();
code = response.getHeader().getMutable().getReturnCode();
text = response.getHeader().getMutable().getReportText();
returnCode = ReturnCode.toReturnCode(code, text);
}
@Override
public void report(EbicsRootElement[] rootElements) throws AxelorException {
log(rootElements);
if (!returnCode.equals(ReturnCode.EBICS_DOWNLOAD_POSTPROCESS_DONE)) {
returnCode.throwException();
}
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
}

View File

@ -0,0 +1,155 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.DataEncryptionInfoType.EncryptionPubKeyDigest;
import com.axelor.apps.account.ebics.schema.h003.DataTransferRequestType;
import com.axelor.apps.account.ebics.schema.h003.DataTransferRequestType.DataEncryptionInfo;
import com.axelor.apps.account.ebics.schema.h003.DataTransferRequestType.SignatureData;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Body;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Header;
import com.axelor.apps.account.ebics.schema.h003.MutableHeaderType;
import com.axelor.apps.account.ebics.schema.h003.StandardOrderParamsType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderOrderDetailsType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderOrderDetailsType.OrderType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests.Authentication;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests.Encryption;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.Product;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.certificate.KeyUtil;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.client.OrderAttribute;
import com.axelor.exception.AxelorException;
import java.util.Calendar;
import javax.crypto.spec.SecretKeySpec;
/**
* The <code>SPRRequestElement</code> is the request element for revoking a subscriber
*
* @author Hachani
*/
public class SPRRequestElement extends InitializationRequestElement {
/**
* Constructs a new SPR request element.
*
* @param session the current ebic session.
*/
public SPRRequestElement(EbicsSession session) throws AxelorException {
super(session, com.axelor.apps.bankpayment.ebics.client.OrderType.SPR, "SPRRequest.xml");
keySpec = new SecretKeySpec(nonce, "EAS");
}
@Override
public void buildInitialization() throws AxelorException {
EbicsRequest request;
Header header;
Body body;
MutableHeaderType mutable;
StaticHeaderType xstatic;
Product product;
BankPubKeyDigests bankPubKeyDigests;
Authentication authentication;
Encryption encryption;
DataTransferRequestType dataTransfer;
DataEncryptionInfo dataEncryptionInfo;
SignatureData signatureData;
EncryptionPubKeyDigest encryptionPubKeyDigest;
StaticHeaderOrderDetailsType orderDetails;
OrderType orderType;
StandardOrderParamsType standardOrderParamsType;
UserSignature userSignature;
EbicsUser ebicsUser = session.getUser();
EbicsPartner ebicsPartner = ebicsUser.getEbicsPartner();
userSignature =
new UserSignature(ebicsUser, generateName("SIG"), "A005", " ".getBytes(), " ".getBytes());
// TODO Manage the ebics ts case, with an eletronic signature of the user
userSignature.build();
userSignature.validate();
mutable = EbicsXmlFactory.createMutableHeaderType("Initialisation", null);
product =
EbicsXmlFactory.createProduct(
session.getProduct().getLanguage(), session.getProduct().getName());
authentication =
EbicsXmlFactory.createAuthentication(
"X002",
"http://www.w3.org/2001/04/xmlenc#sha256",
decodeHex(KeyUtil.getKeyDigest(session.getBankX002Key())));
encryption =
EbicsXmlFactory.createEncryption(
"E002",
"http://www.w3.org/2001/04/xmlenc#sha256",
decodeHex(KeyUtil.getKeyDigest(session.getBankE002Key())));
bankPubKeyDigests = EbicsXmlFactory.createBankPubKeyDigests(authentication, encryption);
orderType = EbicsXmlFactory.createOrderType(type.getOrderType());
standardOrderParamsType = EbicsXmlFactory.createStandardOrderParamsType();
OrderAttribute orderAttribute = new OrderAttribute(type, ebicsPartner.getEbicsTypeSelect());
orderAttribute.build();
orderDetails =
EbicsXmlFactory.createStaticHeaderOrderDetailsType(
ebicsUser.getNextOrderId(),
orderAttribute.getOrderAttributes(),
orderType,
standardOrderParamsType);
xstatic =
EbicsXmlFactory.createStaticHeaderType(
session.getBankID(),
nonce,
0,
ebicsPartner.getPartnerId(),
product,
ebicsUser.getSecurityMedium(),
ebicsUser.getUserId(),
Calendar.getInstance(),
orderDetails,
bankPubKeyDigests);
header = EbicsXmlFactory.createEbicsRequestHeader(true, mutable, xstatic);
encryptionPubKeyDigest =
EbicsXmlFactory.createEncryptionPubKeyDigest(
"E002",
"http://www.w3.org/2001/04/xmlenc#sha256",
decodeHex(KeyUtil.getKeyDigest(session.getBankX002Key())));
signatureData =
EbicsXmlFactory.createSignatureData(
true, EbicsUtils.encrypt(EbicsUtils.zip(userSignature.prettyPrint()), keySpec));
dataEncryptionInfo =
EbicsXmlFactory.createDataEncryptionInfo(
true, encryptionPubKeyDigest, generateTransactionKey());
dataTransfer = EbicsXmlFactory.createDataTransferRequestType(dataEncryptionInfo, signatureData);
body = EbicsXmlFactory.createEbicsRequestBody(dataTransfer);
request = EbicsXmlFactory.createEbicsRequest(1, "H003", header, body);
document = EbicsXmlFactory.createEbicsRequestDocument(request);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private SecretKeySpec keySpec;
}

View File

@ -0,0 +1,60 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsResponseDocument;
import com.axelor.apps.account.ebics.schema.h003.EbicsResponseDocument.EbicsResponse;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.exception.ReturnCode;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
/**
* The <code>SPRResponseElement</code> is the response element for an ebics subscriber revoking.
*
* @author Hachani
*/
public class SPRResponseElement extends DefaultResponseElement {
/**
* Constructs a new SPR response element.
*
* @param factory the content factory
*/
public SPRResponseElement(ContentFactory factory, EbicsUser ebicsUser) {
super(factory, "SPRResponse.xml", ebicsUser);
}
@Override
public void build() throws AxelorException {
String code;
String text;
parse(factory);
response = ((EbicsResponseDocument) document).getEbicsResponse();
code = response.getHeader().getMutable().getReturnCode();
text = response.getHeader().getMutable().getReportText();
returnCode = ReturnCode.toReturnCode(code, text);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private EbicsResponse response;
}

View File

@ -0,0 +1,104 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.s001.PubKeyValueType;
import com.axelor.apps.account.ebics.schema.s001.SignaturePubKeyInfoType;
import com.axelor.apps.account.ebics.schema.s001.SignaturePubKeyOrderDataType;
import com.axelor.apps.account.ebics.schema.xmldsig.RSAKeyValueType;
import com.axelor.apps.account.ebics.schema.xmldsig.X509DataType;
import com.axelor.apps.bankpayment.db.EbicsCertificate;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.exception.AxelorException;
import java.math.BigInteger;
import java.util.Calendar;
import javax.xml.XMLConstants;
/**
* The <code>SignaturePubKeyOrderDataElement</code> is the order data component for the INI request.
*
* @author hachani
*/
public class SignaturePubKeyOrderDataElement extends DefaultEbicsRootElement {
/**
* Creates a new Signature Public Key Order Data element.
*
* @param session the current ebics session
*/
public SignaturePubKeyOrderDataElement(EbicsSession session) {
super(session);
}
@Override
public void build() throws AxelorException {
SignaturePubKeyInfoType signaturePubKeyInfo;
X509DataType x509Data;
RSAKeyValueType rsaKeyValue;
PubKeyValueType pubKeyValue;
SignaturePubKeyOrderDataType signaturePubKeyOrderData;
EbicsCertificate certificate = session.getUser().getA005Certificate();
System.out.println("Certificate : " + new String(certificate.getCertificate()));
System.out.println("Certificate size : " + certificate.getCertificate().length);
EbicsCertificate ebicsEertificate = session.getUser().getA005Certificate();
// Include certificate issuer and serial (certificate informations)
// x509Data = EbicsXmlFactory.createX509DataType(ebicsEertificate.getSubject(), certEncoded,
// ebicsEertificate.getIssuer(), new BigInteger(ebicsEertificate.getSerial(), 16));
x509Data =
EbicsXmlFactory.createX509DataType(
ebicsEertificate.getSubject(), ebicsEertificate.getCertificate());
rsaKeyValue =
EbicsXmlFactory.createRSAKeyValueType(
new BigInteger(ebicsEertificate.getPublicKeyExponent(), 16).toByteArray(),
new BigInteger(ebicsEertificate.getPublicKeyModulus(), 16).toByteArray());
pubKeyValue = EbicsXmlFactory.createPubKeyValueType(rsaKeyValue, Calendar.getInstance());
signaturePubKeyInfo =
EbicsXmlFactory.createSignaturePubKeyInfoType(x509Data, pubKeyValue, "A005");
signaturePubKeyOrderData =
EbicsXmlFactory.createSignaturePubKeyOrderData(
signaturePubKeyInfo,
session.getUser().getEbicsPartner().getPartnerId(),
session.getUser().getUserId());
document = EbicsXmlFactory.createSignaturePubKeyOrderDataDocument(signaturePubKeyOrderData);
}
@Override
public String getName() {
return "SignaturePubKeyOrderData.xml";
}
@Override
public byte[] toByteArray() {
addNamespaceDecl("ds", "http://www.w3.org/2000/09/xmldsig#");
setSaveSuggestedPrefixes("http://www.ebics.org/S001", XMLConstants.DEFAULT_NS_PREFIX);
return super.toByteArray();
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
}

View File

@ -0,0 +1,178 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.xmldsig.CanonicalizationMethodType;
import com.axelor.apps.account.ebics.schema.xmldsig.DigestMethodType;
import com.axelor.apps.account.ebics.schema.xmldsig.ReferenceType;
import com.axelor.apps.account.ebics.schema.xmldsig.SignatureMethodType;
import com.axelor.apps.account.ebics.schema.xmldsig.SignatureType;
import com.axelor.apps.account.ebics.schema.xmldsig.SignedInfoType;
import com.axelor.apps.account.ebics.schema.xmldsig.TransformType;
import com.axelor.apps.account.ebics.schema.xmldsig.TransformsType;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.service.EbicsUserService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import java.io.ByteArrayInputStream;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.transforms.TransformationException;
import org.apache.xml.security.utils.IgnoreAllErrorHandler;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* A representation of the SignedInfo element performing signature for signed ebics requests
*
* @author hachani
*/
public class SignedInfo extends DefaultEbicsRootElement {
/**
* Constructs a new <code>SignedInfo</code> element
*
* @param digest the digest value
*/
public SignedInfo(EbicsUser user, byte[] digest) {
this.user = user;
this.digest = digest;
}
@Override
public void build() throws AxelorException {
CanonicalizationMethodType canonicalizationMethod;
SignatureMethodType signatureMethod;
ReferenceType reference;
TransformsType transforms;
DigestMethodType digestMethod;
TransformType transform;
SignedInfoType signedInfo;
if (digest == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get("digest value cannot be null"));
}
transform = EbicsXmlFactory.createTransformType(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
digestMethod =
EbicsXmlFactory.createDigestMethodType("http://www.w3.org/2001/04/xmlenc#sha256");
transforms = EbicsXmlFactory.createTransformsType(new TransformType[] {transform});
reference =
EbicsXmlFactory.createReferenceType(
"#xpointer(//*[@authenticate='true'])", transforms, digestMethod, digest);
signatureMethod =
EbicsXmlFactory.createSignatureMethodType(
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
canonicalizationMethod =
EbicsXmlFactory.createCanonicalizationMethodType(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
signedInfo =
EbicsXmlFactory.createSignedInfoType(
canonicalizationMethod, signatureMethod, new ReferenceType[] {reference});
document = EbicsXmlFactory.createSignatureType(signedInfo);
}
/**
* Returns the digest value.
*
* @return the digest value.
*/
public byte[] getDigest() {
return digest;
}
/**
* Returns the signed info element as an <code>XmlObject</code>
*
* @return he signed info element
* @throws EbicsException user Signature and Canonicalization errors
*/
public SignatureType getSignatureType() {
return ((SignatureType) document);
}
/**
* Canonizes and signs a given input with the authentication private key. of the EBICS user.
*
* <p>The given input to be signed is first Canonized using the
* http://www.w3.org/TR/2001/REC-xml-c14n-20010315 algorithm.
*
* <p>The element to be canonized is only the SignedInfo element that should be contained in the
* request to be signed. Otherwise, a {@link TransformationException} is thrown.
*
* <p>The namespace of the SignedInfo element should be named <b>ds</b> as specified in the EBICS
* specification for common namespaces nomination.
*
* <p>The signature is ensured using the user X002 private key. This step is done in {@link
* EbicsUser#authenticate(byte[]) authenticate}.
*
* @param toSign the input to sign
* @return the signed input
* @throws EbicsException signature fails.
*/
public byte[] sign(byte[] toSign) throws AxelorException {
try {
DocumentBuilderFactory factory;
DocumentBuilder builder;
Document document;
Node node;
Canonicalizer canonicalizer;
factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
builder = factory.newDocumentBuilder();
builder.setErrorHandler(new IgnoreAllErrorHandler());
document = builder.parse(new ByteArrayInputStream(toSign));
node = XPathAPI.selectSingleNode(document, "//ds:SignedInfo");
canonicalizer = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
return Beans.get(EbicsUserService.class)
.authenticate(user, canonicalizer.canonicalizeSubtree(node));
} catch (Exception e) {
e.printStackTrace();
throw new AxelorException(e, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR);
}
}
@Override
public byte[] toByteArray() {
addNamespaceDecl(XMLConstants.DEFAULT_NS_PREFIX, "http://www.ebics.org/H003");
setSaveSuggestedPrefixes("http://www.w3.org/2000/09/xmldsig#", "ds");
return super.toByteArray();
}
@Override
public String getName() {
return "SignedInfo.xml";
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private byte[] digest;
private EbicsUser user;
}

View File

@ -0,0 +1,135 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import javax.xml.XMLConstants;
/**
* The <code>TransferRequestElement</code> is the common root element for all ebics transfer for the
* bank server.
*
* @author Hachani
*/
public abstract class TransferRequestElement extends DefaultEbicsRootElement {
/**
* Constructs a new <code>TransferRequestElement</code> element.
*
* @param session the current ebics session
* @param name the element name
* @param type the order type
* @param segmentNumber the segment number to be sent
* @param lastSegment is it the last segment?
* @param transactionID the transaction ID
*/
public TransferRequestElement(
EbicsSession session,
String name,
OrderType type,
int segmentNumber,
boolean lastSegment,
byte[] transactionId) {
super(session);
this.type = type;
this.name = name;
this.segmentNumber = segmentNumber;
this.lastSegment = lastSegment;
this.transactionId = transactionId;
}
@Override
public void build() throws AxelorException {
SignedInfo signedInfo;
buildTransfer();
signedInfo = new SignedInfo(session.getUser(), getDigest());
signedInfo.build();
((EbicsRequestDocument) document)
.getEbicsRequest()
.setAuthSignature(signedInfo.getSignatureType());
((EbicsRequestDocument) document)
.getEbicsRequest()
.getAuthSignature()
.setSignatureValue(
EbicsXmlFactory.createSignatureValueType(signedInfo.sign(toByteArray())));
}
@Override
public String getName() {
return name + ".xml";
}
/**
* Returns the digest value of the authenticated XML portions.
*
* @return the digest value.
* @throws EbicsException Failed to retrieve the digest value.
*/
public byte[] getDigest() throws AxelorException {
addNamespaceDecl("ds", "http://www.w3.org/2000/09/xmldsig#");
try {
return MessageDigest.getInstance("SHA-256", "BC").digest(EbicsUtils.canonize(toByteArray()));
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new AxelorException(
e.getCause(), TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
}
}
/**
* Returns the order type of the element.
*
* @return the order type element.
*/
public String getOrderType() {
return type.getOrderType();
}
@Override
public byte[] toByteArray() {
setSaveSuggestedPrefixes("http://www.ebics.org/H003", XMLConstants.DEFAULT_NS_PREFIX);
return super.toByteArray();
}
/**
* Builds the transfer request.
*
* @throws EbicsException
*/
public abstract void buildTransfer() throws AxelorException;
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
protected int segmentNumber;
protected boolean lastSegment;
protected byte[] transactionId;
private OrderType type;
private String name;
}

View File

@ -0,0 +1,62 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.EbicsResponseDocument;
import com.axelor.apps.account.ebics.schema.h003.EbicsResponseDocument.EbicsResponse;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.exception.ReturnCode;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.exception.AxelorException;
/**
* The <code>TransferResponseElement</code> is the common element response for all ebics transfers.
*
* @author Hachani
*/
public class TransferResponseElement extends DefaultResponseElement {
/**
* Constructs a new <code>TransferResponseElement</code> element.
*
* @param factory the content factory
* @param orderType the order type
* @param name the element name;
*/
public TransferResponseElement(ContentFactory factory, String name, EbicsUser ebicsUser) {
super(factory, name, ebicsUser);
}
@Override
public void build() throws AxelorException {
String code;
String text;
parse(factory);
response = ((EbicsResponseDocument) document).getEbicsResponse();
code = response.getHeader().getMutable().getReturnCode();
text = response.getHeader().getMutable().getReportText();
returnCode = ReturnCode.toReturnCode(code, text);
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
protected EbicsResponse response;
}

View File

@ -0,0 +1,294 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.DataEncryptionInfoType.EncryptionPubKeyDigest;
import com.axelor.apps.account.ebics.schema.h003.DataTransferRequestType;
import com.axelor.apps.account.ebics.schema.h003.DataTransferRequestType.DataEncryptionInfo;
import com.axelor.apps.account.ebics.schema.h003.DataTransferRequestType.SignatureData;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Body;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Header;
import com.axelor.apps.account.ebics.schema.h003.FULOrderParamsType;
import com.axelor.apps.account.ebics.schema.h003.FileFormatType;
import com.axelor.apps.account.ebics.schema.h003.MutableHeaderType;
import com.axelor.apps.account.ebics.schema.h003.ParameterDocument.Parameter;
import com.axelor.apps.account.ebics.schema.h003.ParameterDocument.Parameter.Value;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderOrderDetailsType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderOrderDetailsType.OrderType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests.Authentication;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.BankPubKeyDigests.Encryption;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType.Product;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.ebics.certificate.KeyUtil;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.EbicsUtils;
import com.axelor.apps.bankpayment.ebics.client.OrderAttribute;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.apps.bankpayment.ebics.io.Splitter;
import com.axelor.exception.AxelorException;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.XMLConstants;
import org.jdom.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The <code>UInitializationRequestElement</code> is the common initialization element for all ebics
* file uploads.
*
* @author Hachani
*/
public class UInitializationRequestElement extends InitializationRequestElement {
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* Constructs a new <code>UInitializationRequestElement</code> for uploads initializations.
*
* @param session the current ebics session.
* @param orderType the upload order type
* @param userData the user data to be uploaded
* @throws EbicsException
*/
public UInitializationRequestElement(
EbicsSession session,
com.axelor.apps.bankpayment.ebics.client.OrderType orderType,
byte[] userData,
byte[] userSignatureData)
throws AxelorException {
super(session, orderType, generateName(orderType));
this.userData = userData;
this.userSignatureData = userSignatureData;
keySpec = new SecretKeySpec(nonce, "EAS");
splitter = new Splitter(userData);
}
@Override
public void buildInitialization() throws AxelorException {
EbicsRequest request;
Header header;
Body body;
MutableHeaderType mutable;
StaticHeaderType xstatic;
Product product;
BankPubKeyDigests bankPubKeyDigests;
Authentication authentication;
Encryption encryption;
DataTransferRequestType dataTransfer;
DataEncryptionInfo dataEncryptionInfo;
SignatureData signatureData;
EncryptionPubKeyDigest encryptionPubKeyDigest;
StaticHeaderOrderDetailsType orderDetails;
FULOrderParamsType fULOrderParams;
OrderType orderType;
FileFormatType fileFormat;
List<Parameter> parameters;
EbicsUser ebicsUser = session.getUser();
EbicsPartner ebicsPartner = ebicsUser.getEbicsPartner();
if (ebicsPartner.getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_TS) {
EbicsUser signatoryUser = session.getSignatoryUser();
userSignature =
new UserSignature(
signatoryUser, generateName("UserSignature"), "A005", userData, userSignatureData);
} else {
userSignature =
new UserSignature(ebicsUser, generateName("UserSignature"), "A005", userData, null);
}
userSignature.build();
log.debug("user signature pretty print : {}", userSignature.toString());
userSignature.validate();
log.debug("user signature pretty print : {}", userSignature.toString());
splitter.readInput(true, keySpec);
mutable = EbicsXmlFactory.createMutableHeaderType("Initialisation", null);
product =
EbicsXmlFactory.createProduct(
session.getProduct().getLanguage(), session.getProduct().getName());
authentication =
EbicsXmlFactory.createAuthentication(
"X002",
"http://www.w3.org/2001/04/xmlenc#sha256",
decodeHex(KeyUtil.getKeyDigest(session.getBankX002Key())));
encryption =
EbicsXmlFactory.createEncryption(
"E002",
"http://www.w3.org/2001/04/xmlenc#sha256",
decodeHex(KeyUtil.getKeyDigest(session.getBankE002Key())));
bankPubKeyDigests = EbicsXmlFactory.createBankPubKeyDigests(authentication, encryption);
orderType = EbicsXmlFactory.createOrderType(type.getOrderType());
fileFormat =
EbicsXmlFactory.createFileFormatType(
Locale.FRANCE.getCountry(), session.getSessionParam("FORMAT"));
fULOrderParams = EbicsXmlFactory.createFULOrderParamsType(fileFormat);
parameters = new ArrayList<Parameter>();
if (Boolean.valueOf(session.getSessionParam("TEST")).booleanValue()) {
Parameter parameter;
Value value;
value = EbicsXmlFactory.createValue("String", "TRUE");
parameter = EbicsXmlFactory.createParameter("TEST", value);
parameters.add(parameter);
}
if (Boolean.valueOf(session.getSessionParam("EBCDIC")).booleanValue()) {
Parameter parameter;
Value value;
value = EbicsXmlFactory.createValue("String", "TRUE");
parameter = EbicsXmlFactory.createParameter("EBCDIC", value);
parameters.add(parameter);
}
if (parameters.size() > 0) {
fULOrderParams.setParameterArray(parameters.toArray(new Parameter[parameters.size()]));
}
OrderAttribute orderAttribute = new OrderAttribute(type, ebicsPartner.getEbicsTypeSelect());
orderAttribute.build();
orderDetails =
EbicsXmlFactory.createStaticHeaderOrderDetailsType(
ebicsUser.getNextOrderId(),
orderAttribute.getOrderAttributes(),
orderType,
fULOrderParams);
xstatic =
EbicsXmlFactory.createStaticHeaderType(
session.getBankID(),
nonce,
splitter.getSegmentNumber(),
ebicsPartner.getPartnerId(),
product,
ebicsUser.getSecurityMedium(),
ebicsUser.getUserId(),
Calendar.getInstance(),
orderDetails,
bankPubKeyDigests);
header = EbicsXmlFactory.createEbicsRequestHeader(true, mutable, xstatic);
encryptionPubKeyDigest =
EbicsXmlFactory.createEncryptionPubKeyDigest(
"E002",
"http://www.w3.org/2001/04/xmlenc#sha256",
decodeHex(KeyUtil.getKeyDigest(session.getBankE002Key())));
System.out.println(
"signature ----------------------------------------------------------------------------");
System.out.println(userSignature.toString());
// USE PREVALIDATION
// PreValidation preValidation = PreValidation.Factory.newInstance();
// preValidation.setAuthenticate(true);
// DataDigestType dataDigest = DataDigestType.Factory.newInstance();
// dataDigest.setSignatureVersion("A005");
// dataDigest.setStringValue("XXXXXXX);
// preValidation.setDataDigestArray(new DataDigestType[] {dataDigest});
signatureData =
EbicsXmlFactory.createSignatureData(
true, EbicsUtils.encrypt(EbicsUtils.zip(userSignature.prettyPrint()), keySpec));
dataEncryptionInfo =
EbicsXmlFactory.createDataEncryptionInfo(
true, encryptionPubKeyDigest, generateTransactionKey());
dataTransfer = EbicsXmlFactory.createDataTransferRequestType(dataEncryptionInfo, signatureData);
// USE PREVALIDATION
// body = EbicsXmlFactory.createEbicsRequestBody(dataTransfer, preValidation);
body = EbicsXmlFactory.createEbicsRequestBody(dataTransfer);
request = EbicsXmlFactory.createEbicsRequest(1, "H003", header, body);
document = EbicsXmlFactory.createEbicsRequestDocument(request);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try {
this.save(bout);
} catch (JDOMException e) {
// TODO Bloc catch généré automatiquement
e.printStackTrace();
}
System.out.println(
"Requete signature ----------------------------------------------------------------------------");
System.out.println(bout.toString());
}
@Override
public byte[] toByteArray() {
setSaveSuggestedPrefixes("http://www.ebics.org/H003", XMLConstants.DEFAULT_NS_PREFIX);
return super.toByteArray();
}
/**
* Returns the user signature data.
*
* @return the user signature data.
*/
public UserSignature getUserSignature() {
return userSignature;
}
/**
* Returns the content of a given segment.
*
* @param segment the segment number
* @return the content of the given segment
*/
public ContentFactory getContent(int segment) {
return splitter.getContent(segment);
}
/**
* Returns the total segment number.
*
* @return the total segment number.
*/
public int getSegmentNumber() {
return splitter.getSegmentNumber();
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private byte[] userData;
private byte[] userSignatureData;
private UserSignature userSignature;
private SecretKeySpec keySpec;
private Splitter splitter;
}

View File

@ -0,0 +1,103 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.h003.DataTransferRequestType;
import com.axelor.apps.account.ebics.schema.h003.DataTransferRequestType.OrderData;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Body;
import com.axelor.apps.account.ebics.schema.h003.EbicsRequestDocument.EbicsRequest.Header;
import com.axelor.apps.account.ebics.schema.h003.MutableHeaderType;
import com.axelor.apps.account.ebics.schema.h003.MutableHeaderType.SegmentNumber;
import com.axelor.apps.account.ebics.schema.h003.StaticHeaderType;
import com.axelor.apps.bankpayment.ebics.client.EbicsSession;
import com.axelor.apps.bankpayment.ebics.client.OrderType;
import com.axelor.apps.bankpayment.ebics.interfaces.ContentFactory;
import com.axelor.apps.bankpayment.ebics.io.IOUtils;
import com.axelor.exception.AxelorException;
import java.io.ByteArrayOutputStream;
import org.jdom.JDOMException;
/**
* The <code>UTransferRequestElement</code> is the root element for all ebics upload transfers.
*
* @author Hachani
*/
public class UTransferRequestElement extends TransferRequestElement {
/**
* Constructs a new <code>UTransferRequestElement</code> for ebics upload transfer.
*
* @param session the current ebics session
* @param orderType the upload order type
* @param segmentNumber the segment number
* @param lastSegment i it the last segment?
* @param transactionId the transaction ID
* @param content the content factory
*/
public UTransferRequestElement(
EbicsSession session,
OrderType orderType,
int segmentNumber,
boolean lastSegment,
byte[] transactionId,
ContentFactory content) {
super(session, generateName(orderType), orderType, segmentNumber, lastSegment, transactionId);
this.content = content;
}
@Override
public void buildTransfer() throws AxelorException {
EbicsRequest request;
Header header;
Body body;
MutableHeaderType mutable;
SegmentNumber segmentNumber;
StaticHeaderType xstatic;
OrderData orderData;
DataTransferRequestType dataTransfer;
segmentNumber = EbicsXmlFactory.createSegmentNumber(this.segmentNumber, lastSegment);
mutable = EbicsXmlFactory.createMutableHeaderType("Transfer", segmentNumber);
xstatic = EbicsXmlFactory.createStaticHeaderType(session.getBankID(), transactionId);
header = EbicsXmlFactory.createEbicsRequestHeader(true, mutable, xstatic);
orderData = EbicsXmlFactory.createEbicsRequestOrderData(IOUtils.getFactoryContent(content));
dataTransfer = EbicsXmlFactory.createDataTransferRequestType(orderData);
body = EbicsXmlFactory.createEbicsRequestBody(dataTransfer); // TODO CHECK
request = EbicsXmlFactory.createEbicsRequest(1, "H003", header, body);
document = EbicsXmlFactory.createEbicsRequestDocument(request);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try {
this.save(bout);
} catch (JDOMException e) {
// TODO Bloc catch généré automatiquement
e.printStackTrace();
}
System.out.println(
"Requete data ----------------------------------------------------------------------------");
System.out.println(bout.toString());
}
// --------------------------------------------------------------------
// DATA MEMBERS
// --------------------------------------------------------------------
private ContentFactory content;
}

View File

@ -0,0 +1,136 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.account.ebics.schema.s001.OrderSignatureDataType;
import com.axelor.apps.account.ebics.schema.s001.UserSignatureDataSigBookType;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.ebics.service.EbicsUserService;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import java.util.Base64;
import javax.xml.XMLConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A root EBICS element representing the user signature element. The user data is signed with the
* user signature key sent in the INI request to the EBICS bank server
*
* @author hachani
*/
public class UserSignature extends DefaultEbicsRootElement {
private final Logger log = LoggerFactory.getLogger(getClass());
private EbicsUser user;
private String signatureVersion;
private byte[] data;
private byte[] signature;
private String name;
/**
* Constructs a new <code>UserSignature</code> element for an Ebics user and a data to sign
*
* @param user the ebics user
* @param signatureVersion the signature version
* @param toSign the data to be signed
*/
public UserSignature(
EbicsUser user, String name, String signatureVersion, byte[] data, byte[] signature) {
this.user = user;
this.data = data;
this.signature = signature;
this.name = name;
this.signatureVersion = signatureVersion;
}
@Override
public void build() throws AxelorException {
UserSignatureDataSigBookType userSignatureData;
OrderSignatureDataType orderSignatureData;
try {
if (user.getEbicsPartner().getEbicsTypeSelect() == EbicsPartnerRepository.EBICS_TYPE_TS) {
log.debug("Signature (base64) : {}", new String(signature));
log.debug("Signature (base64) length : {}", signature.length);
signature = EbicsUserService.removeOSSpecificChars(signature);
log.debug(
"Signature (base64) length after remove OS specific chars : {}", signature.length);
signature = Base64.getDecoder().decode(signature);
log.debug("Signature (byte) length : {}", signature.length);
} else {
signature = Beans.get(EbicsUserService.class).sign(user, data);
}
} catch (Exception e) {
e.printStackTrace();
}
UserSignatureVerify userSignatureVerify = new UserSignatureVerify(user, data, signature);
userSignatureVerify.verify();
/**
* Include certificate informations
*
* <p>EbicsCertificate ebicsEertificate = user.getA005Certificate();
*
* <p>X509DataType x509Data = EbicsXmlFactory.createX509DataType(ebicsEertificate.getSubject(),
* ebicsEertificate.getCertificate(), ebicsEertificate.getIssuer(), new
* BigInteger(ebicsEertificate.getSerial(), 16));
*
* <p>orderSignatureData = EbicsXmlFactory.createOrderSignatureDataType(signatureVersion,
* user.getEbicsPartner().getPartnerId(), user.getUserId(), signature, x509Data); *
*/
orderSignatureData =
EbicsXmlFactory.createOrderSignatureDataType(
signatureVersion, user.getEbicsPartner().getPartnerId(), user.getUserId(), signature);
userSignatureData =
EbicsXmlFactory.createUserSignatureDataSigBookType(
new OrderSignatureDataType[] {orderSignatureData});
document = EbicsXmlFactory.createUserSignatureDataDocument(userSignatureData);
}
@Override
public String getName() {
return name + ".xml";
}
@Override
public byte[] toByteArray() {
addNamespaceDecl("xsi", "http://www.w3.org/2001/XMLSchema-instance");
setSaveSuggestedPrefixes("http://www.ebics.org/S001", XMLConstants.DEFAULT_NS_PREFIX);
insertSchemaLocation(
"http://www.w3.org/2001/XMLSchema-instance",
"schemaLocation",
"xsi",
"http://www.ebics.org/S001 http://www.ebics.org/S001/ebics_signature.xsd");
return super.toByteArray();
}
}

View File

@ -0,0 +1,127 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.ebics.xml;
import com.axelor.apps.bankpayment.db.EbicsCertificate;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.ebics.service.EbicsUserService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import java.math.BigInteger;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A root EBICS element representing the user signature element. The user data is signed with the
* user signature key sent in the INI request to the EBICS bank server
*
* @author hachani
*/
public class UserSignatureVerify {
private final Logger log = LoggerFactory.getLogger(getClass());
private EbicsUser user;
private byte[] signature;
private byte[] bankOrderContent;
private String modulus;
private String exponent;
/**
* Constructs a new <code>UserSignature</code> element for an Ebics user and a data to sign
*
* @param user the ebics user
* @param signatureVersion the signature version
* @param toSign the data to be signed
*/
public UserSignatureVerify(EbicsUser user, byte[] bankOrderContent, byte[] signature) {
this.user = user;
this.bankOrderContent = bankOrderContent;
this.signature = signature;
loadCertificate();
}
public void verify() throws AxelorException {
String comptedSha256Digest = computeSha256Digest();
String originalDigestFromSignature = getOriginalDigestFromSignature();
if (!comptedSha256Digest.equals(originalDigestFromSignature)) {
String message =
I18n.get(
"Computed digest (SHA256) of the bank order file doesn't match with the digest extract from the signature")
+ " \n";
message +=
I18n.get("Computed digest (SHA256) of the bank order file :")
+ " "
+ comptedSha256Digest
+ "\n";
message +=
I18n.get("Original digest extracted from the signature :")
+ " "
+ originalDigestFromSignature;
throw new AxelorException(message, TraceBackRepository.CATEGORY_INCONSISTENCY);
}
}
public void loadCertificate() {
EbicsCertificate certificate = user.getA005Certificate();
this.modulus = certificate.getPublicKeyModulus();
this.exponent = certificate.getPublicKeyExponent();
}
public String computeSha256Digest() {
bankOrderContent = EbicsUserService.removeOSSpecificChars(bankOrderContent);
String sha = DigestUtils.sha256Hex(bankOrderContent);
log.debug("Digest (SHA256) of bank order content : {}", sha);
return sha;
}
public String getOriginalDigestFromSignature() {
String hexSignature = getHexSignature();
System.out.println("Signature (HEX STRING) : " + hexSignature);
String result =
new BigInteger(hexSignature, 16)
.modPow(new BigInteger(exponent, 16), new BigInteger(modulus, 16))
.toString(16);
log.debug("Orignal digest from signature (with padding) : {}", result);
result = result.substring(result.length() - 64, result.length());
log.debug("Orignal digest from signature : {}", result);
return result;
}
public String getHexSignature() {
return Hex.encodeHexString(signature);
}
}

View File

@ -0,0 +1,225 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.exception;
/**
* Interface of Exceptions. Enum all exception of axelor-account.
*
* @author dubaux
*/
public interface IExceptionMessage {
/** Bank statement service */
static final String BANK_STATEMENT_1 = /*$$(*/
"%s : Computed balance and Ending Balance must be equal" /*)*/;
static final String BANK_STATEMENT_2 = /*$$(*/
"%s : MoveLine amount is not equals with bank statement line %s" /*)*/;
static final String BANK_STATEMENT_3 = /*$$(*/
"%s : Bank statement line %s amount can't be null" /*)*/;
/** Account config Bank Payment Service */
static final String ACCOUNT_CONFIG_41 = /*$$(*/
"%s : Please, configure a default signer for the company %s" /*)*/;
static final String ACCOUNT_CONFIG_1 = /*$$(*/
"%s : You must configure bank payment's information for the company %s" /*)*/;
static final String ACCOUNT_CONFIG_SEQUENCE_5 = /*$$(*/
"%s : Please, configure a sequence for the SEPA Credit Transfers and the company %s" /*)*/;
static final String ACCOUNT_CONFIG_SEQUENCE_6 = /*$$(*/
"%s : Please, configure a sequence for the SEPA Direct Debits and the company %s" /*)*/;
static final String ACCOUNT_CONFIG_SEQUENCE_7 = /*$$(*/
"%s : Please, configure a sequence for the International Credit Transfers and the company %s" /*)*/;
static final String ACCOUNT_CONFIG_SEQUENCE_8 = /*$$(*/
"%s : Please, configure a sequence for the International Direct Debits and the company %s" /*)*/;
static final String ACCOUNT_CONFIG_SEQUENCE_9 = /*$$(*/
"%s : Please, configure a sequence for the International Treasury Transfers and the company %s" /*)*/;
static final String ACCOUNT_CONFIG_SEQUENCE_10 = /*$$(*/
"%s : Please, configure a sequence for the National Treasury Transfers and the company %s" /*)*/;
static final String ACCOUNT_CONFIG_SEQUENCE_11 = /*$$(*/
"%s : Please, configure a sequence for the Other Bank Orders and the company %s" /*)*/;
static final String ACCOUNT_CONFIG_EXTERNAL_BANK_TO_BANK_ACCOUNT = /*$$(*/
"%s : Please, configure an account for the bank order for the external bank to bank transfer for the company %s" /*)*/;
static final String ACCOUNT_CONFIG_INTERNAL_BANK_TO_BANK_ACCOUNT = /*$$(*/
"%s : Please, configure an account for the bank order for the internal bank to bank transfer for the company %s" /*)*/;
static final String ACCOUNT_CONFIG_MISSING_ICS_NUMBER = /*$$(*/
"%s : Please configure an ICS number for the company %s." /*)*/;
/** BankOrder service */
static final String BANK_ORDER_DATE = /*$$(*/ "Bank Order date can't be in the past" /*)*/;
static final String BANK_ORDER_DATE_MISSING = /*$$(*/ "Please fill bank order date" /*)*/;
static final String BANK_ORDER_TYPE_MISSING = /*$$(*/ "Please fill bank order type" /*)*/;
static final String BANK_ORDER_PARTNER_TYPE_MISSING = /*$$(*/
"Please fill partner type for the bank order" /*)*/;
static final String BANK_ORDER_COMPANY_MISSING = /*$$(*/ "Please fill the sender company" /*)*/;
static final String BANK_ORDER_BANK_DETAILS_MISSING = /*$$(*/
"Please fill the bank details" /*)*/;
static final String BANK_ORDER_CURRENCY_MISSING = /*$$(*/
"Please fill currency for the bank order" /*)*/;
static final String BANK_ORDER_AMOUNT_NEGATIVE = /*$$(*/
"Amount value of the bank order is not valid" /*)*/;
static final String BANK_ORDER_PAYMENT_MODE_MISSING = /*$$(*/
"Please select a payment mode" /*)*/;
static final String BANK_ORDER_SIGNATORY_MISSING = /*$$(*/ "Please select a signatory" /*)*/;
static final String BANK_ORDER_WRONG_SENDER_RECORD = /*$$(*/
"Anomaly has been detected during file generation for the sender record of the bank order %s" /*)*/;
static final String BANK_ORDER_WRONG_MAIN_DETAIL_RECORD = /*$$(*/
"Anomaly has been detected during file generation for the detail record of the bank order line %s" /*)*/;
static final String BANK_ORDER_WRONG_BENEFICIARY_BANK_DETAIL_RECORD = /*$$(*/
"Anomaly has been detected during file generation for the beneficiary bank detail record of the bank order line %s" /*)*/;
static final String BANK_ORDER_WRONG_FURTHER_INFORMATION_DETAIL_RECORD = /*$$(*/
"Anomaly has been detected during file generation for the further information detail record of the bank order line %s" /*)*/;
static final String BANK_ORDER_WRONG_TOTAL_RECORD = /*$$(*/
"Anomaly has been detected during file generation for the total record of the bank order %s" /*)*/;
static final String BANK_ORDER_ISSUE_DURING_FILE_GENERATION = /*$$(*/
"Anomaly has been detected during file generation for bank order %s" /*)*/;
static final String BANK_ORDER_COMPANY_NO_SEQUENCE = /*$$(*/
"The company %s does not have bank order sequence" /*)*/;
static final String BANK_ORDER_BANK_DETAILS_EMPTY_IBAN = /*$$(*/
"The Iban is mandatory for the partner %s, bank order %s" /*)*/;
static final String BANK_ORDER_BANK_DETAILS_NOT_ACTIVE = /*$$(*/
"The bank details is inactive." /*)*/;
static final String BANK_ORDER_BANK_DETAILS_TYPE_NOT_COMPATIBLE = /*$$(*/
"The bank details type is not compatible with the accepted types in file format." /*)*/;
static final String BANK_ORDER_BANK_DETAILS_CURRENCY_NOT_COMPATIBLE = /*$$(*/
"The sender bank details currency is not compatible with the currency in bank order." /*)*/;
static final String BANK_ORDER_BANK_DETAILS_MISSING_CURRENCY = /*$$(*/
"Please fill the sender bank details currency." /*)*/;
static final String BANK_ORDER_NOT_PROPERLY_SIGNED = /*$$(*/
"The bank order is not properly signed. Please correct it and sign it again." /*)*/;
static final String BANK_ORDER_CANNOT_REMOVE = /*$$(*/
"Bank orders can only be deleted at draft or canceled status." /*)*/;
String BANK_ORDER_RECEIVER_BANK_DETAILS_MISSING_BANK_ADDRESS = /*$$(*/
"Please fill the bank address in the receiver bank details." /*)*/;
String BANK_ORDER_RECEIVER_BANK_DETAILS_MISSING_BANK = /*$$(*/
"Please fill the bank in the receiver bank details." /*)*/;
/** BankOrder lines */
static final String BANK_ORDER_LINES_MISSING = /*$$(*/
"You can't validate this bank order. you need to fill at least one bank order line" /*)*/;
static final String BANK_ORDER_LINE_COMPANY_MISSING = /*$$(*/
"Please select a company for the bank order lines inserted" /*)*/;
static final String BANK_ORDER_LINE_PARTNER_MISSING = /*$$(*/
"Please select a partner for the bank order lines inserted" /*)*/;
static final String BANK_ORDER_LINE_AMOUNT_NEGATIVE = /*$$(*/
"Amount value of a bank order line is not valid" /*)*/;
static final String BANK_ORDER_LINE_TOTAL_AMOUNT_INVALID = /*$$(*/
"Total amount of bank order lines must be equal to the bank order amount" /*)*/;
static final String BANK_ORDER_LINE_BANK_DETAILS_MISSING = /*$$(*/
"Please fill the receiver bank details" /*)*/;
static final String BANK_ORDER_LINE_BANK_DETAILS_FORBIDDEN = /*$$(*/
"You cannot use this bank account because he is not authorized by the ebics partner." /*)*/;
static final String BANK_ORDER_LINE_BANK_DETAILS_NOT_ACTIVE = /*$$(*/
"The receiver bank details is inactive." /*)*/;
static final String BANK_ORDER_LINE_BANK_DETAILS_TYPE_NOT_COMPATIBLE = /*$$(*/
"The receiver bank details type is not compatible with the accepted types in file format." /*)*/;
static final String BANK_ORDER_LINE_BANK_DETAILS_CURRENCY_NOT_COMPATIBLE = /*$$(*/
"The receiver bank details currency is not compatible with the currency in bank order." /*)*/;
static final String BANK_ORDER_LINE_NO_RECEIVER_ADDRESS = /*$$(*/
"No address has been defined in the receiver %s" /*)*/;
/** BankOrder merge */
static final String BANK_ORDER_MERGE_AT_LEAST_TWO_BANK_ORDERS = /*$$(*/
"Please select at least two bank orders" /*)*/;
static final String BANK_ORDER_MERGE_STATUS = /*$$(*/
"Please select draft or awaiting signature bank orders only" /*)*/;
static final String BANK_ORDER_MERGE_SAME_STATUS = /*$$(*/
"Please select some bank orders that have the same status" /*)*/;
static final String BANK_ORDER_MERGE_SAME_ORDER_TYPE_SELECT = /*$$(*/
"Please select some bank orders that have the same status" /*)*/;
static final String BANK_ORDER_MERGE_SAME_PAYMENT_MODE = /*$$(*/
"Please select some bank orders that have the same payment mode" /*)*/;
static final String BANK_ORDER_MERGE_SAME_PARTNER_TYPE_SELECT = /*$$(*/
"Please select some bank orders that have the same partner type" /*)*/;
static final String BANK_ORDER_MERGE_SAME_SENDER_COMPANY = /*$$(*/
"Please select some bank orders that have the same sender company" /*)*/;
static final String BANK_ORDER_MERGE_SAME_SENDER_BANK_DETAILS = /*$$(*/
"Please select some bank orders that have the same sender bank details" /*)*/;
static final String BANK_ORDER_MERGE_SAME_CURRENCY = /*$$(*/
"Please select some bank orders that have the same currency" /*)*/;
static final String BANK_ORDER_MERGE_NO_BANK_ORDERS = /*$$(*/ "No bank orders found" /*)*/;
/** BankOrder file */
static final String BANK_ORDER_FILE_NO_SENDER_ADDRESS = /*$$(*/
"No address has been defined in the sender company %s" /*)*/;
static final String BANK_ORDER_FILE_NO_FOLDER_PATH = /*$$(*/
"No folder path has been defined in the payment mode %s" /*)*/;
static final String BANK_ORDER_FILE_UNKNOWN_FORMAT = /*$$(*/
"Unknown format for file generation" /*)*/;
static final String BANK_ORDER_FILE_UNKNOWN_SEPA_TYPE = /*$$(*/
"Unknown SEPA type for file generation" /*)*/;
/** Ebics */
static final String EBICS_WRONG_PASSWORD = /*$$(*/ "Incorrect password, please try again" /*)*/;
static final String EBICS_MISSING_PASSWORD = /*$$(*/ "Please insert a password" /*)*/;
static final String EBICS_MISSING_NAME = /*$$(*/ "Please select a user name" /*)*/;
static final String EBICS_TEST_MODE_NOT_ENABLED = /*$$(*/
"Test mode is not enabled or test file is missing" /*)*/;
static final String EBICS_MISSING_CERTIFICATES = /*$$(*/ "Please add certificates to print" /*)*/;
static final String EBICS_INVALID_BANK_URL = /*$$(*/
"Invalid bank url. It must be start with http:// or https://" /*)*/;
static final String EBICS_MISSING_USER_TRANSPORT = /*$$(*/
"Please insert a EBICS user for transport in the EBICS partner" /*)*/;
static final String EBICS_NO_SERVICE_CONFIGURED = /*$$(*/
"No service configured on EBICS partner %s for file format %s" /*)*/;
static final String EBICS_PARTNER_BANK_DETAILS_WARNING = /*$$(*/
"At least one bank details you have entered is missing currency. Here is the list of invalid bank details : %s" /*)*/;
static final String EBICS_MISSING_SIGNATORY_EBICS_USER = /* $$( */
"Signatory EBICS user is missing." /* ) */;
/** Batch bank statement */
String BATCH_BANK_STATEMENT_RETRIEVED_BANK_STATEMENT_COUNT = /*$$(*/
"Number of retrieved bank statements: %d." /*)*/;
/** BankStatement import */
static final String BANK_STATEMENT_FILE_UNKNOWN_FORMAT = /*$$(*/
"Unknown format for file import process" /*)*/;
static final String BANK_STATEMENT_MISSING_FILE = /*$$(*/ "Missing bank statement file" /*)*/;
static final String BANK_STATEMENT_MISSING_FILE_FORMAT = /*$$(*/
"Missing bank statement file format" /*)*/;
static final String BANK_STATEMENT_EBICS_PARTNER = /*$$(*/
"Error with EBICS partner %s: %s" /*)*/;
/*
* Batch direct debit
*/
static final String BATCH_DIRECT_DEBIT_MISSING_COMPANY_BANK_DETAILS = /*$$(*/
"Company bank details is missing in batch." /*)*/;
static final String BATCH_DIRECT_DEBIT_NO_PROCESSED_PAYMENT_SCHEDULE_LINES = /*$$(*/
"No processed payment schedule lines" /*)*/;
static final String BATCH_DIRECT_DEBIT_UNKNOWN_DATA_TYPE = /*$$(*/
"Unknown direct debit data type" /*)*/;
static final String DIRECT_DEBIT_MISSING_PARTNER_ACTIVE_UMR = /*$$(*/
"Please add an Active UMR to the partner." /*)*/;
/** Generate bank order from invoices */
static final String INVOICE_BANK_ORDER_ALREADY_EXIST = /*$$(*/
"A bank order %s already exist for the invoice %s." /*)*/;
static final String MOVE_LINE_ARCHIVE_NOT_OK_BECAUSE_OF_BANK_RECONCILIATION_AMOUNT = /*$$(*/
"This move line %s can not be archived because its bank reconciliation amount is superior to 0." /*)*/;
}

View File

@ -0,0 +1,146 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.module;
import com.axelor.app.AxelorModule;
import com.axelor.apps.account.db.repo.MoveManagementRepository;
import com.axelor.apps.account.service.AccountingReportServiceImpl;
import com.axelor.apps.account.service.PaymentScheduleLineServiceImpl;
import com.axelor.apps.account.service.batch.AccountingBatchService;
import com.axelor.apps.account.service.batch.BatchCreditTransferPartnerReimbursement;
import com.axelor.apps.account.service.batch.BatchCreditTransferSupplierPayment;
import com.axelor.apps.account.service.extract.ExtractContextMoveServiceImpl;
import com.axelor.apps.account.service.move.MoveRemoveService;
import com.axelor.apps.account.service.move.MoveServiceImpl;
import com.axelor.apps.account.service.payment.invoice.payment.InvoicePaymentCancelServiceImpl;
import com.axelor.apps.account.service.payment.invoice.payment.InvoicePaymentCreateServiceImpl;
import com.axelor.apps.account.service.payment.invoice.payment.InvoicePaymentValidateServiceImpl;
import com.axelor.apps.account.web.InvoicePaymentController;
import com.axelor.apps.bankpayment.db.repo.BankOrderLineManagementRepository;
import com.axelor.apps.bankpayment.db.repo.BankOrderLineRepository;
import com.axelor.apps.bankpayment.db.repo.BankOrderManagementRepository;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.bankpayment.db.repo.BankReconciliationManagementRepository;
import com.axelor.apps.bankpayment.db.repo.BankReconciliationRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsBankAccountRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsBankRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsCertificateAccountRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsCertificateRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsUserManagementRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsUserRepository;
import com.axelor.apps.bankpayment.db.repo.MoveBankPaymentRepository;
import com.axelor.apps.bankpayment.ebics.service.EbicsBankService;
import com.axelor.apps.bankpayment.ebics.service.EbicsBankServiceImpl;
import com.axelor.apps.bankpayment.ebics.service.EbicsPartnerService;
import com.axelor.apps.bankpayment.ebics.service.EbicsPartnerServiceImpl;
import com.axelor.apps.bankpayment.service.AccountingReportBankPaymentService;
import com.axelor.apps.bankpayment.service.AccountingReportBankPaymentServiceImpl;
import com.axelor.apps.bankpayment.service.PaymentScheduleLineBankPaymentService;
import com.axelor.apps.bankpayment.service.PaymentScheduleLineBankPaymentServiceImpl;
import com.axelor.apps.bankpayment.service.app.AppBankPaymentService;
import com.axelor.apps.bankpayment.service.app.AppBankPaymentServiceImpl;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderLineOriginService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderLineOriginServiceImpl;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderMergeService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderMergeServiceImpl;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderMoveService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderMoveServiceImpl;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderServiceImpl;
import com.axelor.apps.bankpayment.service.batch.AccountingBatchBankPaymentService;
import com.axelor.apps.bankpayment.service.batch.BatchBankPaymentService;
import com.axelor.apps.bankpayment.service.batch.BatchBankPaymentServiceImpl;
import com.axelor.apps.bankpayment.service.batch.BatchCreditTransferPartnerReimbursementBankPayment;
import com.axelor.apps.bankpayment.service.batch.BatchCreditTransferSupplierPaymentBankPayment;
import com.axelor.apps.bankpayment.service.extract.ExtractContextMoveServiceBankPaymentImpl;
import com.axelor.apps.bankpayment.service.invoice.payment.InvoicePaymentCancelServiceBankPayImpl;
import com.axelor.apps.bankpayment.service.invoice.payment.InvoicePaymentCreateServiceBankPay;
import com.axelor.apps.bankpayment.service.invoice.payment.InvoicePaymentCreateServiceBankPayImpl;
import com.axelor.apps.bankpayment.service.invoice.payment.InvoicePaymentValidateServiceBankPayImpl;
import com.axelor.apps.bankpayment.service.move.MoveRemoveServiceBankPaymentImpl;
import com.axelor.apps.bankpayment.service.move.MoveServiceBankPaymentImpl;
import com.axelor.apps.bankpayment.web.InvoicePaymentBankPayController;
public class BankPaymentModule extends AxelorModule {
@Override
protected void configure() {
bind(AppBankPaymentService.class).to(AppBankPaymentServiceImpl.class);
bind(BankReconciliationRepository.class).to(BankReconciliationManagementRepository.class);
bind(BankOrderRepository.class).to(BankOrderManagementRepository.class);
bind(BankOrderLineRepository.class).to(BankOrderLineManagementRepository.class);
bind(EbicsBankRepository.class).to(EbicsBankAccountRepository.class);
bind(EbicsBankService.class).to(EbicsBankServiceImpl.class);
bind(EbicsPartnerService.class).to(EbicsPartnerServiceImpl.class);
bind(EbicsCertificateRepository.class).to(EbicsCertificateAccountRepository.class);
bind(BankOrderService.class).to(BankOrderServiceImpl.class);
bind(BankOrderMergeService.class).to(BankOrderMergeServiceImpl.class);
bind(BankOrderMoveService.class).to(BankOrderMoveServiceImpl.class);
bind(InvoicePaymentCancelServiceImpl.class).to(InvoicePaymentCancelServiceBankPayImpl.class);
bind(InvoicePaymentValidateServiceImpl.class)
.to(InvoicePaymentValidateServiceBankPayImpl.class);
bind(BatchCreditTransferSupplierPayment.class)
.to(BatchCreditTransferSupplierPaymentBankPayment.class);
bind(BatchCreditTransferPartnerReimbursement.class)
.to(BatchCreditTransferPartnerReimbursementBankPayment.class);
bind(AccountingBatchService.class).to(AccountingBatchBankPaymentService.class);
bind(BatchBankPaymentService.class).to(BatchBankPaymentServiceImpl.class);
bind(PaymentScheduleLineServiceImpl.class).to(PaymentScheduleLineBankPaymentServiceImpl.class);
bind(PaymentScheduleLineBankPaymentService.class)
.to(PaymentScheduleLineBankPaymentServiceImpl.class);
bind(InvoicePaymentCreateServiceBankPay.class).to(InvoicePaymentCreateServiceBankPayImpl.class);
bind(InvoicePaymentCreateServiceImpl.class).to(InvoicePaymentCreateServiceBankPayImpl.class);
bind(InvoicePaymentController.class).to(InvoicePaymentBankPayController.class);
bind(MoveManagementRepository.class).to(MoveBankPaymentRepository.class);
bind(BankOrderLineOriginService.class).to(BankOrderLineOriginServiceImpl.class);
bind(MoveServiceImpl.class).to(MoveServiceBankPaymentImpl.class);
bind(ExtractContextMoveServiceImpl.class).to(ExtractContextMoveServiceBankPaymentImpl.class);
bind(MoveRemoveService.class).to(MoveRemoveServiceBankPaymentImpl.class);
bind(EbicsUserRepository.class).to(EbicsUserManagementRepository.class);
bind(AccountingReportBankPaymentService.class).to(AccountingReportBankPaymentServiceImpl.class);
bind(AccountingReportServiceImpl.class).to(AccountingReportBankPaymentServiceImpl.class);
}
}

View File

@ -0,0 +1,27 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.report;
public interface IReport {
public static final String BANK_ORDER = "BankOrder.rptdesign";
public static final String EBICS_CERTIFICATE = "EbicsCertificate.rptdesign";
public static final String BANK_STATEMENT_AFB120 = "BankStatementAFB120.rptdesign";
public static final String BANK_RECONCILIATION = "BankReconciliation.rptdesign";
public static final String BANK_PAYMENT_REPORT_TYPE = "BankReconciliationStatement.rptdesign";
}

View File

@ -0,0 +1,155 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.report;
public interface ITranslation {
public static final String EBICS_CERTIFICATE_SIGNATURE_TITLE = /*$$(*/
"EbicsCertificateReport.signatureTitle"; /*)*/
public static final String EBICS_CERTIFICATE_AUTHENTICATION_TITLE = /*$$(*/
"EbicsCertificateReport.authenticationTitle"; /*)*/
public static final String EBICS_CERTIFICATE_ENCRYPTION_TITLE = /*$$(*/
"EbicsCertificateReport.encryptionTitle"; /*)*/
public static final String EBICS_CERTIFICATE_SIGNATURE = /*$$(*/
"EbicsCertificateReport.signature"; /*)*/
public static final String EBICS_CERTIFICATE_AUTHENTICATION = /*$$(*/
"EbicsCertificateReport.authentication"; /*)*/
public static final String EBICS_CERTIFICATE_ENCRYPTION = /*$$(*/
"EbicsCertificateReport.encryption"; /*)*/
public static final String EBICS_CERTIFICATE_SIGNATURE_HASH = /*$$(*/
"EbicsCertificateReport.signatureHash"; /*)*/
public static final String EBICS_CERTIFICATE_AUTHENTICATION_HASH = /*$$(*/
"EbicsCertificateReport.authenticationHash"; /*)*/
public static final String EBICS_CERTIFICATE_ENCRYPTION_HASH = /*$$(*/
"EbicsCertificateReport.encryptionHash"; /*)*/
public static final String EBICS_CERTIFICATE_ISSUED_TO = /*$$(*/
"EbicsCertificateReport.issuedTo"; /*)*/
public static final String EBICS_CERTIFICATE_ISSUED_BY = /*$$(*/
"EbicsCertificateReport.issuedBy"; /*)*/
public static final String EBICS_CERTIFICATE_BANK = /*$$(*/ "EbicsCertificateReport.bank"; /*)*/
public static final String EBICS_CERTIFICATE_SIGNATURE_VERSION = /*$$(*/
"EbicsCertificateReport.signatureVersion"; /*)*/
public static final String EBICS_CERTIFICATE_AUTHENTICATION_VERSION = /*$$(*/
"EbicsCertificateReport.authenticationVersion"; /*)*/
public static final String EBICS_CERTIFICATE_ENCRYPTION_VERSION = /*$$(*/
"EbicsCertificateReport.encryptionVersion"; /*)*/
public static final String BANK_STATEMENT_TITLE = /*$$(*/ "BankStatement.title"; /*)*/;
public static final String BANK_STATEMENT_CREATION_DATE_FROM = /*$$(*/
"BankStatement.creationDateFrom"; /*)*/;
public static final String BANK_STATEMENT_CREATION_DATE_TO = /*$$(*/
"BankStatement.creationDateTo"; /*)*/;
public static final String BANK_STATEMENT_HEADER_TEXT = /*$$(*/ "BankStatement.headerText"; /*)*/;
public static final String BANK_STATEMENT_OPERATION_DATE = /*$$(*/
"BankStatement.operationDate"; /*)*/;
public static final String BANK_STATEMENT_VALUE_DATE = /*$$(*/ "BankStatement.valueDate"; /*)*/;
public static final String BANK_STATEMENT_LABEL = /*$$(*/ "BankStatement.label"; /*)*/;
public static final String BANK_STATEMENT_REFERENCE = /*$$(*/ "BankStatement.reference"; /*)*/;
public static final String BANK_STATEMENT_EXEMPT = /*$$(*/ "BankStatement.exempt"; /*)*/;
public static final String BANK_STATEMENT_UNAVAILABLE = /*$$(*/
"BankStatement.unavailable"; /*)*/;
public static final String BANK_STATEMENT_ORIGIN = /*$$(*/ "BankStatement.origin"; /*)*/;
public static final String BANK_STATEMENT_DEBIT = /*$$(*/ "BankStatement.debit"; /*)*/;
public static final String BANK_STATEMENT_CREDIT = /*$$(*/ "BankStatement.credit"; /*)*/;
public static final String BANK_STATEMENT_YES = /*$$(*/ "BankStatement.yes"; /*)*/
public static final String BANK_STATEMENT_INITIAL_BALANCE = /*$$(*/
"BankStatement.initialBalance"; /*)*/;
public static final String BANK_STATEMENT_MOVEMENT = /*$$(*/ "BankStatement.movement"; /*)*/;
public static final String BANK_STATEMENT_FINAL_BALANCE = /*$$(*/
"BankStatement.finalBalance"; /*)*/;
public static final String BANK_STATEMENT_TOTAL_OF_OPERATIONS = /*$$(*/
"BankStatement.totalOfOperations"; /*)*/;
public static final String BANK_STATEMENT_BANK_ACCOUNT = /*$$(*/
"BankStatement.bankAccount"; /*)*/;
public static final String BANK_STATEMENT_IBAN = /*$$(*/ "BankStatement.iban"; /*)*/;
public static final String BANK_STATEMENT_ACCOUNT_CURRENCY = /*$$(*/
"BankStatement.accountCurrency"; /*)*/;
public static final String BANK_RECONCILIATION_REPORT_EDITION_DATE = /*$$(*/
"BankReconciliation.report_edition_date"; /*)*/;
public static final String BANK_RECONCILIATION_PAGE1_TITLE = /*$$(*/
"BankReconciliation.page1Title"; /*)*/;
public static final String BANK_RECONCILIATION_PAGE2_TITLE = /*$$(*/
"BankReconciliation.page2Title"; /*)*/;
public static final String BANK_RECONCILIATION_ACOUNT = /*$$(*/
"BankReconciliation.account"; /*)*/;
public static final String BANK_RECONCILIATION_JOURNAL = /*$$(*/
"BankReconciliation.journal"; /*)*/;
public static final String BANK_RECONCILIATION_CURRENCY = /*$$(*/
"BankReconciliation.currency"; /*)*/;
public static final String BANK_RECONCILIATION_COMPANY = /*$$(*/
"BankReconciliation.company"; /*)*/;
public static final String BANK_RECONCILIATION_REPORT_DATE = /*$$(*/
"BankReconciliation.report_date"; /*)*/;
public static final String BANK_RECONCILIATION_CASH_ACCOUNT = /*$$(*/
"BankReconciliation.cash_account"; /*)*/;
public static final String BANK_RECONCILIATION_TYPE = /*$$(*/ "BankReconciliation.type"; /*)*/;
public static final String BANK_RECONCILIATION_CODE = /*$$(*/ "BankReconciliation.code"; /*)*/;
public static final String BANK_RECONCILIATION_TO_DATE = /*$$(*/
"BankReconciliation.to_date"; /*)*/;
public static final String BANK_RECONCILIATION_REFERENCE = /*$$(*/
"BankReconciliation.reference"; /*)*/;
public static final String BANK_RECONCILIATION_NAME = /*$$(*/ "BankReconciliation.name"; /*)*/;
public static final String BANK_RECONCILIATION_DEBIT = /*$$(*/ "BankReconciliation.debit"; /*)*/;
public static final String BANK_RECONCILIATION_CREDIT = /*$$(*/
"BankReconciliation.credit"; /*)*/;
public static final String BANK_RECONCILIATION_AMOUNT = /*$$(*/
"BankReconciliation.amount"; /*)*/;
public static final String BANK_RECONCILIATION_UNRECONCILED_MOVE_LINES = /*$$(*/
"BankReconciliation.unreconciled_move_lines"; /*)*/;
public static final String BANK_RECONCILIATION_MOVES_UNACCOUNTED = /*$$(*/
"BankReconciliation.moves_unaccounted"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_COMPANY = /*$$(*/
"BankReconciliationStatement.company"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_CURRENCY = /*$$(*/
"BankReconciliationStatement.currency"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_JOURNAL = /*$$(*/
"BankReconciliationStatement.journal"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_DATE = /*$$(*/
"BankReconciliationStatement.date"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_PAGE1TITLE = /*$$(*/
"BankReconciliationStatement.page1Title"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_PAGE2TITLE = /*$$(*/
"BankReconciliationStatement.page2Title"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_CODE = /*$$(*/
"BankReconciliationStatement.code"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_REFERENCE = /*$$(*/
"BankReconciliationStatement.reference"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_DESCRIPTION = /*$$(*/
"BankReconciliationStatement.description"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_DEBIT = /*$$(*/
"BankReconciliationStatement.debit"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_CREDIT = /*$$(*/
"BankReconciliationStatement.credit"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_AMOUNT_REMAINING = /*$$(*/
"BankReconciliationStatement.amountRemaining"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_NAME = /*$$(*/
"BankReconciliationStatement.name"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_ACCOUNT_CODE = /*$$(*/
"BankReconciliationStatement.accountCode"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_JOURNAL_CODE = /*$$(*/
"BankReconciliationStatement.journalCode"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_LINE = /*$$(*/
"BankReconciliationStatement.line"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_AMOUNT_TO_RECONCILE = /*$$(*/
"BankReconciliationStatement.amountToReconcile"; /*)*/;
public static final String BANK_RECONCILIATION_STATEMENT_NO_LINE_FOUND_MESSAGE = /*$$(*/
"BankReconciliationStatement.noLineFoundMessage"; /*)*/;
}

View File

@ -0,0 +1,25 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service;
import com.axelor.apps.account.db.AccountingReport;
public interface AccountingReportBankPaymentService {
public String createDomainForBankDetails(AccountingReport accountingReport);
}

View File

@ -0,0 +1,93 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.account.db.AccountingReport;
import com.axelor.apps.account.db.repo.AccountRepository;
import com.axelor.apps.account.db.repo.AccountingReportRepository;
import com.axelor.apps.account.exception.IExceptionMessage;
import com.axelor.apps.account.service.AccountingReportServiceImpl;
import com.axelor.apps.account.service.app.AppAccountService;
import com.axelor.apps.bankpayment.report.IReport;
import com.axelor.apps.base.db.repo.SequenceRepository;
import com.axelor.apps.base.service.BankDetailsService;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
public class AccountingReportBankPaymentServiceImpl extends AccountingReportServiceImpl
implements AccountingReportBankPaymentService {
@Inject
public AccountingReportBankPaymentServiceImpl(
AppAccountService appBaseService,
AccountingReportRepository accountingReportRepo,
AccountRepository accountRepo) {
super(appBaseService, accountingReportRepo, accountRepo);
}
@Override
public String createDomainForBankDetails(AccountingReport accountingReport) {
return Beans.get(BankDetailsService.class)
.getActiveCompanyBankDetails(accountingReport.getCompany(), accountingReport.getCurrency());
}
@Override
public String getReportFileLink(AccountingReport accountingReport, String name)
throws AxelorException {
if (accountingReport.getTypeSelect()
>= AccountingReportRepository.REPORT_BANK_RECONCILIATION_STATEMENT) {
return ReportFactory.createReport(IReport.BANK_PAYMENT_REPORT_TYPE, name + "-${date}")
.addParam("AccountingReportId", accountingReport.getId())
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addFormat(accountingReport.getExportTypeSelect())
.toAttach(accountingReport)
.generate()
.getFileLink();
} else {
return super.getReportFileLink(accountingReport, name);
}
}
@Override
public String getSequence(AccountingReport accountingReport) throws AxelorException {
SequenceService sequenceService = Beans.get(SequenceService.class);
int accountingReportTypeSelect = accountingReport.getTypeSelect();
if (accountingReportTypeSelect >= 3000) {
String seq =
sequenceService.getSequenceNumber(
SequenceRepository.ACCOUNTING_REPORT, accountingReport.getCompany());
if (seq == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ACCOUNTING_REPORT_1),
I18n.get(com.axelor.apps.base.exceptions.IExceptionMessage.EXCEPTION),
accountingReport.getCompany().getName());
}
return seq;
} else {
return super.getSequence(accountingReport);
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service;
import com.axelor.apps.account.db.InterbankCodeLine;
import com.axelor.apps.account.db.PaymentScheduleLine;
import com.axelor.apps.account.service.PaymentScheduleLineService;
import com.axelor.exception.AxelorException;
import com.axelor.meta.CallMethod;
import java.util.Map;
public interface PaymentScheduleLineBankPaymentService extends PaymentScheduleLineService {
/**
* Reject a payment schedule line.
*
* @param paymentScheduleLine
* @param rejectionReason
* @param represent
* @throws AxelorException
*/
void reject(
PaymentScheduleLine paymentScheduleLine, InterbankCodeLine rejectionReason, boolean represent)
throws AxelorException;
/**
* Reject a payment schedule line.
*
* @param name
* @param rejectionReason
* @param represent
* @throws AxelorException
*/
void reject(String name, InterbankCodeLine rejectionReason, boolean represent)
throws AxelorException;
/**
* Reject payment schedule line from ID.
*
* @param id
* @param rejectionReason
* @param represent
* @throws AxelorException
*/
void reject(long id, InterbankCodeLine rejectionReason, boolean represent) throws AxelorException;
/**
* Reject payment schedule line from a map of IDs.
*
* @param idMap
* @param represent
* @return
*/
int rejectFromIdMap(Map<Long, InterbankCodeLine> idMap, boolean represent);
/**
* Reject payment schedule line from a map of names.
*
* @param nameMap
* @param represent
* @return
*/
int rejectFromNameMap(Map<String, InterbankCodeLine> nameMap, boolean represent);
/**
* Get default rejection reason.
*
* @return
*/
@CallMethod
InterbankCodeLine getDefaultRejectionReason();
}

View File

@ -0,0 +1,285 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service;
import com.axelor.apps.account.db.InterbankCodeLine;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.InvoicePayment;
import com.axelor.apps.account.db.Move;
import com.axelor.apps.account.db.MoveLine;
import com.axelor.apps.account.db.PaymentSchedule;
import com.axelor.apps.account.db.PaymentScheduleLine;
import com.axelor.apps.account.db.Reconcile;
import com.axelor.apps.account.db.repo.InterbankCodeLineRepository;
import com.axelor.apps.account.db.repo.InvoicePaymentRepository;
import com.axelor.apps.account.db.repo.MoveLineRepository;
import com.axelor.apps.account.db.repo.PaymentScheduleLineRepository;
import com.axelor.apps.account.db.repo.PaymentScheduleRepository;
import com.axelor.apps.account.db.repo.ReconcileRepository;
import com.axelor.apps.account.service.AccountingSituationService;
import com.axelor.apps.account.service.PaymentScheduleLineServiceImpl;
import com.axelor.apps.account.service.PaymentScheduleService;
import com.axelor.apps.account.service.move.MoveLineService;
import com.axelor.apps.account.service.move.MoveService;
import com.axelor.apps.account.service.move.MoveToolService;
import com.axelor.apps.account.service.move.MoveValidateService;
import com.axelor.apps.account.service.payment.PaymentModeService;
import com.axelor.apps.account.service.payment.PaymentService;
import com.axelor.apps.account.service.payment.invoice.payment.InvoicePaymentCancelService;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
public class PaymentScheduleLineBankPaymentServiceImpl extends PaymentScheduleLineServiceImpl
implements PaymentScheduleLineBankPaymentService {
protected InvoicePaymentCancelService invoicePaymentCancelService;
protected InterbankCodeLineRepository interbankCodeLineRepo;
protected ReconcileRepository reconcileRepo;
protected InvoicePaymentRepository invoicePaymentRepo;
@Inject
public PaymentScheduleLineBankPaymentServiceImpl(
AppBaseService appBaseService,
PaymentScheduleService paymentScheduleService,
MoveService moveService,
PaymentModeService paymentModeService,
SequenceService sequenceService,
AccountingSituationService accountingSituationService,
MoveToolService moveToolService,
PaymentService paymentService,
InvoicePaymentCancelService invoicePaymentCancelService,
InterbankCodeLineRepository interbankCodeLineRepo,
MoveLineRepository moveLineRepo,
PaymentScheduleLineRepository paymentScheduleLineRepo,
ReconcileRepository reconcileRepo,
InvoicePaymentRepository invoicePaymentRepo) {
super(
appBaseService,
paymentScheduleService,
moveService,
paymentModeService,
sequenceService,
accountingSituationService,
moveToolService,
paymentService,
moveLineRepo,
paymentScheduleLineRepo);
this.invoicePaymentCancelService = invoicePaymentCancelService;
this.interbankCodeLineRepo = interbankCodeLineRepo;
this.reconcileRepo = reconcileRepo;
this.invoicePaymentRepo = invoicePaymentRepo;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void reject(
PaymentScheduleLine paymentScheduleLine, InterbankCodeLine rejectionReason, boolean represent)
throws AxelorException {
Preconditions.checkNotNull(
paymentScheduleLine, I18n.get("Payment schedule line cannot be null."));
PaymentSchedule paymentSchedule = paymentScheduleLine.getPaymentSchedule();
Preconditions.checkNotNull(
paymentSchedule, I18n.get("Parent payment schedule cannot be null."));
if (paymentScheduleLine.getStatusSelect() != PaymentScheduleLineRepository.STATUS_VALIDATED) {
throw new AxelorException(
paymentScheduleLine,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get("Only validated payment schedule lines can be rejected."));
}
Move rejectionMove = createRejectionMove(paymentScheduleLine);
if (paymentSchedule.getTypeSelect() == PaymentScheduleRepository.TYPE_TERMS) {
cancelInvoicePayments(paymentScheduleLine);
}
if (represent) {
representPaymentScheduleLine(paymentScheduleLine);
}
if (rejectionReason == null) {
rejectionReason = getDefaultRejectionReason();
}
MoveLine rejectionMoveLine =
moveService.findMoveLineByAccount(
rejectionMove, paymentScheduleLine.getAdvanceMoveLine().getAccount());
paymentScheduleLine.setInterbankCodeLine(rejectionReason);
paymentScheduleLine.setRejectMoveLine(rejectionMoveLine);
paymentScheduleLine.setRejectDate(rejectionMove.getDate());
paymentScheduleLine.setAmountRejected(paymentScheduleLine.getRejectMoveLine().getDebit());
paymentScheduleLine.setRejectedOk(true);
paymentScheduleLine.setStatusSelect(PaymentScheduleLineRepository.STATUS_CLOSED);
}
@Override
public void reject(
String paymentScheduleLineName, InterbankCodeLine rejectionReason, boolean represent)
throws AxelorException {
PaymentScheduleLine paymentScheduleLine =
paymentScheduleLineRepo.findByName(paymentScheduleLineName);
reject(paymentScheduleLine, rejectionReason, represent);
}
@Override
public InterbankCodeLine getDefaultRejectionReason() {
return interbankCodeLineRepo.findByCode("A3");
}
@Transactional(rollbackOn = {Exception.class})
protected Move createRejectionMove(PaymentScheduleLine paymentScheduleLine)
throws AxelorException {
MoveValidateService moveValidateService = moveService.getMoveValidateService();
MoveLineService moveLineService = moveService.getMoveLineService();
Move advanceOrPaymentMove = paymentScheduleLine.getAdvanceOrPaymentMove();
Move rejectionMove =
moveService.generateReverse(
advanceOrPaymentMove, true, true, false, advanceOrPaymentMove.getDate());
rejectionMove.setRejectOk(true);
moveValidateService.validate(rejectionMove);
List<MoveLine> moveLineList = new ArrayList<>();
moveLineList.addAll(advanceOrPaymentMove.getMoveLineList());
moveLineList.addAll(rejectionMove.getMoveLineList());
moveLineService.reconcileMoveLines(moveLineList);
return rejectionMove;
}
@Transactional(rollbackOn = {Exception.class})
protected void cancelInvoicePayments(PaymentScheduleLine paymentScheduleLine)
throws AxelorException {
MoveLineService moveLineService = moveService.getMoveLineService();
PaymentSchedule paymentSchedule = paymentScheduleLine.getPaymentSchedule();
MoveLine creditMoveLine = paymentScheduleLine.getAdvanceMoveLine();
Set<Invoice> invoiceSet =
MoreObjects.firstNonNull(paymentSchedule.getInvoiceSet(), Collections.emptySet());
for (Invoice invoice : invoiceSet) {
MoveLine debitMoveLine = moveLineService.getDebitCustomerMoveLine(invoice);
Reconcile reconcile = reconcileRepo.findByMoveLines(debitMoveLine, creditMoveLine);
if (reconcile == null) {
continue;
}
for (InvoicePayment invoicePayment : invoicePaymentRepo.findByReconcile(reconcile).fetch()) {
invoicePaymentCancelService.cancel(invoicePayment);
}
}
}
@Transactional
protected PaymentScheduleLine representPaymentScheduleLine(
PaymentScheduleLine paymentScheduleLine) {
PaymentSchedule paymentSchedule = paymentScheduleLine.getPaymentSchedule();
BigDecimal inTaxAmount = paymentScheduleLine.getInTaxAmount();
int scheduleLineSeq = paymentScheduleService.getNextScheduleLineSeq(paymentSchedule);
LocalDate scheduleDate = paymentScheduleLine.getScheduleDate();
PaymentScheduleLine representedPaymentScheduleLine =
createPaymentScheduleLine(paymentSchedule, inTaxAmount, scheduleLineSeq, scheduleDate);
representedPaymentScheduleLine.setFromReject(true);
representedPaymentScheduleLine.setStatusSelect(
PaymentScheduleLineRepository.STATUS_IN_PROGRESS);
return representedPaymentScheduleLine;
}
protected void refindPaymentScheduleLines(
List<PaymentScheduleLine> paymentScheduleLines, int index) {
List<Long> idList =
paymentScheduleLines
.subList(Math.max(index, paymentScheduleLines.size()), paymentScheduleLines.size())
.stream()
.map(PaymentScheduleLine::getId)
.collect(Collectors.toList());
if (!idList.isEmpty()) {
List<PaymentScheduleLine> foundPaymentScheduleLines =
paymentScheduleLineRepo.findByIdList(idList).fetch();
if (foundPaymentScheduleLines.size() != idList.size()) {
throw new IllegalStateException(
String.format(
"Expected size: %d, got: %d", idList.size(), foundPaymentScheduleLines.size()));
}
for (int i = 0; i < foundPaymentScheduleLines.size(); ++i) {
paymentScheduleLines.set(index + i, foundPaymentScheduleLines.get(i));
}
}
}
@Override
public void reject(long id, InterbankCodeLine rejectionReason, boolean represent)
throws AxelorException {
PaymentScheduleLine paymentScheduleLine = paymentScheduleLineRepo.find(id);
reject(paymentScheduleLine, rejectionReason, represent);
}
@Override
public int rejectFromIdMap(Map<Long, InterbankCodeLine> idMap, boolean represent) {
return rejectFromMap(idMap, represent, paymentScheduleLineRepo::find);
}
@Override
public int rejectFromNameMap(Map<String, InterbankCodeLine> nameMap, boolean represent) {
return rejectFromMap(nameMap, represent, paymentScheduleLineRepo::findByName);
}
protected <T> int rejectFromMap(
Map<T, InterbankCodeLine> map, boolean represent, Function<T, PaymentScheduleLine> findFunc) {
int errorCount = 0;
for (Entry<T, InterbankCodeLine> entry : map.entrySet()) {
T key = entry.getKey();
InterbankCodeLine rejectionReason = entry.getValue();
PaymentScheduleLine paymentScheduleLine = findFunc.apply(key);
try {
reject(paymentScheduleLine, rejectionReason, represent);
} catch (Exception e) {
TraceBackService.trace(e);
++errorCount;
}
}
return errorCount;
}
}

View File

@ -0,0 +1,28 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.app;
import com.axelor.apps.base.db.AppBankPayment;
import com.axelor.apps.base.service.app.AppBaseService;
public interface AppBankPaymentService extends AppBaseService {
public AppBankPayment getAppBankPayment();
public void generateBankPaymentConfigurations();
}

View File

@ -0,0 +1,65 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.app;
import com.axelor.apps.bankpayment.db.BankPaymentConfig;
import com.axelor.apps.bankpayment.db.repo.BankPaymentConfigRepository;
import com.axelor.apps.base.db.AppBankPayment;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.repo.AppBankPaymentRepository;
import com.axelor.apps.base.db.repo.CompanyRepository;
import com.axelor.apps.base.service.app.AppBaseServiceImpl;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.util.List;
@Singleton
public class AppBankPaymentServiceImpl extends AppBaseServiceImpl implements AppBankPaymentService {
protected AppBankPaymentRepository appBankPaymentRepo;
protected BankPaymentConfigRepository bankPaymentConfigRepo;
protected CompanyRepository companyRepo;
@Inject
AppBankPaymentServiceImpl(
AppBankPaymentRepository appBankPaymentRepo,
BankPaymentConfigRepository bankPaymentConfigRepo,
CompanyRepository companyRepo) {
this.appBankPaymentRepo = appBankPaymentRepo;
this.bankPaymentConfigRepo = bankPaymentConfigRepo;
this.companyRepo = companyRepo;
}
@Override
public AppBankPayment getAppBankPayment() {
return appBankPaymentRepo.all().fetchOne();
}
@Override
@Transactional
public void generateBankPaymentConfigurations() {
List<Company> companies = companyRepo.all().filter("self.bankPaymentConfig IS NULL").fetch();
for (Company company : companies) {
BankPaymentConfig config = new BankPaymentConfig();
config.setCompany(company);
bankPaymentConfigRepo.save(config);
}
}
}

View File

@ -0,0 +1,199 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.InvoicePayment;
import com.axelor.apps.account.db.PaymentMode;
import com.axelor.apps.account.db.repo.InvoiceRepository;
import com.axelor.apps.account.service.invoice.InvoiceService;
import com.axelor.apps.account.service.invoice.InvoiceToolService;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderFileFormat;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BankOrderCreateService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected BankOrderRepository bankOrderRepo;
protected BankOrderService bankOrderService;
protected BankOrderLineService bankOrderLineService;
protected InvoiceService invoiceService;
@Inject
public BankOrderCreateService(
BankOrderRepository bankOrderRepo,
BankOrderService bankOrderService,
BankOrderLineService bankOrderLineService,
InvoiceService invoiceService) {
this.bankOrderRepo = bankOrderRepo;
this.bankOrderService = bankOrderService;
this.bankOrderLineService = bankOrderLineService;
this.invoiceService = invoiceService;
}
/**
* Créer un ordre bancaire avec tous les paramètres
*
* @param
* @return
* @throws AxelorException
*/
public BankOrder createBankOrder(
PaymentMode paymentMode,
Integer partnerType,
LocalDate bankOrderDate,
Company senderCompany,
BankDetails senderBankDetails,
Currency currency,
String senderReference,
String senderLabel,
int technicalOriginSelect)
throws AxelorException {
BankOrderFileFormat bankOrderFileFormat = paymentMode.getBankOrderFileFormat();
BankOrder bankOrder = new BankOrder();
bankOrder.setOrderTypeSelect(paymentMode.getOrderTypeSelect());
bankOrder.setPaymentMode(paymentMode);
bankOrder.setPartnerTypeSelect(partnerType);
if (!bankOrderFileFormat.getIsMultiDate()) {
bankOrder.setBankOrderDate(bankOrderDate);
}
bankOrder.setStatusSelect(BankOrderRepository.STATUS_DRAFT);
bankOrder.setRejectStatusSelect(BankOrderRepository.REJECT_STATUS_NOT_REJECTED);
bankOrder.setSenderCompany(senderCompany);
bankOrder.setSenderBankDetails(senderBankDetails);
EbicsUser signatoryEbicsUser =
bankOrderService.getDefaultEbicsUserFromBankDetails(senderBankDetails);
User signatoryUser = null;
if (signatoryEbicsUser != null) {
signatoryUser = signatoryEbicsUser.getAssociatedUser();
bankOrder.setSignatoryEbicsUser(signatoryEbicsUser);
}
if (signatoryUser != null) {
bankOrder.setSignatoryUser(signatoryUser);
}
if (!bankOrderFileFormat.getIsMultiCurrency()) {
bankOrder.setBankOrderCurrency(currency);
}
bankOrder.setCompanyCurrency(senderCompany.getCurrency());
bankOrder.setSenderReference(senderReference);
bankOrder.setSenderLabel(senderLabel);
bankOrder.setBankOrderLineList(new ArrayList<BankOrderLine>());
bankOrder.setBankOrderFileFormat(bankOrderFileFormat);
bankOrder.setTechnicalOriginSelect(technicalOriginSelect);
return bankOrder;
}
/**
* Method to create a bank order for an invoice Payment
*
* @param invoicePayment An invoice payment
* @throws AxelorException
*/
public BankOrder createBankOrder(InvoicePayment invoicePayment) throws AxelorException {
Invoice invoice = invoicePayment.getInvoice();
Company company = invoice.getCompany();
PaymentMode paymentMode = invoicePayment.getPaymentMode();
Partner partner = invoice.getPartner();
BigDecimal amount = invoicePayment.getAmount();
Currency currency = invoicePayment.getCurrency();
LocalDate paymentDate = invoicePayment.getPaymentDate();
BankDetails companyBankDetails =
invoicePayment.getCompanyBankDetails() != null
? invoicePayment.getCompanyBankDetails()
: this.getSenderBankDetails(invoice);
String reference =
InvoiceToolService.isPurchase(invoice)
? invoice.getSupplierInvoiceNb()
: invoice.getInvoiceId();
BankOrder bankOrder =
this.createBankOrder(
paymentMode,
this.getBankOrderPartnerType(invoice),
paymentDate,
company,
companyBankDetails,
currency,
reference,
null,
BankOrderRepository.TECHNICAL_ORIGIN_AUTOMATIC);
BankDetails receiverBankDetails = invoiceService.getBankDetails(invoice);
BankOrderLine bankOrderLine =
bankOrderLineService.createBankOrderLine(
paymentMode.getBankOrderFileFormat(),
null,
partner,
receiverBankDetails,
amount,
currency,
paymentDate,
reference,
null,
invoice);
bankOrder.addBankOrderLineListItem(bankOrderLine);
bankOrder = bankOrderRepo.save(bankOrder);
return bankOrder;
}
public int getBankOrderPartnerType(Invoice invoice) {
if (invoice.getOperationTypeSelect() == InvoiceRepository.OPERATION_TYPE_CLIENT_REFUND
|| invoice.getOperationTypeSelect() == InvoiceRepository.OPERATION_TYPE_CLIENT_SALE) {
return BankOrderRepository.PARTNER_TYPE_CUSTOMER;
} else {
return BankOrderRepository.PARTNER_TYPE_SUPPLIER;
}
}
public BankDetails getSenderBankDetails(Invoice invoice) {
if (invoice.getBankDetails() != null) {
return invoice.getCompanyBankDetails();
}
return invoice.getCompany().getDefaultBankDetails();
}
}

View File

@ -0,0 +1,29 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLineOrigin;
import com.axelor.db.Model;
public interface BankOrderLineOriginService {
public BankOrderLineOrigin createBankOrderLineOrigin(Model model);
public boolean existBankOrderLineOrigin(BankOrder bankOrder, Model model);
}

View File

@ -0,0 +1,137 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.PaymentScheduleLine;
import com.axelor.apps.account.db.Reimbursement;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLineOrigin;
import com.axelor.apps.bankpayment.db.repo.BankOrderLineOriginRepository;
import com.axelor.db.EntityHelper;
import com.axelor.db.Model;
import com.google.inject.Inject;
import java.time.LocalDate;
public class BankOrderLineOriginServiceImpl implements BankOrderLineOriginService {
protected BankOrderLineOriginRepository bankOrderLineOriginRepository;
@Inject
public BankOrderLineOriginServiceImpl(
BankOrderLineOriginRepository bankOrderLineOriginRepository) {
this.bankOrderLineOriginRepository = bankOrderLineOriginRepository;
}
public BankOrderLineOrigin createBankOrderLineOrigin(Model model) {
Class<?> klass = EntityHelper.getEntityClass(model);
return this.createBankOrderLineOrigin(
klass.getCanonicalName(),
model.getId(),
computeRelatedToSelectName(model),
computeRelatedToSelectDate(model),
computeRelatedToSelectDueDate(model));
}
protected String computeRelatedToSelectName(Model model) {
if (model instanceof Invoice) {
return ((Invoice) model).getInvoiceId();
} else if (model instanceof PaymentScheduleLine) {
return ((PaymentScheduleLine) model).getName();
} else if (model instanceof Reimbursement) {
return ((Reimbursement) model).getRef();
}
return null;
}
protected LocalDate computeRelatedToSelectDate(Model model) {
if (model instanceof Invoice) {
return ((Invoice) model).getInvoiceDate();
} else if (model instanceof PaymentScheduleLine) {
return ((PaymentScheduleLine) model).getScheduleDate();
} else if (model instanceof Reimbursement) {
return null;
}
return null;
}
protected LocalDate computeRelatedToSelectDueDate(Model model) {
if (model instanceof Invoice) {
return ((Invoice) model).getDueDate();
} else if (model instanceof PaymentScheduleLine) {
return ((PaymentScheduleLine) model).getScheduleDate();
} else if (model instanceof Reimbursement) {
return null;
}
return null;
}
protected BankOrderLineOrigin createBankOrderLineOrigin(
String relatedToSelect,
Long relatedToSelectId,
String relatedToSelectName,
LocalDate relatedToSelectDate,
LocalDate relatedToSelectDueDate) {
BankOrderLineOrigin bankOrderLineOrigin = new BankOrderLineOrigin();
bankOrderLineOrigin.setRelatedToSelect(relatedToSelect);
bankOrderLineOrigin.setRelatedToSelectId(relatedToSelectId);
bankOrderLineOrigin.setRelatedToSelectName(relatedToSelectName);
bankOrderLineOrigin.setRelatedToSelectDate(relatedToSelectDate);
bankOrderLineOrigin.setRelatedToSelectDueDate(relatedToSelectDueDate);
return bankOrderLineOrigin;
}
public boolean existBankOrderLineOrigin(BankOrder bankOrder, Model model) {
Class<?> klass = EntityHelper.getEntityClass(model);
Long count =
bankOrderLineOriginRepository
.all()
.filter(
"self.relatedToSelect = ?1 AND self.relatedToSelectId = ?2",
klass.getCanonicalName(),
model.getId())
.count();
if (count != null && count > 0) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,456 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderFileFormat;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.repo.BankOrderFileFormatRepository;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.repo.BankDetailsRepository;
import com.axelor.apps.base.service.CurrencyService;
import com.axelor.apps.base.service.PartnerService;
import com.axelor.apps.tool.StringTool;
import com.axelor.db.Model;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.CallMethod;
import com.google.inject.Inject;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BankOrderLineService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected BankDetailsRepository bankDetailsRepo;
protected CurrencyService currencyService;
protected BankOrderLineOriginService bankOrderLineOriginService;
@Inject
public BankOrderLineService(
BankDetailsRepository bankDetailsRepo,
CurrencyService currencyService,
BankOrderLineOriginService bankOrderLineOriginService) {
this.bankDetailsRepo = bankDetailsRepo;
this.currencyService = currencyService;
this.bankOrderLineOriginService = bankOrderLineOriginService;
}
/**
* Method to create a specific BankOrderLine for SEPA and international transfer and direct debit
*
* @param partner
* @param amount
* @param receiverReference
* @param receiverLabel
* @return
* @throws AxelorException
*/
public BankOrderLine createBankOrderLine(
BankOrderFileFormat bankOrderFileFormat,
Partner partner,
BigDecimal amount,
Currency currency,
LocalDate bankOrderDate,
String receiverReference,
String receiverLabel,
Model origin)
throws AxelorException {
BankDetails receiverBankDetails = bankDetailsRepo.findDefaultByPartner(partner);
return this.createBankOrderLine(
bankOrderFileFormat,
null,
partner,
receiverBankDetails,
amount,
currency,
bankOrderDate,
receiverReference,
receiverLabel,
origin);
}
/**
* Method to create a specific BankOrderLine for treasury transfer
*
* @param receiverCompany
* @param amount
* @param receiverReference
* @param receiverLabel
* @return
* @throws AxelorException
*/
public BankOrderLine createBankOrderLine(
BankOrderFileFormat bankOrderFileFormat,
Company receiverCompany,
BigDecimal amount,
Currency currency,
LocalDate bankOrderDate,
String receiverReference,
String receiverLabel,
Model origin)
throws AxelorException {
return this.createBankOrderLine(
bankOrderFileFormat,
receiverCompany,
receiverCompany.getPartner(),
receiverCompany.getDefaultBankDetails(),
amount,
currency,
bankOrderDate,
receiverReference,
receiverLabel,
origin);
}
/**
* Generic method to create a BankOrderLine
*
* @param receiverCompany
* @param partner
* @param bankDetails
* @param amount
* @param receiverReference
* @param receiverLabel
* @return
* @throws AxelorException
*/
public BankOrderLine createBankOrderLine(
BankOrderFileFormat bankOrderFileFormat,
Company receiverCompany,
Partner partner,
BankDetails bankDetails,
BigDecimal amount,
Currency currency,
LocalDate bankOrderDate,
String receiverReference,
String receiverLabel,
Model origin)
throws AxelorException {
BankOrderLine bankOrderLine = new BankOrderLine();
bankOrderLine.setReceiverCompany(receiverCompany);
bankOrderLine.setPartner(partner);
bankOrderLine.setReceiverBankDetails(bankDetails);
bankOrderLine.setBankOrderAmount(amount);
if (bankOrderFileFormat.getIsMultiCurrency()) {
bankOrderLine.setBankOrderCurrency(currency);
}
if (bankOrderFileFormat.getIsMultiDate()) {
bankOrderLine.setBankOrderDate(bankOrderDate);
}
bankOrderLine.setReceiverReference(receiverReference);
bankOrderLine.setReceiverLabel(receiverLabel);
if (origin != null) {
bankOrderLine.addBankOrderLineOriginListItem(
bankOrderLineOriginService.createBankOrderLineOrigin(origin));
}
if (bankOrderFileFormat
.getOrderFileFormatSelect()
.equals(BankOrderFileFormatRepository.FILE_FORMAT_PAIN_XXX_CFONB320_XCT)) {
bankOrderLine.setBankOrderEconomicReason(bankOrderFileFormat.getBankOrderEconomicReason());
bankOrderLine.setReceiverCountry(bankOrderFileFormat.getReceiverCountry());
if (bankDetails != null) {
Bank bank = bankDetails.getBank();
if (bank != null && bank.getCountry() != null) {
bankOrderLine.setReceiverCountry(bank.getCountry());
}
} else {
throw new AxelorException(
bankOrderLine,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_BANK_DETAILS_MISSING));
}
bankOrderLine.setPaymentModeSelect(bankOrderFileFormat.getPaymentModeSelect());
bankOrderLine.setFeesImputationModeSelect(bankOrderFileFormat.getFeesImputationModeSelect());
bankOrderLine.setReceiverAddressStr(getReceiverAddress(partner));
}
return bankOrderLine;
}
@CallMethod
public String getReceiverAddress(Partner partner) {
Address receiverAddress = Beans.get(PartnerService.class).getInvoicingAddress(partner);
return receiverAddress != null ? receiverAddress.getFullName() : "";
}
public void checkPreconditions(BankOrderLine bankOrderLine) throws AxelorException {
if (bankOrderLine.getBankOrder().getPartnerTypeSelect()
== BankOrderRepository.PARTNER_TYPE_COMPANY) {
if (bankOrderLine.getReceiverCompany() == null) {
throw new AxelorException(
bankOrderLine,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_COMPANY_MISSING));
}
}
if (bankOrderLine.getPartner() == null) {
throw new AxelorException(
bankOrderLine,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_PARTNER_MISSING));
}
if (bankOrderLine.getReceiverBankDetails() == null) {
throw new AxelorException(
bankOrderLine,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_BANK_DETAILS_MISSING));
}
if (bankOrderLine.getBankOrderAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new AxelorException(
bankOrderLine,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_AMOUNT_NEGATIVE));
}
}
public String createDomainForBankDetails(BankOrderLine bankOrderLine, BankOrder bankOrder) {
String domain = "";
String bankDetailsIds = "";
if ((bankOrderLine == null) || (bankOrder == null)) {
return domain;
}
// the case where the bank order is for a company
if (bankOrder.getPartnerTypeSelect() == BankOrderRepository.PARTNER_TYPE_COMPANY) {
if (bankOrderLine.getReceiverCompany() != null) {
bankDetailsIds =
StringTool.getIdListString(bankOrderLine.getReceiverCompany().getBankDetailsSet());
if (bankOrderLine.getReceiverCompany().getDefaultBankDetails() != null) {
bankDetailsIds += bankDetailsIds.equals("") ? "" : ",";
bankDetailsIds +=
bankOrderLine.getReceiverCompany().getDefaultBankDetails().getId().toString();
}
}
}
// case where the bank order is for a partner
else if (bankOrderLine.getPartner() != null) {
bankDetailsIds = StringTool.getIdListString(bankOrderLine.getPartner().getBankDetailsList());
}
if (bankDetailsIds.equals("")) {
return domain = "";
}
domain = "self.id IN(" + bankDetailsIds + ")";
// filter the result on active bank details
domain += " AND self.active = true";
// filter on the result from bankPartner if the option is active.
EbicsPartner ebicsPartner =
Beans.get(EbicsPartnerRepository.class)
.all()
.filter("? MEMBER OF self.bankDetailsSet", bankOrder.getSenderBankDetails())
.fetchOne();
if (ebicsPartnerIsFiltering(ebicsPartner, bankOrder.getOrderTypeSelect())) {
domain +=
" AND self.id IN ("
+ StringTool.getIdListString(ebicsPartner.getReceiverBankDetailsSet())
+ ")";
}
// filter on the bank details identifier type from the bank order file format
if (bankOrder.getBankOrderFileFormat() != null) {
String acceptedIdentifiers = bankOrder.getBankOrderFileFormat().getBankDetailsTypeSelect();
if (acceptedIdentifiers != null && !acceptedIdentifiers.equals("")) {
domain += " AND self.bank.bankDetailsTypeSelect IN (" + acceptedIdentifiers + ")";
}
}
// filter on the currency if it is set in bank order and in the bankdetails
// and if the bankOrder is not multicurrency
// and if the partner type select is a company
Currency currency = bankOrder.getBankOrderCurrency();
if (!bankOrder.getIsMultiCurrency()
&& currency != null
&& bankOrder.getPartnerTypeSelect() == BankOrderRepository.PARTNER_TYPE_COMPANY) {
String fileFormatCurrencyId = currency.getId().toString();
domain += " AND (self.currency IS NULL OR self.currency.id = " + fileFormatCurrencyId + ")";
}
return domain;
}
/**
* Search the default bank detail in receiver company if partner type select is company. If not
* company, search default in partner of bank order line. If no default bank detail, return the
* alone bank detail present if is active in the partner of bank order line.
*
* @param bankOrderLine The bank order line
* @param bankOrder The bank order
* @return default bank detail if present otherwise the unique bank detail if active
*/
public BankDetails getDefaultBankDetails(BankOrderLine bankOrderLine, BankOrder bankOrder) {
BankDetails candidateBankDetails = null;
if (bankOrder.getPartnerTypeSelect() == BankOrderRepository.PARTNER_TYPE_COMPANY
&& bankOrderLine.getReceiverCompany() != null) {
candidateBankDetails = bankOrderLine.getReceiverCompany().getDefaultBankDetails();
if (candidateBankDetails == null) {
for (BankDetails bankDetails : bankOrderLine.getReceiverCompany().getBankDetailsSet()) {
if (candidateBankDetails != null && bankDetails.getActive()) {
candidateBankDetails = null;
break;
} else if (bankDetails.getActive()) {
candidateBankDetails = bankDetails;
}
}
}
} else if (bankOrder.getPartnerTypeSelect() != BankOrderRepository.PARTNER_TYPE_COMPANY
&& bankOrderLine.getPartner() != null) {
candidateBankDetails = bankDetailsRepo.findDefaultByPartner(bankOrderLine.getPartner());
if (candidateBankDetails == null) {
List<BankDetails> bankDetailsList =
bankDetailsRepo.findActivesByPartner(bankOrderLine.getPartner(), true).fetch();
if (bankDetailsList.size() == 1) {
candidateBankDetails = bankDetailsList.get(0);
}
}
}
try {
checkBankDetails(candidateBankDetails, bankOrder);
} catch (AxelorException e) {
candidateBankDetails = null;
}
return candidateBankDetails;
}
public void checkBankDetails(BankDetails bankDetails, BankOrder bankOrder)
throws AxelorException {
if (bankDetails == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_BANK_DETAILS_MISSING));
}
// check if the bank details is active
if (!bankDetails.getActive()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_BANK_DETAILS_NOT_ACTIVE));
}
// filter on the result from bankPartner if the option is active.
EbicsPartner ebicsPartner =
Beans.get(EbicsPartnerRepository.class)
.all()
.filter("? MEMBER OF self.bankDetailsSet", bankOrder.getSenderBankDetails())
.fetchOne();
if (ebicsPartnerIsFiltering(ebicsPartner, bankOrder.getOrderTypeSelect())) {
if (!ebicsPartner.getReceiverBankDetailsSet().contains(bankDetails)) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_BANK_DETAILS_FORBIDDEN));
}
}
// filter on the bank details identifier type from the bank order file format
if (bankOrder.getBankOrderFileFormat() != null) {
if (!Beans.get(BankOrderService.class)
.checkBankDetailsTypeCompatible(bankDetails, bankOrder.getBankOrderFileFormat())) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_BANK_DETAILS_TYPE_NOT_COMPATIBLE));
}
}
// filter on the currency if the bank order is not multicurrency
// and if the partner type select is a company
if (!bankOrder.getIsMultiCurrency()
&& bankOrder.getBankOrderCurrency() != null
&& bankOrder.getPartnerTypeSelect() == BankOrderRepository.PARTNER_TYPE_COMPANY) {
if (!Beans.get(BankOrderService.class)
.checkBankDetailsCurrencyCompatible(bankDetails, bankOrder)) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_BANK_DETAILS_CURRENCY_NOT_COMPATIBLE));
}
}
}
private boolean ebicsPartnerIsFiltering(EbicsPartner ebicsPartner, int orderType) {
return (ebicsPartner != null)
&& (ebicsPartner.getFilterReceiverBD())
&& (ebicsPartner.getReceiverBankDetailsSet() != null)
&& (!ebicsPartner.getReceiverBankDetailsSet().isEmpty())
&& (ebicsPartner.getOrderTypeSelect() == orderType);
}
public BigDecimal computeCompanyCurrencyAmount(BankOrder bankOrder, BankOrderLine bankOrderLine)
throws AxelorException {
LocalDate bankOrderDate = bankOrder.getBankOrderDate();
if (bankOrder.getIsMultiDate()) {
bankOrderDate = bankOrderLine.getBankOrderDate();
}
Currency bankOrderCurrency = bankOrder.getBankOrderCurrency();
if (bankOrder.getIsMultiCurrency()) {
bankOrderCurrency = bankOrderLine.getBankOrderCurrency();
}
return currencyService
.getAmountCurrencyConvertedAtDate(
bankOrderCurrency,
bankOrder.getCompanyCurrency(),
bankOrderLine.getBankOrderAmount(),
bankOrderDate)
.setScale(2, RoundingMode.HALF_UP); // TODO Manage the number of decimal for currency
}
}

View File

@ -0,0 +1,40 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.account.db.InvoicePayment;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.util.Collection;
public interface BankOrderMergeService {
@Transactional(rollbackOn = {Exception.class})
public BankOrder mergeBankOrders(Collection<BankOrder> bankOrders) throws AxelorException;
/**
* Merge bank orders from invoice payments.
*
* @param invoicePayments
* @return
* @throws AxelorException
*/
BankOrder mergeFromInvoicePayments(Collection<InvoicePayment> invoicePayments)
throws AxelorException;
}

View File

@ -0,0 +1,369 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.InvoicePayment;
import com.axelor.apps.account.db.PaymentMode;
import com.axelor.apps.account.db.PaymentScheduleLine;
import com.axelor.apps.account.db.repo.InvoicePaymentRepository;
import com.axelor.apps.account.db.repo.InvoiceRepository;
import com.axelor.apps.account.db.repo.PaymentScheduleLineRepository;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.db.BankOrderLineOrigin;
import com.axelor.apps.bankpayment.db.repo.BankOrderLineOriginRepository;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BankOrderMergeServiceImpl implements BankOrderMergeService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected BankOrderRepository bankOrderRepo;
protected InvoicePaymentRepository invoicePaymentRepo;
protected BankOrderService bankOrderService;
protected InvoiceRepository invoiceRepository;
protected PaymentScheduleLineRepository paymentScheduleLineRepository;
@Inject
public BankOrderMergeServiceImpl(
BankOrderRepository bankOrderRepo,
InvoicePaymentRepository invoicePaymentRepo,
BankOrderService bankOrderService,
InvoiceRepository invoiceRepository,
PaymentScheduleLineRepository paymentScheduleLineRepository) {
this.bankOrderRepo = bankOrderRepo;
this.invoicePaymentRepo = invoicePaymentRepo;
this.bankOrderService = bankOrderService;
this.invoiceRepository = invoiceRepository;
this.paymentScheduleLineRepository = paymentScheduleLineRepository;
}
@Transactional(rollbackOn = {Exception.class})
public BankOrder mergeBankOrders(Collection<BankOrder> bankOrders) throws AxelorException {
if (bankOrders == null || bankOrders.size() <= 1) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
IExceptionMessage.BANK_ORDER_MERGE_AT_LEAST_TWO_BANK_ORDERS);
}
this.checkSameElements(bankOrders);
BankOrder bankOrder = bankOrders.iterator().next();
bankOrders.remove(bankOrder);
bankOrder.setSenderLabel(null);
bankOrder.setSenderReference(null);
bankOrder.setBankOrderDate(Beans.get(AppBaseService.class).getTodayDate());
bankOrder.setSignatoryUser(null);
bankOrder.setSignatoryEbicsUser(null);
PaymentMode paymentMode = bankOrder.getPaymentMode();
for (BankOrderLine bankOrderLine : this.getAllBankOrderLineList(bankOrders)) {
bankOrder.addBankOrderLineListItem(bankOrderLine);
}
bankOrderRepo.save(bankOrder);
for (BankOrder bankOrderToRemove : bankOrders) {
bankOrderToRemove = bankOrderRepo.find(bankOrderToRemove.getId());
List<InvoicePayment> invoicePaymentList =
invoicePaymentRepo.findByBankOrder(bankOrderToRemove).fetch();
for (InvoicePayment invoicePayment : invoicePaymentList) {
invoicePayment.setBankOrder(bankOrder);
}
bankOrderRepo.remove(bankOrderToRemove);
}
if (paymentMode.getConsoBankOrderLinePerPartner()) {
consolidatePerPartner(bankOrder);
}
bankOrderService.updateTotalAmounts(bankOrder);
return bankOrderRepo.save(bankOrder);
}
protected void checkSameElements(Collection<BankOrder> bankOrders) throws AxelorException {
BankOrder refBankOrder = bankOrders.iterator().next();
int refStatusSelect = refBankOrder.getStatusSelect();
int orderTypeSelect = refBankOrder.getOrderTypeSelect();
PaymentMode refPaymentMode = refBankOrder.getPaymentMode();
int refPartnerTypeSelect = refBankOrder.getPartnerTypeSelect();
Company refSenderCompany = refBankOrder.getSenderCompany();
BankDetails refSenderBankDetails = refBankOrder.getSenderBankDetails();
Currency refCurrency = refBankOrder.getBankOrderCurrency();
boolean isMultiCurrency = refBankOrder.getIsMultiCurrency();
for (BankOrder bankOrder : bankOrders) {
int statusSelect = bankOrder.getStatusSelect();
if (statusSelect != BankOrderRepository.STATUS_DRAFT
&& statusSelect != BankOrderRepository.STATUS_AWAITING_SIGNATURE) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_STATUS));
}
if (statusSelect != refStatusSelect) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_SAME_STATUS));
}
if (!bankOrder.getOrderTypeSelect().equals(orderTypeSelect)) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_SAME_ORDER_TYPE_SELECT));
}
if (!bankOrder.getPaymentMode().equals(refPaymentMode)) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_SAME_PAYMENT_MODE));
}
if (!bankOrder.getPartnerTypeSelect().equals(refPartnerTypeSelect)) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_SAME_PARTNER_TYPE_SELECT));
}
if (!bankOrder.getSenderCompany().equals(refSenderCompany)) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_SAME_SENDER_COMPANY));
}
if (bankOrder.getSenderBankDetails() == null && refSenderBankDetails != null
|| (bankOrder.getSenderBankDetails() != null
&& !bankOrder.getSenderBankDetails().equals(refSenderBankDetails))) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_SAME_SENDER_BANK_DETAILS));
}
if (bankOrder.getIsMultiCurrency() != isMultiCurrency
|| !bankOrder.getIsMultiCurrency()
&& !bankOrder.getBankOrderCurrency().equals(refCurrency)) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_SAME_CURRENCY));
}
}
}
protected List<BankOrderLine> getAllBankOrderLineList(Collection<BankOrder> bankOrders) {
List<BankOrderLine> bankOrderLineList = Lists.newArrayList();
for (BankOrder bankOrder : bankOrders) {
bankOrderLineList.addAll(bankOrder.getBankOrderLineList());
}
return bankOrderLineList;
}
public void consolidatePerPartner(BankOrder bankOrder) {
Map<List<Object>, BankOrderLine> bankOrderLineMap = new HashMap<List<Object>, BankOrderLine>();
int counter = 1;
for (BankOrderLine bankOrderLine : bankOrder.getBankOrderLineList()) {
List<Object> keys = new ArrayList<Object>();
keys.add(bankOrderLine.getPartner());
keys.add(bankOrderLine.getBankOrderCurrency());
keys.add(bankOrderLine.getBankOrderDate());
keys.add(bankOrderLine.getBankOrderEconomicReason());
keys.add(bankOrderLine.getFeesImputationModeSelect());
keys.add(bankOrderLine.getPaymentModeSelect());
keys.add(bankOrderLine.getReceiverBankDetails());
keys.add(bankOrderLine.getReceiverCompany());
if (bankOrderLineMap.containsKey(keys)) {
BankOrderLine consolidateBankOrderLine = bankOrderLineMap.get(keys);
if (consolidateBankOrderLine.getBankOrderLineOriginList() == null) {
consolidateBankOrderLine.setBankOrderLineOriginList(new ArrayList<>());
}
if (bankOrderLine.getBankOrderLineOriginList() != null) {
bankOrderLine
.getBankOrderLineOriginList()
.stream()
.forEach(consolidateBankOrderLine::addBankOrderLineOriginListItem);
}
consolidateBankOrderLine.setBankOrderAmount(
consolidateBankOrderLine.getBankOrderAmount().add(bankOrderLine.getBankOrderAmount()));
consolidateBankOrderLine.setCompanyCurrencyAmount(
consolidateBankOrderLine
.getCompanyCurrencyAmount()
.add(bankOrderLine.getCompanyCurrencyAmount()));
} else {
bankOrderLine.setCounter(counter++);
bankOrderLineMap.put(keys, bankOrderLine);
}
}
bankOrder.getBankOrderLineList().clear();
for (BankOrderLine bankOrderLine : bankOrderLineMap.values()) {
Pair<String, LocalDate> lastReferences = getLastReferences(bankOrderLine);
bankOrderLine.setReceiverReference(lastReferences.getLeft());
bankOrderLine.setBankOrderDate(lastReferences.getRight());
bankOrder.addBankOrderLineListItem(bankOrderLine);
}
}
protected Pair<String, LocalDate> getLastReferences(BankOrderLine bankOrderLine) {
String lastReferenceId = "";
LocalDate lastReferenceDate = null;
for (BankOrderLineOrigin bankOrderLineOrigin : bankOrderLine.getBankOrderLineOriginList()) {
LocalDate originDate = null;
String originReferenceId = null;
switch (bankOrderLineOrigin.getRelatedToSelect()) {
case BankOrderLineOriginRepository.RELATED_TO_INVOICE:
Invoice invoice = invoiceRepository.find(bankOrderLineOrigin.getRelatedToSelectId());
if (!Strings.isNullOrEmpty(invoice.getSupplierInvoiceNb())) {
originReferenceId = invoice.getSupplierInvoiceNb();
} else {
originReferenceId = invoice.getInvoiceId();
}
if (!Strings.isNullOrEmpty(invoice.getSupplierInvoiceNb())) {
originDate = invoice.getOriginDate();
} else {
originDate = invoice.getInvoiceDate();
}
break;
case BankOrderLineOriginRepository.RELATED_TO_PAYMENT_SCHEDULE_LINE:
PaymentScheduleLine paymentScheduleLine =
paymentScheduleLineRepository.find(bankOrderLineOrigin.getRelatedToSelectId());
originReferenceId = paymentScheduleLine.getName();
originDate = paymentScheduleLine.getScheduleDate();
break;
default:
break;
}
if (originDate != null
&& (lastReferenceDate == null || lastReferenceDate.isBefore(originDate))) {
lastReferenceDate = originDate;
lastReferenceId = originReferenceId;
}
}
return Pair.of(lastReferenceId, lastReferenceDate);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public BankOrder mergeFromInvoicePayments(Collection<InvoicePayment> invoicePayments)
throws AxelorException {
if (invoicePayments == null || invoicePayments.isEmpty()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_NO_BANK_ORDERS));
}
Collection<InvoicePayment> invoicePaymentsWithBankOrders = new ArrayList<>();
Collection<BankOrder> bankOrders = new LinkedHashSet<>();
for (InvoicePayment invoicePayment : invoicePayments) {
BankOrder bankOrder = invoicePayment.getBankOrder();
if (bankOrder != null) {
invoicePaymentsWithBankOrders.add(invoicePayment);
bankOrders.add(bankOrder);
}
}
if (bankOrders.size() > 1) {
LocalDate bankOrderDate = bankOrders.iterator().next().getBankOrderDate();
BankOrder mergedBankOrder = mergeBankOrders(bankOrders);
mergedBankOrder.setBankOrderDate(bankOrderDate);
for (InvoicePayment invoicePayment : invoicePaymentsWithBankOrders) {
invoicePayment.setBankOrder(mergedBankOrder);
}
return mergedBankOrder;
}
if (!bankOrders.isEmpty()) {
return bankOrders.iterator().next();
}
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_MERGE_NO_BANK_ORDERS));
}
}

View File

@ -0,0 +1,26 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.exception.AxelorException;
public interface BankOrderMoveService {
public void generateMoves(BankOrder bankOrder) throws AxelorException;
}

View File

@ -0,0 +1,286 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.account.db.Account;
import com.axelor.apps.account.db.AccountingSituation;
import com.axelor.apps.account.db.Journal;
import com.axelor.apps.account.db.Move;
import com.axelor.apps.account.db.MoveLine;
import com.axelor.apps.account.db.PaymentMode;
import com.axelor.apps.account.db.repo.MoveRepository;
import com.axelor.apps.account.service.AccountingSituationService;
import com.axelor.apps.account.service.move.MoveService;
import com.axelor.apps.account.service.payment.PaymentModeService;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.bankpayment.service.config.BankPaymentConfigService;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.inject.Inject;
import java.lang.invoke.MethodHandles;
import java.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BankOrderMoveServiceImpl implements BankOrderMoveService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected MoveService moveService;
protected PaymentModeService paymentModeService;
protected AccountingSituationService accountingSituationService;
protected BankPaymentConfigService bankPaymentConfigService;
protected PaymentMode paymentMode;
protected Company senderCompany;
protected int orderTypeSelect;
protected int partnerTypeSelect;
protected Journal journal;
protected Account senderBankAccount;
protected BankDetails senderBankDetails;
protected boolean isMultiDate;
protected boolean isMultiCurrency;
protected boolean isDebit;
@Inject
public BankOrderMoveServiceImpl(
MoveService moveService,
PaymentModeService paymentModeService,
AccountingSituationService accountingSituationService,
BankPaymentConfigService bankPaymentConfigService) {
this.moveService = moveService;
this.paymentModeService = paymentModeService;
this.accountingSituationService = accountingSituationService;
this.bankPaymentConfigService = bankPaymentConfigService;
}
@Override
public void generateMoves(BankOrder bankOrder) throws AxelorException {
if (bankOrder.getBankOrderLineList() == null || bankOrder.getBankOrderLineList().isEmpty()) {
return;
}
paymentMode = bankOrder.getPaymentMode();
if (paymentMode == null || !paymentMode.getGenerateMoveAutoFromBankOrder()) {
return;
}
orderTypeSelect = bankOrder.getOrderTypeSelect();
senderCompany = bankOrder.getSenderCompany();
senderBankDetails = bankOrder.getSenderBankDetails();
partnerTypeSelect = bankOrder.getPartnerTypeSelect();
journal =
paymentModeService.getPaymentModeJournal(paymentMode, senderCompany, senderBankDetails);
senderBankAccount =
paymentModeService.getPaymentModeAccount(paymentMode, senderCompany, senderBankDetails);
isMultiDate = bankOrder.getIsMultiDate();
isMultiCurrency = bankOrder.getIsMultiCurrency();
if (orderTypeSelect == BankOrderRepository.ORDER_TYPE_INTERNATIONAL_CREDIT_TRANSFER
|| orderTypeSelect == BankOrderRepository.ORDER_TYPE_SEPA_CREDIT_TRANSFER) {
isDebit = true;
} else {
isDebit = false;
}
for (BankOrderLine bankOrderLine : bankOrder.getBankOrderLineList()) {
generateMoves(bankOrderLine);
}
}
protected void generateMoves(BankOrderLine bankOrderLine) throws AxelorException {
bankOrderLine.setSenderMove(generateSenderMove(bankOrderLine));
if (partnerTypeSelect == BankOrderRepository.PARTNER_TYPE_COMPANY) {
bankOrderLine.setReceiverMove(generateReceiverMove(bankOrderLine));
}
}
protected Move generateSenderMove(BankOrderLine bankOrderLine) throws AxelorException {
Partner partner = bankOrderLine.getPartner();
Move senderMove =
moveService
.getMoveCreateService()
.createMove(
journal,
senderCompany,
this.getCurrency(bankOrderLine),
partner,
this.getDate(bankOrderLine),
paymentMode,
MoveRepository.TECHNICAL_ORIGIN_AUTOMATIC);
MoveLine bankMoveLine =
moveService
.getMoveLineService()
.createMoveLine(
senderMove,
partner,
senderBankAccount,
bankOrderLine.getBankOrderAmount(),
!isDebit,
senderMove.getDate(),
1,
bankOrderLine.getReceiverReference(),
bankOrderLine.getReceiverLabel());
senderMove.addMoveLineListItem(bankMoveLine);
MoveLine partnerMoveLine =
moveService
.getMoveLineService()
.createMoveLine(
senderMove,
partner,
getPartnerAccount(partner, senderCompany, senderCompany),
bankOrderLine.getBankOrderAmount(),
isDebit,
senderMove.getDate(),
2,
bankOrderLine.getReceiverReference(),
bankOrderLine.getReceiverLabel());
senderMove.addMoveLineListItem(partnerMoveLine);
return senderMove;
}
protected Move generateReceiverMove(BankOrderLine bankOrderLine) throws AxelorException {
Partner partner = bankOrderLine.getPartner();
Company receiverCompany = bankOrderLine.getReceiverCompany();
BankDetails receiverBankDetails = bankOrderLine.getReceiverBankDetails();
Journal receiverJournal =
paymentModeService.getPaymentModeJournal(paymentMode, receiverCompany, receiverBankDetails);
Account receiverBankAccount =
paymentModeService.getPaymentModeAccount(paymentMode, receiverCompany, receiverBankDetails);
Move receiverMove =
moveService
.getMoveCreateService()
.createMove(
receiverJournal,
receiverCompany,
this.getCurrency(bankOrderLine),
partner,
this.getDate(bankOrderLine),
paymentMode,
MoveRepository.TECHNICAL_ORIGIN_AUTOMATIC);
MoveLine bankMoveLine =
moveService
.getMoveLineService()
.createMoveLine(
receiverMove,
partner,
receiverBankAccount,
bankOrderLine.getBankOrderAmount(),
isDebit,
receiverMove.getDate(),
1,
bankOrderLine.getReceiverReference(),
bankOrderLine.getReceiverLabel());
receiverMove.addMoveLineListItem(bankMoveLine);
MoveLine partnerMoveLine =
moveService
.getMoveLineService()
.createMoveLine(
receiverMove,
partner,
getPartnerAccount(partner, receiverCompany, receiverMove.getCompany()),
bankOrderLine.getBankOrderAmount(),
!isDebit,
receiverMove.getDate(),
2,
bankOrderLine.getReceiverReference(),
bankOrderLine.getReceiverLabel());
receiverMove.addMoveLineListItem(partnerMoveLine);
return receiverMove;
}
protected Account getPartnerAccount(Partner partner, Company receiverCompany, Company moveCompany)
throws AxelorException {
AccountingSituation accountingSituation =
accountingSituationService.getAccountingSituation(partner, receiverCompany);
switch (partnerTypeSelect) {
case BankOrderRepository.PARTNER_TYPE_CUSTOMER:
return accountingSituationService.getCustomerAccount(partner, receiverCompany);
case BankOrderRepository.PARTNER_TYPE_EMPLOYEE:
return accountingSituationService.getEmployeeAccount(partner, receiverCompany);
case BankOrderRepository.PARTNER_TYPE_SUPPLIER:
return accountingSituationService.getSupplierAccount(partner, receiverCompany);
case BankOrderRepository.PARTNER_TYPE_COMPANY:
if (receiverCompany.equals(senderCompany)) {
return bankPaymentConfigService.getInternalBankToBankAccount(
bankPaymentConfigService.getBankPaymentConfig(moveCompany));
} else {
return bankPaymentConfigService.getExternalBankToBankAccount(
bankPaymentConfigService.getBankPaymentConfig(moveCompany));
}
default:
throw new AxelorException(
accountingSituation,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BANK_ORDER_PARTNER_TYPE_MISSING),
I18n.get(com.axelor.apps.base.exceptions.IExceptionMessage.EXCEPTION));
}
}
protected LocalDate getDate(BankOrderLine bankOrderLine) {
if (isMultiDate) {
return bankOrderLine.getBankOrderDate();
} else {
return bankOrderLine.getBankOrder().getBankOrderDate();
}
}
protected Currency getCurrency(BankOrderLine bankOrderLine) {
if (isMultiCurrency) {
return bankOrderLine.getBankOrderCurrency();
} else {
return bankOrderLine.getBankOrder().getBankOrderCurrency();
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderFileFormat;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.exception.AxelorException;
import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
public interface BankOrderService {
public BigDecimal computeBankOrderTotalAmount(BankOrder bankOrder) throws AxelorException;
public BigDecimal computeCompanyCurrencyTotalAmount(BankOrder bankOrder) throws AxelorException;
public void updateTotalAmounts(BankOrder bankOrder) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void confirm(BankOrder bankOrder)
throws AxelorException, JAXBException, IOException, DatatypeConfigurationException;
@Transactional(rollbackOn = {Exception.class})
public void sign(BankOrder bankOrder);
@Transactional(rollbackOn = {Exception.class})
public void validate(BankOrder bankOrder)
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException;
public void realize(BankOrder bankOrder) throws AxelorException;
public File generateFile(BankOrder bankOrder)
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException;
@Transactional(rollbackOn = {Exception.class})
public BankOrder generateSequence(BankOrder bankOrder) throws AxelorException;
public void setSequenceOnBankOrderLines(BankOrder bankOrder);
public void checkLines(BankOrder bankOrder) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void validatePayment(BankOrder bankOrder) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void cancelPayment(BankOrder bankOrder) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void cancelBankOrder(BankOrder bankOrder) throws AxelorException;
@Transactional
public EbicsUser getDefaultEbicsUserFromBankDetails(BankDetails bankDetails);
public String createDomainForBankDetails(BankOrder bankOrder);
public BankDetails getDefaultBankDetails(BankOrder bankOrder);
public void checkBankDetails(BankDetails bankDetails, BankOrder bankOrder) throws AxelorException;
public boolean checkBankDetailsTypeCompatible(
BankDetails bankDetails, BankOrderFileFormat bankOrderFileFormat);
public boolean checkBankDetailsCurrencyCompatible(BankDetails bankDetails, BankOrder bankOrder);
public void resetReceivers(BankOrder bankOrder);
public ActionViewBuilder buildBankOrderLineView(
String gridViewName, String formViewName, String viewDomain);
}

View File

@ -0,0 +1,821 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder;
import com.axelor.apps.account.db.InvoicePayment;
import com.axelor.apps.account.db.PaymentMode;
import com.axelor.apps.account.db.repo.InvoicePaymentRepository;
import com.axelor.apps.account.service.payment.invoice.payment.InvoicePaymentCancelService;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderFileFormat;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.db.BankPaymentConfig;
import com.axelor.apps.bankpayment.db.EbicsPartner;
import com.axelor.apps.bankpayment.db.EbicsUser;
import com.axelor.apps.bankpayment.db.repo.BankOrderFileFormatRepository;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.bankpayment.db.repo.EbicsPartnerRepository;
import com.axelor.apps.bankpayment.ebics.service.EbicsService;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.bankpayment.service.app.AppBankPaymentService;
import com.axelor.apps.bankpayment.service.bankorder.file.directdebit.BankOrderFile00800101Service;
import com.axelor.apps.bankpayment.service.bankorder.file.directdebit.BankOrderFile00800102Service;
import com.axelor.apps.bankpayment.service.bankorder.file.directdebit.BankOrderFile008Service;
import com.axelor.apps.bankpayment.service.bankorder.file.transfer.BankOrderFile00100102Service;
import com.axelor.apps.bankpayment.service.bankorder.file.transfer.BankOrderFile00100103Service;
import com.axelor.apps.bankpayment.service.bankorder.file.transfer.BankOrderFileAFB160ICTService;
import com.axelor.apps.bankpayment.service.bankorder.file.transfer.BankOrderFileAFB320XCTService;
import com.axelor.apps.bankpayment.service.config.BankPaymentConfigService;
import com.axelor.apps.bankpayment.service.invoice.payment.InvoicePaymentValidateServiceBankPayImpl;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.Sequence;
import com.axelor.apps.base.service.BankDetailsService;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.auth.db.User;
import com.axelor.common.ObjectUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.mail.db.repo.MailFollowerRepository;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BankOrderServiceImpl implements BankOrderService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected BankOrderRepository bankOrderRepo;
protected InvoicePaymentRepository invoicePaymentRepo;
protected BankOrderLineService bankOrderLineService;
protected EbicsService ebicsService;
protected InvoicePaymentCancelService invoicePaymentCancelService;
protected BankPaymentConfigService bankPaymentConfigService;
protected SequenceService sequenceService;
protected BankOrderLineOriginService bankOrderLineOriginService;
@Inject
public BankOrderServiceImpl(
BankOrderRepository bankOrderRepo,
InvoicePaymentRepository invoicePaymentRepo,
BankOrderLineService bankOrderLineService,
EbicsService ebicsService,
InvoicePaymentCancelService invoicePaymentCancelService,
BankPaymentConfigService bankPaymentConfigService,
SequenceService sequenceService,
BankOrderLineOriginService bankOrderLineOriginService) {
this.bankOrderRepo = bankOrderRepo;
this.invoicePaymentRepo = invoicePaymentRepo;
this.bankOrderLineService = bankOrderLineService;
this.ebicsService = ebicsService;
this.invoicePaymentCancelService = invoicePaymentCancelService;
this.bankPaymentConfigService = bankPaymentConfigService;
this.sequenceService = sequenceService;
this.bankOrderLineOriginService = bankOrderLineOriginService;
}
public void checkPreconditions(BankOrder bankOrder) throws AxelorException {
LocalDate brankOrderDate = bankOrder.getBankOrderDate();
if (brankOrderDate != null) {
if (brankOrderDate.isBefore(LocalDate.now())) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_DATE));
}
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_DATE_MISSING));
}
if (bankOrder.getOrderTypeSelect() == 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_TYPE_MISSING));
}
if (bankOrder.getPartnerTypeSelect() == 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_PARTNER_TYPE_MISSING));
}
if (bankOrder.getPaymentMode() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_PAYMENT_MODE_MISSING));
}
if (bankOrder.getSenderCompany() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_COMPANY_MISSING));
}
if (bankOrder.getSenderBankDetails() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_BANK_DETAILS_MISSING));
}
if (!bankOrder.getIsMultiCurrency() && bankOrder.getBankOrderCurrency() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_CURRENCY_MISSING));
}
if (bankOrder.getSignatoryUser() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_SIGNATORY_MISSING));
}
}
@Override
public BigDecimal computeBankOrderTotalAmount(BankOrder bankOrder) throws AxelorException {
BigDecimal bankOrderTotalAmount = BigDecimal.ZERO;
List<BankOrderLine> bankOrderLines = bankOrder.getBankOrderLineList();
if (bankOrderLines != null) {
for (BankOrderLine bankOrderLine : bankOrderLines) {
BigDecimal amount = bankOrderLine.getBankOrderAmount();
if (amount != null) {
bankOrderTotalAmount = bankOrderTotalAmount.add(amount);
}
}
}
return bankOrderTotalAmount;
}
@Override
public BigDecimal computeCompanyCurrencyTotalAmount(BankOrder bankOrder) throws AxelorException {
BigDecimal companyCurrencyTotalAmount = BigDecimal.ZERO;
List<BankOrderLine> bankOrderLines = bankOrder.getBankOrderLineList();
if (bankOrderLines != null) {
for (BankOrderLine bankOrderLine : bankOrderLines) {
BigDecimal amount = bankOrderLine.getCompanyCurrencyAmount();
if (amount != null) {
companyCurrencyTotalAmount = companyCurrencyTotalAmount.add(amount);
}
}
}
return companyCurrencyTotalAmount;
}
@Override
public void updateTotalAmounts(BankOrder bankOrder) throws AxelorException {
if (bankOrder.getOrderTypeSelect().equals(BankOrderRepository.ORDER_TYPE_SEND_BANK_ORDER)) {
bankOrder.setArithmeticTotal(bankOrder.getBankOrderTotalAmount());
} else {
bankOrder.setArithmeticTotal(this.computeBankOrderTotalAmount(bankOrder));
}
if (!bankOrder.getIsMultiCurrency()) {
bankOrder.setBankOrderTotalAmount(bankOrder.getArithmeticTotal());
}
bankOrder.setCompanyCurrencyTotalAmount(this.computeCompanyCurrencyTotalAmount(bankOrder));
}
@Override
@Transactional(rollbackOn = {Exception.class})
public BankOrder generateSequence(BankOrder bankOrder) throws AxelorException {
if (bankOrder.getBankOrderSeq() == null) {
Sequence sequence = getSequence(bankOrder);
setBankOrderSeq(bankOrder, sequence);
bankOrderRepo.save(bankOrder);
}
return bankOrder;
}
@Override
public void checkLines(BankOrder bankOrder) throws AxelorException {
List<BankOrderLine> bankOrderLines = bankOrder.getBankOrderLineList();
if (bankOrderLines.isEmpty()) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINES_MISSING));
} else {
validateBankOrderLines(
bankOrderLines, bankOrder.getOrderTypeSelect(), bankOrder.getArithmeticTotal());
}
}
public void validateBankOrderLines(
List<BankOrderLine> bankOrderLines, int orderType, BigDecimal arithmeticTotal)
throws AxelorException {
BigDecimal totalAmount = BigDecimal.ZERO;
for (BankOrderLine bankOrderLine : bankOrderLines) {
bankOrderLineService.checkPreconditions(bankOrderLine);
totalAmount = totalAmount.add(bankOrderLine.getBankOrderAmount());
bankOrderLineService.checkBankDetails(
bankOrderLine.getReceiverBankDetails(), bankOrderLine.getBankOrder());
}
if (!totalAmount.equals(arithmeticTotal)) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_LINE_TOTAL_AMOUNT_INVALID));
}
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void validatePayment(BankOrder bankOrder) throws AxelorException {
List<InvoicePayment> invoicePaymentList = invoicePaymentRepo.findByBankOrder(bankOrder).fetch();
InvoicePaymentValidateServiceBankPayImpl invoicePaymentValidateServiceBankPayImpl =
Beans.get(InvoicePaymentValidateServiceBankPayImpl.class);
for (InvoicePayment invoicePayment : invoicePaymentList) {
if (invoicePayment != null
&& invoicePayment.getStatusSelect() != InvoicePaymentRepository.STATUS_VALIDATED
&& invoicePayment.getInvoice() != null) {
if (bankOrderLineOriginService.existBankOrderLineOrigin(
bankOrder, invoicePayment.getInvoice())) {
invoicePaymentValidateServiceBankPayImpl.validateFromBankOrder(invoicePayment, true);
}
}
}
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void cancelPayment(BankOrder bankOrder) throws AxelorException {
List<InvoicePayment> invoicePaymentList = invoicePaymentRepo.findByBankOrder(bankOrder).fetch();
for (InvoicePayment invoicePayment : invoicePaymentList) {
if (invoicePayment != null
&& invoicePayment.getStatusSelect() != InvoicePaymentRepository.STATUS_CANCELED) {
invoicePaymentCancelService.cancel(invoicePayment);
}
}
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void confirm(BankOrder bankOrder)
throws AxelorException, JAXBException, IOException, DatatypeConfigurationException {
checkBankDetails(bankOrder.getSenderBankDetails(), bankOrder);
if (bankOrder.getGeneratedMetaFile() == null) {
checkLines(bankOrder);
}
setNbOfLines(bankOrder);
setSequenceOnBankOrderLines(bankOrder);
generateFile(bankOrder);
if (Beans.get(AppBankPaymentService.class).getAppBankPayment().getEnableEbicsModule()) {
bankOrder.setConfirmationDateTime(
Beans.get(AppBaseService.class).getTodayDateTime().toLocalDateTime());
bankOrder.setStatusSelect(BankOrderRepository.STATUS_AWAITING_SIGNATURE);
makeEbicsUserFollow(bankOrder);
bankOrderRepo.save(bankOrder);
} else {
validate(bankOrder);
}
}
@Override
@Transactional
public void sign(BankOrder bankOrder) {
// TODO
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void validate(BankOrder bankOrder) throws AxelorException {
bankOrder.setValidationDateTime(LocalDateTime.now());
bankOrder.setStatusSelect(BankOrderRepository.STATUS_VALIDATED);
if (bankPaymentConfigService
.getBankPaymentConfig(bankOrder.getSenderCompany())
.getGenerateMoveOnBankOrderValidation()) {
validatePayment(bankOrder);
}
bankOrderRepo.save(bankOrder);
}
@Override
public void realize(BankOrder bankOrder) throws AxelorException {
if (Beans.get(AppBankPaymentService.class).getAppBankPayment().getEnableEbicsModule()) {
if (bankOrder.getSignatoryEbicsUser() == null) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.EBICS_MISSING_SIGNATORY_EBICS_USER));
}
if (bankOrder.getSignatoryEbicsUser().getEbicsPartner().getTransportEbicsUser() == null) {
throw new AxelorException(
bankOrder.getSignatoryEbicsUser().getEbicsPartner(),
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.EBICS_MISSING_USER_TRANSPORT));
}
sendBankOrderFile(bankOrder);
}
realizeBankOrder(bankOrder);
validatePayment(bankOrder);
}
protected void sendBankOrderFile(BankOrder bankOrder) throws AxelorException {
File dataFileToSend = null;
File signatureFileToSend = null;
if (bankOrder.getSignatoryEbicsUser().getEbicsPartner().getEbicsTypeSelect()
== EbicsPartnerRepository.EBICS_TYPE_TS) {
if (bankOrder.getSignedMetaFile() == null) {
throw new AxelorException(
I18n.get(IExceptionMessage.BANK_ORDER_NOT_PROPERLY_SIGNED),
TraceBackRepository.CATEGORY_NO_VALUE);
}
signatureFileToSend = MetaFiles.getPath(bankOrder.getSignedMetaFile()).toFile();
}
dataFileToSend = MetaFiles.getPath(bankOrder.getGeneratedMetaFile()).toFile();
sendFile(bankOrder, dataFileToSend, signatureFileToSend);
}
@Transactional(rollbackOn = {Exception.class})
protected void realizeBankOrder(BankOrder bankOrder) throws AxelorException {
AppBaseService appBaseService = Beans.get(AppBaseService.class);
Beans.get(BankOrderMoveService.class).generateMoves(bankOrder);
bankOrder.setSendingDateTime(appBaseService.getTodayDateTime().toLocalDateTime());
bankOrder.setStatusSelect(BankOrderRepository.STATUS_CARRIED_OUT);
if (Beans.get(AppBankPaymentService.class).getAppBankPayment().getEnableEbicsModule()) {
bankOrder.setTestMode(bankOrder.getSignatoryEbicsUser().getEbicsPartner().getTestMode());
}
bankOrderRepo.save(bankOrder);
}
protected void sendFile(BankOrder bankOrder, File dataFileToSend, File signatureFileToSend)
throws AxelorException {
PaymentMode paymentMode = bankOrder.getPaymentMode();
if (paymentMode != null && !paymentMode.getAutomaticTransmission()) {
return;
}
EbicsUser signatoryEbicsUser = bankOrder.getSignatoryEbicsUser();
ebicsService.sendFULRequest(
signatoryEbicsUser.getEbicsPartner().getTransportEbicsUser(),
signatoryEbicsUser,
null,
dataFileToSend,
bankOrder.getBankOrderFileFormat(),
signatureFileToSend);
}
@Override
public void setSequenceOnBankOrderLines(BankOrder bankOrder) {
if (bankOrder.getBankOrderLineList() == null) {
return;
}
String bankOrderSeq = bankOrder.getBankOrderSeq();
int counter = 1;
for (BankOrderLine bankOrderLine : bankOrder.getBankOrderLineList()) {
bankOrderLine.setCounter(counter);
bankOrderLine.setSequence(bankOrderSeq + "-" + Integer.toString(counter++));
}
}
private void setNbOfLines(BankOrder bankOrder) {
if (bankOrder.getBankOrderLineList() == null) {
return;
}
bankOrder.setNbOfLines(bankOrder.getBankOrderLineList().size());
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void cancelBankOrder(BankOrder bankOrder) throws AxelorException {
bankOrder.setStatusSelect(BankOrderRepository.STATUS_CANCELED);
this.cancelPayment(bankOrder);
bankOrderRepo.save(bankOrder);
}
@Override
@Transactional
public EbicsUser getDefaultEbicsUserFromBankDetails(BankDetails bankDetails) {
EbicsPartner ebicsPartner =
Beans.get(EbicsPartnerRepository.class)
.all()
.filter("? MEMBER OF self.bankDetailsSet", bankDetails)
.fetchOne();
if (ebicsPartner != null) {
return ebicsPartner.getDefaultSignatoryEbicsUser();
} else {
return null;
}
}
@Override
public String createDomainForBankDetails(BankOrder bankOrder) {
String domain =
Beans.get(BankDetailsService.class)
.getActiveCompanyBankDetails(bankOrder.getSenderCompany());
// filter on the bank details identifier type from the bank order file
// format
if (bankOrder.getBankOrderFileFormat() != null) {
String acceptedIdentifiers = bankOrder.getBankOrderFileFormat().getBankDetailsTypeSelect();
if (acceptedIdentifiers != null && !acceptedIdentifiers.equals("")) {
domain += " AND self.bank.bankDetailsTypeSelect IN (" + acceptedIdentifiers + ")";
}
}
// filter on the currency if it is set in file format and in the bankdetails
Currency currency = bankOrder.getBankOrderCurrency();
if (currency != null
&& !bankOrder.getBankOrderFileFormat().getAllowOrderCurrDiffFromBankDetails()) {
String fileFormatCurrencyId = currency.getId().toString();
domain += " AND (self.currency IS NULL OR self.currency.id = " + fileFormatCurrencyId + ")";
}
return domain;
}
@Override
public BankDetails getDefaultBankDetails(BankOrder bankOrder) {
BankDetails candidateBankDetails;
if (bankOrder.getSenderCompany() == null) {
return null;
}
candidateBankDetails = bankOrder.getSenderCompany().getDefaultBankDetails();
try {
this.checkBankDetails(candidateBankDetails, bankOrder);
} catch (AxelorException e) {
return null;
}
return candidateBankDetails;
}
@Override
public void checkBankDetails(BankDetails bankDetails, BankOrder bankOrder)
throws AxelorException {
if (bankDetails == null) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_BANK_DETAILS_MISSING));
}
if (!bankDetails.getActive()) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_BANK_DETAILS_NOT_ACTIVE));
}
if (bankOrder.getBankOrderFileFormat() != null) {
if (!this.checkBankDetailsTypeCompatible(bankDetails, bankOrder.getBankOrderFileFormat())) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_BANK_DETAILS_TYPE_NOT_COMPATIBLE));
}
if (!bankOrder.getBankOrderFileFormat().getAllowOrderCurrDiffFromBankDetails()
&& !this.checkBankDetailsCurrencyCompatible(bankDetails, bankOrder)) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_BANK_DETAILS_CURRENCY_NOT_COMPATIBLE));
}
}
if (bankOrder.getBankOrderFileFormat() != null
&& bankOrder.getBankOrderFileFormat().getAllowOrderCurrDiffFromBankDetails()
&& bankDetails.getCurrency() == null) {
throw new AxelorException(
I18n.get(IExceptionMessage.BANK_ORDER_BANK_DETAILS_MISSING_CURRENCY),
TraceBackRepository.CATEGORY_MISSING_FIELD);
}
}
@Override
public boolean checkBankDetailsTypeCompatible(
BankDetails bankDetails, BankOrderFileFormat bankOrderFileFormat) {
// filter on the bank details identifier type from the bank order file
// format
String acceptedIdentifiers = bankOrderFileFormat.getBankDetailsTypeSelect();
if (acceptedIdentifiers != null && !acceptedIdentifiers.equals("")) {
String[] identifiers = acceptedIdentifiers.replaceAll("\\s", "").split(",");
int i = 0;
while (i < identifiers.length
&& bankDetails.getBank().getBankDetailsTypeSelect() != Integer.parseInt(identifiers[i])) {
i++;
}
if (i == identifiers.length) {
return false;
}
}
return true;
}
@Override
public boolean checkBankDetailsCurrencyCompatible(BankDetails bankDetails, BankOrder bankOrder) {
// filter on the currency if it is set in file format
if (bankOrder.getBankOrderCurrency() != null) {
if (bankDetails.getCurrency() != null
&& bankDetails.getCurrency() != bankOrder.getBankOrderCurrency()) {
return false;
}
}
return true;
}
@Override
public File generateFile(BankOrder bankOrder)
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException {
if (bankOrder.getBankOrderLineList() == null || bankOrder.getBankOrderLineList().isEmpty()) {
return null;
}
bankOrder.setFileGenerationDateTime(LocalDateTime.now());
BankOrderFileFormat bankOrderFileFormat = bankOrder.getBankOrderFileFormat();
File file = null;
switch (bankOrderFileFormat.getOrderFileFormatSelect()) {
case BankOrderFileFormatRepository.FILE_FORMAT_PAIN_001_001_02_SCT:
file = new BankOrderFile00100102Service(bankOrder).generateFile();
break;
case BankOrderFileFormatRepository.FILE_FORMAT_PAIN_001_001_03_SCT:
file = new BankOrderFile00100103Service(bankOrder).generateFile();
break;
case BankOrderFileFormatRepository.FILE_FORMAT_PAIN_XXX_CFONB320_XCT:
file = new BankOrderFileAFB320XCTService(bankOrder).generateFile();
break;
case BankOrderFileFormatRepository.FILE_FORMAT_PAIN_XXX_CFONB160_ICT:
file = new BankOrderFileAFB160ICTService(bankOrder).generateFile();
break;
case BankOrderFileFormatRepository.FILE_FORMAT_PAIN_008_001_01_SDD:
file =
new BankOrderFile00800101Service(bankOrder, BankOrderFile008Service.SEPA_TYPE_CORE)
.generateFile();
break;
case BankOrderFileFormatRepository.FILE_FORMAT_PAIN_008_001_01_SBB:
file =
new BankOrderFile00800101Service(bankOrder, BankOrderFile008Service.SEPA_TYPE_SBB)
.generateFile();
break;
case BankOrderFileFormatRepository.FILE_FORMAT_PAIN_008_001_02_SDD:
file =
new BankOrderFile00800102Service(bankOrder, BankOrderFile008Service.SEPA_TYPE_CORE)
.generateFile();
break;
case BankOrderFileFormatRepository.FILE_FORMAT_PAIN_008_001_02_SBB:
file =
new BankOrderFile00800102Service(bankOrder, BankOrderFile008Service.SEPA_TYPE_SBB)
.generateFile();
break;
default:
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_FILE_UNKNOWN_FORMAT));
}
if (file == null) {
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_ISSUE_DURING_FILE_GENERATION),
bankOrder.getBankOrderSeq());
}
MetaFiles metaFiles = Beans.get(MetaFiles.class);
try (InputStream is = new FileInputStream(file)) {
metaFiles.attach(is, file.getName(), bankOrder);
bankOrder.setGeneratedMetaFile(metaFiles.upload(file));
}
return file;
}
protected Sequence getSequence(BankOrder bankOrder) throws AxelorException {
BankPaymentConfig bankPaymentConfig =
Beans.get(BankPaymentConfigService.class)
.getBankPaymentConfig(bankOrder.getSenderCompany());
switch (bankOrder.getOrderTypeSelect()) {
case BankOrderRepository.ORDER_TYPE_SEPA_DIRECT_DEBIT:
return bankPaymentConfigService.getSepaDirectDebitSequence(bankPaymentConfig);
case BankOrderRepository.ORDER_TYPE_SEPA_CREDIT_TRANSFER:
return bankPaymentConfigService.getSepaCreditTransSequence(bankPaymentConfig);
case BankOrderRepository.ORDER_TYPE_INTERNATIONAL_DIRECT_DEBIT:
return bankPaymentConfigService.getIntDirectDebitSequence(bankPaymentConfig);
case BankOrderRepository.ORDER_TYPE_INTERNATIONAL_CREDIT_TRANSFER:
return bankPaymentConfigService.getIntCreditTransSequence(bankPaymentConfig);
case BankOrderRepository.ORDER_TYPE_NATIONAL_TREASURY_TRANSFER:
return bankPaymentConfigService.getNatTreasuryTransSequence(bankPaymentConfig);
case BankOrderRepository.ORDER_TYPE_INTERNATIONAL_TREASURY_TRANSFER:
return bankPaymentConfigService.getIntTreasuryTransSequence(bankPaymentConfig);
default:
return bankPaymentConfigService.getOtherBankOrderSequence(bankPaymentConfig);
}
}
protected void setBankOrderSeq(BankOrder bankOrder, Sequence sequence) throws AxelorException {
bankOrder.setBankOrderSeq(
(sequenceService.getSequenceNumber(sequence, bankOrder.getBankOrderDate())));
if (bankOrder.getBankOrderSeq() != null) {
return;
}
throw new AxelorException(
bankOrder,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BANK_ORDER_COMPANY_NO_SEQUENCE),
bankOrder.getSenderCompany().getName());
}
/**
* The signatory ebics user will follow the bank order record
*
* @param bankOrder
*/
protected void makeEbicsUserFollow(BankOrder bankOrder) {
EbicsUser ebicsUser = bankOrder.getSignatoryEbicsUser();
if (ebicsUser != null) {
User signatoryUser = ebicsUser.getAssociatedUser();
Beans.get(MailFollowerRepository.class).follow(bankOrder, signatoryUser);
}
}
@Override
public void resetReceivers(BankOrder bankOrder) {
if (ObjectUtils.isEmpty(bankOrder.getBankOrderLineList())) {
return;
}
resetPartners(bankOrder);
resetBankDetails(bankOrder);
}
private void resetPartners(BankOrder bankOrder) {
if (bankOrder.getPartnerTypeSelect() == BankOrderRepository.PARTNER_TYPE_COMPANY) {
for (BankOrderLine bankOrderLine : bankOrder.getBankOrderLineList()) {
bankOrderLine.setPartner(null);
}
return;
}
for (BankOrderLine bankOrderLine : bankOrder.getBankOrderLineList()) {
bankOrderLine.setReceiverCompany(null);
Partner partner = bankOrderLine.getPartner();
if (partner == null) {
continue;
}
boolean keep;
switch (bankOrder.getPartnerTypeSelect()) {
case BankOrderRepository.PARTNER_TYPE_SUPPLIER:
keep = partner.getIsSupplier();
break;
case BankOrderRepository.PARTNER_TYPE_EMPLOYEE:
keep = partner.getIsEmployee();
break;
case BankOrderRepository.PARTNER_TYPE_CUSTOMER:
keep = partner.getIsCustomer();
break;
default:
keep = false;
}
if (!keep) {
bankOrderLine.setPartner(null);
}
}
}
private void resetBankDetails(BankOrder bankOrder) {
for (BankOrderLine bankOrderLine : bankOrder.getBankOrderLineList()) {
if (bankOrderLine.getReceiverBankDetails() == null) {
continue;
}
Collection<BankDetails> bankDetailsCollection;
if (bankOrderLine.getReceiverCompany() != null) {
bankDetailsCollection = bankOrderLine.getReceiverCompany().getBankDetailsSet();
} else if (bankOrderLine.getPartner() != null) {
bankDetailsCollection = bankOrderLine.getPartner().getBankDetailsList();
} else {
bankDetailsCollection = Collections.emptyList();
}
if (ObjectUtils.isEmpty(bankDetailsCollection)
|| !bankDetailsCollection.contains(bankOrderLine.getReceiverBankDetails())) {
bankOrderLine.setReceiverBankDetails(null);
}
}
}
@Override
public ActionViewBuilder buildBankOrderLineView(
String gridViewName, String formViewName, String viewDomain) {
ActionViewBuilder actionViewBuilder =
ActionView.define(I18n.get("Bank Order Lines"))
.model(BankOrderLine.class.getName())
.add("grid", gridViewName)
.add("form", formViewName)
.domain(viewDomain);
return actionViewBuilder;
}
}

View File

@ -0,0 +1,165 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder.file;
import com.axelor.apps.account.db.PaymentMode;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderFileFormat;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.tool.file.FileTool;
import com.axelor.apps.tool.xml.Marschaller;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.common.base.Strings;
import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BankOrderFileService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected static final String FILE_EXTENSION_XML = "xml";
protected static final String FILE_EXTENSION_TXT = "txt";
protected PaymentMode paymentMode;
protected BankOrderFileFormat bankOrderFileFormat;
protected LocalDate bankOrderDate;
protected BankDetails senderBankDetails;
protected Company senderCompany;
protected Currency bankOrderCurrency;
protected BigDecimal bankOrderTotalAmount;
protected BigDecimal arithmeticTotal;
protected int nbOfLines;
protected LocalDateTime generationDateTime;
protected String bankOrderSeq;
protected boolean isMultiDates;
protected boolean isMultiCurrencies;
protected List<BankOrderLine> bankOrderLineList;
protected Object fileToCreate;
protected String context;
protected String fileExtension;
public BankOrderFileService(BankOrder bankOrder) {
this.paymentMode = bankOrder.getPaymentMode();
this.bankOrderFileFormat = bankOrder.getBankOrderFileFormat();
this.bankOrderDate = bankOrder.getBankOrderDate();
this.senderBankDetails = bankOrder.getSenderBankDetails();
this.senderCompany = bankOrder.getSenderCompany();
this.bankOrderCurrency = bankOrder.getBankOrderCurrency();
this.bankOrderTotalAmount = bankOrder.getBankOrderTotalAmount();
this.arithmeticTotal = bankOrder.getArithmeticTotal();
this.nbOfLines = bankOrder.getNbOfLines();
this.generationDateTime = bankOrder.getFileGenerationDateTime();
this.bankOrderSeq = bankOrder.getBankOrderSeq();
this.bankOrderLineList = bankOrder.getBankOrderLineList();
this.isMultiDates = bankOrder.getIsMultiDate();
this.isMultiCurrencies = bankOrder.getIsMultiCurrency();
}
protected String getSenderAddress() throws AxelorException {
Address senderAddress = this.senderCompany.getAddress();
if (senderAddress == null || Strings.isNullOrEmpty(senderAddress.getFullName())) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_FILE_NO_SENDER_ADDRESS),
senderCompany.getName());
}
return senderAddress.getFullName();
}
protected String getFolderPath() throws AxelorException {
String folderPath = paymentMode.getBankOrderExportFolderPath();
if (Strings.isNullOrEmpty(folderPath)) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_FILE_NO_FOLDER_PATH),
paymentMode.getName());
}
return folderPath;
}
/**
* Create the order XML file
*
* @throws AxelorException
* @throws IOException
* @throws JAXBException
*/
@SuppressWarnings("unchecked")
public File generateFile()
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException {
switch (fileExtension) {
case FILE_EXTENSION_XML:
return Marschaller.marschalFile(
fileToCreate, context, this.getFolderPath(), this.computeFileName());
case FILE_EXTENSION_TXT:
try {
return FileTool.writer(
this.getFolderPath(), this.computeFileName(), (List<String>) fileToCreate);
} catch (IOException e) {
throw new AxelorException(
e.getCause(),
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(com.axelor.apps.account.exception.IExceptionMessage.CFONB_EXPORT_2),
I18n.get(com.axelor.apps.base.exceptions.IExceptionMessage.EXCEPTION),
e);
}
default:
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BANK_ORDER_FILE_UNKNOWN_FORMAT),
paymentMode.getName());
}
}
public String computeFileName() {
return String.format(
"%s%s.%s",
bankOrderFileFormat.getOrderFileFormatSelect(),
generationDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")),
fileExtension);
}
}

View File

@ -0,0 +1,662 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder.file.directdebit;
import com.axelor.apps.account.db.Umr;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.bankpayment.service.config.BankPaymentConfigService;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.AccountIdentification3Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.BranchAndFinancialInstitutionIdentification3;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.CashAccount7;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.ChargeBearerType1Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.CurrencyAndAmount;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.DirectDebitTransaction1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.DirectDebitTransactionInformation1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.Document;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.FinancialInstitutionIdentification5Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.GenericIdentification3;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.GenericIdentification4;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.GroupHeader1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.Grouping1Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.LocalInstrument1Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.MandateRelatedInformation1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.ObjectFactory;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.Pain00800101;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.Party2Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.PartyIdentification8;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.PaymentIdentification1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.PaymentInstructionInformation2;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.PaymentMethod2Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.PaymentTypeInformation2;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.PersonIdentification3;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.RemittanceInformation1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.SequenceType1Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.ServiceLevel2Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01.ServiceLevel3Choice;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
public class BankOrderFile00800101Service extends BankOrderFile008Service {
protected ObjectFactory factory;
protected String sepaType;
@Inject
public BankOrderFile00800101Service(BankOrder bankOrder, String sepaType) {
super(bankOrder);
context = "com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_01";
factory = new ObjectFactory();
this.sepaType = sepaType;
}
/**
* Generates the XML SEPA Direct Debit file (pain.008.001.01)
*
* @return the SEPA Direct Debit file (pain.008.001.01)
* @throws JAXBException
* @throws IOException
* @throws AxelorException
* @throws DatatypeConfigurationException
*/
@Override
public File generateFile()
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException {
// Creditor
PartyIdentification8 creditor = factory.createPartyIdentification8();
creditor.setNm(senderBankDetails.getOwnerName());
/*
* Hierarchy of a XML file
*
* GroupHeader : This building block is mandatory and present once.
* It contains elements such as Message Identification,
* Creation Date And Time, Grouping indicator.
* Payment Information : This building block is mandatory and repetitive.
* It contains, among other things, elements related
* to the Credit side of the transaction, such as
* Creditor and Payment Type Information.
* Direct Debit Transaction Information : This building block is mandatory and repetitive.
* It contains, among other things, elements related
* to the debit side of the transaction, such as
* Debtor and Remittance Information Rules.
*
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
* <Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.001.01">
* <pain.008.001.01>
* <GrpHdr> <-- occ : 1..1
* </GrpHdr>
* <PmtInf> <-- occ : 1..n
* <DrctDbtTxInf> <-- occ : 1..n
* </DrctDbtTxInf>
* </PmtInf>
* </pain.008.001.01>
* </Document>
*/
/*
* Document, <Document> tag
*/
Document document = factory.createDocument();
/*
* pain.008.001.01, <pain.008.001.01> tag
*/
Pain00800101 pain00800101 = factory.createPain00800101();
document.setPain00800101(pain00800101);
/*
* Group Header, <GrpHdr> tag
* Set of characteristics shared by all individual transactions included in the message.
*/
GroupHeader1 groupHeader = factory.createGroupHeader1();
createGrpHdr(groupHeader, creditor);
pain00800101.setGrpHdr(groupHeader);
/*
* Payment Information, <PmtInf> tag
* Does not need to set the List<PaymentInstructionInformation2> to the pain00800101 object (see doc).
*/
createPmtInf(pain00800101.getPmtInf(), creditor);
fileToCreate = factory.createDocument(document);
return super.generateFile();
}
/**
* Builds the GroupHeader part ({@code <GrpHdr>} tag) of the file, into the provided {@link
* GroupHeader1} object
*
* @param groupHeader the {@link GroupHeader1} to build
* @param creditor the creditor of the SEPA Direct Debit file
* @throws DatatypeConfigurationException
*/
protected void createGrpHdr(GroupHeader1 groupHeader, PartyIdentification8 creditor)
throws DatatypeConfigurationException {
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
/*
* Message Identification (mandatory)
* Point to point reference assigned by the instructing party and sent to the next party in the chain to unambiguously identify the message.
*/
groupHeader.setMsgId(bankOrderSeq);
/*
* Creation Date Time (mandatory)
* Date and time at which a (group of) payment instruction(s) was created by the instructing party.
*
* Format : YYYY-MM-DDThh:mm:ss
* Example : <CreDtTm>2009-12-02T08:35:30</CreDtTm>
*/
groupHeader.setCreDtTm(
datatypeFactory.newXMLGregorianCalendar(
generationDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))));
/*
* Batch Booking (optional)
* Identifies whether a single entry per individual transaction or a batch entry for the sum of the amounts of all transactions in the message is required.
*
* Usage : Recommended "true". If absent then default "true".
*
* 'true' if : Identifies that a batch entry for the sum of the amounts of all
* transactions in a Payment Information Block is required.
* (one credit for all transactions in a Payment Information Block)
* 'false' if : Identifies that a single entry for each of the transactions
* in a message is required.
*
*/
groupHeader.setBtchBookg(true);
/*
* Number Of Transactions (mandatory)
* Number of individual transactions contained in the message.
*/
groupHeader.setNbOfTxs(Integer.toString(nbOfLines));
/*
* Pas documenté dans le fichier, repris du fichier BankOrderFile00100103Service.java
*/
groupHeader.setCtrlSum(arithmeticTotal);
/*
* Grouping (mandatory)
* Indicates whether common accounting information in the transaction is included once for all transactions or repeated for each single transaction.
*/
groupHeader.setGrpg(Grouping1Code.MIXD); // TO CHECK
/*
* Initiating Party (mandatory)
* Party initiating the payment. In the direct debit context, this can be the creditor, or the party that initiates the payment on behalf of the creditor.
*/
groupHeader.setInitgPty(creditor);
/*
* Pas documenté dans le fichier
*/
// groupHeader.setFwdgAgt(???);
}
/**
* Builds the PaymentInformation part ({@code <PmtInf>} tag) of the file, and adds it into the
* provided {@link PaymentInstructionInformation2} list
*
* @param paymentInstructionInformationList the list to add the {@link
* PaymentInstructionInformation2} objects into
* @param creditor the creditor of the SEPA Direct Debit file
* @throws DatatypeConfigurationException
*/
protected void createPmtInf(
List<PaymentInstructionInformation2> paymentInstructionInformationList,
PartyIdentification8 creditor)
throws AxelorException, DatatypeConfigurationException {
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
/*
* Payment Information (mandatory)
* Set of characteristics that apply to the credit side of the payment transactions included in the direct debit transaction initiation.
*/
PaymentInstructionInformation2 paymentInstructionInformation2 =
factory.createPaymentInstructionInformation2();
paymentInstructionInformationList.add(paymentInstructionInformation2);
/*
* Payment Information Identification (optional)
* Reference assigned by a sending party to unambiguously identify the payment information block within the message.
*/
paymentInstructionInformation2.setPmtInfId(bankOrderSeq);
/*
* Payment Method (mandatory, always 'DD')
* Specifies the means of payment that will be used to move the amount of money.
*/
paymentInstructionInformation2.setPmtMtd(PaymentMethod2Code.DD);
/*
* Payment Type Information (mandatory)
* Set of elements that further specifies the type of transaction.
*/
PaymentTypeInformation2 paymentTypeInformation2 = factory.createPaymentTypeInformation2();
paymentInstructionInformation2.setPmtTpInf(paymentTypeInformation2);
/*
* ServiceLevel (mandatory)
* Agreement under which or rules under which the transaction should be processed.
*/
ServiceLevel3Choice serviceLevel3Choice = factory.createServiceLevel3Choice();
paymentTypeInformation2.setSvcLvl(serviceLevel3Choice);
/*
* Code (mandatory, always 'SEPA')
* Identification of a pre-agreed level of service between the parties in a coded form.
*/
serviceLevel3Choice.setCd(ServiceLevel2Code.SEPA);
/*
* Local Instrument (mandatory)
* User community specific instrument.
*/
LocalInstrument1Choice localInstrument1Choice = factory.createLocalInstrument1Choice();
/*
* Code (mandatory)
*
* Format : either 'CORE' or 'B2B'
* Rule : The mixing of Core Direct Debits and Business-to-Business Direct Debits is not
* allowed in the same message.
*/
switch (sepaType) {
case SEPA_TYPE_CORE:
localInstrument1Choice.setCd(SEPA_TYPE_CORE);
break;
case SEPA_TYPE_SBB:
localInstrument1Choice.setCd(SEPA_TYPE_SBB);
break;
default:
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BANK_ORDER_FILE_UNKNOWN_SEPA_TYPE));
}
paymentTypeInformation2.setLclInstrm(localInstrument1Choice);
/*
* Sequence Type (mandatory) // TO CHECK
* Identifies the direct debit sequence, e.g. first, recurrent, final or one-off.
*
* Either one of the following values.
* CODE Name Definition
* ------------------------------------------------------------------------------------
* FRST First First collection of a series of direct debit instructions.
* RCUR Recurrent Direct debit instruction where the debtor's authorisation is used for
* regular direct debit transactions initiated by the creditor.
* FNAL Final Final collection of a series of direct debit instructions.
* OOFF One Off Direct debit instruction where the debtor's authorisation is used to
* initiate one single direct debit transaction.
*/
paymentTypeInformation2.setSeqTp(SequenceType1Code.FRST);
/*
* Category Purpose (optional)
* Specifies the purpose of the payment based on a set of pre-defined categories.
*
* iso20022.org -> 'PaymentCategoryPurpose1Code' for all codes available and definitions.
* Rule : The usage and impact of these codes is to be agreed with your bank.
*/
// paymentTypeInformation2.setCtgyPurp(PaymentCategoryPurpose1Code.CASH);
/*
* Requested Collection Date (mandatory)
* Date at which the creditor requests the amount of money to be collected from the debtor.
*
* Format : YYYY-MM-DD
* Usage : The minimum delay between sending date and requested collection date is depending
* on the type of direct debit (B2B or CORE) and on the sequence type (FRST, OOFF,
* RCUR, FNAL).
*/
paymentInstructionInformation2.setReqdColltnDt(
datatypeFactory.newXMLGregorianCalendar(
bankOrderDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
/*
* Creditor (mandatory)
* Party to which an amount of money is due.
*/
paymentInstructionInformation2.setCdtr(creditor);
/*
* Creditor Account (mandatory)
* Unambiguous identification of the account of the creditor to which a credit entry will be posted as a result of the payment transaction.
*/
AccountIdentification3Choice accountIdentification3Choice =
factory.createAccountIdentification3Choice();
accountIdentification3Choice.setIBAN(senderBankDetails.getIban());
CashAccount7 cashAccount7 = factory.createCashAccount7();
cashAccount7.setId(accountIdentification3Choice);
paymentInstructionInformation2.setCdtrAcct(cashAccount7);
/*
* Creditor Agent (mandatory)
* Financial institution servicing an account for the creditor.
*
* Note : The Bank Identifier Code (BIC) is composed of 8 or 11 characters, of which only the
* first 8 characters are significant.
*/
FinancialInstitutionIdentification5Choice financialInstitutionIdentification5Choice =
factory.createFinancialInstitutionIdentification5Choice();
financialInstitutionIdentification5Choice.setBIC(senderBankDetails.getBank().getCode()); // BIC
BranchAndFinancialInstitutionIdentification3 branchAndFinancialInstitutionIdentification3 =
factory.createBranchAndFinancialInstitutionIdentification3();
branchAndFinancialInstitutionIdentification3.setFinInstnId(
financialInstitutionIdentification5Choice);
paymentInstructionInformation2.setCdtrAgt(branchAndFinancialInstitutionIdentification3);
/*
* Ultimate Creditor (optional)
* Ultimate party to which an amount of money is due. Ultimate Creditor is only to be used if different from Creditor.
*/
// paymentInstructionInformation2.setUltmtCdtr();
/*
* Charge Bearer (mandatory) // TO CHECK : option dans la vue BankOrder
* Specifies which party/parties will bear the charges associated with the processing of the payment transaction.
*
* CODE Name Description
* ----------------------------------------------------------------------------------------
* DEBT BorneByDebtor All transaction charges are to be borne by the debtor
* CRED BorneByCreditor All transaction charges are to be borne by the creditor
* SHAR Shared In a direct debit context, means that transaction charges on
* the sender side are to be borne by the creditor, transaction
* charges on the receiver side are to be borne by the debtor.
* SLEV FollowingServiceLevel Charges are to be applied following the rules agreed in the
* service level and/or scheme.
*/
paymentInstructionInformation2.setChrgBr(ChargeBearerType1Code.SLEV);
/*
* Direct Debit Transaction Information, <DrctDbtTxInf> tag
* Does not need to set the List<DirectDebitTransactionInformation1> to the paymentInstructionInformation2 object (see doc)
*/
createDrctDbtTxInf(paymentInstructionInformation2.getDrctDbtTxInf(), creditor);
}
/**
* Builds the DirectDebitTransactionInformation part ({@code <DrctDbtTxInf>} tag) of the file, and
* adds it into the provided {@link DirectDebitTransactionInformation1} list
*
* @param directDebitTransactionInformation1List the list to add the {@link
* DirectDebitTransactionInformation1} objects into
* @param creditor the creditor of the SEPA Direct Debit file
* @throws DatatypeConfigurationException
* @throws AxelorException
*/
protected void createDrctDbtTxInf(
List<DirectDebitTransactionInformation1> directDebitTransactionInformation1List,
PartyIdentification8 creditor)
throws DatatypeConfigurationException, AxelorException {
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
for (BankOrderLine bankOrderLine : bankOrderLineList) {
BankDetails receiverBankDetails = bankOrderLine.getReceiverBankDetails();
Umr receiverUmr = bankOrderLine.getPartner().getActiveUmr();
if (receiverUmr == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.DIRECT_DEBIT_MISSING_PARTNER_ACTIVE_UMR));
}
/*
* Direct Debit Transaction Information (mandatory)
* Set of elements providing information specific to the individual transaction(s) included in the message.
*/
DirectDebitTransactionInformation1 directDebitTransactionInformation1 =
factory.createDirectDebitTransactionInformation1();
directDebitTransactionInformation1List.add(directDebitTransactionInformation1);
/*
* Payment Identification (mandatory)
* Set of elements to reference a payment instruction.
*/
PaymentIdentification1 paymentIdentification1 = factory.createPaymentIdentification1();
directDebitTransactionInformation1.setPmtId(paymentIdentification1);
/*
* Instruction Identification (optional)
* The Instruction Identification is a unique reference assigned by the Initiator to unambiguously identify the transaction.
* It can be used in status messages related to the transaction.
*/
// paymentIdentification1.setInstrId();
/*
* End To End Identification (mandatory)
* Unique identification assigned by the initiating party to unumbiguously identify the transaction.
* This identification is passed on, unchanged, throughout the entire end-to-end chain.
*/
paymentIdentification1.setEndToEndId(bankOrderLine.getSequence());
/*
* Instructed Amount (mandatory)
* Amount of the direct debit, expressed in euro.
*
* Format : Max. 11 digits of which 2 for the fractional part.
* Decimal separator is "."
* Currency "EUR" is explicit, and included in the XML tag.
* Usage : Amount must be between 0.01 and 999999999.99
*/
CurrencyAndAmount currencyAndAmount = factory.createCurrencyAndAmount();
currencyAndAmount.setCcy(CURRENCY_CODE);
currencyAndAmount.setValue(bankOrderLine.getBankOrderAmount());
directDebitTransactionInformation1.setInstdAmt(currencyAndAmount);
/*
* Direct Debit Transaction (mandatory)
* Set of elements providing information specific to the direct debit mandate.
*/
DirectDebitTransaction1 directDebitTransaction1 = factory.createDirectDebitTransaction1();
directDebitTransactionInformation1.setDrctDbtTx(directDebitTransaction1);
/*
* Mandate Related Information (mandatory)
* Set of elements used to provide further details related to a direct debit mandate signed between the creditor and the debtor.
*/
MandateRelatedInformation1 mandateRelatedInformation1 =
factory.createMandateRelatedInformation1();
directDebitTransaction1.setMndtRltdInf(mandateRelatedInformation1);
/*
* Mandate Identification (mandatory)
* Reference of the direct debit mandate that has been signed between by the debtor and the creditor.
*/
mandateRelatedInformation1.setMndtId(receiverUmr.getUmrNumber());
/*
* Date of Signature (mandatory)
* Date on which the direct debit mandate has been signed by the debtor.
*
* Format : YYYY-MM-DD
*/
mandateRelatedInformation1.setDtOfSgntr(
datatypeFactory.newXMLGregorianCalendar(
receiverUmr
.getMandateSignatureDate()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
/*
* Amendment Indicator (optional)
* Indicator notifying whether the underlying mandate is amended or not.
*
* Usage : - If not present, considered as "false".
* - If true, 'Amendment Information Details' is mandatory.
*
* 'true' if : The mandate is amended or migrated from Dom'80.
* 'false' if : The mandate is not amended.
*/
// mandateRelatedInformation1.setAmdmntInd(???);
/*
* Amendment Info Details (optional)
* List of direct debit mandate elements that have been modified.
*/
// AmendmentInformationDetails1 amendmentInformationDetails1 =
// factory.createAmendmentInformationDetails1();
// mandateRelatedInformation1.setAmdmntInfDtls(amendmentInformationDetails1);
// amendmentInformationDetails1.setOrgnlMndtId(???);
// amendmentInformationDetails1.setOrgnlCdtrSchmeId(???);
// amendmentInformationDetails1.setOrgnlDbtrAcct(???);
// amendmentInformationDetails1.setOrgnlDbtrAgt(???);
/*
* Electronic Signature (optional)
* Digital signature as provided by the creditor.
*
* Usage : - If the direct debit is based on an electronic mandate, this data
* element must contain the reference of the Mandate Acceptance Report.
* - If the direct debit is based on a paper mandate, this data element
* is not allowed.
*/
// mandateRelatedInformation1.setElctrncSgntr(???);
/*
* Creditor Scheme Identification (mandatory)
* Credit party that signs the direct debit mandate.
*/
PartyIdentification8 creditorSchemeId = factory.createPartyIdentification8();
directDebitTransaction1.setCdtrSchmeId(creditorSchemeId);
Party2Choice party2Choice = factory.createParty2Choice();
creditorSchemeId.setId(party2Choice);
PersonIdentification3 personIdentification3 = factory.createPersonIdentification3();
party2Choice.getPrvtId().add(personIdentification3);
GenericIdentification4 genericIdentification4 = factory.createGenericIdentification4();
personIdentification3.setOthrId(genericIdentification4);
genericIdentification4.setId(
Beans.get(BankPaymentConfigService.class)
.getIcsNumber(senderCompany.getBankPaymentConfig()));
genericIdentification4.setIdTp("SEPA");
/*
* Ultimate Creditor (optional)
* Ultimate party to which an amount of money is due. Ultimate Creditor is only to be used if different from Creditor.
*/
// directDebitTransaction1.setUltmtCdtr();
/*
* Debtor Agent (mandatory)
* Financial institution servicing an account for the debtor.
*/
BranchAndFinancialInstitutionIdentification3 branchAndFinancialInstitutionIdentification3 =
factory.createBranchAndFinancialInstitutionIdentification3();
FinancialInstitutionIdentification5Choice financialInstitutionIdentification5Choice =
factory.createFinancialInstitutionIdentification5Choice();
fillBic(financialInstitutionIdentification5Choice, receiverBankDetails.getBank()); // BIC
branchAndFinancialInstitutionIdentification3.setFinInstnId(
financialInstitutionIdentification5Choice);
directDebitTransactionInformation1.setDbtrAgt(branchAndFinancialInstitutionIdentification3);
/*
* Debtor (mandatory)
* Party that owes an amount of money to the (ultimate) creditor.
*/
PartyIdentification8 debtor = factory.createPartyIdentification8();
debtor.setNm(receiverBankDetails.getOwnerName());
directDebitTransactionInformation1.setDbtr(debtor);
/*
* Debtor Account (mandatory)
* Identification of the account of the debtor to which a debit entry will be made to execute the transfer.
*/
AccountIdentification3Choice accountIdentification3Choice =
factory.createAccountIdentification3Choice();
accountIdentification3Choice.setIBAN(receiverBankDetails.getIban());
CashAccount7 cashAccount7 = factory.createCashAccount7();
cashAccount7.setId(accountIdentification3Choice);
directDebitTransactionInformation1.setDbtrAcct(cashAccount7);
/*
* Ultimate Debtor (optional)
* Ultimate party that owes an amount of money to the (ultimate) creditor. Ultimate Debtor is only to be used if different from Debtor.
*/
// directDebitTransactionInformation1.setUltmtDbtr(???);
/*
* Purpose (optional)
* Underlying reason for the payment transaction.
* Purpose is used by the Debtor to provide information to the Creditor, concerning thenature of the payment transaction.
* It is not used for processing by any of the banks involved.
*/
// Purpose1Choice purpose1Choice = factory.createPurpose1Choice();
// directDebitTransactionInformation1.setPurp(purpose1Choice);
/*
* Code (mandatory)
* Specifies the underlying reason of the payment transaction.
*/
// purpose1Choice.setCd(???);
/*
* Remittance Information (optional)
* Information that enables the matching, ie, reconciliation, of a payment with the items that the payment
* is intended to settle, eg, commercial invoices in an account receivable system.
*
* Usage : Either Structured or Unstructured, but not both.
*/
RemittanceInformation1 remittanceInformation1 = factory.createRemittanceInformation1();
directDebitTransactionInformation1.setRmtInf(remittanceInformation1);
/*
* Unstructured (choice 1 of 2)
* Information supplied to enable the matching of an entry with the items that the transfer is intended
* to settle, eg, commercial invoices in an accounts' receivable system in an unstructured form.
*/
remittanceInformation1.getUstrd().add(bankOrderLine.getReceiverReference());
/*
* Structured (choice 2 of 2)
* Information supplied to enable the matching of an entry with the items that the transfer is intended
* to settle, eg, commercial invoices in an accounts' receivable system in a structured form.
*/
// StructuredRemittanceInformation6 structuredRemittanceInformation6 =
// factory.createStructuredRemittanceInformation6();
// remittanceInformation1.getStrd().add(structuredRemittanceInformation6);
}
}
/**
* Method to fill the BIC information. If the BIC is not provided or in Iban only mode, we put
* NOTPROVIDED value. In this case, the bank ignore the BIC and use the Iban only.
*
* @param finInstnId The financial instituation identification tag of the generated file.
* @param bank The bank from which the BIC is get.
*/
protected void fillBic(FinancialInstitutionIdentification5Choice finInstnId, Bank bank) {
if (bankOrderFileFormat.getIbanOnly()
|| bank == null
|| Strings.isNullOrEmpty(bank.getCode())) {
GenericIdentification3 genId = new GenericIdentification3();
genId.setId(BIC_NOT_PROVIDED);
finInstnId.setPrtryId(genId);
} else {
finInstnId.setBIC(bank.getCode());
}
}
}

View File

@ -0,0 +1,702 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder.file.directdebit;
import com.axelor.apps.account.db.Umr;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.bankpayment.service.config.BankPaymentConfigService;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.AccountIdentification4Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.ActiveOrHistoricCurrencyAndAmount;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.BranchAndFinancialInstitutionIdentification4;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.CashAccount16;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.ChargeBearerType1Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.CustomerDirectDebitInitiationV02;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.DirectDebitTransaction6;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.DirectDebitTransactionInformation9;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.Document;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.FinancialInstitutionIdentification7;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.GenericFinancialIdentification1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.GenericPersonIdentification1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.GroupHeader39;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.LocalInstrument2Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.MandateRelatedInformation6;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.ObjectFactory;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.Party6Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.PartyIdentification32;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.PaymentIdentification1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.PaymentInstructionInformation4;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.PaymentMethod2Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.PaymentTypeInformation20;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.PersonIdentification5;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.PersonIdentificationSchemeName1Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.RemittanceInformation5;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.SequenceType1Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02.ServiceLevel8Choice;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
public class BankOrderFile00800102Service extends BankOrderFile008Service {
protected ObjectFactory factory;
protected String sepaType;
@Inject
public BankOrderFile00800102Service(BankOrder bankOrder, String sepaType) {
super(bankOrder);
context = "com.axelor.apps.bankpayment.xsd.sepa.pain_008_001_02";
factory = new ObjectFactory();
this.sepaType = sepaType;
}
/**
* Generates the XML SEPA Direct Debit file (pain.008.001.02)
*
* @return the SEPA Direct Debit file (pain.008.001.02)
* @throws JAXBException
* @throws IOException
* @throws AxelorException
* @throws DatatypeConfigurationException
*/
@Override
public File generateFile()
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException {
// Creditor
PartyIdentification32 creditor = factory.createPartyIdentification32();
creditor.setNm(senderBankDetails.getOwnerName());
/*
* Hierarchy of a XML file
*
* GroupHeader : This building block is mandatory and present once.
* It contains elements such as Message Identification,
* Creation Date And Time, Grouping indicator.
* Payment Information : This building block is mandatory and repetitive.
* It contains, among other things, elements related
* to the Credit side of the transaction, such as
* Creditor and Payment Type Information.
* Direct Debit Transaction Information : This building block is mandatory and repetitive.
* It contains, among other things, elements related
* to the debit side of the transaction, such as
* Debtor and Remittance Information Rules.
*
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
* <Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.001.02">
* <CstmrDrctDbtInitn>
* <GrpHdr> <-- occ : 1..1
* </GrpHdr>
* <PmtInf> <-- occ : 1..n
* <DrctDbtTxInf> <-- occ : 1..n
* </DrctDbtTxInf>
* </PmtInf>
* </CstmrDrctDbtInitn>
* </Document>
*/
/*
* Document, <Document> tag
*/
Document document = factory.createDocument();
/*
* Customer Direct Debit Initiation, <CstmrDrctDbtInitn> tag
*/
CustomerDirectDebitInitiationV02 customerDirectDebitInitiationV02 =
factory.createCustomerDirectDebitInitiationV02();
document.setCstmrDrctDbtInitn(customerDirectDebitInitiationV02);
/*
* Group Header, <GrpHdr> tag
* Set of characteristics shared by all individual transactions included in the message.
*/
GroupHeader39 groupHeader = factory.createGroupHeader39();
createGrpHdr(groupHeader, creditor);
customerDirectDebitInitiationV02.setGrpHdr(groupHeader);
/*
* Payment Information, <PmtInf> tag
* Does not need to set the List<PaymentInstructionInformation4> to the customerDirectDebitInitiationV02 object (see doc).
*/
createPmtInf(customerDirectDebitInitiationV02.getPmtInf(), creditor);
fileToCreate = factory.createDocument(document);
return super.generateFile();
}
/**
* Builds the GroupHeader part ({@code <GrpHdr>} tag) of the file, into the provided {@link
* GroupHeader39} object
*
* @param groupHeader the {@link GroupHeader39} to build
* @param creditor the creditor of the SEPA Direct Debit file
* @throws DatatypeConfigurationException
*/
protected void createGrpHdr(GroupHeader39 groupHeader, PartyIdentification32 creditor)
throws DatatypeConfigurationException {
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
/*
* Message Identification (mandatory)
* Point to point reference assigned by the instructing party and sent to the next party in the chain to unambiguously identify the message.
*/
groupHeader.setMsgId(bankOrderSeq);
/*
* Creation Date Time (mandatory)
* Date and time at which a (group of) payment instruction(s) was created by the instructing party.
*
* Format : YYYY-MM-DDThh:mm:ss
* Example : <CreDtTm>2010-12-02T08:35:30</CreDtTm>
*/
groupHeader.setCreDtTm(
datatypeFactory.newXMLGregorianCalendar(
generationDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))));
/*
* Number Of Transactions (mandatory)
* Number of individual transactions contained in the message.
*/
groupHeader.setNbOfTxs(Integer.toString(nbOfLines));
/*
* Control Sum
* Total of all individual amounts included in the message, irrespective of currencies
*
* Format : Max. 18 digits of which 2 for the fractional part.
* Decimal separator is "."
*/
groupHeader.setCtrlSum(arithmeticTotal);
/*
* Initiating Party (mandatory)
* Party initiating the payment. In the direct debit context, this can be the creditor, or the party that initiates the payment on behalf of the creditor.
*/
groupHeader.setInitgPty(creditor);
/*
* Pas documenté dans le fichier
*/
// groupHeader.setFwdgAgt(???);
}
/**
* Builds the PaymentInformation part ({@code <PmtInf>} tag) of the file, and adds it into the
* provided {@link PaymentInstructionInformation4} list
*
* @param paymentInstructionInformationList the list to add the {@link
* PaymentInstructionInformation4} objects into
* @param creditor the creditor of the SEPA Direct Debit file
* @throws DatatypeConfigurationException
*/
protected void createPmtInf(
List<PaymentInstructionInformation4> paymentInstructionInformationList,
PartyIdentification32 creditor)
throws AxelorException, DatatypeConfigurationException {
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
/*
* Payment Information (mandatory)
* Set of characteristics that apply to the credit side of the payment transactions included in the direct debit transaction initiation.
*/
PaymentInstructionInformation4 paymentInstructionInformation4 =
factory.createPaymentInstructionInformation4();
paymentInstructionInformationList.add(paymentInstructionInformation4);
/*
* Payment Information Identification (mandatory)
* Reference assigned by a sending party to unambiguously identify the payment information block within the message.
*/
paymentInstructionInformation4.setPmtInfId(bankOrderSeq);
/*
* Payment Method (mandatory, always 'DD')
* Specifies the means of payment that will be used to move the amount of money.
*/
paymentInstructionInformation4.setPmtMtd(PaymentMethod2Code.DD);
/*
* Batch Booking (optional)
* Identifies whether a single entry per individual transaction or a batch entry for the sum of the amounts of all transactions in the group is required.
*
* Usage : Recommended "true". If absent then default "true".
*
* 'true' if : Identifies that a batch entry for the sum of the amounts of all
* transactions in a Payment Information Block is required.
* (one credit for all transactions in a Payment Information Block)
* 'false' if : Identifies that a single entry for each of the transactions
* in a message is required.
*/
paymentInstructionInformation4.setBtchBookg(true);
/*
* Number Of Transactions (optional)
* Number of individual transactions contained in the message.
*/
paymentInstructionInformation4.setNbOfTxs(Integer.toString(nbOfLines));
/*
* Control Sum (optional)
* Total of all individual amounts included in the payment block, irrespective of currencies.
*
* Format : Max. 18 digits of which 2 for the fractional part.
* Decimal separator is "."
*/
paymentInstructionInformation4.setCtrlSum(arithmeticTotal);
/*
* Payment Type Information (mandatory)
* Set of elements that further specifies the type of transaction.
*/
PaymentTypeInformation20 paymentTypeInformation20 = factory.createPaymentTypeInformation20();
paymentInstructionInformation4.setPmtTpInf(paymentTypeInformation20);
/*
* ServiceLevel (mandatory)
* Agreement under which or rules under which the transaction should be processed.
*/
ServiceLevel8Choice serviceLevel8Choice = factory.createServiceLevel8Choice();
paymentTypeInformation20.setSvcLvl(serviceLevel8Choice);
/*
* Code (mandatory, always 'SEPA')
* Identification of a pre-agreed level of service between the parties in a coded form.
*/
serviceLevel8Choice.setCd("SEPA");
/*
* Local Instrument (mandatory)
* User community specific instrument.
*/
LocalInstrument2Choice localInstrument2Choice = factory.createLocalInstrument2Choice();
/*
* Code (mandatory)
*
* Format : either 'CORE' or 'B2B'
* Rule : The mixing of Core Direct Debits and Business-to-Business Direct Debits is not
* allowed in the same message.
*/
switch (sepaType) {
case SEPA_TYPE_CORE:
localInstrument2Choice.setCd(SEPA_TYPE_CORE);
break;
case SEPA_TYPE_SBB:
localInstrument2Choice.setCd(SEPA_TYPE_SBB);
break;
default:
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BANK_ORDER_FILE_UNKNOWN_SEPA_TYPE));
}
paymentTypeInformation20.setLclInstrm(localInstrument2Choice);
/*
* Sequence Type (mandatory) // TO CHECK
* Identifies the direct debit sequence, e.g. first, recurrent, final or one-off.
*
* Either one of the following values.
* CODE Name Definition
* ------------------------------------------------------------------------------------
* FRST First First collection of a series of direct debit instructions.
* RCUR Recurrent Direct debit instruction where the debtor's authorisation is used for
* regular direct debit transactions initiated by the creditor.
* FNAL Final Final collection of a series of direct debit instructions.
* OOFF One Off Direct debit instruction where the debtor's authorisation is used to
* initiate one single direct debit transaction.
*/
paymentTypeInformation20.setSeqTp(SequenceType1Code.FRST);
/*
* Category Purpose (optional)
* Specifies the purpose of the payment based on a set of pre-defined categories.
*/
// CategoryPurpose1Choice categoryPurpose1Choice = factory.createCategoryPurpose1Choice();
// paymentTypeInformation20.setCtgyPurp(categoryPurpose1Choice);
/*
* Code (mandatory)
* Specifies the underlying reason of the payment transaction.
*
* iso20022.org -> 'PaymentCategoryPurpose1Code' for all codes available and definitions.
*/
// categoryPurpose1Choice.setCd("CASH");
/*
* Requested Collection Date (mandatory)
* Date at which the creditor requests the amount of money to be collected from the debtor.
*
* Format : YYYY-MM-DD
* Usage : The minimum delay between sending date and requested collection date is depending
* on the type of direct debit (B2B or CORE) and on the sequence type (FRST, OOFF,
* RCUR, FNAL).
*/
paymentInstructionInformation4.setReqdColltnDt(
datatypeFactory.newXMLGregorianCalendar(
bankOrderDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
/*
* Creditor (mandatory)
* Party to which an amount of money is due.
*/
paymentInstructionInformation4.setCdtr(creditor);
/*
* Creditor Account (mandatory)
* Unambiguous identification of the account of the creditor to which a credit entry will be posted as a result of the payment transaction.
*/
CashAccount16 cashAccount16 = factory.createCashAccount16();
/*
* IBAN (mandatory)
*/
AccountIdentification4Choice accountIdentification4Choice =
factory.createAccountIdentification4Choice();
accountIdentification4Choice.setIBAN(senderBankDetails.getIban());
cashAccount16.setId(accountIdentification4Choice);
/*
* Currency (optional)
*
* Rule : Currency of the account must be EUR. For usage of another currency, please
* contact your bank.
*/
cashAccount16.setCcy(CURRENCY_CODE);
paymentInstructionInformation4.setCdtrAcct(cashAccount16);
/*
* Creditor Agent (mandatory)
* Financial institution servicing an account for the creditor.
*
* Note : The Bank Identifier Code (BIC) is composed of 8 or 11 characters, of which only the
* first 8 characters are significant.
*/
FinancialInstitutionIdentification7 financialInstitutionIdentification7 =
factory.createFinancialInstitutionIdentification7();
fillBic(financialInstitutionIdentification7, senderBankDetails.getBank()); // BIC
BranchAndFinancialInstitutionIdentification4 branchAndFinancialInstitutionIdentification4 =
factory.createBranchAndFinancialInstitutionIdentification4();
branchAndFinancialInstitutionIdentification4.setFinInstnId(financialInstitutionIdentification7);
paymentInstructionInformation4.setCdtrAgt(branchAndFinancialInstitutionIdentification4);
/*
* Ultimate Creditor (optional)
* Ultimate party to which an amount of money is due. Ultimate Creditor is only to be used if different from Creditor.
*/
// paymentInstructionInformation4.setUltmtCdtr();
/*
* Charge Bearer (mandatory) // TO CHECK
* Specifies which party/parties will bear the charges associated with the processing of the payment transaction.
*
* CODE Name Description
* ----------------------------------------------------------------------------------------
* DEBT BorneByDebtor All transaction charges are to be borne by the debtor
* CRED BorneByCreditor All transaction charges are to be borne by the creditor
* SHAR Shared In a direct debit context, means that transaction charges on
* the sender side are to be borne by the creditor, transaction
* charges on the receiver side are to be borne by the debtor.
* SLEV FollowingServiceLevel Charges are to be applied following the rules agreed in the
* service level and/or scheme.
*/
paymentInstructionInformation4.setChrgBr(ChargeBearerType1Code.SLEV);
/*
* Creditor Scheme Identification (optional)
* Credit party that signs the Direct Debit mandate.
*/
// paymentInstructionInformation4.setCdtrSchmeId(creditor);
/*
* Direct Debit Transaction Information, <DrctDbtTxInf> tag
* Does not need to set the List<DirectDebitTransactionInformation1> to the paymentInstructionInformation2 object (see doc)
*/
createDrctDbtTxInf(paymentInstructionInformation4.getDrctDbtTxInf(), creditor);
}
/**
* Builds the DirectDebitTransactionInformation part ({@code <DrctDbtTxInf>} tag) of the file, and
* adds it into the provided {@link DirectDebitTransactionInformation9} list
*
* @param directDebitTransactionInformation9List the list to add the {@link
* DirectDebitTransactionInformation9} objects into
* @param creditor the creditor of the SEPA Direct Debit file
* @throws DatatypeConfigurationException
* @throws AxelorException
*/
protected void createDrctDbtTxInf(
List<DirectDebitTransactionInformation9> directDebitTransactionInformation9List,
PartyIdentification32 creditor)
throws DatatypeConfigurationException, AxelorException {
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
for (BankOrderLine bankOrderLine : bankOrderLineList) {
BankDetails receiverBankDetails = bankOrderLine.getReceiverBankDetails();
Umr receiverUmr = bankOrderLine.getPartner().getActiveUmr();
if (receiverUmr == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.DIRECT_DEBIT_MISSING_PARTNER_ACTIVE_UMR));
}
/*
* Direct Debit Transaction Information (mandatory)
* Set of elements providing information specific to the individual transaction(s) included in the message.
*/
DirectDebitTransactionInformation9 directDebitTransactionInformation9 =
factory.createDirectDebitTransactionInformation9();
directDebitTransactionInformation9List.add(directDebitTransactionInformation9);
/*
* Payment Identification (mandatory)
* Set of elements to reference a payment instruction.
*/
PaymentIdentification1 paymentIdentification1 = factory.createPaymentIdentification1();
directDebitTransactionInformation9.setPmtId(paymentIdentification1);
/*
* Instruction Identification (optional)
* The Instruction Identification is a unique reference assigned by the Initiator to unambiguously identify the transaction.
* It can be used in status messages related to the transaction.
*/
// paymentIdentification1.setInstrId();
/*
* End To End Identification (mandatory)
* Unique identification assigned by the initiating party to unumbiguously identify the transaction.
* This identification is passed on, unchanged, throughout the entire end-to-end chain.
*/
paymentIdentification1.setEndToEndId(bankOrderLine.getSequence());
/*
* Instructed Amount (mandatory)
* Amount of the direct debit, expressed in euro.
*
* Format : Max. 11 digits of which 2 for the fractional part.
* Decimal separator is "."
* Currency "EUR" is explicit, and included in the XML tag.
* Usage : Amount must be between 0.01 and 999999999.99
*/
ActiveOrHistoricCurrencyAndAmount activeOrHistoricCurrencyAndAmount =
factory.createActiveOrHistoricCurrencyAndAmount();
activeOrHistoricCurrencyAndAmount.setCcy(CURRENCY_CODE);
activeOrHistoricCurrencyAndAmount.setValue(bankOrderLine.getBankOrderAmount());
directDebitTransactionInformation9.setInstdAmt(activeOrHistoricCurrencyAndAmount);
/*
* Direct Debit Transaction (mandatory)
* Set of elements providing information specific to the direct debit mandate.
*/
DirectDebitTransaction6 directDebitTransaction6 = factory.createDirectDebitTransaction6();
directDebitTransactionInformation9.setDrctDbtTx(directDebitTransaction6);
/*
* Mandate Related Information (mandatory)
* Set of elements used to provide further details related to a direct debit mandate signed between the creditor and the debtor.
*/
MandateRelatedInformation6 mandateRelatedInformation6 =
factory.createMandateRelatedInformation6();
directDebitTransaction6.setMndtRltdInf(mandateRelatedInformation6);
/*
* Mandate Identification (mandatory)
* Reference of the direct debit mandate that has been signed between by the debtor and the creditor.
*/
mandateRelatedInformation6.setMndtId(receiverUmr.getUmrNumber());
/*
* Date of Signature (mandatory)
* Date on which the direct debit mandate has been signed by the debtor.
*
* Format : YYYY-MM-DD
*/
mandateRelatedInformation6.setDtOfSgntr(
datatypeFactory.newXMLGregorianCalendar(
receiverUmr
.getMandateSignatureDate()
.format(DateTimeFormatter.ofPattern(("yyyy-MM-dd")))));
/*
* Amendment Indicator (optional)
* Indicator notifying whether the underlying mandate is amended or not.
*
* Usage : - If not present, considered as "false".
* - If true, 'Amendment Information Details' is mandatory.
*
* 'true' if : The mandate is amended or migrated from Dom'80.
* 'false' if : The mandate is not amended.
*/
// mandateRelatedInformation6.setAmdmntInd(???);
/*
* Amendment Info Details (optional)
* List of direct debit mandate elements that have been modified.
*/
// AmendmentInformationDetails6 amendmentInformationDetails6 =
// factory.createAmendmentInformationDetails6();
// mandateRelatedInformation6.setAmdmntInfDtls(amendmentInformationDetails6);
// amendmentInformationDetails6.setOrgnlMndtId(???);
// amendmentInformationDetails6.setOrgnlCdtrSchmeId(???);
// amendmentInformationDetails6.setOrgnlDbtrAcct(???);
// amendmentInformationDetails6.setOrgnlDbtrAgt(???);
/*
* Electronic Signature (optional)
* Digital signature as provided by the creditor.
*
* Usage : - If the direct debit is based on an electronic mandate, this data
* element must contain the reference of the Mandate Acceptance Report.
* - If the direct debit is based on a paper mandate, this data element
* is not allowed.
*/
// mandateRelatedInformation6.setElctrncSgntr(???);
/*
* Creditor Scheme Identification
* Creditor identification as given by his bank.
*/
PartyIdentification32 creditorSchemeId = factory.createPartyIdentification32();
directDebitTransaction6.setCdtrSchmeId(creditorSchemeId);
Party6Choice party6Choice = factory.createParty6Choice();
creditorSchemeId.setId(party6Choice);
PersonIdentification5 personIdentification5 = factory.createPersonIdentification5();
party6Choice.setPrvtId(personIdentification5);
GenericPersonIdentification1 genericPersonIdentification1 =
factory.createGenericPersonIdentification1();
personIdentification5.getOthr().add(genericPersonIdentification1);
genericPersonIdentification1.setId(
Beans.get(BankPaymentConfigService.class)
.getIcsNumber(senderCompany.getBankPaymentConfig()));
PersonIdentificationSchemeName1Choice personIdentificationSchemeName1Choice =
factory.createPersonIdentificationSchemeName1Choice();
genericPersonIdentification1.setSchmeNm(personIdentificationSchemeName1Choice);
personIdentificationSchemeName1Choice.setPrtry("SEPA");
/*
* Ultimate Creditor (optional)
* Ultimate party to which an amount of money is due. Ultimate Creditor is only to be used if different from Creditor.
*/
// directDebitTransaction6.setUltmtCdtr();
/*
* Debtor Agent (mandatory)
* Financial institution servicing an account for the debtor.
*/
BranchAndFinancialInstitutionIdentification4 branchAndFinancialInstitutionIdentification4 =
factory.createBranchAndFinancialInstitutionIdentification4();
FinancialInstitutionIdentification7 financialInstitutionIdentification7 =
factory.createFinancialInstitutionIdentification7();
fillBic(financialInstitutionIdentification7, receiverBankDetails.getBank()); // BIC
branchAndFinancialInstitutionIdentification4.setFinInstnId(
financialInstitutionIdentification7);
directDebitTransactionInformation9.setDbtrAgt(branchAndFinancialInstitutionIdentification4);
/*
* Debtor (mandatory)
* Party that owes an amount of money to the (ultimate) creditor.
*/
PartyIdentification32 debtor = factory.createPartyIdentification32();
debtor.setNm(receiverBankDetails.getOwnerName());
directDebitTransactionInformation9.setDbtr(debtor);
/*
* Debtor Account (mandatory)
* Identification of the account of the debtor to which a debit entry will be made to execute the transfer.
*/
AccountIdentification4Choice accountIdentification4Choice =
factory.createAccountIdentification4Choice();
accountIdentification4Choice.setIBAN(receiverBankDetails.getIban());
CashAccount16 cashAccount16 = factory.createCashAccount16();
cashAccount16.setId(accountIdentification4Choice);
directDebitTransactionInformation9.setDbtrAcct(cashAccount16);
/*
* Ultimate Debtor (optional)
* Ultimate party that owes an amount of money to the (ultimate) creditor. Ultimate Debtor is only to be used if different from Debtor.
*/
// directDebitTransactionInformation9.setUltmtDbtr(???);
/*
* Purpose (optional)
* Underlying reason for the payment transaction.
* Purpose is used by the Debtor to provide information to the Creditor, concerning thenature of the payment transaction.
* It is not used for processing by any of the banks involved.
*/
// Purpose2Choice purpose2Choice = factory.createPurpose2Choice();
// directDebitTransactionInformation9.setPurp(purpose2Choice);
/*
* Code (mandatory)
* Specifies the underlying reason of the payment transaction.
*/
// purpose2Choice.setCd(???);
/*
* Remittance Information (optional)
* Information that enables the matching, ie, reconciliation, of a payment with the items that the payment
* is intended to settle, eg, commercial invoices in an account receivable system.
*
* Usage : Either Structured or Unstructured, but not both.
*/
RemittanceInformation5 remittanceInformation5 = factory.createRemittanceInformation5();
directDebitTransactionInformation9.setRmtInf(remittanceInformation5);
/*
* Unstructured (choice 1 of 2)
* Information supplied to enable the matching of an entry with the items that the transfer is intended
* to settle, eg, commercial invoices in an accounts' receivable system in an unstructured form.
*/
remittanceInformation5.getUstrd().add(bankOrderLine.getReceiverReference());
/*
* Structured (choice 2 of 2)
* Information supplied to enable the matching of an entry with the items that the transfer is intended
* to settle, eg, commercial invoices in an accounts' receivable system in a structured form.
*/
// StructuredRemittanceInformation7 structuredRemittanceInformation7 =
// factory.createStructuredRemittanceInformation7();
// remittanceInformation5.getStrd().add(structuredRemittanceInformation7);
}
}
/**
* Method to fill the BIC information. If the BIC is not provided or in Iban only mode, we put
* NOTPROVIDED value. In this case, the bank ignore the BIC and use the Iban only.
*
* @param finInstnId The financial instituation identification tag of the generated file.
* @param bank The bank from which the BIC is get.
*/
protected void fillBic(FinancialInstitutionIdentification7 finInstnId, Bank bank) {
if (bankOrderFileFormat.getIbanOnly()
|| bank == null
|| Strings.isNullOrEmpty(bank.getCode())) {
GenericFinancialIdentification1 genFinId = new GenericFinancialIdentification1();
genFinId.setId(BIC_NOT_PROVIDED);
finInstnId.setOthr(genFinId);
} else {
finInstnId.setBIC(bank.getCode());
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder.file.directdebit;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.service.bankorder.file.BankOrderFileService;
public abstract class BankOrderFile008Service extends BankOrderFileService {
public static final String SEPA_TYPE_CORE = "CORE";
public static final String SEPA_TYPE_SBB = "SBB";
protected static final String BIC_NOT_PROVIDED = "NOTPROVIDED";
protected static final String CURRENCY_CODE = "EUR";
public BankOrderFile008Service(BankOrder bankOrder) {
super(bankOrder);
fileExtension = FILE_EXTENSION_XML;
}
}

View File

@ -0,0 +1,265 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder.file.transfer;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.service.bankorder.file.BankOrderFileService;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.AccountIdentification3Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.AmountType2Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.BranchAndFinancialInstitutionIdentification3;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.CashAccount7;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.CreditTransferTransactionInformation1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.CurrencyAndAmount;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.Document;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.FinancialInstitutionIdentification5Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.GenericIdentification3;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.GroupHeader1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.Grouping1Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.ObjectFactory;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.Pain00100102;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.PartyIdentification8;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.PaymentIdentification1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.PaymentInstructionInformation1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.PaymentMethod3Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.PaymentTypeInformation1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.RemittanceInformation1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.ServiceLevel1Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02.ServiceLevel2Choice;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.exception.AxelorException;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
public class BankOrderFile00100102Service extends BankOrderFileService {
protected static final String BIC_NOT_PROVIDED = "NOTPROVIDED";
@Inject
public BankOrderFile00100102Service(BankOrder bankOrder) {
super(bankOrder);
context = "com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_02";
fileExtension = FILE_EXTENSION_XML;
}
/**
* Method to create an XML file for SEPA transfer pain.001.001.02
*
* @throws AxelorException
* @throws DatatypeConfigurationException
* @throws JAXBException
* @throws IOException
*/
@Override
public File generateFile()
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException {
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
ObjectFactory factory = new ObjectFactory();
ServiceLevel2Choice svcLvl = factory.createServiceLevel2Choice();
svcLvl.setCd(ServiceLevel1Code.SEPA);
PaymentTypeInformation1 pmtTpInf = factory.createPaymentTypeInformation1();
pmtTpInf.setSvcLvl(svcLvl);
// Payer
PartyIdentification8 dbtr = factory.createPartyIdentification8();
dbtr.setNm(senderBankDetails.getOwnerName());
// IBAN
AccountIdentification3Choice iban = factory.createAccountIdentification3Choice();
iban.setIBAN(senderBankDetails.getIban());
CashAccount7 dbtrAcct = factory.createCashAccount7();
dbtrAcct.setId(iban);
// BIC
FinancialInstitutionIdentification5Choice finInstnId =
factory.createFinancialInstitutionIdentification5Choice();
fillBic(finInstnId, senderBankDetails.getBank());
BranchAndFinancialInstitutionIdentification3 dbtrAgt =
factory.createBranchAndFinancialInstitutionIdentification3();
dbtrAgt.setFinInstnId(finInstnId);
PaymentInstructionInformation1 pmtInf = factory.createPaymentInstructionInformation1();
pmtInf.setPmtInfId(bankOrderSeq);
pmtInf.setPmtMtd(PaymentMethod3Code.TRF);
pmtInf.setPmtTpInf(pmtTpInf);
/**
* RequestedExecutionDate Definition : Date at which the initiating party asks the Debtor's Bank
* to process the payment. This is the date on which the debtor's account(s) is (are) to be
* debited. XML Tag : <ReqdExctnDt> Occurrences : [1..1] Format : YYYY-MM-DD Rules : date is
* limited to maximum one year in the future.
*/
pmtInf.setReqdExctnDt(
datatypeFactory.newXMLGregorianCalendar(
bankOrderDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
pmtInf.setDbtr(dbtr);
pmtInf.setDbtrAcct(dbtrAcct);
pmtInf.setDbtrAgt(dbtrAgt);
CreditTransferTransactionInformation1 cdtTrfTxInf = null;
PaymentIdentification1 pmtId = null;
AmountType2Choice amt = null;
CurrencyAndAmount instdAmt = null;
PartyIdentification8 cbtr = null;
CashAccount7 cbtrAcct = null;
BranchAndFinancialInstitutionIdentification3 cbtrAgt = null;
RemittanceInformation1 rmtInf = null;
for (BankOrderLine bankOrderLine : bankOrderLineList) {
BankDetails receiverBankDetails = bankOrderLine.getReceiverBankDetails();
// Reference
pmtId = factory.createPaymentIdentification1();
// pmtId.setInstrId(bankOrderLine.getSequence());
pmtId.setEndToEndId(bankOrderLine.getSequence());
// Amount
instdAmt = factory.createCurrencyAndAmount();
instdAmt.setCcy(bankOrderCurrency.getCode());
instdAmt.setValue(bankOrderLine.getBankOrderAmount());
amt = factory.createAmountType2Choice();
amt.setInstdAmt(instdAmt);
// Receiver
cbtr = factory.createPartyIdentification8();
cbtr.setNm(receiverBankDetails.getOwnerName());
// IBAN
iban = factory.createAccountIdentification3Choice();
iban.setIBAN(receiverBankDetails.getIban());
cbtrAcct = factory.createCashAccount7();
cbtrAcct.setId(iban);
// BIC
finInstnId = factory.createFinancialInstitutionIdentification5Choice();
fillBic(finInstnId, receiverBankDetails.getBank());
cbtrAgt = factory.createBranchAndFinancialInstitutionIdentification3();
cbtrAgt.setFinInstnId(finInstnId);
rmtInf = factory.createRemittanceInformation1();
String ustrd = "";
if (!Strings.isNullOrEmpty(bankOrderLine.getReceiverReference())) {
ustrd += bankOrderLine.getReceiverReference();
}
if (!Strings.isNullOrEmpty(bankOrderLine.getReceiverLabel())) {
if (!Strings.isNullOrEmpty(ustrd)) {
ustrd += " - ";
}
ustrd += bankOrderLine.getReceiverLabel();
}
if (!Strings.isNullOrEmpty(ustrd)) {
rmtInf.getUstrd().add(ustrd);
}
// StructuredRemittanceInformation6 strd = factory.createStructuredRemittanceInformation6();
//
// CreditorReferenceInformation1 cdtrRefInf = factory.createCreditorReferenceInformation1();
// cdtrRefInf.setCdtrRef(bankOrderLine.getReceiverReference());
//
// strd.setCdtrRefInf(cdtrRefInf);
//
// rmtInf.getStrd().add(strd);
// Transaction
cdtTrfTxInf = factory.createCreditTransferTransactionInformation1();
cdtTrfTxInf.setPmtId(pmtId);
cdtTrfTxInf.setAmt(amt);
cdtTrfTxInf.setCdtr(cbtr);
cdtTrfTxInf.setCdtrAcct(cbtrAcct);
cdtTrfTxInf.setCdtrAgt(cbtrAgt);
cdtTrfTxInf.setRmtInf(rmtInf);
pmtInf.getCdtTrfTxInf().add(cdtTrfTxInf);
}
// Header
GroupHeader1 grpHdr = factory.createGroupHeader1();
/** Référence du message qui n'est pas utilisée comme référence fonctionnelle. */
grpHdr.setMsgId(bankOrderSeq);
/**
* CreationDateTime Definition : Date and Time at which a (group of) payment instruction(s) was
* created by the instructing party. XML Tag : <CreDtTm> Occurrences : [1..1] Format :
* YYYY-MM-DDThh:mm:ss
*/
grpHdr.setCreDtTm(
datatypeFactory.newXMLGregorianCalendar(
generationDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))));
grpHdr.setNbOfTxs(Integer.toString(nbOfLines));
grpHdr.setCtrlSum(arithmeticTotal);
grpHdr.setGrpg(Grouping1Code.MIXD);
grpHdr.setInitgPty(dbtr);
// Parent
Pain00100102 pain00100102 = factory.createPain00100102();
pain00100102.setGrpHdr(grpHdr);
pain00100102.getPmtInf().add(pmtInf);
// Document
Document xml = factory.createDocument();
xml.setPain00100102(pain00100102);
fileToCreate = factory.createDocument(xml);
return super.generateFile();
}
/**
* Method to fill the BIC information. If the BIC is not provided or in Iban only mode, we put
* NOTPROVIDED value. In this case, the bank ignore the BIC and use the Iban only.
*
* @param finInstnId The financial instituation identification tag of the generated file.
* @param bank The bank from which the BIC is get.
*/
protected void fillBic(FinancialInstitutionIdentification5Choice finInstnId, Bank bank) {
if (bankOrderFileFormat.getIbanOnly()
|| bank == null
|| Strings.isNullOrEmpty(bank.getCode())) {
GenericIdentification3 genId = new GenericIdentification3();
genId.setId(BIC_NOT_PROVIDED);
finInstnId.setPrtryId(genId);
} else {
finInstnId.setBIC(bank.getCode());
}
}
}

View File

@ -0,0 +1,263 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder.file.transfer;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.service.bankorder.file.BankOrderFileService;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.AccountIdentification4Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.ActiveOrHistoricCurrencyAndAmount;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.AmountType3Choice;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.BranchAndFinancialInstitutionIdentification4;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.CashAccount16;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.CreditTransferTransactionInformation10;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.CustomerCreditTransferInitiationV03;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.Document;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.FinancialInstitutionIdentification7;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.GenericFinancialIdentification1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.GroupHeader32;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.ObjectFactory;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.PartyIdentification32;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.PaymentIdentification1;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.PaymentInstructionInformation3;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.PaymentMethod3Code;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.PaymentTypeInformation19;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.RemittanceInformation5;
import com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03.ServiceLevel8Choice;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.exception.AxelorException;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
public class BankOrderFile00100103Service extends BankOrderFileService {
protected static final String BIC_NOT_PROVIDED = "NOTPROVIDED";
@Inject
public BankOrderFile00100103Service(BankOrder bankOrder) {
super(bankOrder);
context = "com.axelor.apps.bankpayment.xsd.sepa.pain_001_001_03";
fileExtension = FILE_EXTENSION_XML;
}
/**
* Method to create an XML file for SEPA transfer pain.001.001.03
*
* @throws AxelorException
* @throws DatatypeConfigurationException
* @throws JAXBException
* @throws IOException
*/
@Override
public File generateFile()
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException {
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
ObjectFactory factory = new ObjectFactory();
ServiceLevel8Choice svcLvl = factory.createServiceLevel8Choice();
svcLvl.setCd("SEPA");
PaymentTypeInformation19 pmtTpInf = factory.createPaymentTypeInformation19();
pmtTpInf.setSvcLvl(svcLvl);
// Payer
PartyIdentification32 dbtr = factory.createPartyIdentification32();
dbtr.setNm(senderBankDetails.getOwnerName());
// IBAN
AccountIdentification4Choice iban = factory.createAccountIdentification4Choice();
iban.setIBAN(senderBankDetails.getIban());
CashAccount16 dbtrAcct = factory.createCashAccount16();
dbtrAcct.setId(iban);
// BIC
FinancialInstitutionIdentification7 finInstnId =
factory.createFinancialInstitutionIdentification7();
fillBic(finInstnId, senderBankDetails.getBank());
BranchAndFinancialInstitutionIdentification4 dbtrAgt =
factory.createBranchAndFinancialInstitutionIdentification4();
dbtrAgt.setFinInstnId(finInstnId);
PaymentInstructionInformation3 pmtInf = factory.createPaymentInstructionInformation3();
pmtInf.setPmtInfId(bankOrderSeq);
pmtInf.setPmtMtd(PaymentMethod3Code.TRF);
pmtInf.setPmtTpInf(pmtTpInf);
/**
* RequestedExecutionDate Definition : Date at which the initiating party asks the Debtor's Bank
* to process the payment. This is the date on which the debtor's account(s) is (are) to be
* debited. XML Tag : <ReqdExctnDt> Occurrences : [1..1] Format : YYYY-MM-DD Rules : date is
* limited to maximum one year in the future.
*/
pmtInf.setReqdExctnDt(
datatypeFactory.newXMLGregorianCalendar(
bankOrderDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
pmtInf.setDbtr(dbtr);
pmtInf.setDbtrAcct(dbtrAcct);
pmtInf.setDbtrAgt(dbtrAgt);
CreditTransferTransactionInformation10 cdtTrfTxInf = null;
PaymentIdentification1 pmtId = null;
AmountType3Choice amt = null;
ActiveOrHistoricCurrencyAndAmount instdAmt = null;
PartyIdentification32 cbtr = null;
CashAccount16 cbtrAcct = null;
BranchAndFinancialInstitutionIdentification4 cbtrAgt = null;
RemittanceInformation5 rmtInf = null;
for (BankOrderLine bankOrderLine : bankOrderLineList) {
BankDetails receiverBankDetails = bankOrderLine.getReceiverBankDetails();
// Reference
pmtId = factory.createPaymentIdentification1();
// pmtId.setInstrId(bankOrderLine.getSequence());
pmtId.setEndToEndId(bankOrderLine.getSequence());
// Amount
instdAmt = factory.createActiveOrHistoricCurrencyAndAmount();
instdAmt.setCcy(bankOrderCurrency.getCode());
instdAmt.setValue(bankOrderLine.getBankOrderAmount());
amt = factory.createAmountType3Choice();
amt.setInstdAmt(instdAmt);
// Receiver
cbtr = factory.createPartyIdentification32();
cbtr.setNm(receiverBankDetails.getOwnerName());
// IBAN
iban = factory.createAccountIdentification4Choice();
iban.setIBAN(receiverBankDetails.getIban());
cbtrAcct = factory.createCashAccount16();
cbtrAcct.setId(iban);
// BIC
finInstnId = factory.createFinancialInstitutionIdentification7();
fillBic(finInstnId, receiverBankDetails.getBank());
cbtrAgt = factory.createBranchAndFinancialInstitutionIdentification4();
cbtrAgt.setFinInstnId(finInstnId);
rmtInf = factory.createRemittanceInformation5();
String ustrd = "";
if (!Strings.isNullOrEmpty(bankOrderLine.getReceiverReference())) {
ustrd += bankOrderLine.getReceiverReference();
}
if (!Strings.isNullOrEmpty(bankOrderLine.getReceiverLabel())) {
if (!Strings.isNullOrEmpty(ustrd)) {
ustrd += " - ";
}
ustrd += bankOrderLine.getReceiverLabel();
}
if (!Strings.isNullOrEmpty(ustrd)) {
rmtInf.getUstrd().add(ustrd);
}
// StructuredRemittanceInformation7 strd = factory.createStructuredRemittanceInformation7();
//
// CreditorReferenceInformation2 cdtrRefInf = factory.createCreditorReferenceInformation2();
// cdtrRefInf.setRef(bankOrderLine.getReceiverReference());
//
// strd.setCdtrRefInf(cdtrRefInf);
//
// rmtInf.getStrd().add(strd);
// Transaction
cdtTrfTxInf = factory.createCreditTransferTransactionInformation10();
cdtTrfTxInf.setPmtId(pmtId);
cdtTrfTxInf.setAmt(amt);
cdtTrfTxInf.setCdtr(cbtr);
cdtTrfTxInf.setCdtrAcct(cbtrAcct);
cdtTrfTxInf.setCdtrAgt(cbtrAgt);
cdtTrfTxInf.setRmtInf(rmtInf);
pmtInf.getCdtTrfTxInf().add(cdtTrfTxInf);
}
// Header
GroupHeader32 grpHdr = factory.createGroupHeader32();
/** Référence du message qui n'est pas utilisée comme référence fonctionnelle. */
grpHdr.setMsgId(bankOrderSeq);
/**
* CreationDateTime Definition : Date and Time at which a (group of) payment instruction(s) was
* created by the instructing party. XML Tag : <CreDtTm> Occurrences : [1..1] Format :
* YYYY-MM-DDThh:mm:ss
*/
grpHdr.setCreDtTm(
datatypeFactory.newXMLGregorianCalendar(
generationDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))));
grpHdr.setNbOfTxs(Integer.toString(nbOfLines));
grpHdr.setCtrlSum(arithmeticTotal);
grpHdr.setInitgPty(dbtr);
// Parent
CustomerCreditTransferInitiationV03 customerCreditTransferInitiationV03 =
factory.createCustomerCreditTransferInitiationV03();
customerCreditTransferInitiationV03.setGrpHdr(grpHdr);
customerCreditTransferInitiationV03.getPmtInf().add(pmtInf);
// Document
Document xml = factory.createDocument();
xml.setCstmrCdtTrfInitn(customerCreditTransferInitiationV03);
fileToCreate = factory.createDocument(xml);
return super.generateFile();
}
/**
* Method to fill the BIC information. If the BIC is not provided or in Iban only mode, we put
* NOTPROVIDED value. In this case, the bank ignore the BIC and use the Iban only.
*
* @param finInstnId The financial instituation identification tag of the generated file.
* @param bank The bank from which the BIC is get.
*/
protected void fillBic(FinancialInstitutionIdentification7 finInstnId, Bank bank) {
if (bankOrderFileFormat.getIbanOnly()
|| bank == null
|| Strings.isNullOrEmpty(bank.getCode())) {
GenericFinancialIdentification1 genFinId = new GenericFinancialIdentification1();
genFinId.setId(BIC_NOT_PROVIDED);
finInstnId.setOthr(genFinId);
} else {
finInstnId.setBIC(bank.getCode());
}
}
}

View File

@ -0,0 +1,58 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder.file.transfer;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
public class BankOrderFileAFB160ICTService extends BankOrderFileAFB160Service {
@Inject
public BankOrderFileAFB160ICTService(BankOrder bankOrder) throws AxelorException {
super(bankOrder);
}
/**
* B1. Code opération La liste des codes opération possibles est indiquée page 4 au paragraphe
* "types de virements pouvant être émis par la clientèle". Les codes utilisés doivent faire
* l'objet d'un accord contractuel avec la banque réceptrice.
*
* @return
*/
@Override
protected String getB1Area() {
return OPERATION_TREASURY_TRANSFER;
}
@Override
protected String getSenderEArea() {
return null;
}
@Override
protected String getC11Area() {
return null;
}
@Override
protected String getB3Area() {
return null;
}
}

View File

@ -0,0 +1,838 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.bankpayment.service.bankorder.file.transfer;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.BankOrderLine;
import com.axelor.apps.bankpayment.exception.IExceptionMessage;
import com.axelor.apps.bankpayment.service.bankorder.file.BankOrderFileService;
import com.axelor.apps.bankpayment.service.cfonb.CfonbToolService;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.service.PartnerService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
public abstract class BankOrderFileAFB160Service extends BankOrderFileService {
protected CfonbToolService cfonbToolService;
protected PartnerService partnerService;
protected final int NB_CHAR_PER_LINE = 160;
// Virements Ordinaires
protected final String OPERATION_STANDARD_TRANSFER = "02";
// Virements à vérifier
protected final String OPERATION_TO_CHECK_TRANSFER = "29";
// Virements Particuliers ("Aide Personnalisée au Logement", APL identifié par le n° d'émetteur
// 900xxx)
protected final String OPERATION_INDIVIDUAL_TRANSFER = "22";
// Virements à Echéance "E-3" (à échanger 3 jours ouvrés avant l'échéance)
protected final String OPERATION_SCHEDULE_E3_TRANSFER = "27";
// Virements à Echéance "E-2" (à échanger 2 jours ouvrés avant l'échéance)
protected final String OPERATION_SCHEDULE_E2_TRANSFER = "28";
// Virements de Trésorerie (VSOT)
protected final String OPERATION_TREASURY_TRANSFER = "76";
@Inject
public BankOrderFileAFB160Service(BankOrder bankOrder) throws AxelorException {
super(bankOrder);
this.partnerService = Beans.get(PartnerService.class);
this.cfonbToolService = Beans.get(CfonbToolService.class);
fileExtension = FILE_EXTENSION_TXT;
}
/**
* Method to create an XML file for SEPA transfer pain.001.001.02
*
* @throws AxelorException
* @throws DatatypeConfigurationException
* @throws JAXBException
* @throws IOException
*/
@Override
public File generateFile()
throws JAXBException, IOException, AxelorException, DatatypeConfigurationException {
List<String> records = Lists.newArrayList();
records.add(this.createSenderRecord());
for (BankOrderLine bankOrderLine : bankOrderLineList) {
records.add(this.createDetailRecord(bankOrderLine));
if (this.useOptionalFurtherInformationRecord(bankOrderLine)) {
records.add(this.createOptionalFurtherInformationRecord(bankOrderLine));
}
}
records.add(this.createTotalRecord());
fileToCreate = records;
return super.generateFile();
}
protected boolean useOptionalFurtherInformationRecord(BankOrderLine bankOrderLine) {
if (Strings.isNullOrEmpty(bankOrderLine.getPaymentReasonLine1())) {
return false;
}
return true;
}
/**
* B1. Code opération La liste des codes opération possibles est indiquée page 4 au paragraphe
* "types de virements pouvant être émis par la clientèle". Les codes utilisés doivent faire
* l'objet d'un accord contractuel avec la banque réceptrice.
*
* @return
*/
abstract String getB1Area();
/**
* B3. Numéro d'émetteur Attribué par la banque du donneur d'ordre (ou numéro d'identification
* pour le Virement particulier code 22). Pour le Virement d'APL il est constitué de la façon
* suivante : • positions 13 à 15 = 900 pour l'APL • positions 16 à 17 = numéro de département de
* la Caisse émettrice (alphanumérique) • position 18 = rang de la Caisse dans le département (1 à
* 9 pour les CAF, 0 pour le régime agricole)
*
* @return
*/
abstract String getB3Area();
/**
* C1-1. Code " CCD " (comportement en cas de décalage) Utilisé pour le Virement "E-3" code
* opération 27 • 0 ou blanc = pas d'instruction • 6 = maintien de l'échéance (transformation du
* virement en "E-2") • 7 = maintien de l'anticipation Pour les autres Virements cette zone n'est
* pas utilisée.
*
* @return
*/
abstract String getC11Area();
/**
* C1-3. Date Facultative mais recommandée pour les Virements ordinaires, particuliers et de
* trésorerie : date demandée pour le règlement interbancaire. Obligatoire pour les Virements à
* échéance : date d'échéance demandée.
*
* @return
*/
protected String getSenderC13Area() {
int year = this.bankOrderDate.getYear();
return this.bankOrderDate.format(DateTimeFormatter.ofPattern("ddMM"))
+ (String.valueOf(year).substring(3));
}
/**
* E. Identifiant du donneur d'ordre Dans le cas d'utilisation de cet identifiant cette zone se
* décompose de la façon suivante : 1 caractère (position 103) signe recognitif = ")" signalant la
* présence d'un code identifiant et d'un identifiant 1 caractère (position 104) code identifiant
* : • 1 = SIRET • 2 = AUTRE 14 caractères (positions 105 à 118) identifiant donneur d'ordre
*
* @return
*/
abstract String getSenderEArea();
/**
* D1. Domiciliation ¾ Si le compte du bénéficiaire est un compte de résident : Désignation en
* clair de la banque et du guichet domiciliataires. Informations figurant sur le RIB du
* bénéficiaire. Cette information est optionnelle, mais lorsque la zone est utilisée elle doit
* être cadrée à gauche. ¾ Si le compte du bénéficiaire est un compte de non résident : ♦ Pour les
* Virements de Salaires, Pensions et Prestations Assimilées, la zone D1 se décompose de la façon
* suivante : D1-1. (20 caractères) positions 55 à 74 pour indiquer la domiciliation D1-2. (4
* caractères) positions 75 à 78 • 1 caractère : Code nature économique pouvant prendre les
* valeurs, 5 = salaires transférés par des employeurs du secteur officiel 6 = salaires transférés
* par des employeurs privés 7 = autres rémunérations du travail 8 = pensions, retraites et
* prestations sociales • 3 caractères : identification géographique du pays de résidence du
* bénéficiaire du Virement. Code géonomenclature de la CEE (liste de codes pays fournie par la
* Banque de France) ♦ Pour la Déclaration à la Balance des Paiements, cette zone D1 se décompose
* de la façon suivante : D1-1. (9 caractères) positions 55 à 63 numéro SIREN du résident D1-2.
* (15 caractères) positions 64 à 78 : pour indiquer la domiciliation la zone D2 se décompose en 4
* sous zones : D2-1. (1 caractère) position 79, code type de déclaration D2-2. (3 caractères)
* positions 80 à 82, code nature économique D2-3. (3 caractères) positions 83 à 85, code pays
* D2-4. (1 caractère) position 86, zone réservée
*
* @return
*/
protected String getDetailD1Area(BankOrderLine bankOrderLine) throws AxelorException {
if (bankOrderLine.getReceiverBankDetails().getBankAddress() == null) {
throw new AxelorException(
I18n.get(IExceptionMessage.BANK_ORDER_RECEIVER_BANK_DETAILS_MISSING_BANK_ADDRESS),
TraceBackRepository.CATEGORY_MISSING_FIELD);
}
return bankOrderLine.getReceiverBankDetails().getBankAddress().getAddress();
}
protected String getDetailD2Area(BankOrderLine bankOrderLine) {
return "";
}
/**
* E. Montant Exprimé en "centimes" (00 s'il y a lieu) cadré à droite, non signé, complété
* éventuellement à gauche par des zéros. Le montant est nul (à zéro) pour les virements à
* vérifier
*
* @param bankOrderLine
* @return
*/
protected BigDecimal getDetailEAreaAmount(BankOrderLine bankOrderLine) {
if (getB1Area().equals(OPERATION_TO_CHECK_TRANSFER)) {
return BigDecimal.ZERO;
}
return bankOrderLine.getBankOrderAmount();
}
/**
* F. Libellé Zone à la disposition du donneur d'ordre pour l'indication d'un libellé au
* bénéficiaire dans le cadre d'un accord bilatéral entre le donneur d'ordre et sa banque. A
* partir du premier novembre 2004 la banque du donneur d'ordre acheminera sans altération au
* moins les 30 premiers caractères de cette zone jusqu'à la banque du bénéficiaire. Cette zone
* peut être structurée de la façon suivante, afin de permettre au bénéficiaire du virement de
* l'identifier, en tant que "REFERENCE DE L'OPERATION". Elle se décompose de la façon suivante :
* F1. (1 caractère) position 119 = ")" F2. (12 caractères) positions 120 à 131 - Référence
* commerciale F3. (18 caractères) positions 132 à 149 - A la disposition du donneur d'ordre
*
* <p>Pour les Virements Particuliers d'APL, cette zone se décompose de la façon suivante : • 13
* caractères : Référence numéro de dossier (idem certificat de prêt) • 1 caractère : Rang de
* l'échéance du prêt (*) • 1 caractère : Mois de l'échéance valeurs = A = janvier B = février C =
* mars ......... • 1 caractère : Mois d'APL payé à l'établissement prêteur (*) Valeur Sans compte
* de trésorerie Avec compte de trésorerie "1" échéance du 5 échéance du 7 au 11 "2" échéance du
* 10 échéance du 12 au 26 "3" échéance du 25 échéance du 27 au 6
*
* <p>(en cas de rappel ces deux dernières positions comportent les lettres "RA")
*
* <p>• 15 caractères : Numéro de l'allocataire
*
* @return
*/
protected String getDetailFArea(BankOrderLine bankOrderLine) {
return bankOrderLine.getReceiverLabel();
}
/**
* Method to create a sender record for national transfer AFB160
*
* @return
* @throws AxelorException
*/
protected String createSenderRecord() throws AxelorException {
try {
// Zone A : Code enregistrement
String senderRecord =
cfonbToolService.createZone(
"A", "03", cfonbToolService.STATUS_MANDATORY, cfonbToolService.FORMAT_NUMERIC, 2);
// Zone B1 : Code opération
senderRecord +=
cfonbToolService.createZone(
"B1",
getB1Area(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
2);
// Zone B2 : Zone réservée
senderRecord +=
cfonbToolService.createZone(
"B2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 8);
// Zone B3 : Numéro d'émetteur ou d'identification pour le virement particulier code 22
// (Virement APL)
senderRecord +=
cfonbToolService.createZone(
"B3",
getB3Area(),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
6);
// Zone C1-1 : Code CCD (virement à échéance "E-3")
senderRecord +=
cfonbToolService.createZone(
"C1-1",
getC11Area(),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
1);
// Zone C1-2 : Zone réservée
senderRecord +=
cfonbToolService.createZone(
"C1-2",
"",
cfonbToolService.STATUS_NOT_USED,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
6);
// Zone C1-3 : Date (JJMMA)
senderRecord +=
cfonbToolService.createZone(
"C1-3",
getSenderC13Area(),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_NUMERIC,
5);
// Zone C2 : Nom/Raison sociale du donneur d'ordre
senderRecord +=
cfonbToolService.createZone(
I18n.get("C2 - Company name"),
senderCompany.getName(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
24);
// Zone D1-1 : Référence de la remise (à blanc ou a zéro si non utilisée)
senderRecord +=
cfonbToolService.createZone(
I18n.get("D1-1 - Bank order sequence"),
bankOrderSeq,
cfonbToolService.STATUS_OPTIONAL,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
7);
// Zone D1-2 : Zone réservée
senderRecord +=
cfonbToolService.createZone(
"D1-2",
"",
cfonbToolService.STATUS_NOT_USED,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
17);
// Zone D2-1 : Zone réservée
senderRecord +=
cfonbToolService.createZone(
"D2-1",
"",
cfonbToolService.STATUS_NOT_USED,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
2);
// Zone D2-2 : Code monnaie
senderRecord +=
cfonbToolService.createZone(
"D2-2", "E", cfonbToolService.STATUS_MANDATORY, cfonbToolService.FORMAT_ALPHA, 1);
// Zone D2-3 : Zone réservée
senderRecord +=
cfonbToolService.createZone(
"D2-3",
"",
cfonbToolService.STATUS_NOT_USED,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
5);
// Zone D3 : Code guichet de la banque du donneur d'ordre
senderRecord +=
cfonbToolService.createZone(
I18n.get("D3 - Sort code"),
senderBankDetails.getSortCode(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
5);
// Zone D4 : Numéro de compte du donneur d'ordre
senderRecord +=
cfonbToolService.createZone(
I18n.get("D4 - Account number"),
senderBankDetails.getAccountNbr(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
11);
// Zone E : Identifiant du donneur d'ordre
senderRecord +=
cfonbToolService.createZone(
"E",
getSenderEArea(),
cfonbToolService.STATUS_OPTIONAL,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
16);
// Zone F : Zone réservée
senderRecord +=
cfonbToolService.createZone(
"F", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 31);
// Zone G1 : Code établissement de la banque du donneur d'ordre
senderRecord +=
cfonbToolService.createZone(
I18n.get("G1 - Bank code"),
senderBankDetails.getBankCode(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
5);
// Zone G2 : Zone réservée
senderRecord +=
cfonbToolService.createZone(
"G2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 6);
cfonbToolService.toUpperCase(senderRecord);
cfonbToolService.testLength(senderRecord, NB_CHAR_PER_LINE);
return senderRecord;
} catch (Exception e) {
throw new AxelorException(
e,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.BANK_ORDER_WRONG_SENDER_RECORD) + ": " + e.getMessage(),
bankOrderSeq);
}
}
/**
* Method to create a recipient record for national transfer AFB160
*
* @param bankOrderLine
* @return
* @throws AxelorException
*/
protected String createDetailRecord(BankOrderLine bankOrderLine) throws AxelorException {
try {
BankDetails receiverBankDetails = bankOrderLine.getReceiverBankDetails();
// Zone A : Code enregistrement
String detailRecord =
cfonbToolService.createZone(
"A", "06", cfonbToolService.STATUS_MANDATORY, cfonbToolService.FORMAT_NUMERIC, 2);
// Zone B1 : Code opération
detailRecord +=
cfonbToolService.createZone(
"B1",
getB1Area(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
2);
// Zone B2 : Zone réservée
detailRecord +=
cfonbToolService.createZone(
"B2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 8);
// Zone B3 : Numéro d'émetteur ou d'identification pour le virement particulier code 22
// (Virement APL)
detailRecord +=
cfonbToolService.createZone(
"B3",
getB3Area(),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
6);
// Zone C1 : Référence
detailRecord +=
cfonbToolService.createZone(
I18n.get("C1 - Sequence"),
bankOrderLine.getSequence(),
cfonbToolService.STATUS_OPTIONAL,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
12);
// Zone C2 : Nom/Raison sociale du bénéficiaire
detailRecord +=
cfonbToolService.createZone(
I18n.get("C2 - Receiver company name"),
bankOrderLine.getReceiverCompany().getName(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
24);
// Zone D1 : Domiciliation
detailRecord +=
cfonbToolService.createZone(
I18n.get("D1 - Bank address"),
getDetailD1Area(bankOrderLine),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
24);
// Zone D2 : Déclaration à la balance des paiements
detailRecord +=
cfonbToolService.createZone(
"D2",
getDetailD2Area(bankOrderLine),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
8);
// Zone D3 : Code guichet de la banque qui tient le compte du bénéficiaire
detailRecord +=
cfonbToolService.createZone(
I18n.get("D3 - Sort code"),
receiverBankDetails.getSortCode(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
5);
// Zone D4 : Numéro de compte du bénéficiaire
detailRecord +=
cfonbToolService.createZone(
I18n.get("D4 - Account number"),
receiverBankDetails.getAccountNbr(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
11);
// Zone E : Montant du virement
detailRecord +=
cfonbToolService.createZone(
I18n.get("E - Bank order amount"),
getDetailEAreaAmount(bankOrderLine),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
16);
// Zone F : Libellé
detailRecord +=
cfonbToolService.createZone(
I18n.get("F - Receiver label"),
getDetailFArea(bankOrderLine),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
31);
// Zone G1 : Code établissement de la banque qui tient le compte du bénéficiaire
detailRecord +=
cfonbToolService.createZone(
"G1",
receiverBankDetails.getBankCode(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
5);
// Zone G2 : Zone réservée
detailRecord +=
cfonbToolService.createZone(
"G2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 6);
cfonbToolService.toUpperCase(detailRecord);
cfonbToolService.testLength(detailRecord, NB_CHAR_PER_LINE);
return detailRecord;
} catch (Exception e) {
throw new AxelorException(
e,
bankOrderLine,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.BANK_ORDER_WRONG_MAIN_DETAIL_RECORD) + ": " + e.getMessage(),
bankOrderLine.getSequence());
}
}
/**
* Method to create an optional further information record for national transfer AFB160
*
* @param bankOrderLine
* @return
* @throws AxelorException
*/
protected String createOptionalFurtherInformationRecord(BankOrderLine bankOrderLine)
throws AxelorException {
try {
BankDetails receiverBankDetails = bankOrderLine.getReceiverBankDetails();
// Zone A : Code enregistrement
String totalRecord =
cfonbToolService.createZone(
"A", "07", cfonbToolService.STATUS_MANDATORY, cfonbToolService.FORMAT_NUMERIC, 2);
// Zone B1 : Code opération
totalRecord +=
cfonbToolService.createZone(
"B1",
getB1Area(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
2);
// Zone B2 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"B2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 8);
// Zone B3 : Numéro d'émetteur ou d'identification pour le virement particulier code 22
// (Virement APL)
totalRecord +=
cfonbToolService.createZone(
"B3",
getB3Area(),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
6);
// Zone C1 : Référence
totalRecord +=
cfonbToolService.createZone(
I18n.get("C1 - Sequence"),
bankOrderLine.getSequence(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
12);
// Zone C2 : Nom/Raison sociale du bénéficiaire
totalRecord +=
cfonbToolService.createZone(
I18n.get("C2 - Receiver company name"),
bankOrderLine.getReceiverCompany().getName(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
24);
// Zone D1 : Domiciliation
if (bankOrderLine.getReceiverBankDetails().getBankAddress() == null) {
throw new AxelorException(
I18n.get(IExceptionMessage.BANK_ORDER_RECEIVER_BANK_DETAILS_MISSING_BANK_ADDRESS),
TraceBackRepository.CATEGORY_MISSING_FIELD);
}
totalRecord +=
cfonbToolService.createZone(
I18n.get("D1 - Bank address"),
receiverBankDetails.getBankAddress().getAddress(),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
24);
// Zone D2 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"D2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 8);
// Zone D3 : Code guichet de la banque qui tient le compte du bénéficiaire
totalRecord +=
cfonbToolService.createZone(
I18n.get("D3 - Sort code"),
receiverBankDetails.getSortCode(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
5);
// Zone D4 : Numéro de compte du bénéficiaire
totalRecord +=
cfonbToolService.createZone(
I18n.get("D4 - Account number"),
receiverBankDetails.getAccountNbr(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
11);
// Zone E : Montant du virement
totalRecord +=
cfonbToolService.createZone(
I18n.get("E - Bank order amount"),
bankOrderLine.getBankOrderAmount(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
16);
// Zone F : Libellé complémentaire
totalRecord +=
cfonbToolService.createZone(
I18n.get("F - Receiver label"),
bankOrderLine.getPaymentReasonLine1(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
31);
// Zone G1 : Code établissement de la banque qui tient le compte du bénéficiaire
totalRecord +=
cfonbToolService.createZone(
I18n.get("G1 - Bank establisment code"),
receiverBankDetails.getBankCode(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
5);
// Zone G2 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"G2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 6);
cfonbToolService.toUpperCase(totalRecord);
cfonbToolService.testLength(totalRecord, NB_CHAR_PER_LINE);
return totalRecord;
} catch (Exception e) {
throw new AxelorException(
e,
bankOrderLine,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.BANK_ORDER_WRONG_FURTHER_INFORMATION_DETAIL_RECORD)
+ ": "
+ e.getMessage(),
bankOrderLine.getSequence());
}
}
/**
* Method to create a total record for national transfer AFB160
*
* @param company
* @param dateTime
* @return
* @throws AxelorException
*/
protected String createTotalRecord() throws AxelorException {
try {
// Zone A : Code enregistrement
String totalRecord =
cfonbToolService.createZone(
"A", "08", cfonbToolService.STATUS_MANDATORY, cfonbToolService.FORMAT_NUMERIC, 2);
// Zone B1 : Code opération
totalRecord +=
cfonbToolService.createZone(
"B1",
getB1Area(),
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
2);
// Zone B2 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"B2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 8);
// Zone B3 : Numéro d'émetteur ou d'identification pour le virement particulier code 22
// (Virement APL)
totalRecord +=
cfonbToolService.createZone(
"B3",
getB3Area(),
cfonbToolService.STATUS_DEPENDENT,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
6);
// Zone C1 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"C1",
"",
cfonbToolService.STATUS_NOT_USED,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
12);
// Zone C2 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"C2",
"",
cfonbToolService.STATUS_NOT_USED,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
24);
// Zone D1 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"D1",
"",
cfonbToolService.STATUS_NOT_USED,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
24);
// Zone D2 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"D2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 8);
// Zone D3 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"D3", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 5);
// Zone D4 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"D4",
"",
cfonbToolService.STATUS_NOT_USED,
cfonbToolService.FORMAT_ALPHA_NUMERIC,
11);
// Zone E : Montant du virement
totalRecord +=
cfonbToolService.createZone(
"E",
arithmeticTotal,
cfonbToolService.STATUS_MANDATORY,
cfonbToolService.FORMAT_NUMERIC,
16);
// Zone F : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"F", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 31);
// Zone G1 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"G1", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 5);
// Zone G2 : Zone réservée
totalRecord +=
cfonbToolService.createZone(
"G2", "", cfonbToolService.STATUS_NOT_USED, cfonbToolService.FORMAT_ALPHA_NUMERIC, 6);
cfonbToolService.toUpperCase(totalRecord);
cfonbToolService.testLength(totalRecord, NB_CHAR_PER_LINE);
return totalRecord;
} catch (Exception e) {
throw new AxelorException(
e,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.BANK_ORDER_WRONG_TOTAL_RECORD) + ": " + e.getMessage(),
bankOrderSeq);
}
}
}

Some files were not shown because too many files have changed in this diff Show More