FINERACT-1926: External owner accounting

This commit is contained in:
Adam Saghy
2023-06-12 11:24:31 +02:00
committed by Arnold Gálovics
parent 437b8e9cae
commit 582f36ad34
416 changed files with 1952 additions and 613 deletions
@@ -21,6 +21,7 @@ dependencies {
implementation(project(':custom:acme:note:service'))
implementation('org.springframework.boot:spring-boot-starter')
testImplementation(project(':fineract-core'))
testImplementation(project(':fineract-loan'))
testImplementation(project(':fineract-provider'))
testImplementation('org.springframework.boot:spring-boot-starter-jdbc')
testImplementation('org.springframework.boot:spring-boot-starter-data-jpa')
+7
View File
@@ -23,6 +23,9 @@ dependencies {
// Note that we never use 'api', because Fineract at least currently is a simple monolithic application ("WAR"), not a library.
// We also (normally should have) no need to ever use 'compileOnly'.
implementation(
project(path: ':fineract-avro-schemas')
)
// implementation dependencies are directly used (compiled against) in src/main (and src/test)
//
@@ -30,8 +33,11 @@ dependencies {
'org.springframework.boot:spring-boot-starter-web',
'org.springframework.boot:spring-boot-starter-security',
'org.springframework.boot:spring-boot-starter-validation',
'org.springframework.boot:spring-boot-starter-batch',
'org.springframework.batch:spring-batch-integration',
'jakarta.ws.rs:jakarta.ws.rs-api',
'org.glassfish.jersey.media:jersey-media-multipart',
'org.apache.avro:avro',
'com.google.guava:guava',
'com.google.code.gson:gson',
@@ -49,6 +55,7 @@ dependencies {
'org.mapstruct:mapstruct',
'io.github.resilience4j:resilience4j-spring-boot2',
'org.apache.httpcomponents:httpcore',
)
implementation ('org.springframework.boot:spring-boot-starter-data-jpa') {
exclude group: 'org.hibernate'
@@ -32,11 +32,10 @@ import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.loanaccount.guarantor.command.GuarantorCommand;
import org.springframework.stereotype.Component;
/**
* Implementation of {@link FromApiJsonDeserializer} for {@link GuarantorCommand}'s.
* Implementation of {@link FromApiJsonDeserializer}
*/
@Component
@RequiredArgsConstructor
@@ -31,11 +31,10 @@ import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.loanaccount.guarantor.command.GuarantorCommand;
import org.springframework.stereotype.Component;
/**
* Implementation of {@link FromApiJsonDeserializer} for {@link GuarantorCommand}'s.
* Implementation of {@link FromApiJsonDeserializer}
*/
@Component
@RequiredArgsConstructor
@@ -18,7 +18,6 @@
*/
package org.apache.fineract.accounting.glaccount.service;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingConstants;
@@ -37,7 +36,6 @@ import org.apache.fineract.accounting.glaccount.exception.GLAccountInvalidUpdate
import org.apache.fineract.accounting.glaccount.exception.GLAccountNotFoundException;
import org.apache.fineract.accounting.glaccount.exception.InvalidParentGLAccountHeadException;
import org.apache.fineract.accounting.glaccount.serialization.GLAccountCommandFromApiJsonDeserializer;
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
@@ -148,9 +146,9 @@ public class GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccount
**/
if (changesOnly.containsKey(GLAccountJsonInputParams.USAGE.getValue())) {
if (glAccount.isHeaderAccount()) {
final List<JournalEntry> journalEntriesForAccount = this.glJournalEntryRepository
.findFirstJournalEntryForAccount(glAccountId);
if (journalEntriesForAccount.size() > 0) {
final boolean journalEntriesForAccountExist = this.glJournalEntryRepository
.exists((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("glAccountId"), glAccountId));
if (journalEntriesForAccountExist) {
throw new GLAccountInvalidUpdateException(GlAccountInvalidUpdateReason.TRANSANCTIONS_LOGGED, glAccountId);
}
}
@@ -193,8 +191,9 @@ public class GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccount
}
// does this account have transactions logged against it
final List<JournalEntry> journalEntriesForAccount = this.glJournalEntryRepository.findFirstJournalEntryForAccount(glAccountId);
if (journalEntriesForAccount.size() > 0) {
final boolean journalEntriesForAccountExist = this.glJournalEntryRepository
.exists((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("glAccountId"), glAccountId));
if (journalEntriesForAccountExist) {
throw new GLAccountInvalidDeleteException(GlAccountInvalidDeleteReason.TRANSANCTIONS_LOGGED, glAccountId);
}
this.glAccountRepository.delete(glAccount);
@@ -0,0 +1,106 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
@Mapper(config = MapstructMapperConfig.class)
public interface JournalEntryMapper {
@Mapping(target = "id", source = "id")
@Mapping(target = "officeId", source = "office.id")
@Mapping(target = "officeName", source = "office.name")
@Mapping(target = "glAccountId", source = "glAccount.id")
@Mapping(target = "glAccountCode", source = "glAccount.glCode")
@Mapping(target = "glAccountName", source = "glAccount.name")
@Mapping(target = "glAccountType", source = "glAccount.type", qualifiedByName = "glAccountType")
@Mapping(target = "transactionDate", source = "transactionDate")
@Mapping(target = "entryType", source = "type", qualifiedByName = "journalEntryType")
@Mapping(target = "amount", source = "amount")
@Mapping(target = "entityType", source = "entityType", qualifiedByName = "entityType")
@Mapping(target = "entityId", source = "entityId")
@Mapping(target = "submittedOnDate", source = "submittedOnDate")
@Mapping(target = "transactionId", source = "transactionId")
@Mapping(target = "currency", source = "currencyCode")
@Mapping(target = "manualEntry", source = "manualEntry")
@Mapping(target = "reversed", source = "reversed")
@Mapping(target = "referenceNumber", source = "referenceNumber")
@Mapping(target = "paymentTypeId", source = "paymentDetail.paymentType.id")
@Mapping(target = "accountNumber", source = "paymentDetail.accountNumber")
@Mapping(target = "checkNumber", source = "paymentDetail.checkNumber")
@Mapping(target = "routingCode", source = "paymentDetail.routingCode")
@Mapping(target = "receiptNumber", source = "paymentDetail.receiptNumber")
@Mapping(target = "bankNumber", source = "paymentDetail.bankNumber")
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "createdByUserId", ignore = true)
@Mapping(target = "createdByUserName", ignore = true)
@Mapping(target = "comments", ignore = true)
@Mapping(target = "officeRunningBalance", ignore = true)
@Mapping(target = "organizationRunningBalance", ignore = true)
@Mapping(target = "runningBalanceComputed", ignore = true)
@Mapping(target = "rowIndex", ignore = true)
@Mapping(target = "dateFormat", ignore = true)
@Mapping(target = "locale", ignore = true)
@Mapping(target = "credits", ignore = true)
@Mapping(target = "debits", ignore = true)
@Mapping(target = "transactionDetails", ignore = true)
@Mapping(target = "savingTransactionId", ignore = true)
JournalEntryData map(JournalEntry journalEntry);
@Named("entityType")
default PortfolioProductType mapEntityType(Integer entityTypeId) {
return PortfolioProductType.fromInt(entityTypeId);
}
@Named("glAccountType")
default GLAccountType mapGlAccountType(Integer typeId) {
return GLAccountType.fromInt(typeId);
}
@Named("journalEntryType")
default JournalEntryType mapJournalEntryType(Integer typeId) {
return JournalEntryType.fromInt(typeId);
}
default EnumOptionData mapGlAccountType(GLAccountType accountType) {
return new EnumOptionData((long) accountType.getValue(), accountType.getCode(), accountType.name());
}
default EnumOptionData mapJournalEntryType(JournalEntryType journalEntryType) {
return new EnumOptionData((long) journalEntryType.getValue(), journalEntryType.getCode(), journalEntryType.name());
}
default EnumOptionData mapEntityType(PortfolioProductType entityType) {
return new EnumOptionData((long) entityType.getValue(), entityType.getCode(), entityType.name());
}
default CurrencyData mapCurrency(String currencyCode) {
return new CurrencyData(currencyCode);
}
}
@@ -21,7 +21,7 @@ package org.apache.fineract.accounting.journalentry.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import lombok.Getter;
import lombok.Data;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
@@ -31,55 +31,55 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
*
* Note: no getter/setters required as google will produce json from fields of object.
*/
@Getter
@Data
public class JournalEntryData {
private final Long id;
private final Long officeId;
private Long id;
private Long officeId;
@SuppressWarnings("unused")
private final String officeName;
private String officeName;
@SuppressWarnings("unused")
private final String glAccountName;
private final Long glAccountId;
private String glAccountName;
private Long glAccountId;
@SuppressWarnings("unused")
private final String glAccountCode;
private final EnumOptionData glAccountType;
private String glAccountCode;
private EnumOptionData glAccountType;
@SuppressWarnings("unused")
private final LocalDate transactionDate;
private final EnumOptionData entryType;
private final BigDecimal amount;
private LocalDate transactionDate;
private EnumOptionData entryType;
private BigDecimal amount;
@SuppressWarnings("unused")
private final CurrencyData currency;
private final String transactionId;
private CurrencyData currency;
private String transactionId;
@SuppressWarnings("unused")
private final Boolean manualEntry;
private Boolean manualEntry;
@SuppressWarnings("unused")
private final EnumOptionData entityType;
private EnumOptionData entityType;
@SuppressWarnings("unused")
private final Long entityId;
private Long entityId;
@SuppressWarnings("unused")
private final Long createdByUserId;
private Long createdByUserId;
@SuppressWarnings("unused")
private final LocalDate createdDate;
private LocalDate createdDate;
@SuppressWarnings("unused")
private final String createdByUserName;
private String createdByUserName;
@SuppressWarnings("unused")
private final String comments;
private String comments;
@SuppressWarnings("unused")
private final Boolean reversed;
private Boolean reversed;
@SuppressWarnings("unused")
private final String referenceNumber;
private String referenceNumber;
@SuppressWarnings("unused")
private final BigDecimal officeRunningBalance;
private BigDecimal officeRunningBalance;
@SuppressWarnings("unused")
private final BigDecimal organizationRunningBalance;
private BigDecimal organizationRunningBalance;
@SuppressWarnings("unused")
private final Boolean runningBalanceComputed;
private Boolean runningBalanceComputed;
@SuppressWarnings("unused")
private final TransactionDetailData transactionDetails;
private TransactionDetailData transactionDetails;
@SuppressWarnings("unused")
private final LocalDate submittedOnDate;
private LocalDate submittedOnDate;
// import fields
private transient Integer rowIndex;
@@ -96,6 +96,8 @@ public class JournalEntryData {
private String bankNumber;
private transient Long savingTransactionId;
public JournalEntryData() {}
// for opening bal bulk import
public JournalEntryData(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
List<CreditDebit> debits, String locale, String dateFormat) {
@@ -139,18 +141,6 @@ public class JournalEntryData {
this.submittedOnDate = null;
}
public static JournalEntryData importInstance(Long officeId, LocalDate transactionDate, String currencyCode, Long paymentTypeId,
Integer rowIndex, List<CreditDebit> credits, List<CreditDebit> debits, String accountNumber, String checkNumber,
String routingCode, String receiptNumber, String bankNumber, String comments, String locale, String dateFormat) {
return new JournalEntryData(officeId, transactionDate, currencyCode, paymentTypeId, rowIndex, credits, debits, accountNumber,
checkNumber, routingCode, receiptNumber, bankNumber, comments, locale, dateFormat);
}
public static JournalEntryData importInstance1(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
List<CreditDebit> debits, String locale, String dateFormat) {
return new JournalEntryData(officeId, transactionDate, currencyCode, credits, debits, locale, dateFormat);
}
private JournalEntryData(Long officeId, LocalDate transactionDate, String currencyCode, Long paymentTypeId, Integer rowIndex,
List<CreditDebit> credits, List<CreditDebit> debits, String accountNumber, String checkNumber, String routingCode,
String receiptNumber, String bankNumber, String comments, String locale, String dateFormat) {
@@ -195,23 +185,6 @@ public class JournalEntryData {
this.transactionDetails = null;
}
public Integer getRowIndex() {
return rowIndex;
}
public LocalDate getTransactionDate() {
return transactionDate;
}
public void addDebits(CreditDebit debit) {
this.debits.add(debit);
}
public void addCredits(CreditDebit credit) {
this.credits.add(credit);
}
public JournalEntryData(final Long id, final Long officeId, final String officeName, final String glAccountName, final Long glAccountId,
final String glAccountCode, final EnumOptionData glAccountClassification, final LocalDate transactionDate,
final EnumOptionData entryType, final BigDecimal amount, final String transactionId, final Boolean manualEntry,
@@ -247,6 +220,18 @@ public class JournalEntryData {
this.currency = currency;
}
public static JournalEntryData importInstance(Long officeId, LocalDate transactionDate, String currencyCode, Long paymentTypeId,
Integer rowIndex, List<CreditDebit> credits, List<CreditDebit> debits, String accountNumber, String checkNumber,
String routingCode, String receiptNumber, String bankNumber, String comments, String locale, String dateFormat) {
return new JournalEntryData(officeId, transactionDate, currencyCode, paymentTypeId, rowIndex, credits, debits, accountNumber,
checkNumber, routingCode, receiptNumber, bankNumber, comments, locale, dateFormat);
}
public static JournalEntryData importInstance1(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
List<CreditDebit> debits, String locale, String dateFormat) {
return new JournalEntryData(officeId, transactionDate, currencyCode, credits, debits, locale, dateFormat);
}
public static JournalEntryData fromGLAccountData(final GLAccountData glAccountData) {
final Long id = null;
@@ -279,4 +264,21 @@ public class JournalEntryData {
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
runningBalanceComputed, transactionDetailData, currency);
}
public Integer getRowIndex() {
return rowIndex;
}
public LocalDate getTransactionDate() {
return transactionDate;
}
public void addDebits(CreditDebit debit) {
this.debits.add(debit);
}
public void addCredits(CreditDebit credit) {
this.credits.add(credit);
}
}
@@ -26,17 +26,16 @@ import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.client.domain.ClientTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
@Entity
@Getter
@Table(name = "acc_gl_journal_entry")
public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom {
@@ -62,17 +61,14 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom {
@Column(name = "transaction_id", nullable = false, length = 50)
private String transactionId;
@ManyToOne
@JoinColumn(name = "loan_transaction_id", nullable = false)
private LoanTransaction loanTransaction;
@Column(name = "loan_transaction_id")
private Long loanTransactionId;
@ManyToOne
@JoinColumn(name = "savings_transaction_id", nullable = false)
private SavingsAccountTransaction savingsTransaction;
@Column(name = "savings_transaction_id")
private Long savingsTransactionId;
@ManyToOne
@JoinColumn(name = "client_transaction_id", nullable = false)
private ClientTransaction clientTransaction;
@Column(name = "client_transaction_id")
private Long clientTransactionId;
@Column(name = "share_transaction_id")
private Long shareTransactionId;
@@ -107,16 +103,6 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom {
@Column(name = "submitted_on_date", nullable = false)
private LocalDate submittedOnDate;
public static JournalEntry createNew(final Office office, final PaymentDetail paymentDetail, final GLAccount glAccount,
final String currencyCode, final String transactionId, final boolean manualEntry, final LocalDate transactionDate,
final JournalEntryType journalEntryType, final BigDecimal amount, final String description, final Integer entityType,
final Long entityId, final String referenceNumber, final LoanTransaction loanTransaction,
final SavingsAccountTransaction savingsTransaction, final ClientTransaction clientTransaction, Long shareTransactionId) {
return new JournalEntry(office, paymentDetail, glAccount, currencyCode, transactionId, manualEntry, transactionDate,
journalEntryType.getValue(), amount, description, entityType, entityId, referenceNumber, loanTransaction,
savingsTransaction, clientTransaction, shareTransactionId);
}
protected JournalEntry() {
//
}
@@ -124,8 +110,7 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom {
public JournalEntry(final Office office, final PaymentDetail paymentDetail, final GLAccount glAccount, final String currencyCode,
final String transactionId, final boolean manualEntry, final LocalDate transactionDate, final Integer type,
final BigDecimal amount, final String description, final Integer entityType, final Long entityId, final String referenceNumber,
final LoanTransaction loanTransaction, final SavingsAccountTransaction savingsTransaction,
final ClientTransaction clientTransaction, final Long shareTransactionId) {
final Long loanTransactionId, final Long savingsTransactionId, final Long clientTransactionId, final Long shareTransactionId) {
this.office = office;
this.glAccount = glAccount;
this.reversalJournalEntry = null;
@@ -140,42 +125,28 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom {
this.entityId = entityId;
this.referenceNumber = referenceNumber;
this.currencyCode = currencyCode;
this.loanTransaction = loanTransaction;
this.savingsTransaction = savingsTransaction;
this.clientTransaction = clientTransaction;
this.loanTransactionId = loanTransactionId;
this.savingsTransactionId = savingsTransactionId;
this.clientTransactionId = clientTransactionId;
this.paymentDetail = paymentDetail;
this.shareTransactionId = shareTransactionId;
this.submittedOnDate = DateUtils.getBusinessLocalDate();
}
public static JournalEntry createNew(final Office office, final PaymentDetail paymentDetail, final GLAccount glAccount,
final String currencyCode, final String transactionId, final boolean manualEntry, final LocalDate transactionDate,
final JournalEntryType journalEntryType, final BigDecimal amount, final String description, final Integer entityType,
final Long entityId, final String referenceNumber, final Long loanTransaction, final Long savingsTransaction,
final Long clientTransaction, Long shareTransactionId) {
return new JournalEntry(office, paymentDetail, glAccount, currencyCode, transactionId, manualEntry, transactionDate,
journalEntryType.getValue(), amount, description, entityType, entityId, referenceNumber, loanTransaction,
savingsTransaction, clientTransaction, shareTransactionId);
}
public boolean isDebitEntry() {
return JournalEntryType.DEBIT.getValue().equals(this.type);
}
public Integer getType() {
return this.type;
}
public Office getOffice() {
return this.office;
}
public GLAccount getGlAccount() {
return this.glAccount;
}
public LocalDate getTransactionDate() {
return this.transactionDate;
}
public BigDecimal getAmount() {
return this.amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public void setReversalJournalEntry(final JournalEntry reversalJournalEntry) {
this.reversalJournalEntry = reversalJournalEntry;
}
@@ -183,57 +154,4 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom {
public void setReversed(final boolean reversed) {
this.reversed = reversed;
}
public String getReferenceNumber() {
return this.referenceNumber;
}
public String getCurrencyCode() {
return this.currencyCode;
}
public LoanTransaction getLoanTransaction() {
return this.loanTransaction;
}
public SavingsAccountTransaction getSavingsTransaction() {
return this.savingsTransaction;
}
public PaymentDetail getPaymentDetails() {
return this.paymentDetail;
}
public String getTransactionId() {
return transactionId;
}
public ClientTransaction getClientTransaction() {
return this.clientTransaction;
}
public Long getEntityId() {
return this.entityId;
}
public Integer getEntityType() {
return this.entityType;
}
public Long getShareTransactionId() {
return this.shareTransactionId;
}
public boolean isReversed() {
return this.reversed;
}
public String getDescription() {
return this.description;
}
public LocalDate getSubmittedOnDate() {
return this.submittedOnDate;
}
}
@@ -24,8 +24,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface JournalEntryRepository
extends JpaRepository<JournalEntry, Long>, JpaSpecificationExecutor<JournalEntry>, JournalEntryRepositoryCustom {
public interface JournalEntryRepository extends JpaRepository<JournalEntry, Long>, JpaSpecificationExecutor<JournalEntry> {
@Query("select journalEntry from JournalEntry journalEntry where journalEntry.transactionId= :transactionId and journalEntry.reversed=false and journalEntry.manualEntry=true")
List<JournalEntry> findUnReversedManualJournalEntriesByTransactionId(@Param("transactionId") String transactionId);
@@ -42,15 +41,4 @@ public interface JournalEntryRepository
@Query("select journalEntry from JournalEntry journalEntry where journalEntry.transactionId= :transactionId and journalEntry.reversed=false and journalEntry.entityType = :entityType")
List<JournalEntry> findJournalEntries(@Param("transactionId") String transactionId, @Param("entityType") Integer entityType);
/*
* @Query("select journalEntry from JournalEntry journalEntry where glAccount.id= :accountId and transactionId= :transactionId and transactionDate= :transactionDate"
* +
* " and type= :journalEntryType and entityType=1 and entityId= :loanId and loanTransaction.id= :loanTransactionId"
* ) JournalEntry findLOANJournalEntryWith(@Param("accountId") Long accountId, @Param("transactionId") String
* transactionId,
*
* @Param("transactionDate") Date transactionDate, @Param("journalEntryType") Integer journalEntryType,
*
* @Param("loanId") Long loanId, @Param("loanTransactionId") Long loanTransactionId);
*/
}
@@ -0,0 +1,114 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.batch.exception;
import com.google.gson.Gson;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException;
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
import org.apache.fineract.infrastructure.core.exceptionmapper.PlatformApiDataValidationExceptionMapper;
import org.apache.fineract.infrastructure.core.exceptionmapper.PlatformDataIntegrityExceptionMapper;
import org.apache.fineract.infrastructure.core.exceptionmapper.PlatformDomainRuleExceptionMapper;
import org.apache.fineract.infrastructure.core.exceptionmapper.PlatformInternalServerExceptionMapper;
import org.apache.fineract.infrastructure.core.exceptionmapper.PlatformResourceNotFoundExceptionMapper;
import org.apache.fineract.infrastructure.core.exceptionmapper.UnsupportedParameterExceptionMapper;
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
import org.apache.http.HttpStatus;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.transaction.TransactionException;
/**
* Provides an Error Handler method that returns an object of type {@link ErrorInfo} to the CommandStrategy which raised
* the exception. This class uses various subclasses of RuntimeException to check the kind of exception raised and
* provide appropriate status and error codes for each one of the raised exception.
*
* @author Rishabh Shukla
* @see org.apache.fineract.batch.command.CommandStrategy
*/
public final class ErrorHandler {
private static final Gson JSON_HELPER = GoogleGsonSerializerHelper.createGsonBuilder(true).create();
private static final LinkedHashMap<Class<? extends Exception>, Function<RuntimeException, ErrorInfo>> EXCEPTION_HANDLERS = new LinkedHashMap<>();
private static final Map.Entry<Class<? extends Exception>, Function<RuntimeException, ErrorInfo>> DEFAULT_ERROR_HANDLER = Map.entry(
RuntimeException.class,
runtimeException -> new ErrorInfo(500, 9999, "{\"Exception\": %s}".formatted(runtimeException.getMessage())));
static {
EXCEPTION_HANDLERS.put(AbstractPlatformResourceNotFoundException.class,
runtimeException -> handleException(runtimeException, new PlatformResourceNotFoundExceptionMapper(), 1001));
EXCEPTION_HANDLERS.put(UnsupportedParameterException.class,
runtimeException -> handleException(runtimeException, new UnsupportedParameterExceptionMapper(), 2001));
EXCEPTION_HANDLERS.put(PlatformApiDataValidationException.class,
runtimeException -> handleException(runtimeException, new PlatformApiDataValidationExceptionMapper(), 2002));
EXCEPTION_HANDLERS.put(PlatformDataIntegrityException.class,
runtimeException -> handleException(runtimeException, new PlatformDataIntegrityExceptionMapper(), 3001));
EXCEPTION_HANDLERS.put(AbstractPlatformDomainRuleException.class,
runtimeException -> handleException(runtimeException, new PlatformDomainRuleExceptionMapper(), 9999));
EXCEPTION_HANDLERS.put(TransactionException.class, runtimeException -> new ErrorInfo(HttpStatus.SC_BAD_REQUEST, 4001,
"{\"Exception\": %s}".formatted(runtimeException.getMessage())));
EXCEPTION_HANDLERS.put(PlatformInternalServerException.class,
runtimeException -> handleException(runtimeException, new PlatformInternalServerExceptionMapper(), 5001));
EXCEPTION_HANDLERS.put(NonTransientDataAccessException.class, runtimeException -> new ErrorInfo(HttpStatus.SC_BAD_REQUEST, 4002,
"{\"Exception\": %s}".formatted(runtimeException.getMessage())));
EXCEPTION_HANDLERS.put(DEFAULT_ERROR_HANDLER.getKey(), DEFAULT_ERROR_HANDLER.getValue());
}
private ErrorHandler() {}
public static void registerNewErrorHandler(final Class<? extends RuntimeException> exceptionClass, final ExceptionMapper mapper,
int errorCode) {
LinkedHashMap<Class<? extends Exception>, Function<RuntimeException, ErrorInfo>> newHandlers = new LinkedHashMap<>();
newHandlers.put(exceptionClass, runtimeException -> handleException(runtimeException, mapper, errorCode));
EXCEPTION_HANDLERS.forEach(newHandlers::putIfAbsent);
}
public static void registerNewErrorHandler(final Class<? extends RuntimeException> exceptionClass,
Function<RuntimeException, ErrorInfo> function) {
LinkedHashMap<Class<? extends Exception>, Function<RuntimeException, ErrorInfo>> newHandlers = new LinkedHashMap<>();
newHandlers.put(exceptionClass, function);
EXCEPTION_HANDLERS.forEach(newHandlers::putIfAbsent);
}
private static ErrorInfo handleException(final RuntimeException exception, final ExceptionMapper mapper, final int errorCode) {
final Response response = mapper.toResponse(exception);
final String errorBody = JSON_HELPER.toJson(response.getEntity());
return new ErrorInfo(response.getStatus(), errorCode, errorBody);
}
/**
* Returns an object of ErrorInfo type containing the information regarding the raised error.
*
* @param exception
* @return ErrorInfo
*/
public static ErrorInfo handler(final RuntimeException exception) {
Map.Entry<Class<? extends Exception>, Function<RuntimeException, ErrorInfo>> errorhandler = EXCEPTION_HANDLERS.entrySet().stream()
.filter(e -> e.getKey().isAssignableFrom(exception.getClass())).findFirst().orElse(DEFAULT_ERROR_HANDLER);
return errorhandler.getValue().apply(exception);
}
}

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