Merge remote-tracking branch 'self-hosted/feature/account-charges' into feature/Backend-DSC-MFA
This commit is contained in:
+109
-108
@@ -1,108 +1,109 @@
|
||||
/**
|
||||
* 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 java.time.OffsetDateTime;
|
||||
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.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.PortfolioProductType;
|
||||
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", expression = "java(journalEntry.getCreatedDate().orElse(null))")
|
||||
@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)
|
||||
@Mapping(target = "externalAssetOwner", 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);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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 java.time.OffsetDateTime;
|
||||
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.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.PortfolioProductType;
|
||||
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", expression = "java(journalEntry.getCreatedDate().orElse(null))")
|
||||
@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)
|
||||
@Mapping(target = "externalAssetOwner", ignore = true)
|
||||
@Mapping(target = "savingsAccountNo", 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);
|
||||
}
|
||||
}
|
||||
|
||||
+291
-287
@@ -1,287 +1,291 @@
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Immutable object representing a General Ledger Account
|
||||
*
|
||||
* Note: no getter/setters required as google will produce json from fields of object.
|
||||
*/
|
||||
@Data
|
||||
public class JournalEntryData {
|
||||
|
||||
private Long id;
|
||||
private Long officeId;
|
||||
@SuppressWarnings("unused")
|
||||
private String officeName;
|
||||
@SuppressWarnings("unused")
|
||||
private String glAccountName;
|
||||
private Long glAccountId;
|
||||
@SuppressWarnings("unused")
|
||||
private String glAccountCode;
|
||||
private EnumOptionData glAccountType;
|
||||
@SuppressWarnings("unused")
|
||||
private LocalDate transactionDate;
|
||||
private EnumOptionData entryType;
|
||||
private BigDecimal amount;
|
||||
@SuppressWarnings("unused")
|
||||
private CurrencyData currency;
|
||||
private String transactionId;
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean manualEntry;
|
||||
@SuppressWarnings("unused")
|
||||
private EnumOptionData entityType;
|
||||
@SuppressWarnings("unused")
|
||||
private Long entityId;
|
||||
@SuppressWarnings("unused")
|
||||
private Long createdByUserId;
|
||||
@SuppressWarnings("unused")
|
||||
private OffsetDateTime createdDate;
|
||||
@SuppressWarnings("unused")
|
||||
private String createdByUserName;
|
||||
@SuppressWarnings("unused")
|
||||
private String comments;
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean reversed;
|
||||
@SuppressWarnings("unused")
|
||||
private String referenceNumber;
|
||||
@SuppressWarnings("unused")
|
||||
private BigDecimal officeRunningBalance;
|
||||
@SuppressWarnings("unused")
|
||||
private BigDecimal organizationRunningBalance;
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean runningBalanceComputed;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private TransactionDetailData transactionDetails;
|
||||
@SuppressWarnings("unused")
|
||||
private OffsetDateTime submittedOnDate;
|
||||
|
||||
// import fields
|
||||
private transient Integer rowIndex;
|
||||
private String dateFormat;
|
||||
private String locale;
|
||||
private List<CreditDebit> credits;
|
||||
private List<CreditDebit> debits;
|
||||
private Long paymentTypeId;
|
||||
private String currencyCode;
|
||||
private String accountNumber;
|
||||
private String checkNumber;
|
||||
private String routingCode;
|
||||
private String receiptNumber;
|
||||
private String bankNumber;
|
||||
private String externalAssetOwner;
|
||||
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) {
|
||||
this.officeId = officeId;
|
||||
this.dateFormat = dateFormat;
|
||||
this.locale = locale;
|
||||
this.transactionDate = transactionDate;
|
||||
this.currencyCode = currencyCode;
|
||||
this.credits = credits;
|
||||
this.debits = debits;
|
||||
this.rowIndex = null;
|
||||
this.paymentTypeId = null;
|
||||
this.accountNumber = null;
|
||||
this.checkNumber = null;
|
||||
this.routingCode = null;
|
||||
this.receiptNumber = null;
|
||||
this.bankNumber = null;
|
||||
this.comments = null;
|
||||
this.id = null;
|
||||
this.officeName = null;
|
||||
this.glAccountName = null;
|
||||
this.glAccountId = null;
|
||||
this.glAccountCode = null;
|
||||
this.glAccountType = null;
|
||||
this.entryType = null;
|
||||
this.amount = null;
|
||||
this.currency = null;
|
||||
this.transactionId = null;
|
||||
this.manualEntry = null;
|
||||
this.entityType = null;
|
||||
this.entityId = null;
|
||||
this.createdByUserId = null;
|
||||
this.createdDate = null;
|
||||
this.createdByUserName = null;
|
||||
this.reversed = null;
|
||||
this.referenceNumber = null;
|
||||
this.officeRunningBalance = null;
|
||||
this.organizationRunningBalance = null;
|
||||
this.runningBalanceComputed = null;
|
||||
this.transactionDetails = null;
|
||||
this.submittedOnDate = null;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
this.officeId = officeId;
|
||||
this.dateFormat = dateFormat;
|
||||
this.locale = locale;
|
||||
this.transactionDate = transactionDate;
|
||||
this.currencyCode = currencyCode;
|
||||
this.rowIndex = rowIndex;
|
||||
this.credits = credits;
|
||||
this.debits = debits;
|
||||
this.paymentTypeId = paymentTypeId;
|
||||
this.accountNumber = accountNumber;
|
||||
this.checkNumber = checkNumber;
|
||||
this.routingCode = routingCode;
|
||||
this.receiptNumber = receiptNumber;
|
||||
this.bankNumber = bankNumber;
|
||||
this.comments = comments;
|
||||
this.id = null;
|
||||
this.officeName = null;
|
||||
this.glAccountName = null;
|
||||
this.glAccountId = null;
|
||||
this.glAccountCode = null;
|
||||
this.glAccountType = null;
|
||||
this.entryType = null;
|
||||
this.amount = null;
|
||||
this.currency = null;
|
||||
this.transactionId = null;
|
||||
this.manualEntry = null;
|
||||
this.entityType = null;
|
||||
this.entityId = null;
|
||||
this.createdByUserId = null;
|
||||
this.submittedOnDate = null;
|
||||
this.createdDate = null;
|
||||
this.createdByUserName = null;
|
||||
this.reversed = null;
|
||||
this.referenceNumber = null;
|
||||
this.officeRunningBalance = null;
|
||||
this.organizationRunningBalance = null;
|
||||
this.runningBalanceComputed = null;
|
||||
this.transactionDetails = null;
|
||||
}
|
||||
|
||||
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,
|
||||
final EnumOptionData entityType, final Long entityId, final Long createdByUserId, final OffsetDateTime submittedOnDate,
|
||||
final String createdByUserName, final String comments, final Boolean reversed, final String referenceNumber,
|
||||
final BigDecimal officeRunningBalance, final BigDecimal organizationRunningBalance, final Boolean runningBalanceComputed,
|
||||
final TransactionDetailData transactionDetailData, final CurrencyData currency, final String externalAssetOwner) {
|
||||
this.id = id;
|
||||
this.officeId = officeId;
|
||||
this.officeName = officeName;
|
||||
this.glAccountName = glAccountName;
|
||||
this.glAccountId = glAccountId;
|
||||
this.glAccountCode = glAccountCode;
|
||||
this.glAccountType = glAccountClassification;
|
||||
this.transactionDate = transactionDate;
|
||||
this.entryType = entryType;
|
||||
this.amount = amount;
|
||||
this.transactionId = transactionId;
|
||||
this.manualEntry = manualEntry;
|
||||
this.entityType = entityType;
|
||||
this.entityId = entityId;
|
||||
this.createdByUserId = createdByUserId;
|
||||
this.createdDate = submittedOnDate;
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
this.createdByUserName = createdByUserName;
|
||||
this.comments = comments;
|
||||
this.reversed = reversed;
|
||||
this.referenceNumber = referenceNumber;
|
||||
this.officeRunningBalance = officeRunningBalance;
|
||||
this.organizationRunningBalance = organizationRunningBalance;
|
||||
this.runningBalanceComputed = runningBalanceComputed;
|
||||
this.transactionDetails = transactionDetailData;
|
||||
this.currency = currency;
|
||||
this.externalAssetOwner = externalAssetOwner;
|
||||
}
|
||||
|
||||
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;
|
||||
final Long officeId = null;
|
||||
final String officeName = null;
|
||||
final String glAccountName = glAccountData.getName();
|
||||
final Long glAccountId = glAccountData.getId();
|
||||
final String glAccountCode = glAccountData.getGlCode();
|
||||
final EnumOptionData glAccountClassification = glAccountData.getType();
|
||||
final LocalDate transactionDate = null;
|
||||
final EnumOptionData entryType = null;
|
||||
final BigDecimal amount = null;
|
||||
final String transactionId = null;
|
||||
final Boolean manualEntry = null;
|
||||
final EnumOptionData entityType = null;
|
||||
final Long entityId = null;
|
||||
final Long createdByUserId = null;
|
||||
final OffsetDateTime submittedOnDate = null;
|
||||
final String createdByUserName = null;
|
||||
final String comments = null;
|
||||
final Boolean reversed = null;
|
||||
final String referenceNumber = null;
|
||||
final BigDecimal officeRunningBalance = null;
|
||||
final BigDecimal organizationRunningBalance = null;
|
||||
final Boolean runningBalanceComputed = null;
|
||||
final TransactionDetailData transactionDetailData = null;
|
||||
final CurrencyData currency = null;
|
||||
final String externalAssetOwner = null;
|
||||
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glAccountCode, glAccountClassification,
|
||||
transactionDate, entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
|
||||
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
|
||||
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Immutable object representing a General Ledger Account
|
||||
*
|
||||
* Note: no getter/setters required as google will produce json from fields of object.
|
||||
*/
|
||||
@Data
|
||||
public class JournalEntryData {
|
||||
|
||||
private Long id;
|
||||
private Long officeId;
|
||||
@SuppressWarnings("unused")
|
||||
private String officeName;
|
||||
@SuppressWarnings("unused")
|
||||
private String glAccountName;
|
||||
private Long glAccountId;
|
||||
@SuppressWarnings("unused")
|
||||
private String glAccountCode;
|
||||
private EnumOptionData glAccountType;
|
||||
@SuppressWarnings("unused")
|
||||
private LocalDate transactionDate;
|
||||
private EnumOptionData entryType;
|
||||
private BigDecimal amount;
|
||||
@SuppressWarnings("unused")
|
||||
private CurrencyData currency;
|
||||
private String transactionId;
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean manualEntry;
|
||||
@SuppressWarnings("unused")
|
||||
private EnumOptionData entityType;
|
||||
@SuppressWarnings("unused")
|
||||
private Long entityId;
|
||||
@SuppressWarnings("unused")
|
||||
private String savingsAccountNo;
|
||||
@SuppressWarnings("unused")
|
||||
private Long createdByUserId;
|
||||
@SuppressWarnings("unused")
|
||||
private OffsetDateTime createdDate;
|
||||
@SuppressWarnings("unused")
|
||||
private String createdByUserName;
|
||||
@SuppressWarnings("unused")
|
||||
private String comments;
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean reversed;
|
||||
@SuppressWarnings("unused")
|
||||
private String referenceNumber;
|
||||
@SuppressWarnings("unused")
|
||||
private BigDecimal officeRunningBalance;
|
||||
@SuppressWarnings("unused")
|
||||
private BigDecimal organizationRunningBalance;
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean runningBalanceComputed;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private TransactionDetailData transactionDetails;
|
||||
@SuppressWarnings("unused")
|
||||
private OffsetDateTime submittedOnDate;
|
||||
|
||||
// import fields
|
||||
private transient Integer rowIndex;
|
||||
private String dateFormat;
|
||||
private String locale;
|
||||
private List<CreditDebit> credits;
|
||||
private List<CreditDebit> debits;
|
||||
private Long paymentTypeId;
|
||||
private String currencyCode;
|
||||
private String accountNumber;
|
||||
private String checkNumber;
|
||||
private String routingCode;
|
||||
private String receiptNumber;
|
||||
private String bankNumber;
|
||||
private String externalAssetOwner;
|
||||
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) {
|
||||
this.officeId = officeId;
|
||||
this.dateFormat = dateFormat;
|
||||
this.locale = locale;
|
||||
this.transactionDate = transactionDate;
|
||||
this.currencyCode = currencyCode;
|
||||
this.credits = credits;
|
||||
this.debits = debits;
|
||||
this.rowIndex = null;
|
||||
this.paymentTypeId = null;
|
||||
this.accountNumber = null;
|
||||
this.checkNumber = null;
|
||||
this.routingCode = null;
|
||||
this.receiptNumber = null;
|
||||
this.bankNumber = null;
|
||||
this.comments = null;
|
||||
this.id = null;
|
||||
this.officeName = null;
|
||||
this.glAccountName = null;
|
||||
this.glAccountId = null;
|
||||
this.glAccountCode = null;
|
||||
this.glAccountType = null;
|
||||
this.entryType = null;
|
||||
this.amount = null;
|
||||
this.currency = null;
|
||||
this.transactionId = null;
|
||||
this.manualEntry = null;
|
||||
this.entityType = null;
|
||||
this.entityId = null;
|
||||
this.createdByUserId = null;
|
||||
this.createdDate = null;
|
||||
this.createdByUserName = null;
|
||||
this.reversed = null;
|
||||
this.referenceNumber = null;
|
||||
this.officeRunningBalance = null;
|
||||
this.organizationRunningBalance = null;
|
||||
this.runningBalanceComputed = null;
|
||||
this.transactionDetails = null;
|
||||
this.submittedOnDate = null;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
this.officeId = officeId;
|
||||
this.dateFormat = dateFormat;
|
||||
this.locale = locale;
|
||||
this.transactionDate = transactionDate;
|
||||
this.currencyCode = currencyCode;
|
||||
this.rowIndex = rowIndex;
|
||||
this.credits = credits;
|
||||
this.debits = debits;
|
||||
this.paymentTypeId = paymentTypeId;
|
||||
this.accountNumber = accountNumber;
|
||||
this.checkNumber = checkNumber;
|
||||
this.routingCode = routingCode;
|
||||
this.receiptNumber = receiptNumber;
|
||||
this.bankNumber = bankNumber;
|
||||
this.comments = comments;
|
||||
this.id = null;
|
||||
this.officeName = null;
|
||||
this.glAccountName = null;
|
||||
this.glAccountId = null;
|
||||
this.glAccountCode = null;
|
||||
this.glAccountType = null;
|
||||
this.entryType = null;
|
||||
this.amount = null;
|
||||
this.currency = null;
|
||||
this.transactionId = null;
|
||||
this.manualEntry = null;
|
||||
this.entityType = null;
|
||||
this.entityId = null;
|
||||
this.createdByUserId = null;
|
||||
this.submittedOnDate = null;
|
||||
this.createdDate = null;
|
||||
this.createdByUserName = null;
|
||||
this.reversed = null;
|
||||
this.referenceNumber = null;
|
||||
this.officeRunningBalance = null;
|
||||
this.organizationRunningBalance = null;
|
||||
this.runningBalanceComputed = null;
|
||||
this.transactionDetails = null;
|
||||
}
|
||||
|
||||
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,
|
||||
final EnumOptionData entityType, final Long entityId, final Long createdByUserId, final OffsetDateTime submittedOnDate,
|
||||
final String createdByUserName, final String comments, final Boolean reversed, final String referenceNumber,
|
||||
final BigDecimal officeRunningBalance, final BigDecimal organizationRunningBalance, final Boolean runningBalanceComputed,
|
||||
final TransactionDetailData transactionDetailData, final CurrencyData currency, final String externalAssetOwner,
|
||||
final String savingsAccountNo) {
|
||||
this.id = id;
|
||||
this.officeId = officeId;
|
||||
this.officeName = officeName;
|
||||
this.glAccountName = glAccountName;
|
||||
this.glAccountId = glAccountId;
|
||||
this.glAccountCode = glAccountCode;
|
||||
this.glAccountType = glAccountClassification;
|
||||
this.transactionDate = transactionDate;
|
||||
this.entryType = entryType;
|
||||
this.amount = amount;
|
||||
this.transactionId = transactionId;
|
||||
this.manualEntry = manualEntry;
|
||||
this.entityType = entityType;
|
||||
this.entityId = entityId;
|
||||
this.createdByUserId = createdByUserId;
|
||||
this.createdDate = submittedOnDate;
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
this.createdByUserName = createdByUserName;
|
||||
this.comments = comments;
|
||||
this.reversed = reversed;
|
||||
this.referenceNumber = referenceNumber;
|
||||
this.officeRunningBalance = officeRunningBalance;
|
||||
this.organizationRunningBalance = organizationRunningBalance;
|
||||
this.runningBalanceComputed = runningBalanceComputed;
|
||||
this.transactionDetails = transactionDetailData;
|
||||
this.currency = currency;
|
||||
this.externalAssetOwner = externalAssetOwner;
|
||||
this.savingsAccountNo = savingsAccountNo;
|
||||
}
|
||||
|
||||
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;
|
||||
final Long officeId = null;
|
||||
final String officeName = null;
|
||||
final String glAccountName = glAccountData.getName();
|
||||
final Long glAccountId = glAccountData.getId();
|
||||
final String glAccountCode = glAccountData.getGlCode();
|
||||
final EnumOptionData glAccountClassification = glAccountData.getType();
|
||||
final LocalDate transactionDate = null;
|
||||
final EnumOptionData entryType = null;
|
||||
final BigDecimal amount = null;
|
||||
final String transactionId = null;
|
||||
final Boolean manualEntry = null;
|
||||
final EnumOptionData entityType = null;
|
||||
final Long entityId = null;
|
||||
final Long createdByUserId = null;
|
||||
final OffsetDateTime submittedOnDate = null;
|
||||
final String createdByUserName = null;
|
||||
final String comments = null;
|
||||
final Boolean reversed = null;
|
||||
final String referenceNumber = null;
|
||||
final BigDecimal officeRunningBalance = null;
|
||||
final BigDecimal organizationRunningBalance = null;
|
||||
final Boolean runningBalanceComputed = null;
|
||||
final TransactionDetailData transactionDetailData = null;
|
||||
final CurrencyData currency = null;
|
||||
final String externalAssetOwner = null;
|
||||
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glAccountCode, glAccountClassification,
|
||||
transactionDate, entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
|
||||
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
|
||||
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner, 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);
|
||||
}
|
||||
}
|
||||
|
||||
+161
-144
@@ -1,144 +1,161 @@
|
||||
/**
|
||||
* 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.infrastructure.core.service;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class SearchParameters {
|
||||
|
||||
public static final int DEFAULT_MAX_LIMIT = 200;
|
||||
|
||||
private Long officeId;
|
||||
private String externalId;
|
||||
private String name;
|
||||
private String hierarchy;
|
||||
private String firstname;
|
||||
private String lastname;
|
||||
private String status;
|
||||
private Integer offset;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Integer limit;
|
||||
private String orderBy;
|
||||
private String sortOrder;
|
||||
private String accountNo;
|
||||
private String currencyCode;
|
||||
private Long staffId;
|
||||
private Long loanId;
|
||||
private Long clientId;
|
||||
private Long savingsId;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Boolean orphansOnly;
|
||||
private Long provisioningEntryId;
|
||||
private Long productId;
|
||||
private Long categoryId;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Boolean isSelfUser;
|
||||
private Integer legalForm;
|
||||
|
||||
public Integer getLimit() {
|
||||
if (limit == null) {
|
||||
return DEFAULT_MAX_LIMIT;
|
||||
}
|
||||
|
||||
if (limit > 0) {
|
||||
return limit;
|
||||
}
|
||||
|
||||
return null; // unlimited (0 or less)
|
||||
}
|
||||
|
||||
public Boolean getOrphansOnly() {
|
||||
return Boolean.TRUE.equals(orphansOnly);
|
||||
}
|
||||
|
||||
public Boolean getIsSelfUser() {
|
||||
return Boolean.TRUE.equals(isSelfUser);
|
||||
}
|
||||
|
||||
public boolean hasOrderBy() {
|
||||
return StringUtils.isNotBlank(this.orderBy);
|
||||
}
|
||||
|
||||
public boolean hasSortOrder() {
|
||||
return StringUtils.isNotBlank(this.sortOrder);
|
||||
}
|
||||
|
||||
public boolean hasOfficeId() {
|
||||
return this.officeId != null && this.officeId != 0;
|
||||
}
|
||||
|
||||
public boolean hasCurrencyCode() {
|
||||
return StringUtils.isNotBlank(this.currencyCode);
|
||||
}
|
||||
|
||||
public boolean hasLimit() {
|
||||
return this.limit != null && this.limit > 0;
|
||||
}
|
||||
|
||||
public boolean hasOffset() {
|
||||
return this.offset != null;
|
||||
}
|
||||
|
||||
public boolean hasHierarchy() {
|
||||
return StringUtils.isNotBlank(this.hierarchy);
|
||||
}
|
||||
|
||||
public boolean hasStaffId() {
|
||||
return this.staffId != null && this.staffId != 0;
|
||||
}
|
||||
|
||||
public boolean hasLoanId() {
|
||||
return this.loanId != null && this.loanId != 0;
|
||||
}
|
||||
|
||||
public boolean hasSavingsId() {
|
||||
return this.savingsId != null && this.savingsId != 0;
|
||||
}
|
||||
|
||||
public boolean hasProvisioningEntryId() {
|
||||
return this.provisioningEntryId != null && this.provisioningEntryId != 0;
|
||||
}
|
||||
|
||||
public boolean hasProductId() {
|
||||
return this.productId != null && this.productId != 0;
|
||||
}
|
||||
|
||||
public boolean hasCategoryId() {
|
||||
return this.categoryId != null && this.categoryId != 0;
|
||||
}
|
||||
|
||||
public boolean isPerson() {
|
||||
return this.legalForm != null && this.legalForm == 1;
|
||||
}
|
||||
|
||||
public boolean isEntity() {
|
||||
return this.legalForm != null && this.legalForm == 2;
|
||||
}
|
||||
|
||||
public boolean hasLegalForm() {
|
||||
return this.legalForm != null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.infrastructure.core.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class SearchParameters {
|
||||
|
||||
public static final int DEFAULT_MAX_LIMIT = 200;
|
||||
|
||||
private Long officeId;
|
||||
private String externalId;
|
||||
private String name;
|
||||
private String hierarchy;
|
||||
private String firstname;
|
||||
private String lastname;
|
||||
private String status;
|
||||
private Integer offset;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Integer limit;
|
||||
private String orderBy;
|
||||
private String sortOrder;
|
||||
private String accountNo;
|
||||
private Integer depositType;
|
||||
private String currencyCode;
|
||||
private Long staffId;
|
||||
private Long loanId;
|
||||
private Long clientId;
|
||||
private Long savingsId;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Boolean orphansOnly;
|
||||
private Long provisioningEntryId;
|
||||
private Long productId;
|
||||
private Long categoryId;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Boolean isSelfUser;
|
||||
private Integer legalForm;
|
||||
private LocalDate fromDate;
|
||||
private LocalDate toDate;
|
||||
private LocalDate asOnDate;
|
||||
|
||||
public boolean hasFromDate() {
|
||||
return this.fromDate != null;
|
||||
}
|
||||
|
||||
public boolean hasToDate() {
|
||||
return this.toDate != null;
|
||||
}
|
||||
|
||||
public boolean hasAsOnDate() {
|
||||
return this.asOnDate != null;
|
||||
}
|
||||
|
||||
public Integer getLimit() {
|
||||
if (limit == null) {
|
||||
return DEFAULT_MAX_LIMIT;
|
||||
}
|
||||
|
||||
if (limit > 0) {
|
||||
return limit;
|
||||
}
|
||||
|
||||
return null; // unlimited (0 or less)
|
||||
}
|
||||
|
||||
public Boolean getOrphansOnly() {
|
||||
return Boolean.TRUE.equals(orphansOnly);
|
||||
}
|
||||
|
||||
public Boolean getIsSelfUser() {
|
||||
return Boolean.TRUE.equals(isSelfUser);
|
||||
}
|
||||
|
||||
public boolean hasOrderBy() {
|
||||
return StringUtils.isNotBlank(this.orderBy);
|
||||
}
|
||||
|
||||
public boolean hasSortOrder() {
|
||||
return StringUtils.isNotBlank(this.sortOrder);
|
||||
}
|
||||
|
||||
public boolean hasOfficeId() {
|
||||
return this.officeId != null && this.officeId != 0;
|
||||
}
|
||||
|
||||
public boolean hasCurrencyCode() {
|
||||
return StringUtils.isNotBlank(this.currencyCode);
|
||||
}
|
||||
|
||||
public boolean hasLimit() {
|
||||
return this.limit != null && this.limit > 0;
|
||||
}
|
||||
|
||||
public boolean hasOffset() {
|
||||
return this.offset != null;
|
||||
}
|
||||
|
||||
public boolean hasHierarchy() {
|
||||
return StringUtils.isNotBlank(this.hierarchy);
|
||||
}
|
||||
|
||||
public boolean hasStaffId() {
|
||||
return this.staffId != null && this.staffId != 0;
|
||||
}
|
||||
|
||||
public boolean hasLoanId() {
|
||||
return this.loanId != null && this.loanId != 0;
|
||||
}
|
||||
|
||||
public boolean hasSavingsId() {
|
||||
return this.savingsId != null && this.savingsId != 0;
|
||||
}
|
||||
|
||||
public boolean hasProvisioningEntryId() {
|
||||
return this.provisioningEntryId != null && this.provisioningEntryId != 0;
|
||||
}
|
||||
|
||||
public boolean hasProductId() {
|
||||
return this.productId != null && this.productId != 0;
|
||||
}
|
||||
|
||||
public boolean hasCategoryId() {
|
||||
return this.categoryId != null && this.categoryId != 0;
|
||||
}
|
||||
|
||||
public boolean isPerson() {
|
||||
return this.legalForm != null && this.legalForm == 1;
|
||||
}
|
||||
|
||||
public boolean isEntity() {
|
||||
return this.legalForm != null && this.legalForm == 2;
|
||||
}
|
||||
|
||||
public boolean hasLegalForm() {
|
||||
return this.legalForm != null;
|
||||
}
|
||||
}
|
||||
|
||||
+193
-149
@@ -1,149 +1,193 @@
|
||||
/**
|
||||
* 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.portfolio.currentaccount.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_current_account_transaction")
|
||||
@Getter
|
||||
@Setter
|
||||
public class CurrentAccountTransaction extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "current_account_id", nullable = false)
|
||||
private CurrentAccount currentAccount;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "office_id", nullable = false)
|
||||
private Office office;
|
||||
|
||||
// 1=Deposit, 2=Withdrawal, 3=InterestPosting, 4=Charge, 5=ChargeWaiver, 6=Transfer, 7=Reversal
|
||||
@Column(name = "transaction_type_enum", nullable = false)
|
||||
private Integer transactionTypeEnum;
|
||||
|
||||
@Column(name = "transaction_date", nullable = false)
|
||||
private LocalDate transactionDate;
|
||||
|
||||
@Column(name = "amount", nullable = false, precision = 19, scale = 6)
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(name = "ref_no", length = 100)
|
||||
private String refNo;
|
||||
|
||||
@Column(name = "remarks", length = 500)
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "is_reversed", nullable = false)
|
||||
private boolean reversed = false;
|
||||
|
||||
@Column(name = "is_reversal", nullable = false)
|
||||
private boolean reversal = false;
|
||||
|
||||
@Column(name = "original_transaction_id")
|
||||
private Long originalTransactionId;
|
||||
|
||||
@Column(name = "is_manual", nullable = false)
|
||||
private boolean manual = false;
|
||||
|
||||
@Column(name = "running_balance_derived", precision = 19, scale = 6)
|
||||
private BigDecimal runningBalanceDerived;
|
||||
|
||||
@Column(name = "cumulative_balance_derived", precision = 19, scale = 6)
|
||||
private BigDecimal cumulativeBalanceDerived;
|
||||
|
||||
@Column(name = "balance_end_date_derived")
|
||||
private LocalDate balanceEndDateDerived;
|
||||
|
||||
@Column(name = "balance_number_of_days_derived")
|
||||
private Integer balanceNumberOfDaysDerived;
|
||||
|
||||
@Column(name = "overdraft_amount_derived", precision = 19, scale = 6)
|
||||
private BigDecimal overdraftAmountDerived;
|
||||
|
||||
@Column(name = "is_lien_transaction", nullable = false)
|
||||
private boolean lienTransaction = false;
|
||||
|
||||
@Column(name = "release_id_of_hold_amount_transaction")
|
||||
private Long releaseIdOfHoldAmountTransaction;
|
||||
|
||||
@Column(name = "reason_for_block", length = 200)
|
||||
private String reasonForBlock;
|
||||
|
||||
@Column(name = "submitted_on_date")
|
||||
private LocalDate submittedOnDate;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
@Column(name = "created_by")
|
||||
private Long createdBy;
|
||||
|
||||
protected CurrentAccountTransaction() {}
|
||||
|
||||
public static CurrentAccountTransaction deposit(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate, String refNo) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 1;
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.refNo = refNo;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
|
||||
public static CurrentAccountTransaction chargeDeduction(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 7; // PAY_CHARGE
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
|
||||
public static CurrentAccountTransaction withdrawal(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate, String refNo) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 2;
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.refNo = refNo;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.portfolio.currentaccount.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_current_account_transaction")
|
||||
@Getter
|
||||
@Setter
|
||||
public class CurrentAccountTransaction extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "current_account_id", nullable = false)
|
||||
private CurrentAccount currentAccount;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "office_id", nullable = false)
|
||||
private Office office;
|
||||
|
||||
// 1=Deposit, 2=Withdrawal, 3=InterestPosting, 4=Charge, 5=ChargeWaiver, 6=Transfer, 7=Reversal
|
||||
@Column(name = "transaction_type_enum", nullable = false)
|
||||
private Integer transactionTypeEnum;
|
||||
|
||||
@Column(name = "transaction_date", nullable = false)
|
||||
private LocalDate transactionDate;
|
||||
|
||||
@Column(name = "amount", nullable = false, precision = 19, scale = 6)
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(name = "ref_no", length = 100)
|
||||
private String refNo;
|
||||
|
||||
@Column(name = "remarks", length = 500)
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "is_reversed", nullable = false)
|
||||
private boolean reversed = false;
|
||||
|
||||
@Column(name = "is_reversal", nullable = false)
|
||||
private boolean reversal = false;
|
||||
|
||||
@Column(name = "original_transaction_id")
|
||||
private Long originalTransactionId;
|
||||
|
||||
@Column(name = "is_manual", nullable = false)
|
||||
private boolean manual = false;
|
||||
|
||||
@Column(name = "running_balance_derived", precision = 19, scale = 6)
|
||||
private BigDecimal runningBalanceDerived;
|
||||
|
||||
@Column(name = "cumulative_balance_derived", precision = 19, scale = 6)
|
||||
private BigDecimal cumulativeBalanceDerived;
|
||||
|
||||
@Column(name = "balance_end_date_derived")
|
||||
private LocalDate balanceEndDateDerived;
|
||||
|
||||
@Column(name = "balance_number_of_days_derived")
|
||||
private Integer balanceNumberOfDaysDerived;
|
||||
|
||||
@Column(name = "overdraft_amount_derived", precision = 19, scale = 6)
|
||||
private BigDecimal overdraftAmountDerived;
|
||||
|
||||
@Column(name = "is_lien_transaction", nullable = false)
|
||||
private boolean lienTransaction = false;
|
||||
|
||||
@Column(name = "release_id_of_hold_amount_transaction")
|
||||
private Long releaseIdOfHoldAmountTransaction;
|
||||
|
||||
@Column(name = "reason_for_block", length = 200)
|
||||
private String reasonForBlock;
|
||||
|
||||
@Column(name = "submitted_on_date")
|
||||
private LocalDate submittedOnDate;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
@Column(name = "created_by")
|
||||
private Long createdBy;
|
||||
|
||||
protected CurrentAccountTransaction() {}
|
||||
|
||||
public static CurrentAccountTransaction deposit(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate, String refNo) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 1;
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.refNo = refNo;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
|
||||
public static CurrentAccountTransaction chargeDeduction(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 7; // PAY_CHARGE
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
|
||||
public static CurrentAccountTransaction withdrawal(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate, String refNo) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 2;
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.refNo = refNo;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
|
||||
// transactionTypeEnum = 8 (Hold Amount)
|
||||
public static CurrentAccountTransaction holdAmount(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate, String reasonForBlock) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 8;
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.lienTransaction = true;
|
||||
txn.reasonForBlock = reasonForBlock;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
|
||||
// transactionTypeEnum = 9 (Release Amount)
|
||||
public static CurrentAccountTransaction releaseAmount(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate, Long holdTransactionId) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 9;
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.releaseIdOfHoldAmountTransaction = holdTransactionId;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
|
||||
// transactionTypeEnum = 7 (Reversal / Undo)
|
||||
public static CurrentAccountTransaction reversal(CurrentAccount account, Office office,
|
||||
BigDecimal amount, LocalDate transactionDate, Long originalTransactionId) {
|
||||
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||
txn.currentAccount = account;
|
||||
txn.office = office;
|
||||
txn.transactionTypeEnum = 7;
|
||||
txn.amount = amount;
|
||||
txn.transactionDate = transactionDate;
|
||||
txn.reversal = true;
|
||||
txn.originalTransactionId = originalTransactionId;
|
||||
txn.submittedOnDate = transactionDate;
|
||||
return txn;
|
||||
}
|
||||
}
|
||||
|
||||
+35
-29
@@ -1,29 +1,35 @@
|
||||
/**
|
||||
* 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.portfolio.currentaccount.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface CurrentAccountReadPlatformService {
|
||||
|
||||
Map<String, Object> retrieveOne(Long accountId);
|
||||
|
||||
List<Map<String, Object>> retrieveAll();
|
||||
}
|
||||
/**
|
||||
* 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.portfolio.currentaccount.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface CurrentAccountReadPlatformService {
|
||||
|
||||
Map<String, Object> retrieveOne(Long accountId);
|
||||
|
||||
List<Map<String, Object>> retrieveAll();
|
||||
|
||||
List<Map<String, Object>> retrieveAll(Integer offset, Integer limit, String accountNo, String clientName);
|
||||
|
||||
List<Map<String, Object>> retrieveAll(Integer offset, Integer limit, String accountNo, String clientName,
|
||||
String status, LocalDate fromDate, LocalDate toDate, LocalDate asOnDate);
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.portfolio.currentaccount.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface CurrentAccountTransactionWritePlatformService {
|
||||
|
||||
CommandProcessingResult deposit(Long accountId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult withdrawal(Long accountId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult holdAmount(Long accountId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult releaseAmount(Long accountId, Long transactionId);
|
||||
|
||||
CommandProcessingResult undoTransaction(Long accountId, Long transactionId, boolean isReversal);
|
||||
|
||||
CommandProcessingResult reverseTransaction(Long accountId, Long transactionId, boolean isReversal, JsonCommand command);
|
||||
|
||||
CommandProcessingResult adjustTransaction(Long accountId, Long transactionId, JsonCommand command);
|
||||
}
|
||||
+39
-38
@@ -1,38 +1,39 @@
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class SavingsDTO {
|
||||
|
||||
private Long savingsId;
|
||||
private Long savingsProductId;
|
||||
private Long officeId;
|
||||
private String currencyCode;
|
||||
private boolean cashBasedAccountingEnabled;
|
||||
private boolean accrualBasedAccountingEnabled;
|
||||
private List<SavingsTransactionDTO> newSavingsTransactions;
|
||||
}
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class SavingsDTO {
|
||||
|
||||
private Long savingsId;
|
||||
private Long savingsProductId;
|
||||
private Long officeId;
|
||||
private String currencyCode;
|
||||
private boolean cashBasedAccountingEnabled;
|
||||
private boolean accrualBasedAccountingEnabled;
|
||||
private List<SavingsTransactionDTO> newSavingsTransactions;
|
||||
private int depositAccountType;
|
||||
}
|
||||
|
||||
+1409
-1386
File diff suppressed because it is too large
Load Diff
+290
-285
@@ -1,285 +1,290 @@
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.closure.domain.GLClosure;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForSavings;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
||||
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AccrualBasedAccountingProcessorForSavings implements AccountingProcessorForSavings {
|
||||
|
||||
private final AccountingProcessorHelper helper;
|
||||
|
||||
@Override
|
||||
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
||||
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
||||
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
||||
final Long savingsId = savingsDTO.getSavingsId();
|
||||
final String currencyCode = savingsDTO.getCurrencyCode();
|
||||
for (final SavingsTransactionDTO savingsTransactionDTO : savingsDTO.getNewSavingsTransactions()) {
|
||||
final LocalDate transactionDate = savingsTransactionDTO.getTransactionDate();
|
||||
final String transactionId = savingsTransactionDTO.getTransactionId();
|
||||
final Office office = this.helper.getOfficeById(savingsTransactionDTO.getOfficeId());
|
||||
final Long paymentTypeId = savingsTransactionDTO.getPaymentTypeId();
|
||||
final boolean isReversal = savingsTransactionDTO.isReversed();
|
||||
final BigDecimal amount = savingsTransactionDTO.getAmount();
|
||||
final BigDecimal overdraftAmount = savingsTransactionDTO.getOverdraftAmount();
|
||||
final List<ChargePaymentDTO> feePayments = savingsTransactionDTO.getFeePayments();
|
||||
final List<ChargePaymentDTO> penaltyPayments = savingsTransactionDTO.getPenaltyPayments();
|
||||
|
||||
this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
|
||||
|
||||
if (savingsTransactionDTO.getTransactionType().isWithdrawal() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId,
|
||||
transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isDeposit() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Deposits and reversals of deposits **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isDeposit()) {
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Deposits and reversals of Dividend pay outs **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isDividendPayout()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.PAYABLE_DIVIDENDS.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** Handle withdrawals and reversals of withdrawals **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithdrawal()) {
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isEscheat()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), AccrualAccountsForSavings.ESCHEAT_LIABILITY.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
/**
|
||||
* Handle Interest Applications and reversals of Interest Applications
|
||||
**/
|
||||
else if (savingsTransactionDTO.getTransactionType().isInterestPosting() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
// Post journal entry if earned interest amount is greater than
|
||||
// zero
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isInterestPosting()) {
|
||||
// Post journal entry if earned interest amount is greater than
|
||||
// zero
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_PAYABLE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isAccrual()) {
|
||||
// Post journal entry for Accrual Recognition
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
if (MathUtil.isGreaterThanZero(overdraftAmount)) {
|
||||
this.helper.createAccrualBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
this.helper.createAccrualBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_PAYABLE.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createAccrualBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_RECEIVABLE.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
this.helper.createAccrualBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INCOME_FROM_INTEREST.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithholdTax()) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsTax(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.SAVINGS_REFERENCE, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal,
|
||||
savingsTransactionDTO.getTaxPayments());
|
||||
}
|
||||
|
||||
/** Handle Fees Deductions and reversals of Fees Deductions **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
// Is the Charge a penalty?
|
||||
if (penaltyPayments.size() > 0) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES,
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
|
||||
penaltyPayments);
|
||||
if (isPositive) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES,
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal, penaltyPayments);
|
||||
}
|
||||
} else {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES,
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
|
||||
feePayments);
|
||||
if (isPositive) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal,
|
||||
feePayments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction()) {
|
||||
// Is the Charge a penalty?
|
||||
if (penaltyPayments.size() > 0) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, penaltyPayments);
|
||||
} else {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Transfers proposal **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isInitiateTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), AccrualAccountsForSavings.TRANSFERS_SUSPENSE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** Handle Transfer Withdrawal or Acceptance **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithdrawTransfer()
|
||||
|| savingsTransactionDTO.getTransactionType().isApproveTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.TRANSFERS_SUSPENSE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** overdraft **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isOverdraftInterest()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), AccrualAccountsForSavings.INCOME_FROM_INTEREST.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else if (savingsTransactionDTO.getTransactionType().isWrittenoff()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(),
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
} else if (savingsTransactionDTO.getTransactionType().isOverdraftFee()) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.closure.domain.GLClosure;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForSavings;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
||||
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AccrualBasedAccountingProcessorForSavings implements AccountingProcessorForSavings {
|
||||
|
||||
private final AccountingProcessorHelper helper;
|
||||
|
||||
@Override
|
||||
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
||||
AccountingProcessorHelper.setDepositAccountTypeContext(savingsDTO.getDepositAccountType());
|
||||
try {
|
||||
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
||||
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
||||
final Long savingsId = savingsDTO.getSavingsId();
|
||||
final String currencyCode = savingsDTO.getCurrencyCode();
|
||||
for (final SavingsTransactionDTO savingsTransactionDTO : savingsDTO.getNewSavingsTransactions()) {
|
||||
final LocalDate transactionDate = savingsTransactionDTO.getTransactionDate();
|
||||
final String transactionId = savingsTransactionDTO.getTransactionId();
|
||||
final Office office = this.helper.getOfficeById(savingsTransactionDTO.getOfficeId());
|
||||
final Long paymentTypeId = savingsTransactionDTO.getPaymentTypeId();
|
||||
final boolean isReversal = savingsTransactionDTO.isReversed();
|
||||
final BigDecimal amount = savingsTransactionDTO.getAmount();
|
||||
final BigDecimal overdraftAmount = savingsTransactionDTO.getOverdraftAmount();
|
||||
final List<ChargePaymentDTO> feePayments = savingsTransactionDTO.getFeePayments();
|
||||
final List<ChargePaymentDTO> penaltyPayments = savingsTransactionDTO.getPenaltyPayments();
|
||||
|
||||
this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
|
||||
|
||||
if (savingsTransactionDTO.getTransactionType().isWithdrawal() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId,
|
||||
transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isDeposit() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Deposits and reversals of deposits **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isDeposit()) {
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Deposits and reversals of Dividend pay outs **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isDividendPayout()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.PAYABLE_DIVIDENDS.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** Handle withdrawals and reversals of withdrawals **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithdrawal()) {
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isEscheat()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), AccrualAccountsForSavings.ESCHEAT_LIABILITY.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
/**
|
||||
* Handle Interest Applications and reversals of Interest Applications
|
||||
**/
|
||||
else if (savingsTransactionDTO.getTransactionType().isInterestPosting() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
// Post journal entry if earned interest amount is greater than
|
||||
// zero
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isInterestPosting()) {
|
||||
// Post journal entry if earned interest amount is greater than
|
||||
// zero
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_PAYABLE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isAccrual()) {
|
||||
// Post journal entry for Accrual Recognition
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
if (MathUtil.isGreaterThanZero(overdraftAmount)) {
|
||||
this.helper.createAccrualBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
this.helper.createAccrualBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_PAYABLE.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createAccrualBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INTEREST_RECEIVABLE.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
this.helper.createAccrualBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.INCOME_FROM_INTEREST.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithholdTax()) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsTax(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.SAVINGS_REFERENCE, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal,
|
||||
savingsTransactionDTO.getTaxPayments());
|
||||
}
|
||||
|
||||
/** Handle Fees Deductions and reversals of Fees Deductions **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
// Is the Charge a penalty?
|
||||
if (penaltyPayments.size() > 0) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES,
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
|
||||
penaltyPayments);
|
||||
if (isPositive) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES,
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal, penaltyPayments);
|
||||
}
|
||||
} else {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES,
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
|
||||
feePayments);
|
||||
if (isPositive) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal,
|
||||
feePayments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction()) {
|
||||
// Is the Charge a penalty?
|
||||
if (penaltyPayments.size() > 0) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, penaltyPayments);
|
||||
} else {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Transfers proposal **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isInitiateTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), AccrualAccountsForSavings.TRANSFERS_SUSPENSE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** Handle Transfer Withdrawal or Acceptance **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithdrawTransfer()
|
||||
|| savingsTransactionDTO.getTransactionType().isApproveTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.TRANSFERS_SUSPENSE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** overdraft **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isOverdraftInterest()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), AccrualAccountsForSavings.INCOME_FROM_INTEREST.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else if (savingsTransactionDTO.getTransactionType().isWrittenoff()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
AccrualAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(),
|
||||
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount, isReversal);
|
||||
} else if (savingsTransactionDTO.getTransactionType().isOverdraftFee()) {
|
||||
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
AccrualAccountsForSavings.SAVINGS_REFERENCE, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
AccountingProcessorHelper.clearDepositAccountTypeContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+260
-255
@@ -1,255 +1,260 @@
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.closure.domain.GLClosure;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
||||
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CashBasedAccountingProcessorForSavings implements AccountingProcessorForSavings {
|
||||
|
||||
private final AccountingProcessorHelper helper;
|
||||
|
||||
@Override
|
||||
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
||||
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
||||
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
||||
final Long savingsId = savingsDTO.getSavingsId();
|
||||
final String currencyCode = savingsDTO.getCurrencyCode();
|
||||
for (final SavingsTransactionDTO savingsTransactionDTO : savingsDTO.getNewSavingsTransactions()) {
|
||||
final LocalDate transactionDate = savingsTransactionDTO.getTransactionDate();
|
||||
final String transactionId = savingsTransactionDTO.getTransactionId();
|
||||
final Office office = this.helper.getOfficeById(savingsTransactionDTO.getOfficeId());
|
||||
final Long paymentTypeId = savingsTransactionDTO.getPaymentTypeId();
|
||||
final boolean isReversal = savingsTransactionDTO.isReversed();
|
||||
final BigDecimal amount = savingsTransactionDTO.getAmount();
|
||||
final BigDecimal overdraftAmount = savingsTransactionDTO.getOverdraftAmount();
|
||||
final List<ChargePaymentDTO> feePayments = savingsTransactionDTO.getFeePayments();
|
||||
final List<ChargePaymentDTO> penaltyPayments = savingsTransactionDTO.getPenaltyPayments();
|
||||
|
||||
this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
|
||||
|
||||
if (savingsTransactionDTO.getTransactionType().isWithdrawal() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId,
|
||||
transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
} else if (savingsTransactionDTO.getTransactionType().isDeposit() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Deposits and reversals of deposits **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isDeposit()) {
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Deposits and reversals of Dividend pay outs **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isDividendPayout()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.PAYABLE_DIVIDENDS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
/** Handle withdrawals and reversals of withdrawals **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithdrawal()) {
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isEscheat()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.ESCHEAT_LIABILITY.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
/**
|
||||
* Handle Interest Applications and reversals of Interest Applications
|
||||
**/
|
||||
else if (savingsTransactionDTO.getTransactionType().isInterestPosting() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
// Post journal entry if earned interest amount is greater than
|
||||
// zero
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isInterestPosting()) {
|
||||
// Post journal entry if earned interest amount is greater than
|
||||
// zero
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithholdTax()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsTax(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.SAVINGS_REFERENCE, savingsProductId, paymentTypeId,
|
||||
savingsId, transactionId, transactionDate, amount, isReversal, savingsTransactionDTO.getTaxPayments());
|
||||
}
|
||||
|
||||
/** Handle Fees Deductions and reversals of Fees Deductions **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
// Is the Charge a penalty?
|
||||
if (penaltyPayments.size() > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES,
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
|
||||
penaltyPayments);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal,
|
||||
penaltyPayments);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal, feePayments);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal,
|
||||
feePayments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction()) {
|
||||
// Is the Charge a penalty?
|
||||
if (penaltyPayments.size() > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, penaltyPayments);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Transfers proposal **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isInitiateTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** Handle Transfer Withdrawal or Acceptance **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithdrawTransfer()
|
||||
|| savingsTransactionDTO.getTransactionType().isApproveTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** overdraft **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isOverdraftInterest()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.INCOME_FROM_INTEREST.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else if (savingsTransactionDTO.getTransactionType().isWrittenoff()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(), CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else if (savingsTransactionDTO.getTransactionType().isOverdraftFee()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId, paymentTypeId,
|
||||
savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.closure.domain.GLClosure;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
||||
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CashBasedAccountingProcessorForSavings implements AccountingProcessorForSavings {
|
||||
|
||||
private final AccountingProcessorHelper helper;
|
||||
|
||||
@Override
|
||||
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
||||
AccountingProcessorHelper.setDepositAccountTypeContext(savingsDTO.getDepositAccountType());
|
||||
try {
|
||||
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
||||
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
||||
final Long savingsId = savingsDTO.getSavingsId();
|
||||
final String currencyCode = savingsDTO.getCurrencyCode();
|
||||
for (final SavingsTransactionDTO savingsTransactionDTO : savingsDTO.getNewSavingsTransactions()) {
|
||||
final LocalDate transactionDate = savingsTransactionDTO.getTransactionDate();
|
||||
final String transactionId = savingsTransactionDTO.getTransactionId();
|
||||
final Office office = this.helper.getOfficeById(savingsTransactionDTO.getOfficeId());
|
||||
final Long paymentTypeId = savingsTransactionDTO.getPaymentTypeId();
|
||||
final boolean isReversal = savingsTransactionDTO.isReversed();
|
||||
final BigDecimal amount = savingsTransactionDTO.getAmount();
|
||||
final BigDecimal overdraftAmount = savingsTransactionDTO.getOverdraftAmount();
|
||||
final List<ChargePaymentDTO> feePayments = savingsTransactionDTO.getFeePayments();
|
||||
final List<ChargePaymentDTO> penaltyPayments = savingsTransactionDTO.getPenaltyPayments();
|
||||
|
||||
this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
|
||||
|
||||
if (savingsTransactionDTO.getTransactionType().isWithdrawal() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId,
|
||||
transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
} else if (savingsTransactionDTO.getTransactionType().isDeposit() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Deposits and reversals of deposits **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isDeposit()) {
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Deposits and reversals of Dividend pay outs **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isDividendPayout()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
FinancialActivity.PAYABLE_DIVIDENDS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
/** Handle withdrawals and reversals of withdrawals **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithdrawal()) {
|
||||
if (savingsTransactionDTO.isAccountTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isEscheat()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.ESCHEAT_LIABILITY.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
/**
|
||||
* Handle Interest Applications and reversals of Interest Applications
|
||||
**/
|
||||
else if (savingsTransactionDTO.getTransactionType().isInterestPosting() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
// Post journal entry if earned interest amount is greater than
|
||||
// zero
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, overdraftAmount, isReversal);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
|
||||
amount.subtract(overdraftAmount), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isInterestPosting()) {
|
||||
// Post journal entry if earned interest amount is greater than
|
||||
// zero
|
||||
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithholdTax()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsTax(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.SAVINGS_REFERENCE, savingsProductId, paymentTypeId,
|
||||
savingsId, transactionId, transactionDate, amount, isReversal, savingsTransactionDTO.getTaxPayments());
|
||||
}
|
||||
|
||||
/** Handle Fees Deductions and reversals of Fees Deductions **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
|
||||
// Is the Charge a penalty?
|
||||
if (penaltyPayments.size() > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES,
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
|
||||
penaltyPayments);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal,
|
||||
penaltyPayments);
|
||||
}
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal, feePayments);
|
||||
if (isPositive) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal,
|
||||
feePayments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction()) {
|
||||
// Is the Charge a penalty?
|
||||
if (penaltyPayments.size() > 0) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, penaltyPayments);
|
||||
} else {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Transfers proposal **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isInitiateTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** Handle Transfer Withdrawal or Acceptance **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithdrawTransfer()
|
||||
|| savingsTransactionDTO.getTransactionType().isApproveTransfer()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
/** overdraft **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isOverdraftInterest()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.INCOME_FROM_INTEREST.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else if (savingsTransactionDTO.getTransactionType().isWrittenoff()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CashAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(), CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
} else if (savingsTransactionDTO.getTransactionType().isOverdraftFee()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId, paymentTypeId,
|
||||
savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
AccountingProcessorHelper.clearDepositAccountTypeContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+765
-757
File diff suppressed because it is too large
Load Diff
+285
-285
@@ -1,285 +1,285 @@
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams;
|
||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
|
||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryDataValidator;
|
||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntryRunningBalanceUpdateService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
private final OfficeRepositoryWrapper officeRepositoryWrapper;
|
||||
|
||||
private final JournalEntryDataValidator dataValidator;
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||
|
||||
private final GLJournalEntryMapper entryMapper = new GLJournalEntryMapper();
|
||||
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
|
||||
@Override
|
||||
public void updateRunningBalance() {
|
||||
String dateFinder = "select MIN(je.entry_date) as entityDate from acc_gl_journal_entry je "
|
||||
+ "where je.is_running_balance_calculated=false ";
|
||||
try {
|
||||
LocalDate entityDate = this.jdbcTemplate.queryForObject(dateFinder, LocalDate.class);
|
||||
updateOrganizationRunningBalance(entityDate);
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
log.debug("No results found for updation of running balance ");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult updateOfficeRunningBalance(JsonCommand command) {
|
||||
this.dataValidator.validateForUpdateRunningBalance(command);
|
||||
final Long officeId = this.fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue(),
|
||||
command.parsedJson());
|
||||
CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId());
|
||||
if (officeId == null) {
|
||||
updateRunningBalance();
|
||||
} else {
|
||||
this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
|
||||
String dateFinder = "select MIN(je.entry_date) as entityDate " + "from acc_gl_journal_entry je "
|
||||
+ "where je.is_running_balance_calculated=false and je.office_id=?";
|
||||
try {
|
||||
LocalDate entityDate = this.jdbcTemplate.queryForObject(dateFinder, LocalDate.class, officeId);
|
||||
updateRunningBalance(officeId, entityDate);
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
log.debug("No results found for updation of office running balance with office id: {}", officeId);
|
||||
}
|
||||
commandProcessingResultBuilder.withOfficeId(officeId);
|
||||
}
|
||||
return commandProcessingResultBuilder.build();
|
||||
}
|
||||
|
||||
private void updateOrganizationRunningBalance(LocalDate entityDate) {
|
||||
Map<Long, BigDecimal> runningBalanceMap = new HashMap<>(5);
|
||||
Map<Long, Map<Long, BigDecimal>> officesRunningBalance = new HashMap<>();
|
||||
|
||||
final String organizationRunningBalanceQuery = "select je.organization_running_balance as runningBalance,je.account_id as accountId from acc_gl_journal_entry je "
|
||||
+ "inner join (select max(id) as id from acc_gl_journal_entry where entry_date < ? group by account_id,entry_date) je2 ON je2.id = je.id "
|
||||
+ "inner join (select max(entry_date) as date from acc_gl_journal_entry where entry_date < ? group by account_id) je3 ON je.entry_date = je3.date "
|
||||
+ "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
|
||||
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(organizationRunningBalanceQuery, // NOSONAR
|
||||
entityDate, entityDate);
|
||||
|
||||
for (Map<String, Object> entries : list) {
|
||||
Long accountId = Long.parseLong(entries.get("accountId").toString()); // Drizzle
|
||||
// is
|
||||
// returning
|
||||
// Big
|
||||
// Integer
|
||||
// where
|
||||
// as
|
||||
// MySQL
|
||||
// returns
|
||||
// Long.
|
||||
if (!runningBalanceMap.containsKey(accountId)) {
|
||||
runningBalanceMap.put(accountId, (BigDecimal) entries.get("runningBalance"));
|
||||
}
|
||||
}
|
||||
|
||||
final String offlineRunningBalanceQuery = "select je.office_running_balance as runningBalance,je.account_id as accountId,je.office_id as officeId "
|
||||
+ "from acc_gl_journal_entry je "
|
||||
+ "inner join (select max(id) as id from acc_gl_journal_entry where entry_date < ? group by office_id,account_id,entry_date) je2 ON je2.id = je.id "
|
||||
+ "inner join (select max(entry_date) as date from acc_gl_journal_entry where entry_date < ? group by office_id,account_id) je3 ON je.entry_date = je3.date "
|
||||
+ "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
|
||||
|
||||
List<Map<String, Object>> officesRunningBalanceList = jdbcTemplate.queryForList(offlineRunningBalanceQuery, // NOSONAR
|
||||
entityDate, entityDate);
|
||||
for (Map<String, Object> entries : officesRunningBalanceList) {
|
||||
Long accountId = Long.parseLong(entries.get("accountId").toString());
|
||||
Long officeId = Long.parseLong(entries.get("officeId").toString());
|
||||
Map<Long, BigDecimal> runningBalance;
|
||||
if (officesRunningBalance.containsKey(officeId)) {
|
||||
runningBalance = officesRunningBalance.get(officeId);
|
||||
} else {
|
||||
runningBalance = new HashMap<>();
|
||||
officesRunningBalance.put(officeId, runningBalance);
|
||||
}
|
||||
if (!runningBalance.containsKey(accountId)) {
|
||||
runningBalance.put(accountId, (BigDecimal) entries.get("runningBalance"));
|
||||
}
|
||||
}
|
||||
|
||||
List<JournalEntryData> entryDataList = jdbcTemplate.query(entryMapper.organizationRunningBalanceSchema(), entryMapper, entityDate);
|
||||
if (entryDataList.size() > 0) {
|
||||
// run a batch update of 1000 SQL statements at a time
|
||||
final int batchUpdateSize = 1000;
|
||||
List<Object[]> params = new ArrayList<>();
|
||||
int batchIndex = 0;
|
||||
String sql = "UPDATE acc_gl_journal_entry SET is_running_balance_calculated=?, organization_running_balance=?,"
|
||||
+ "office_running_balance=?, last_modified_by=?, last_modified_on_utc=? WHERE id=?";
|
||||
for (int index = 0; index < entryDataList.size(); index++) {
|
||||
JournalEntryData entryData = entryDataList.get(index);
|
||||
Map<Long, BigDecimal> officeRunningBalanceMap;
|
||||
if (officesRunningBalance.containsKey(entryData.getOfficeId())) {
|
||||
officeRunningBalanceMap = officesRunningBalance.get(entryData.getOfficeId());
|
||||
} else {
|
||||
officeRunningBalanceMap = new HashMap<>();
|
||||
officesRunningBalance.put(entryData.getOfficeId(), officeRunningBalanceMap);
|
||||
}
|
||||
BigDecimal officeRunningBalance = calculateRunningBalance(entryData, officeRunningBalanceMap);
|
||||
BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap);
|
||||
|
||||
params.add(new Object[] { Boolean.TRUE, runningBalance, officeRunningBalance,
|
||||
platformSecurityContext.authenticatedUser().getId(), DateUtils.getAuditOffsetDateTime(), entryData.getId() });
|
||||
batchIndex++;
|
||||
if (batchIndex == batchUpdateSize || index == entryDataList.size() - 1) {
|
||||
this.jdbcTemplate.batchUpdate(sql, params);
|
||||
// reset counter and string array
|
||||
batchIndex = 0;
|
||||
params.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateRunningBalance(Long officeId, LocalDate entityDate) {
|
||||
Map<Long, BigDecimal> runningBalanceMap = new HashMap<>(5);
|
||||
|
||||
final String offlineRunningBalanceQuery = "select je.office_running_balance as runningBalance,je.account_id as accountId from acc_gl_journal_entry je "
|
||||
+ "inner join (select max(id) as id from acc_gl_journal_entry where office_id=? and entry_date < ? group by account_id,entry_date) je2 ON je2.id = je.id "
|
||||
+ "inner join (select max(entry_date) as date from acc_gl_journal_entry where office_id=? and entry_date < ? group by account_id) je3 ON je.entry_date = je3.date "
|
||||
+ "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
|
||||
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(offlineRunningBalanceQuery, // NOSONAR
|
||||
officeId, entityDate, officeId, entityDate);
|
||||
for (Map<String, Object> entries : list) {
|
||||
Long accountId = (Long) entries.get("accountId");
|
||||
if (!runningBalanceMap.containsKey(accountId)) {
|
||||
runningBalanceMap.put(accountId, (BigDecimal) entries.get("runningBalance"));
|
||||
}
|
||||
}
|
||||
List<JournalEntryData> entryDataList = jdbcTemplate.query(entryMapper.officeRunningBalanceSchema(), entryMapper, officeId,
|
||||
entityDate);
|
||||
List<Object[]> params = new ArrayList<>();
|
||||
|
||||
String sql = "UPDATE acc_gl_journal_entry SET office_running_balance=?, last_modified_by=?, last_modified_on_utc=? WHERE id=?";
|
||||
for (JournalEntryData entryData : entryDataList) {
|
||||
BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap);
|
||||
params.add(new Object[] { runningBalance, platformSecurityContext.authenticatedUser().getId(),
|
||||
DateUtils.getAuditOffsetDateTime(), entryData.getId() });
|
||||
}
|
||||
this.jdbcTemplate.batchUpdate(sql, params);
|
||||
}
|
||||
|
||||
private BigDecimal calculateRunningBalance(JournalEntryData entry, Map<Long, BigDecimal> runningBalanceMap) {
|
||||
BigDecimal runningBalance = BigDecimal.ZERO;
|
||||
if (runningBalanceMap.containsKey(entry.getGlAccountId())) {
|
||||
runningBalance = runningBalanceMap.get(entry.getGlAccountId());
|
||||
}
|
||||
GLAccountType accountType = GLAccountType.fromInt(entry.getGlAccountType().getId().intValue());
|
||||
JournalEntryType entryType = JournalEntryType.fromInt(entry.getEntryType().getId().intValue());
|
||||
boolean isIncrease = false;
|
||||
switch (accountType) {
|
||||
case ASSET:
|
||||
case EXPENSE:
|
||||
if (entryType.isDebitType()) {
|
||||
isIncrease = true;
|
||||
}
|
||||
break;
|
||||
case EQUITY:
|
||||
case INCOME:
|
||||
case LIABILITY:
|
||||
if (entryType.isCreditType()) {
|
||||
isIncrease = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isIncrease) {
|
||||
runningBalance = runningBalance.add(entry.getAmount());
|
||||
} else {
|
||||
runningBalance = runningBalance.subtract(entry.getAmount());
|
||||
}
|
||||
runningBalanceMap.put(entry.getGlAccountId(), runningBalance);
|
||||
return runningBalance;
|
||||
}
|
||||
|
||||
private static final class GLJournalEntryMapper implements RowMapper<JournalEntryData> {
|
||||
|
||||
public String officeRunningBalanceSchema() {
|
||||
return "select je.id as id,je.account_id as glAccountId,je.type_enum as entryType,je.amount as amount, "
|
||||
+ "glAccount.classification_enum as classification,je.office_id as officeId "
|
||||
+ "from acc_gl_journal_entry je , acc_gl_account glAccount " + "where je.account_id = glAccount.id "
|
||||
+ "and je.office_id=? and je.entry_date >= ? order by je.entry_date,je.id";
|
||||
}
|
||||
|
||||
public String organizationRunningBalanceSchema() {
|
||||
return "select je.id as id,je.account_id as glAccountId," + "je.type_enum as entryType,je.amount as amount, "
|
||||
+ "glAccount.classification_enum as classification,je.office_id as officeId "
|
||||
+ "from acc_gl_journal_entry je , acc_gl_account glAccount " + "where je.account_id = glAccount.id "
|
||||
+ "and je.entry_date >= ? order by je.entry_date,je.id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JournalEntryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
final Long glAccountId = rs.getLong("glAccountId");
|
||||
final Long officeId = rs.getLong("officeId");
|
||||
final int accountTypeId = JdbcSupport.getInteger(rs, "classification");
|
||||
final EnumOptionData accountType = AccountingEnumerations.gLAccountType(accountTypeId);
|
||||
final BigDecimal amount = rs.getBigDecimal("amount");
|
||||
final int entryTypeId = JdbcSupport.getInteger(rs, "entryType");
|
||||
final EnumOptionData entryType = AccountingEnumerations.journalEntryType(entryTypeId);
|
||||
|
||||
return new JournalEntryData(id, officeId, null, null, glAccountId, null, accountType, null, entryType, amount, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams;
|
||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
|
||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryDataValidator;
|
||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntryRunningBalanceUpdateService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
private final OfficeRepositoryWrapper officeRepositoryWrapper;
|
||||
|
||||
private final JournalEntryDataValidator dataValidator;
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||
|
||||
private final GLJournalEntryMapper entryMapper = new GLJournalEntryMapper();
|
||||
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
|
||||
@Override
|
||||
public void updateRunningBalance() {
|
||||
String dateFinder = "select MIN(je.entry_date) as entityDate from acc_gl_journal_entry je "
|
||||
+ "where je.is_running_balance_calculated=false ";
|
||||
try {
|
||||
LocalDate entityDate = this.jdbcTemplate.queryForObject(dateFinder, LocalDate.class);
|
||||
updateOrganizationRunningBalance(entityDate);
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
log.debug("No results found for updation of running balance ");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult updateOfficeRunningBalance(JsonCommand command) {
|
||||
this.dataValidator.validateForUpdateRunningBalance(command);
|
||||
final Long officeId = this.fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue(),
|
||||
command.parsedJson());
|
||||
CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId());
|
||||
if (officeId == null) {
|
||||
updateRunningBalance();
|
||||
} else {
|
||||
this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
|
||||
String dateFinder = "select MIN(je.entry_date) as entityDate " + "from acc_gl_journal_entry je "
|
||||
+ "where je.is_running_balance_calculated=false and je.office_id=?";
|
||||
try {
|
||||
LocalDate entityDate = this.jdbcTemplate.queryForObject(dateFinder, LocalDate.class, officeId);
|
||||
updateRunningBalance(officeId, entityDate);
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
log.debug("No results found for updation of office running balance with office id: {}", officeId);
|
||||
}
|
||||
commandProcessingResultBuilder.withOfficeId(officeId);
|
||||
}
|
||||
return commandProcessingResultBuilder.build();
|
||||
}
|
||||
|
||||
private void updateOrganizationRunningBalance(LocalDate entityDate) {
|
||||
Map<Long, BigDecimal> runningBalanceMap = new HashMap<>(5);
|
||||
Map<Long, Map<Long, BigDecimal>> officesRunningBalance = new HashMap<>();
|
||||
|
||||
final String organizationRunningBalanceQuery = "select je.organization_running_balance as runningBalance,je.account_id as accountId from acc_gl_journal_entry je "
|
||||
+ "inner join (select max(id) as id from acc_gl_journal_entry where entry_date < ? group by account_id,entry_date) je2 ON je2.id = je.id "
|
||||
+ "inner join (select max(entry_date) as date from acc_gl_journal_entry where entry_date < ? group by account_id) je3 ON je.entry_date = je3.date "
|
||||
+ "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
|
||||
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(organizationRunningBalanceQuery, // NOSONAR
|
||||
entityDate, entityDate);
|
||||
|
||||
for (Map<String, Object> entries : list) {
|
||||
Long accountId = Long.parseLong(entries.get("accountId").toString()); // Drizzle
|
||||
// is
|
||||
// returning
|
||||
// Big
|
||||
// Integer
|
||||
// where
|
||||
// as
|
||||
// MySQL
|
||||
// returns
|
||||
// Long.
|
||||
if (!runningBalanceMap.containsKey(accountId)) {
|
||||
runningBalanceMap.put(accountId, (BigDecimal) entries.get("runningBalance"));
|
||||
}
|
||||
}
|
||||
|
||||
final String offlineRunningBalanceQuery = "select je.office_running_balance as runningBalance,je.account_id as accountId,je.office_id as officeId "
|
||||
+ "from acc_gl_journal_entry je "
|
||||
+ "inner join (select max(id) as id from acc_gl_journal_entry where entry_date < ? group by office_id,account_id,entry_date) je2 ON je2.id = je.id "
|
||||
+ "inner join (select max(entry_date) as date from acc_gl_journal_entry where entry_date < ? group by office_id,account_id) je3 ON je.entry_date = je3.date "
|
||||
+ "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
|
||||
|
||||
List<Map<String, Object>> officesRunningBalanceList = jdbcTemplate.queryForList(offlineRunningBalanceQuery, // NOSONAR
|
||||
entityDate, entityDate);
|
||||
for (Map<String, Object> entries : officesRunningBalanceList) {
|
||||
Long accountId = Long.parseLong(entries.get("accountId").toString());
|
||||
Long officeId = Long.parseLong(entries.get("officeId").toString());
|
||||
Map<Long, BigDecimal> runningBalance;
|
||||
if (officesRunningBalance.containsKey(officeId)) {
|
||||
runningBalance = officesRunningBalance.get(officeId);
|
||||
} else {
|
||||
runningBalance = new HashMap<>();
|
||||
officesRunningBalance.put(officeId, runningBalance);
|
||||
}
|
||||
if (!runningBalance.containsKey(accountId)) {
|
||||
runningBalance.put(accountId, (BigDecimal) entries.get("runningBalance"));
|
||||
}
|
||||
}
|
||||
|
||||
List<JournalEntryData> entryDataList = jdbcTemplate.query(entryMapper.organizationRunningBalanceSchema(), entryMapper, entityDate);
|
||||
if (entryDataList.size() > 0) {
|
||||
// run a batch update of 1000 SQL statements at a time
|
||||
final int batchUpdateSize = 1000;
|
||||
List<Object[]> params = new ArrayList<>();
|
||||
int batchIndex = 0;
|
||||
String sql = "UPDATE acc_gl_journal_entry SET is_running_balance_calculated=?, organization_running_balance=?,"
|
||||
+ "office_running_balance=?, last_modified_by=?, last_modified_on_utc=? WHERE id=?";
|
||||
for (int index = 0; index < entryDataList.size(); index++) {
|
||||
JournalEntryData entryData = entryDataList.get(index);
|
||||
Map<Long, BigDecimal> officeRunningBalanceMap;
|
||||
if (officesRunningBalance.containsKey(entryData.getOfficeId())) {
|
||||
officeRunningBalanceMap = officesRunningBalance.get(entryData.getOfficeId());
|
||||
} else {
|
||||
officeRunningBalanceMap = new HashMap<>();
|
||||
officesRunningBalance.put(entryData.getOfficeId(), officeRunningBalanceMap);
|
||||
}
|
||||
BigDecimal officeRunningBalance = calculateRunningBalance(entryData, officeRunningBalanceMap);
|
||||
BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap);
|
||||
|
||||
params.add(new Object[] { Boolean.TRUE, runningBalance, officeRunningBalance,
|
||||
platformSecurityContext.authenticatedUser().getId(), DateUtils.getAuditOffsetDateTime(), entryData.getId() });
|
||||
batchIndex++;
|
||||
if (batchIndex == batchUpdateSize || index == entryDataList.size() - 1) {
|
||||
this.jdbcTemplate.batchUpdate(sql, params);
|
||||
// reset counter and string array
|
||||
batchIndex = 0;
|
||||
params.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateRunningBalance(Long officeId, LocalDate entityDate) {
|
||||
Map<Long, BigDecimal> runningBalanceMap = new HashMap<>(5);
|
||||
|
||||
final String offlineRunningBalanceQuery = "select je.office_running_balance as runningBalance,je.account_id as accountId from acc_gl_journal_entry je "
|
||||
+ "inner join (select max(id) as id from acc_gl_journal_entry where office_id=? and entry_date < ? group by account_id,entry_date) je2 ON je2.id = je.id "
|
||||
+ "inner join (select max(entry_date) as date from acc_gl_journal_entry where office_id=? and entry_date < ? group by account_id) je3 ON je.entry_date = je3.date "
|
||||
+ "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
|
||||
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(offlineRunningBalanceQuery, // NOSONAR
|
||||
officeId, entityDate, officeId, entityDate);
|
||||
for (Map<String, Object> entries : list) {
|
||||
Long accountId = (Long) entries.get("accountId");
|
||||
if (!runningBalanceMap.containsKey(accountId)) {
|
||||
runningBalanceMap.put(accountId, (BigDecimal) entries.get("runningBalance"));
|
||||
}
|
||||
}
|
||||
List<JournalEntryData> entryDataList = jdbcTemplate.query(entryMapper.officeRunningBalanceSchema(), entryMapper, officeId,
|
||||
entityDate);
|
||||
List<Object[]> params = new ArrayList<>();
|
||||
|
||||
String sql = "UPDATE acc_gl_journal_entry SET office_running_balance=?, last_modified_by=?, last_modified_on_utc=? WHERE id=?";
|
||||
for (JournalEntryData entryData : entryDataList) {
|
||||
BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap);
|
||||
params.add(new Object[] { runningBalance, platformSecurityContext.authenticatedUser().getId(),
|
||||
DateUtils.getAuditOffsetDateTime(), entryData.getId() });
|
||||
}
|
||||
this.jdbcTemplate.batchUpdate(sql, params);
|
||||
}
|
||||
|
||||
private BigDecimal calculateRunningBalance(JournalEntryData entry, Map<Long, BigDecimal> runningBalanceMap) {
|
||||
BigDecimal runningBalance = BigDecimal.ZERO;
|
||||
if (runningBalanceMap.containsKey(entry.getGlAccountId())) {
|
||||
runningBalance = runningBalanceMap.get(entry.getGlAccountId());
|
||||
}
|
||||
GLAccountType accountType = GLAccountType.fromInt(entry.getGlAccountType().getId().intValue());
|
||||
JournalEntryType entryType = JournalEntryType.fromInt(entry.getEntryType().getId().intValue());
|
||||
boolean isIncrease = false;
|
||||
switch (accountType) {
|
||||
case ASSET:
|
||||
case EXPENSE:
|
||||
if (entryType.isDebitType()) {
|
||||
isIncrease = true;
|
||||
}
|
||||
break;
|
||||
case EQUITY:
|
||||
case INCOME:
|
||||
case LIABILITY:
|
||||
if (entryType.isCreditType()) {
|
||||
isIncrease = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isIncrease) {
|
||||
runningBalance = runningBalance.add(entry.getAmount());
|
||||
} else {
|
||||
runningBalance = runningBalance.subtract(entry.getAmount());
|
||||
}
|
||||
runningBalanceMap.put(entry.getGlAccountId(), runningBalance);
|
||||
return runningBalance;
|
||||
}
|
||||
|
||||
private static final class GLJournalEntryMapper implements RowMapper<JournalEntryData> {
|
||||
|
||||
public String officeRunningBalanceSchema() {
|
||||
return "select je.id as id,je.account_id as glAccountId,je.type_enum as entryType,je.amount as amount, "
|
||||
+ "glAccount.classification_enum as classification,je.office_id as officeId "
|
||||
+ "from acc_gl_journal_entry je , acc_gl_account glAccount " + "where je.account_id = glAccount.id "
|
||||
+ "and je.office_id=? and je.entry_date >= ? order by je.entry_date,je.id";
|
||||
}
|
||||
|
||||
public String organizationRunningBalanceSchema() {
|
||||
return "select je.id as id,je.account_id as glAccountId," + "je.type_enum as entryType,je.amount as amount, "
|
||||
+ "glAccount.classification_enum as classification,je.office_id as officeId "
|
||||
+ "from acc_gl_journal_entry je , acc_gl_account glAccount " + "where je.account_id = glAccount.id "
|
||||
+ "and je.entry_date >= ? order by je.entry_date,je.id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JournalEntryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
final Long glAccountId = rs.getLong("glAccountId");
|
||||
final Long officeId = rs.getLong("officeId");
|
||||
final int accountTypeId = JdbcSupport.getInteger(rs, "classification");
|
||||
final EnumOptionData accountType = AccountingEnumerations.gLAccountType(accountTypeId);
|
||||
final BigDecimal amount = rs.getBigDecimal("amount");
|
||||
final int entryTypeId = JdbcSupport.getInteger(rs, "entryType");
|
||||
final EnumOptionData entryType = AccountingEnumerations.journalEntryType(entryTypeId);
|
||||
|
||||
return new JournalEntryData(id, officeId, null, null, glAccountId, null, accountType, null, entryType, amount, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+57
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
@@ -32,6 +34,10 @@ import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
@@ -43,10 +49,14 @@ import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamE
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadService;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/currentaccounts/{accountId}/transactions")
|
||||
@@ -55,11 +65,58 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountTransactionsApiResource {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<SavingsAccountTransactionData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
|
||||
private final PaymentTypeReadService paymentTypeReadService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve current account transaction template")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String retrieveTemplate(@PathParam("accountId") final Long accountId, @Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
final String sql = "SELECT ca.currency_code, ca.currency_digits, ca.currency_multiplesof, "
|
||||
+ "mc.display_symbol, mc.name AS currency_name, mc.internationalized_name_code "
|
||||
+ "FROM m_current_account ca "
|
||||
+ "LEFT JOIN m_currency mc ON mc.code = ca.currency_code "
|
||||
+ "WHERE ca.id = ?";
|
||||
|
||||
final Map<String, Object> template;
|
||||
try {
|
||||
template = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
|
||||
final Map<String, Object> map = new LinkedHashMap<>();
|
||||
final LocalDate today = LocalDate.now();
|
||||
map.put("date", new int[] { today.getYear(), today.getMonthValue(), today.getDayOfMonth() });
|
||||
|
||||
final Map<String, Object> currency = new LinkedHashMap<>();
|
||||
currency.put("code", rs.getString("currency_code"));
|
||||
currency.put("decimalPlaces", rs.getInt("currency_digits"));
|
||||
currency.put("inMultiplesOf", rs.getInt("currency_multiplesof"));
|
||||
currency.put("displaySymbol", rs.getString("display_symbol"));
|
||||
currency.put("name", rs.getString("currency_name"));
|
||||
currency.put("nameCode", rs.getString("internationalized_name_code"));
|
||||
map.put("currency", currency);
|
||||
return map;
|
||||
}, accountId);
|
||||
} catch (org.springframework.dao.EmptyResultDataAccessException e) {
|
||||
throw new CurrentAccountNotFoundException(accountId);
|
||||
}
|
||||
|
||||
final Collection<PaymentTypeData> paymentTypeOptions = paymentTypeReadService.retrieveAllPaymentTypes();
|
||||
template.put("paymentTypeOptions", paymentTypeOptions);
|
||||
|
||||
return GSON.toJson(template);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{transactionId}")
|
||||
|
||||
+225
-214
@@ -1,214 +1,225 @@
|
||||
/**
|
||||
* 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.portfolio.savings.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/currentaccounts")
|
||||
@Component
|
||||
@Tag(name = "Current Account", description = "Current accounts are instances of a particular current product created for an individual or group.")
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountsApiResource {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
private final CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final SqlValidator sqlValidator;
|
||||
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||
private final CurrentAccountReadPlatformService currentAccountReadPlatformService;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve Current Account Template")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String template(@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@QueryParam("groupId") @Parameter(description = "groupId") final Long groupId,
|
||||
@QueryParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
final SavingsAccountData savingsAccount = currentAccountTemplateReadPlatformService.retrieveTemplate(clientId, groupId, productId,
|
||||
staffInSelectedOfficeOnly);
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiSetConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List current accounts")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String retrieveAll(@Context final UriInfo uriInfo,
|
||||
@QueryParam("externalId") final String externalId,
|
||||
@QueryParam("offset") final Integer offset,
|
||||
@QueryParam("limit") final Integer limit,
|
||||
@QueryParam("orderBy") final String orderBy,
|
||||
@QueryParam("sortOrder") final String sortOrder) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
final List<Map<String, Object>> accounts = currentAccountReadPlatformService.retrieveAll();
|
||||
return GSON.toJson(accounts);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Submit new current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.submitApplication(command);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a current account")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String retrieveOne(@PathParam("accountId") final Long accountId,
|
||||
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
|
||||
@DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
final Map<String, Object> account = currentAccountReadPlatformService.retrieveOne(accountId);
|
||||
return GSON.toJson(account);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Modify a current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String update(@PathParam("accountId") final Long accountId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson,
|
||||
@QueryParam("command") final String commandParam) {
|
||||
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.modifyApplication(accountId, command);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve/Reject/Activate/Close/Deposit/Withdrawal and other current account commands")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String handleCommands(@PathParam("accountId") final Long accountId,
|
||||
@QueryParam("command") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
String jsonApiRequest = apiRequestBodyAsJson;
|
||||
if (StringUtils.isBlank(jsonApiRequest)) {
|
||||
jsonApiRequest = "{}";
|
||||
}
|
||||
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(jsonApiRequest));
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "reject")) {
|
||||
result = currentAccountWritePlatformService.rejectApplication(accountId, command);
|
||||
} else if (is(commandParam, "withdrawnByApplicant")) {
|
||||
result = currentAccountWritePlatformService.withdrawApplication(accountId, command);
|
||||
} else if (is(commandParam, "approve")) {
|
||||
result = currentAccountWritePlatformService.approveApplication(accountId, command);
|
||||
} else if (is(commandParam, "undoapproval")) {
|
||||
result = currentAccountWritePlatformService.undoApproval(accountId, command);
|
||||
} else if (is(commandParam, "activate")) {
|
||||
result = currentAccountWritePlatformService.activate(accountId, command);
|
||||
} else if (is(commandParam, "close")) {
|
||||
result = currentAccountWritePlatformService.closeAccount(accountId, command);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "close" });
|
||||
}
|
||||
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Delete a current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String delete(@PathParam("accountId") final Long accountId) {
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.deleteApplication(accountId);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 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.portfolio.savings.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/currentaccounts")
|
||||
@Component
|
||||
@Tag(name = "Current Account", description = "Current accounts are instances of a particular current product created for an individual or group.")
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountsApiResource {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
private final CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final SqlValidator sqlValidator;
|
||||
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||
private final CurrentAccountReadPlatformService currentAccountReadPlatformService;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve Current Account Template")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String template(@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@QueryParam("groupId") @Parameter(description = "groupId") final Long groupId,
|
||||
@QueryParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
final SavingsAccountData savingsAccount = currentAccountTemplateReadPlatformService.retrieveTemplate(clientId, groupId, productId,
|
||||
staffInSelectedOfficeOnly);
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiSetConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List current accounts")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String retrieveAll(@Context final UriInfo uriInfo,
|
||||
@QueryParam("externalId") final String externalId,
|
||||
@QueryParam("offset") final Integer offset,
|
||||
@QueryParam("limit") final Integer limit,
|
||||
@QueryParam("orderBy") final String orderBy,
|
||||
@QueryParam("sortOrder") final String sortOrder,
|
||||
@QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo,
|
||||
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
|
||||
@QueryParam("status") @Parameter(description = "status") final String status,
|
||||
@QueryParam("fromDate") @Parameter(description = "fromDate (YYYY-MM-DD)") final String fromDate,
|
||||
@QueryParam("toDate") @Parameter(description = "toDate (YYYY-MM-DD)") final String toDate,
|
||||
@QueryParam("asOnDate") @Parameter(description = "asOnDate (YYYY-MM-DD)") final String asOnDate) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
final List<Map<String, Object>> accounts = currentAccountReadPlatformService.retrieveAll(offset, limit, accountNo, clientName,
|
||||
status,
|
||||
fromDate != null ? LocalDate.parse(fromDate) : null,
|
||||
toDate != null ? LocalDate.parse(toDate) : null,
|
||||
asOnDate != null ? LocalDate.parse(asOnDate) : null);
|
||||
return GSON.toJson(accounts);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Submit new current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.submitApplication(command);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a current account")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String retrieveOne(@PathParam("accountId") final Long accountId,
|
||||
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
|
||||
@DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
final Map<String, Object> account = currentAccountReadPlatformService.retrieveOne(accountId);
|
||||
return GSON.toJson(account);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Modify a current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String update(@PathParam("accountId") final Long accountId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson,
|
||||
@QueryParam("command") final String commandParam) {
|
||||
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.modifyApplication(accountId, command);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve/Reject/Activate/Close/Deposit/Withdrawal and other current account commands")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String handleCommands(@PathParam("accountId") final Long accountId,
|
||||
@QueryParam("command") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
String jsonApiRequest = apiRequestBodyAsJson;
|
||||
if (StringUtils.isBlank(jsonApiRequest)) {
|
||||
jsonApiRequest = "{}";
|
||||
}
|
||||
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(jsonApiRequest));
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "reject")) {
|
||||
result = currentAccountWritePlatformService.rejectApplication(accountId, command);
|
||||
} else if (is(commandParam, "withdrawnByApplicant")) {
|
||||
result = currentAccountWritePlatformService.withdrawApplication(accountId, command);
|
||||
} else if (is(commandParam, "approve")) {
|
||||
result = currentAccountWritePlatformService.approveApplication(accountId, command);
|
||||
} else if (is(commandParam, "undoapproval")) {
|
||||
result = currentAccountWritePlatformService.undoApproval(accountId, command);
|
||||
} else if (is(commandParam, "activate")) {
|
||||
result = currentAccountWritePlatformService.activate(accountId, command);
|
||||
} else if (is(commandParam, "close")) {
|
||||
result = currentAccountWritePlatformService.closeAccount(accountId, command);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "close" });
|
||||
}
|
||||
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Delete a current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String delete(@PathParam("accountId") final Long accountId) {
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.deleteApplication(accountId);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+531
-520
File diff suppressed because it is too large
Load Diff
+481
-470
@@ -1,470 +1,481 @@
|
||||
/**
|
||||
* 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.portfolio.savings.api;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.bulkimport.data.GlobalEntityType;
|
||||
import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookPopulatorService;
|
||||
import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonQuery;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
|
||||
import org.apache.fineract.infrastructure.core.data.UploadRequest;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.DepositAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositAccountPreMatureCalculationPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.RecurringDepositAccountInterestCalculationService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Path("/v1/recurringdepositaccounts")
|
||||
@Component
|
||||
@Tag(name = "Recurring Deposit Account", description = "Recurring Deposit accounts are instances of a praticular recurring deposit product created. An application process around the creation of accounts is also supported.")
|
||||
@RequiredArgsConstructor
|
||||
public class RecurringDepositAccountsApiResource {
|
||||
|
||||
private final DepositAccountReadPlatformService depositAccountReadPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<DepositAccountData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
private final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService;
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final RecurringDepositAccountInterestCalculationService recurringDepositAccountInterestCalculationService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve recurring Deposit Account Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for recurring deposit applications. The template data returned consists of any or all of:\n"
|
||||
+ "\n" + "Field Defaults\n" + "Allowed Value Lists\n\n" + "Example Requests:\n" + "\n"
|
||||
+ "recurringdepositaccounts/template?clientId=1\n" + "\n" + "\n" + "recurringdepositaccounts/template?clientId=1&productId=1")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsTemplateResponse.class))) })
|
||||
public String template(@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@QueryParam("groupId") @Parameter(description = "groupId") final Long groupId,
|
||||
@QueryParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
final DepositAccountData account = this.depositAccountReadPlatformService.retrieveTemplate(DepositAccountType.RECURRING_DEPOSIT,
|
||||
clientId, groupId, productId, staffInSelectedOfficeOnly);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Recurring deposit applications/accounts", description = "Lists Recurring deposit applications/accounts\n\n"
|
||||
+ "Example Requests:\n" + "\n" + "recurringdepositaccounts\n" + "\n" + "\n" + "recurringdepositaccounts?fields=name")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsResponse.class)))) })
|
||||
public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("paged") @Parameter(description = "paged") final Boolean paged,
|
||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final PaginationParameters paginationParameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit)
|
||||
.offset(offset).orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
if (paginationParameters.isPaged()) {
|
||||
final Page<DepositAccountData> account = this.depositAccountReadPlatformService
|
||||
.retrieveAllPaged(DepositAccountType.RECURRING_DEPOSIT, paginationParameters);
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
final Collection<DepositAccountData> account = this.depositAccountReadPlatformService
|
||||
.retrieveAll(DepositAccountType.RECURRING_DEPOSIT, paginationParameters);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Submit new recurring deposit application", description = "Submits new recurring deposit application\n\n"
|
||||
+ "Mandatory Fields: clientId or groupId, productId, submittedOnDate, depositAmount, depositPeriod, depositPeriodFrequencyId\n\n"
|
||||
+ "Optional Fields: accountNo, externalId, fieldOfficerId,linkAccountId(if provided initial deposit amount will be collected from this account),transferInterestToSavings(By enabling this flag all interest postings will be transferred to linked saving account )\n\n"
|
||||
+ "Inherited from Product (if not provided): interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency, lockinPeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnTypeId, charts, withHoldTax")
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsResponse.class))) })
|
||||
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createRecurringDepositAccount().withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a recurring deposit application/account", description = "Retrieves a recurring deposit application/account\n\n"
|
||||
+ "Example Requests :\n" + "\n" + "recurringdepositaccounts/1\n" + "\n" + "\n" + "recurringdepositaccounts/1?associations=all")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsAccountIdResponse.class))) })
|
||||
public String retrieveOne(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
|
||||
@DefaultValue("all") @QueryParam("chargeStatus") @Parameter(description = "chargeStatus") final String chargeStatus,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) {
|
||||
throw new UnrecognizedQueryParamException("status", chargeStatus, new Object[] { "all", "active", "inactive" });
|
||||
}
|
||||
|
||||
final RecurringDepositAccountData account = (RecurringDepositAccountData) this.depositAccountReadPlatformService
|
||||
.retrieveOneWithChartSlabs(DepositAccountType.RECURRING_DEPOSIT, accountId);
|
||||
|
||||
final Set<String> mandatoryResponseParameters = new HashSet<>();
|
||||
final RecurringDepositAccountData accountTemplate = populateTemplateAndAssociations(accountId, account, staffInSelectedOfficeOnly,
|
||||
chargeStatus, uriInfo, mandatoryResponseParameters);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
|
||||
mandatoryResponseParameters);
|
||||
return this.toApiJsonSerializer.serialize(settings, accountTemplate,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
private RecurringDepositAccountData populateTemplateAndAssociations(final Long accountId,
|
||||
final RecurringDepositAccountData savingsAccount, final boolean staffInSelectedOfficeOnly, final String chargeStatus,
|
||||
final UriInfo uriInfo, final Set<String> mandatoryResponseParameters) {
|
||||
|
||||
Collection<SavingsAccountTransactionData> transactions = null;
|
||||
Collection<SavingsAccountChargeData> charges = null;
|
||||
|
||||
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (!associationParameters.isEmpty()) {
|
||||
|
||||
if (associationParameters.contains("all")) {
|
||||
associationParameters.addAll(Arrays.asList(SavingsApiConstants.transactions, SavingsApiConstants.charges));
|
||||
}
|
||||
|
||||
if (associationParameters.contains(SavingsApiConstants.transactions)) {
|
||||
mandatoryResponseParameters.add(SavingsApiConstants.transactions);
|
||||
final Collection<SavingsAccountTransactionData> currentTransactions = this.depositAccountReadPlatformService
|
||||
.retrieveAllTransactions(DepositAccountType.RECURRING_DEPOSIT, accountId);
|
||||
if (!CollectionUtils.isEmpty(currentTransactions)) {
|
||||
transactions = currentTransactions;
|
||||
}
|
||||
}
|
||||
|
||||
if (associationParameters.contains(SavingsApiConstants.charges)) {
|
||||
mandatoryResponseParameters.add(SavingsApiConstants.charges);
|
||||
final Collection<SavingsAccountChargeData> currentCharges = this.savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountCharges(accountId, chargeStatus);
|
||||
if (!CollectionUtils.isEmpty(currentCharges)) {
|
||||
charges = currentCharges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RecurringDepositAccountData templateData = null;
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
if (settings.isTemplate()) {
|
||||
templateData = (RecurringDepositAccountData) this.depositAccountReadPlatformService.retrieveTemplate(
|
||||
DepositAccountType.RECURRING_DEPOSIT, savingsAccount.getClientId(), savingsAccount.getGroupId(),
|
||||
savingsAccount.getDepositProductId(), staffInSelectedOfficeOnly);
|
||||
}
|
||||
|
||||
return RecurringDepositAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Modify a recurring deposit application", description = "Recurring deposit application can only be modified when in 'Submitted and pending approval' state. Once the application is approved, the details cannot be changed using this method. Specific api endpoints will be created to allow change of interest detail such as rate, compounding period, posting period etc")
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PutRecurringDepositAccountsAccountIdRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PutRecurringDepositAccountsAccountIdResponse.class))) })
|
||||
public String update(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRecurringDepositAccount(accountId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve recurring deposit application | Undo approval recurring deposit application | Reject recurring deposit application | Withdraw recurring deposit application | Activate a recurring deposit account | Update the recommended deposit amount for a recurring deposit account | Close a recurring deposit account | Premature Close a recurring deposit account | Calculate Premature amount on Recurring deposit account | Calculate Interest on recurring Deposit Account | Post Interest on recurring Deposit Account", description = "Approve recurring deposit application:\n\n"
|
||||
+ "Approves recurring deposit application so long as its in 'Submitted and pending approval' state.\n\n"
|
||||
+ "Undo approval recurring deposit application:\n\n"
|
||||
+ "Will move 'approved' recurring deposit application back to 'Submitted and pending approval' state.\n\n"
|
||||
+ "Reject recurring deposit application\n\n"
|
||||
+ "Rejects recurring deposit application so long as its in 'Submitted and pending approval' state.\n\n"
|
||||
+ "Withdraw recurring deposit application:\n\n"
|
||||
+ "Used when an applicant withdraws from the recurring deposit application. It must be in 'Submitted and pending approval' state.\n\n"
|
||||
+ "Activate a recurring deposit account:\n\n"
|
||||
+ "Results in an approved recurring deposit application being converted into an 'active' recurring deposit account.\n\n"
|
||||
+ "Update the recommended deposit amount for a recurring deposit account:\n\n"
|
||||
+ "Updates the recommended deposit amount for a RD account as on the effective date.\n\n"
|
||||
+ "Close a recurring deposit account\n\n"
|
||||
+ "Results in a Matured recurring deposit account being converted into a 'closed' recurring deposit account.\n" + "\n"
|
||||
+ "On account close allowed actions are." + "Premature Close a recurring deposit account:\n\n"
|
||||
+ "Results in an Active recurring deposit account being converted into a 'Premature Closed' recurring deposit account with options to withdraw prematured amount. (premature amount is calculated using interest rate chart applicable along with penal interest if any.)\n"
|
||||
+ "\n" + "On account premature closure allowed actions are.\n\n"
|
||||
+ "Calculate Premature amount on Recurring deposit account:\n\n"
|
||||
+ "Calculate premature amount on recurring deposit till premature close date. Premature amount is calculated based on interest chart and penal interest applicable if any.\n\n"
|
||||
+ "Calculate Interest on recurring Deposit Account:\n\n"
|
||||
+ "Calculates interest earned on a recurring deposit account based on todays date. It does not attempt to post or credit the interest on the account. That is responsibility of the Post Interest API that will likely be called by overnight process.\n\n"
|
||||
+ "Post Interest on recurring Deposit Account:\n\n"
|
||||
+ "Calculates and Posts interest earned on a recurring deposit account based on todays date and whether an interest posting or crediting event is due.\n\n"
|
||||
+ "Showing request/response for 'Post Interest on recurring Deposit Account'")
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsAccountIdRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsAccountIdResponse.class))) })
|
||||
public String handleCommands(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||
@QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
String jsonApiRequest = apiRequestBodyAsJson;
|
||||
if (StringUtils.isBlank(jsonApiRequest)) {
|
||||
jsonApiRequest = "{}";
|
||||
}
|
||||
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest);
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "reject")) {
|
||||
final CommandWrapper commandRequest = builder.rejectRecurringDepositAccountApplication(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "withdrawnByApplicant")) {
|
||||
final CommandWrapper commandRequest = builder.withdrawRecurringDepositAccountApplication(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "approve")) {
|
||||
final CommandWrapper commandRequest = builder.approveRecurringDepositAccountApplication(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "undoapproval")) {
|
||||
final CommandWrapper commandRequest = builder.undoRecurringDepositAccountApplication(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "activate")) {
|
||||
final CommandWrapper commandRequest = builder.recurringDepositAccountActivation(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "calculateInterest")) {
|
||||
final CommandWrapper commandRequest = builder.withNoJsonBody().recurringDepositAccountInterestCalculation(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, DepositsApiConstants.UPDATE_DEPOSIT_AMOUNT)) {
|
||||
final CommandWrapper commandRequest = builder.updateDepositAmountForRecurringDepositAccount(accountId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "postInterest")) {
|
||||
final CommandWrapper commandRequest = builder.recurringDepositAccountInterestPosting(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "close")) {
|
||||
final CommandWrapper commandRequest = builder.closeRecurringDepositAccount(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "prematureClose")) {
|
||||
final CommandWrapper commandRequest = builder.prematureCloseRecurringDepositAccount(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "calculatePrematureAmount")) {
|
||||
final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
|
||||
final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
|
||||
final DepositAccountData account = this.accountPreMatureCalculationPlatformService.calculatePreMatureAmount(accountId, query,
|
||||
DepositAccountType.RECURRING_DEPOSIT);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest",
|
||||
"postInterest", "close", "prematureClose", "calculatePrematureAmount" });
|
||||
}
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("calculate-rd-interest")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String calculateRecurringDepositInterest(@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
|
||||
JsonElement jsonElement = fromJsonHelper.parse(apiRequestBodyAsJson);
|
||||
HashMap<String, Object> result = recurringDepositAccountInterestCalculationService
|
||||
.calculateInterest(new JsonQuery(apiRequestBodyAsJson, jsonElement, fromJsonHelper));
|
||||
|
||||
return toApiJsonSerializer.serializeResult(result);
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Delete a recurring deposit application", description = "At present we support hard delete of recurring deposit application so long as its in 'Submitted and pending approval' state. One the application is moves past this state, it is not possible to do a 'hard' delete of the application or the account. An API endpoint will be added to close/de-activate the recurring deposit account.")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.DeleteRecurringDepositAccountsResponse.class))) })
|
||||
public String delete(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteRecurringDepositAccount(accountId).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{accountId}/template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String accountClosureTemplate(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||
@QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
DepositAccountData account = null;
|
||||
if (is(commandParam, "close")) {
|
||||
account = this.depositAccountReadPlatformService.retrieveOneWithClosureTemplate(DepositAccountType.RECURRING_DEPOSIT,
|
||||
accountId);
|
||||
|
||||
}
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("downloadtemplate")
|
||||
@Produces("application/vnd.ms-excel")
|
||||
public Response getRecurringDepositTemplate(@QueryParam("officeId") final Long officeId, @QueryParam("staffId") final Long staffId,
|
||||
@QueryParam("dateFormat") final String dateFormat) {
|
||||
return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS.toString(), officeId, staffId,
|
||||
dateFormat);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("uploadtemplate")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@RequestBody(description = "Upload recurring deposit template", content = {
|
||||
@Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) })
|
||||
public String postRecurringDepositTemplate(@FormDataParam("file") InputStream uploadedInputStream,
|
||||
@FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale,
|
||||
@FormDataParam("dateFormat") final String dateFormat) {
|
||||
final Long importDocumentId = this.bulkImportWorkbookService.importWorkbook(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS.toString(),
|
||||
uploadedInputStream, fileDetail, locale, dateFormat);
|
||||
return this.toApiJsonSerializer.serialize(importDocumentId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("transactions/downloadtemplate")
|
||||
@Produces("application/vnd.ms-excel")
|
||||
public Response getRecurringDepositTransactionTemplate(@QueryParam("officeId") final Long officeId,
|
||||
@QueryParam("dateFormat") final String dateFormat) {
|
||||
return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS_TRANSACTIONS.toString(), officeId,
|
||||
null, dateFormat);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("transactions/uploadtemplate")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@RequestBody(description = "Upload recurring deposit transactions template", content = {
|
||||
@Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) })
|
||||
public String postRecurringDepositTransactionsTemplate(@FormDataParam("file") InputStream uploadedInputStream,
|
||||
@FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale,
|
||||
@FormDataParam("dateFormat") final String dateFormat) {
|
||||
final Long importDocumentId = this.bulkImportWorkbookService.importWorkbook(
|
||||
GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS_TRANSACTIONS.toString(), uploadedInputStream, fileDetail, locale, dateFormat);
|
||||
return this.toApiJsonSerializer.serialize(importDocumentId);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.portfolio.savings.api;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.bulkimport.data.GlobalEntityType;
|
||||
import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookPopulatorService;
|
||||
import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonQuery;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
|
||||
import org.apache.fineract.infrastructure.core.data.UploadRequest;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.DepositAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositAccountPreMatureCalculationPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.RecurringDepositAccountInterestCalculationService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Path("/v1/recurringdepositaccounts")
|
||||
@Component
|
||||
@Tag(name = "Recurring Deposit Account", description = "Recurring Deposit accounts are instances of a praticular recurring deposit product created. An application process around the creation of accounts is also supported.")
|
||||
@RequiredArgsConstructor
|
||||
public class RecurringDepositAccountsApiResource {
|
||||
|
||||
private final DepositAccountReadPlatformService depositAccountReadPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<DepositAccountData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
private final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService;
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final RecurringDepositAccountInterestCalculationService recurringDepositAccountInterestCalculationService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve recurring Deposit Account Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for recurring deposit applications. The template data returned consists of any or all of:\n"
|
||||
+ "\n" + "Field Defaults\n" + "Allowed Value Lists\n\n" + "Example Requests:\n" + "\n"
|
||||
+ "recurringdepositaccounts/template?clientId=1\n" + "\n" + "\n" + "recurringdepositaccounts/template?clientId=1&productId=1")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsTemplateResponse.class))) })
|
||||
public String template(@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@QueryParam("groupId") @Parameter(description = "groupId") final Long groupId,
|
||||
@QueryParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
final DepositAccountData account = this.depositAccountReadPlatformService.retrieveTemplate(DepositAccountType.RECURRING_DEPOSIT,
|
||||
clientId, groupId, productId, staffInSelectedOfficeOnly);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Recurring deposit applications/accounts", description = "Lists Recurring deposit applications/accounts\n\n"
|
||||
+ "Example Requests:\n" + "\n" + "recurringdepositaccounts\n" + "\n" + "\n" + "recurringdepositaccounts?fields=name")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsResponse.class)))) })
|
||||
public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("paged") @Parameter(description = "paged") final Boolean paged,
|
||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
||||
@QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo,
|
||||
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
|
||||
@QueryParam("status") @Parameter(description = "status") final String status,
|
||||
@QueryParam("fromDate") @Parameter(description = "fromDate (YYYY-MM-DD)") final String fromDate,
|
||||
@QueryParam("toDate") @Parameter(description = "toDate (YYYY-MM-DD)") final String toDate,
|
||||
@QueryParam("asOnDate") @Parameter(description = "asOnDate (YYYY-MM-DD)") final String asOnDate) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final PaginationParameters paginationParameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit)
|
||||
.offset(offset).orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
if (paginationParameters.isPaged()) {
|
||||
final Page<DepositAccountData> account = this.depositAccountReadPlatformService
|
||||
.retrieveAllPaged(DepositAccountType.RECURRING_DEPOSIT, paginationParameters, accountNo, clientName,
|
||||
status,
|
||||
fromDate != null ? LocalDate.parse(fromDate) : null,
|
||||
toDate != null ? LocalDate.parse(toDate) : null,
|
||||
asOnDate != null ? LocalDate.parse(asOnDate) : null);
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
final Collection<DepositAccountData> account = this.depositAccountReadPlatformService
|
||||
.retrieveAll(DepositAccountType.RECURRING_DEPOSIT, paginationParameters);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Submit new recurring deposit application", description = "Submits new recurring deposit application\n\n"
|
||||
+ "Mandatory Fields: clientId or groupId, productId, submittedOnDate, depositAmount, depositPeriod, depositPeriodFrequencyId\n\n"
|
||||
+ "Optional Fields: accountNo, externalId, fieldOfficerId,linkAccountId(if provided initial deposit amount will be collected from this account),transferInterestToSavings(By enabling this flag all interest postings will be transferred to linked saving account )\n\n"
|
||||
+ "Inherited from Product (if not provided): interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency, lockinPeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnTypeId, charts, withHoldTax")
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsResponse.class))) })
|
||||
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createRecurringDepositAccount().withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a recurring deposit application/account", description = "Retrieves a recurring deposit application/account\n\n"
|
||||
+ "Example Requests :\n" + "\n" + "recurringdepositaccounts/1\n" + "\n" + "\n" + "recurringdepositaccounts/1?associations=all")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsAccountIdResponse.class))) })
|
||||
public String retrieveOne(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
|
||||
@DefaultValue("all") @QueryParam("chargeStatus") @Parameter(description = "chargeStatus") final String chargeStatus,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) {
|
||||
throw new UnrecognizedQueryParamException("status", chargeStatus, new Object[] { "all", "active", "inactive" });
|
||||
}
|
||||
|
||||
final RecurringDepositAccountData account = (RecurringDepositAccountData) this.depositAccountReadPlatformService
|
||||
.retrieveOneWithChartSlabs(DepositAccountType.RECURRING_DEPOSIT, accountId);
|
||||
|
||||
final Set<String> mandatoryResponseParameters = new HashSet<>();
|
||||
final RecurringDepositAccountData accountTemplate = populateTemplateAndAssociations(accountId, account, staffInSelectedOfficeOnly,
|
||||
chargeStatus, uriInfo, mandatoryResponseParameters);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
|
||||
mandatoryResponseParameters);
|
||||
return this.toApiJsonSerializer.serialize(settings, accountTemplate,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
private RecurringDepositAccountData populateTemplateAndAssociations(final Long accountId,
|
||||
final RecurringDepositAccountData savingsAccount, final boolean staffInSelectedOfficeOnly, final String chargeStatus,
|
||||
final UriInfo uriInfo, final Set<String> mandatoryResponseParameters) {
|
||||
|
||||
Collection<SavingsAccountTransactionData> transactions = null;
|
||||
Collection<SavingsAccountChargeData> charges = null;
|
||||
|
||||
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (!associationParameters.isEmpty()) {
|
||||
|
||||
if (associationParameters.contains("all")) {
|
||||
associationParameters.addAll(Arrays.asList(SavingsApiConstants.transactions, SavingsApiConstants.charges));
|
||||
}
|
||||
|
||||
if (associationParameters.contains(SavingsApiConstants.transactions)) {
|
||||
mandatoryResponseParameters.add(SavingsApiConstants.transactions);
|
||||
final Collection<SavingsAccountTransactionData> currentTransactions = this.depositAccountReadPlatformService
|
||||
.retrieveAllTransactions(DepositAccountType.RECURRING_DEPOSIT, accountId);
|
||||
if (!CollectionUtils.isEmpty(currentTransactions)) {
|
||||
transactions = currentTransactions;
|
||||
}
|
||||
}
|
||||
|
||||
if (associationParameters.contains(SavingsApiConstants.charges)) {
|
||||
mandatoryResponseParameters.add(SavingsApiConstants.charges);
|
||||
final Collection<SavingsAccountChargeData> currentCharges = this.savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountCharges(accountId, chargeStatus);
|
||||
if (!CollectionUtils.isEmpty(currentCharges)) {
|
||||
charges = currentCharges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RecurringDepositAccountData templateData = null;
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
if (settings.isTemplate()) {
|
||||
templateData = (RecurringDepositAccountData) this.depositAccountReadPlatformService.retrieveTemplate(
|
||||
DepositAccountType.RECURRING_DEPOSIT, savingsAccount.getClientId(), savingsAccount.getGroupId(),
|
||||
savingsAccount.getDepositProductId(), staffInSelectedOfficeOnly);
|
||||
}
|
||||
|
||||
return RecurringDepositAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Modify a recurring deposit application", description = "Recurring deposit application can only be modified when in 'Submitted and pending approval' state. Once the application is approved, the details cannot be changed using this method. Specific api endpoints will be created to allow change of interest detail such as rate, compounding period, posting period etc")
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PutRecurringDepositAccountsAccountIdRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PutRecurringDepositAccountsAccountIdResponse.class))) })
|
||||
public String update(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRecurringDepositAccount(accountId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve recurring deposit application | Undo approval recurring deposit application | Reject recurring deposit application | Withdraw recurring deposit application | Activate a recurring deposit account | Update the recommended deposit amount for a recurring deposit account | Close a recurring deposit account | Premature Close a recurring deposit account | Calculate Premature amount on Recurring deposit account | Calculate Interest on recurring Deposit Account | Post Interest on recurring Deposit Account", description = "Approve recurring deposit application:\n\n"
|
||||
+ "Approves recurring deposit application so long as its in 'Submitted and pending approval' state.\n\n"
|
||||
+ "Undo approval recurring deposit application:\n\n"
|
||||
+ "Will move 'approved' recurring deposit application back to 'Submitted and pending approval' state.\n\n"
|
||||
+ "Reject recurring deposit application\n\n"
|
||||
+ "Rejects recurring deposit application so long as its in 'Submitted and pending approval' state.\n\n"
|
||||
+ "Withdraw recurring deposit application:\n\n"
|
||||
+ "Used when an applicant withdraws from the recurring deposit application. It must be in 'Submitted and pending approval' state.\n\n"
|
||||
+ "Activate a recurring deposit account:\n\n"
|
||||
+ "Results in an approved recurring deposit application being converted into an 'active' recurring deposit account.\n\n"
|
||||
+ "Update the recommended deposit amount for a recurring deposit account:\n\n"
|
||||
+ "Updates the recommended deposit amount for a RD account as on the effective date.\n\n"
|
||||
+ "Close a recurring deposit account\n\n"
|
||||
+ "Results in a Matured recurring deposit account being converted into a 'closed' recurring deposit account.\n" + "\n"
|
||||
+ "On account close allowed actions are." + "Premature Close a recurring deposit account:\n\n"
|
||||
+ "Results in an Active recurring deposit account being converted into a 'Premature Closed' recurring deposit account with options to withdraw prematured amount. (premature amount is calculated using interest rate chart applicable along with penal interest if any.)\n"
|
||||
+ "\n" + "On account premature closure allowed actions are.\n\n"
|
||||
+ "Calculate Premature amount on Recurring deposit account:\n\n"
|
||||
+ "Calculate premature amount on recurring deposit till premature close date. Premature amount is calculated based on interest chart and penal interest applicable if any.\n\n"
|
||||
+ "Calculate Interest on recurring Deposit Account:\n\n"
|
||||
+ "Calculates interest earned on a recurring deposit account based on todays date. It does not attempt to post or credit the interest on the account. That is responsibility of the Post Interest API that will likely be called by overnight process.\n\n"
|
||||
+ "Post Interest on recurring Deposit Account:\n\n"
|
||||
+ "Calculates and Posts interest earned on a recurring deposit account based on todays date and whether an interest posting or crediting event is due.\n\n"
|
||||
+ "Showing request/response for 'Post Interest on recurring Deposit Account'")
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsAccountIdRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsAccountIdResponse.class))) })
|
||||
public String handleCommands(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||
@QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
String jsonApiRequest = apiRequestBodyAsJson;
|
||||
if (StringUtils.isBlank(jsonApiRequest)) {
|
||||
jsonApiRequest = "{}";
|
||||
}
|
||||
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest);
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "reject")) {
|
||||
final CommandWrapper commandRequest = builder.rejectRecurringDepositAccountApplication(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "withdrawnByApplicant")) {
|
||||
final CommandWrapper commandRequest = builder.withdrawRecurringDepositAccountApplication(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "approve")) {
|
||||
final CommandWrapper commandRequest = builder.approveRecurringDepositAccountApplication(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "undoapproval")) {
|
||||
final CommandWrapper commandRequest = builder.undoRecurringDepositAccountApplication(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "activate")) {
|
||||
final CommandWrapper commandRequest = builder.recurringDepositAccountActivation(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "calculateInterest")) {
|
||||
final CommandWrapper commandRequest = builder.withNoJsonBody().recurringDepositAccountInterestCalculation(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, DepositsApiConstants.UPDATE_DEPOSIT_AMOUNT)) {
|
||||
final CommandWrapper commandRequest = builder.updateDepositAmountForRecurringDepositAccount(accountId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "postInterest")) {
|
||||
final CommandWrapper commandRequest = builder.recurringDepositAccountInterestPosting(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "close")) {
|
||||
final CommandWrapper commandRequest = builder.closeRecurringDepositAccount(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "prematureClose")) {
|
||||
final CommandWrapper commandRequest = builder.prematureCloseRecurringDepositAccount(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "calculatePrematureAmount")) {
|
||||
final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
|
||||
final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
|
||||
final DepositAccountData account = this.accountPreMatureCalculationPlatformService.calculatePreMatureAmount(accountId, query,
|
||||
DepositAccountType.RECURRING_DEPOSIT);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest",
|
||||
"postInterest", "close", "prematureClose", "calculatePrematureAmount" });
|
||||
}
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("calculate-rd-interest")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String calculateRecurringDepositInterest(@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
|
||||
JsonElement jsonElement = fromJsonHelper.parse(apiRequestBodyAsJson);
|
||||
HashMap<String, Object> result = recurringDepositAccountInterestCalculationService
|
||||
.calculateInterest(new JsonQuery(apiRequestBodyAsJson, jsonElement, fromJsonHelper));
|
||||
|
||||
return toApiJsonSerializer.serializeResult(result);
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Delete a recurring deposit application", description = "At present we support hard delete of recurring deposit application so long as its in 'Submitted and pending approval' state. One the application is moves past this state, it is not possible to do a 'hard' delete of the application or the account. An API endpoint will be added to close/de-activate the recurring deposit account.")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.DeleteRecurringDepositAccountsResponse.class))) })
|
||||
public String delete(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteRecurringDepositAccount(accountId).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{accountId}/template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String accountClosureTemplate(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||
@QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
DepositAccountData account = null;
|
||||
if (is(commandParam, "close")) {
|
||||
account = this.depositAccountReadPlatformService.retrieveOneWithClosureTemplate(DepositAccountType.RECURRING_DEPOSIT,
|
||||
accountId);
|
||||
|
||||
}
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, account,
|
||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("downloadtemplate")
|
||||
@Produces("application/vnd.ms-excel")
|
||||
public Response getRecurringDepositTemplate(@QueryParam("officeId") final Long officeId, @QueryParam("staffId") final Long staffId,
|
||||
@QueryParam("dateFormat") final String dateFormat) {
|
||||
return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS.toString(), officeId, staffId,
|
||||
dateFormat);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("uploadtemplate")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@RequestBody(description = "Upload recurring deposit template", content = {
|
||||
@Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) })
|
||||
public String postRecurringDepositTemplate(@FormDataParam("file") InputStream uploadedInputStream,
|
||||
@FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale,
|
||||
@FormDataParam("dateFormat") final String dateFormat) {
|
||||
final Long importDocumentId = this.bulkImportWorkbookService.importWorkbook(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS.toString(),
|
||||
uploadedInputStream, fileDetail, locale, dateFormat);
|
||||
return this.toApiJsonSerializer.serialize(importDocumentId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("transactions/downloadtemplate")
|
||||
@Produces("application/vnd.ms-excel")
|
||||
public Response getRecurringDepositTransactionTemplate(@QueryParam("officeId") final Long officeId,
|
||||
@QueryParam("dateFormat") final String dateFormat) {
|
||||
return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS_TRANSACTIONS.toString(), officeId,
|
||||
null, dateFormat);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("transactions/uploadtemplate")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@RequestBody(description = "Upload recurring deposit transactions template", content = {
|
||||
@Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) })
|
||||
public String postRecurringDepositTransactionsTemplate(@FormDataParam("file") InputStream uploadedInputStream,
|
||||
@FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale,
|
||||
@FormDataParam("dateFormat") final String dateFormat) {
|
||||
final Long importDocumentId = this.bulkImportWorkbookService.importWorkbook(
|
||||
GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS_TRANSACTIONS.toString(), uploadedInputStream, fileDetail, locale, dateFormat);
|
||||
return this.toApiJsonSerializer.serialize(importDocumentId);
|
||||
}
|
||||
}
|
||||
|
||||
+656
-643
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "ADJUSTTRANSACTION")
|
||||
public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountTransactionAdjustmentCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public CurrentAccountTransactionAdjustmentCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,6 @@ public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCom
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
final Long transactionId = Long.valueOf(command.getTransactionId());
|
||||
return this.writePlatformService.adjustSavingsTransaction(command.getSavingsId(), transactionId, command);
|
||||
return this.writePlatformService.adjustTransaction(command.getSavingsId(), transactionId, command);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "DEPOSIT")
|
||||
public class DepositCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public DepositCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public DepositCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "HOLDAMOUNT")
|
||||
public class HoldAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public HoldAmountCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public HoldAmountCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "RELEASEAMOUNT")
|
||||
public class ReleaseAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ReleaseAmountCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public ReleaseAmountCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "REVERSETRANSACTION")
|
||||
public class ReverseTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ReverseTransactionCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public ReverseTransactionCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "UNDOTRANSACTION")
|
||||
public class UndoTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UndoTransactionCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public UndoTransactionCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "WITHDRAWAL")
|
||||
public class WithdrawCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public WithdrawCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public WithdrawCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
|
||||
+631
-533
File diff suppressed because it is too large
Load Diff
+468
@@ -0,0 +1,468 @@
|
||||
/**
|
||||
* 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.portfolio.savings.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
|
||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
|
||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.apache.fineract.portfolio.PortfolioProductType;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository;
|
||||
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountTransactionWritePlatformServiceImpl implements CurrentAccountTransactionWritePlatformService {
|
||||
|
||||
// Savings entity type used for GL journal entries (EntityType.SAVINGS = 2)
|
||||
private static final Integer ENTITY_TYPE_SAVINGS = 2;
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final CurrentAccountRepository currentAccountRepository;
|
||||
private final CurrentAccountTransactionRepository currentAccountTransactionRepository;
|
||||
private final CurrentProductRepository currentProductRepository;
|
||||
private final ProductToGLAccountMappingRepository productToGLAccountMappingRepository;
|
||||
private final JournalEntryRepository journalEntryRepository;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult deposit(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final CurrentAccount account = findActiveAccount(accountId);
|
||||
final Office office = account.getOffice();
|
||||
|
||||
LocalDate txnDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
if (txnDate == null) txnDate = LocalDate.now();
|
||||
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||
validatePositiveAmount(amount, accountId, "deposit");
|
||||
|
||||
final String refNo = command.stringValueOfParameterNamed("accountNumber");
|
||||
|
||||
final CurrentAccountTransaction txn = CurrentAccountTransaction.deposit(account, office, amount, txnDate, refNo);
|
||||
|
||||
final BigDecimal oldBalance = safeBalance(account.getAccountBalanceDerived());
|
||||
final BigDecimal newBalance = oldBalance.add(amount);
|
||||
txn.setRunningBalanceDerived(newBalance);
|
||||
|
||||
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||
|
||||
account.setAccountBalanceDerived(newBalance);
|
||||
account.setTotalDepositsDerived(safeBalance(account.getTotalDepositsDerived()).add(amount));
|
||||
currentAccountRepository.save(account);
|
||||
|
||||
postTransactionGl(account, txn, JournalEntryType.DEBIT, JournalEntryType.CREDIT, "Current Account Deposit");
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(txn.getId())
|
||||
.withSavingsId(accountId)
|
||||
.withClientId(account.getClient() != null ? account.getClient().getId() : null)
|
||||
.withOfficeId(office != null ? office.getId() : null)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult withdrawal(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final CurrentAccount account = findActiveAccount(accountId);
|
||||
final Office office = account.getOffice();
|
||||
|
||||
LocalDate txnDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
if (txnDate == null) txnDate = LocalDate.now();
|
||||
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||
validatePositiveAmount(amount, accountId, "withdrawal");
|
||||
|
||||
final BigDecimal currentBalance = safeBalance(account.getAccountBalanceDerived());
|
||||
final BigDecimal onHold = safeBalance(account.getOnHoldFundsDerived());
|
||||
final BigDecimal available = currentBalance.subtract(onHold);
|
||||
|
||||
if (!account.isAllowOverdraft() && amount.compareTo(available) > 0) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.insufficient.balance",
|
||||
"Insufficient available balance. Available: " + available + ", Requested: " + amount, accountId);
|
||||
}
|
||||
|
||||
final String refNo = command.stringValueOfParameterNamed("accountNumber");
|
||||
|
||||
final CurrentAccountTransaction txn = CurrentAccountTransaction.withdrawal(account, office, amount, txnDate, refNo);
|
||||
|
||||
final BigDecimal newBalance = currentBalance.subtract(amount);
|
||||
txn.setRunningBalanceDerived(newBalance);
|
||||
|
||||
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||
|
||||
account.setAccountBalanceDerived(newBalance);
|
||||
account.setTotalWithdrawalsDerived(safeBalance(account.getTotalWithdrawalsDerived()).add(amount));
|
||||
currentAccountRepository.save(account);
|
||||
|
||||
// Withdrawal GL: DR SAVINGS_CONTROL (liability down), CR SAVINGS_REFERENCE (cash out)
|
||||
postTransactionGl(account, txn, JournalEntryType.CREDIT, JournalEntryType.DEBIT, "Current Account Withdrawal");
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(txn.getId())
|
||||
.withSavingsId(accountId)
|
||||
.withClientId(account.getClient() != null ? account.getClient().getId() : null)
|
||||
.withOfficeId(office != null ? office.getId() : null)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult holdAmount(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final CurrentAccount account = findActiveAccount(accountId);
|
||||
final Office office = account.getOffice();
|
||||
|
||||
LocalDate txnDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
if (txnDate == null) txnDate = LocalDate.now();
|
||||
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||
validatePositiveAmount(amount, accountId, "hold amount");
|
||||
|
||||
final BigDecimal currentBalance = safeBalance(account.getAccountBalanceDerived());
|
||||
final BigDecimal onHold = safeBalance(account.getOnHoldFundsDerived());
|
||||
final BigDecimal available = currentBalance.subtract(onHold);
|
||||
|
||||
if (amount.compareTo(available) > 0) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.insufficient.balance.for.hold",
|
||||
"Insufficient available balance to place hold. Available: " + available + ", Requested: " + amount, accountId);
|
||||
}
|
||||
|
||||
final String reason = command.stringValueOfParameterNamed("reasonForBlock");
|
||||
|
||||
final CurrentAccountTransaction txn = CurrentAccountTransaction.holdAmount(account, office, amount, txnDate, reason);
|
||||
txn.setRunningBalanceDerived(currentBalance);
|
||||
|
||||
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||
|
||||
account.setOnHoldFundsDerived(onHold.add(amount));
|
||||
currentAccountRepository.save(account);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(txn.getId())
|
||||
.withSavingsId(accountId)
|
||||
.withClientId(account.getClient() != null ? account.getClient().getId() : null)
|
||||
.withOfficeId(office != null ? office.getId() : null)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult releaseAmount(final Long accountId, final Long transactionId) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final CurrentAccount account = findActiveAccount(accountId);
|
||||
final Office office = account.getOffice();
|
||||
|
||||
final CurrentAccountTransaction holdTxn = currentAccountTransactionRepository.findById(transactionId)
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.current.account.transaction.not.found",
|
||||
"Hold transaction not found: " + transactionId, transactionId));
|
||||
|
||||
if (!holdTxn.getCurrentAccount().getId().equals(accountId)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.not.found",
|
||||
"Transaction does not belong to this account.", transactionId);
|
||||
}
|
||||
|
||||
if (holdTxn.isReversed()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.already.reversed",
|
||||
"Hold transaction is already released/reversed.", transactionId);
|
||||
}
|
||||
|
||||
final BigDecimal holdAmount = holdTxn.getAmount();
|
||||
final BigDecimal currentBalance = safeBalance(account.getAccountBalanceDerived());
|
||||
|
||||
// Mark hold transaction as reversed
|
||||
holdTxn.setReversed(true);
|
||||
currentAccountTransactionRepository.save(holdTxn);
|
||||
|
||||
// Create release transaction
|
||||
final CurrentAccountTransaction releaseTxn = CurrentAccountTransaction.releaseAmount(
|
||||
account, office, holdAmount, LocalDate.now(), transactionId);
|
||||
releaseTxn.setRunningBalanceDerived(currentBalance);
|
||||
currentAccountTransactionRepository.saveAndFlush(releaseTxn);
|
||||
|
||||
// Reduce onHold
|
||||
final BigDecimal onHold = safeBalance(account.getOnHoldFundsDerived());
|
||||
account.setOnHoldFundsDerived(onHold.subtract(holdAmount).max(BigDecimal.ZERO));
|
||||
currentAccountRepository.save(account);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(releaseTxn.getId())
|
||||
.withSavingsId(accountId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult undoTransaction(final Long accountId, final Long transactionId, final boolean isReversal) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final CurrentAccount account = findActiveAccount(accountId);
|
||||
|
||||
final CurrentAccountTransaction txn = currentAccountTransactionRepository.findById(transactionId)
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.current.account.transaction.not.found",
|
||||
"Transaction not found: " + transactionId, transactionId));
|
||||
|
||||
if (!txn.getCurrentAccount().getId().equals(accountId)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.not.found",
|
||||
"Transaction does not belong to this account.", transactionId);
|
||||
}
|
||||
|
||||
if (txn.isReversed()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.already.reversed",
|
||||
"Transaction is already reversed.", transactionId);
|
||||
}
|
||||
|
||||
reverseTransactionBalances(account, txn);
|
||||
|
||||
txn.setReversed(true);
|
||||
currentAccountTransactionRepository.save(txn);
|
||||
currentAccountRepository.save(account);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(transactionId)
|
||||
.withSavingsId(accountId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult reverseTransaction(final Long accountId, final Long transactionId,
|
||||
final boolean isReversal, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final CurrentAccount account = findActiveAccount(accountId);
|
||||
|
||||
final CurrentAccountTransaction original = currentAccountTransactionRepository.findById(transactionId)
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.current.account.transaction.not.found",
|
||||
"Transaction not found: " + transactionId, transactionId));
|
||||
|
||||
if (!original.getCurrentAccount().getId().equals(accountId)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.not.found",
|
||||
"Transaction does not belong to this account.", transactionId);
|
||||
}
|
||||
|
||||
if (original.isReversed()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.already.reversed",
|
||||
"Transaction is already reversed.", transactionId);
|
||||
}
|
||||
|
||||
reverseTransactionBalances(account, original);
|
||||
original.setReversed(true);
|
||||
currentAccountTransactionRepository.save(original);
|
||||
|
||||
// Create a reversal entry
|
||||
final CurrentAccountTransaction reversalTxn = CurrentAccountTransaction.reversal(
|
||||
account, account.getOffice(), original.getAmount(), LocalDate.now(), transactionId);
|
||||
reversalTxn.setRunningBalanceDerived(safeBalance(account.getAccountBalanceDerived()));
|
||||
currentAccountTransactionRepository.saveAndFlush(reversalTxn);
|
||||
|
||||
currentAccountRepository.save(account);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(reversalTxn.getId())
|
||||
.withSavingsId(accountId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult adjustTransaction(final Long accountId, final Long transactionId,
|
||||
final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final CurrentAccount account = findActiveAccount(accountId);
|
||||
final Office office = account.getOffice();
|
||||
|
||||
final CurrentAccountTransaction original = currentAccountTransactionRepository.findById(transactionId)
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.current.account.transaction.not.found",
|
||||
"Transaction not found: " + transactionId, transactionId));
|
||||
|
||||
if (!original.getCurrentAccount().getId().equals(accountId)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.not.found",
|
||||
"Transaction does not belong to this account.", transactionId);
|
||||
}
|
||||
|
||||
if (original.isReversed()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.already.reversed",
|
||||
"Transaction is already reversed.", transactionId);
|
||||
}
|
||||
|
||||
final BigDecimal newAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||
validatePositiveAmount(newAmount, accountId, "adjust");
|
||||
|
||||
LocalDate newDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
if (newDate == null) newDate = original.getTransactionDate();
|
||||
|
||||
// Undo original
|
||||
reverseTransactionBalances(account, original);
|
||||
original.setReversed(true);
|
||||
currentAccountTransactionRepository.save(original);
|
||||
|
||||
// Create adjusted transaction of the same type
|
||||
final CurrentAccountTransaction adjusted;
|
||||
if (original.getTransactionTypeEnum() == 1) {
|
||||
adjusted = CurrentAccountTransaction.deposit(account, office, newAmount, newDate,
|
||||
command.stringValueOfParameterNamed("accountNumber"));
|
||||
} else {
|
||||
adjusted = CurrentAccountTransaction.withdrawal(account, office, newAmount, newDate,
|
||||
command.stringValueOfParameterNamed("accountNumber"));
|
||||
}
|
||||
adjusted.setOriginalTransactionId(transactionId);
|
||||
adjusted.setRunningBalanceDerived(safeBalance(account.getAccountBalanceDerived()));
|
||||
currentAccountTransactionRepository.saveAndFlush(adjusted);
|
||||
|
||||
// Apply new transaction to balance
|
||||
applyTransactionToBalance(account, adjusted);
|
||||
currentAccountRepository.save(account);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(adjusted.getId())
|
||||
.withSavingsId(accountId)
|
||||
.build();
|
||||
}
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
private CurrentAccount findActiveAccount(final Long accountId) {
|
||||
final CurrentAccount account = currentAccountRepository.findById(accountId)
|
||||
.orElseThrow(() -> new CurrentAccountNotFoundException(accountId));
|
||||
if (!Integer.valueOf(300).equals(account.getStatusEnum())) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.not.active",
|
||||
"Current account is not in Active state.", accountId);
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
private void validatePositiveAmount(final BigDecimal amount, final Long accountId, final String operation) {
|
||||
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new GeneralPlatformDomainRuleException(
|
||||
"error.msg.current.account." + operation + ".invalid.amount",
|
||||
"Transaction amount must be greater than zero for " + operation + ".", accountId);
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal safeBalance(final BigDecimal value) {
|
||||
return value != null ? value : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
private void reverseTransactionBalances(final CurrentAccount account, final CurrentAccountTransaction txn) {
|
||||
final BigDecimal amount = txn.getAmount();
|
||||
final Integer type = txn.getTransactionTypeEnum();
|
||||
|
||||
if (Integer.valueOf(1).equals(type)) {
|
||||
// Was a deposit: reverse it
|
||||
account.setAccountBalanceDerived(safeBalance(account.getAccountBalanceDerived()).subtract(amount));
|
||||
account.setTotalDepositsDerived(safeBalance(account.getTotalDepositsDerived()).subtract(amount).max(BigDecimal.ZERO));
|
||||
} else if (Integer.valueOf(2).equals(type)) {
|
||||
// Was a withdrawal: reverse it
|
||||
account.setAccountBalanceDerived(safeBalance(account.getAccountBalanceDerived()).add(amount));
|
||||
account.setTotalWithdrawalsDerived(safeBalance(account.getTotalWithdrawalsDerived()).subtract(amount).max(BigDecimal.ZERO));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyTransactionToBalance(final CurrentAccount account, final CurrentAccountTransaction txn) {
|
||||
final BigDecimal amount = txn.getAmount();
|
||||
final Integer type = txn.getTransactionTypeEnum();
|
||||
|
||||
if (Integer.valueOf(1).equals(type)) {
|
||||
account.setAccountBalanceDerived(safeBalance(account.getAccountBalanceDerived()).add(amount));
|
||||
account.setTotalDepositsDerived(safeBalance(account.getTotalDepositsDerived()).add(amount));
|
||||
} else if (Integer.valueOf(2).equals(type)) {
|
||||
account.setAccountBalanceDerived(safeBalance(account.getAccountBalanceDerived()).subtract(amount));
|
||||
account.setTotalWithdrawalsDerived(safeBalance(account.getTotalWithdrawalsDerived()).add(amount));
|
||||
}
|
||||
}
|
||||
|
||||
// GL: cashEntryType=DEBIT/CREDIT for SAVINGS_REFERENCE; controlEntryType for SAVINGS_CONTROL
|
||||
private void postTransactionGl(final CurrentAccount account, final CurrentAccountTransaction txn,
|
||||
final JournalEntryType cashEntryType, final JournalEntryType controlEntryType, final String description) {
|
||||
try {
|
||||
final CurrentProduct product = currentProductRepository.findById(account.getProductId()).orElse(null);
|
||||
if (product == null) return;
|
||||
final Integer accountingType = product.getAccountingType();
|
||||
if (accountingType == null || accountingType == 1) return; // NONE = 1
|
||||
|
||||
final GLAccount cashAccount = findGlAccount(account.getProductId(),
|
||||
CashAccountsForSavings.SAVINGS_REFERENCE.getValue());
|
||||
final GLAccount controlAccount = findGlAccount(account.getProductId(),
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue());
|
||||
|
||||
if (cashAccount == null || controlAccount == null) {
|
||||
log.warn("[GL] Missing GL account mapping for current account product id={}", account.getProductId());
|
||||
return;
|
||||
}
|
||||
|
||||
final String ref = "CAT" + txn.getId();
|
||||
postGlEntry(account, cashAccount, cashEntryType, txn.getAmount(), txn.getTransactionDate(), ref, description);
|
||||
postGlEntry(account, controlAccount, controlEntryType, txn.getAmount(), txn.getTransactionDate(), ref, description);
|
||||
} catch (Exception e) {
|
||||
log.error("[GL] Failed GL posting for CA transaction id={}: {}", txn.getId(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private GLAccount findGlAccount(final Long productId, final int financialAccountType) {
|
||||
final var mapping = productToGLAccountMappingRepository
|
||||
.findByProductIdAndProductTypeAndFinancialAccountTypeAndPaymentTypeId(
|
||||
productId, PortfolioProductType.SAVING.getValue(), financialAccountType, null);
|
||||
return mapping != null ? mapping.getGlAccount() : null;
|
||||
}
|
||||
|
||||
private void postGlEntry(final CurrentAccount account, final GLAccount glAccount,
|
||||
final JournalEntryType entryType, final BigDecimal amount,
|
||||
final LocalDate date, final String transactionRef, final String description) {
|
||||
final JournalEntry entry = JournalEntry.createNew(
|
||||
account.getOffice(), null, glAccount,
|
||||
account.getCurrencyCode(), transactionRef,
|
||||
false, date, entryType, amount,
|
||||
description,
|
||||
ENTITY_TYPE_SAVINGS, account.getId(),
|
||||
null, null, null, null, null);
|
||||
journalEntryRepository.save(entry);
|
||||
}
|
||||
}
|
||||
+63
-56
@@ -1,56 +1,63 @@
|
||||
/**
|
||||
* 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.portfolio.savings.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.DepositAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
|
||||
public interface DepositAccountReadPlatformService {
|
||||
|
||||
Collection<DepositAccountData> retrieveAll(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
|
||||
|
||||
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
|
||||
|
||||
Collection<DepositAccountData> retrieveAllForLookup(DepositAccountType depositAccountType);
|
||||
|
||||
DepositAccountData retrieveOne(DepositAccountType depositAccountType, Long accountId);
|
||||
|
||||
DepositAccountData retrieveOneWithClosureTemplate(DepositAccountType depositAccountType, Long accountId);
|
||||
|
||||
DepositAccountData retrieveOneWithChartSlabs(DepositAccountType depositAccountType, Long productId);
|
||||
|
||||
Collection<SavingsAccountTransactionData> retrieveAllTransactions(DepositAccountType depositAccountType, Long accountId);
|
||||
|
||||
DepositAccountData retrieveTemplate(DepositAccountType depositAccountType, Long clientId, Long groupId, Long productId,
|
||||
boolean staffInSelectedOfficeOnly);
|
||||
|
||||
Collection<DepositAccountData> retrieveForMaturityUpdate();
|
||||
|
||||
SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(Long accountId);
|
||||
|
||||
Collection<AccountTransferDTO> retrieveDataForInterestTransfer();
|
||||
|
||||
Collection<Map<String, Object>> retriveDataForRDScheduleCreation();
|
||||
}
|
||||
/**
|
||||
* 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.portfolio.savings.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.DepositAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
|
||||
public interface DepositAccountReadPlatformService {
|
||||
|
||||
Collection<DepositAccountData> retrieveAll(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
|
||||
|
||||
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
|
||||
|
||||
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters,
|
||||
String accountNo, String clientName);
|
||||
|
||||
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters,
|
||||
String accountNo, String clientName, String status, LocalDate fromDate, LocalDate toDate, LocalDate asOnDate);
|
||||
|
||||
Collection<DepositAccountData> retrieveAllForLookup(DepositAccountType depositAccountType);
|
||||
|
||||
DepositAccountData retrieveOne(DepositAccountType depositAccountType, Long accountId);
|
||||
|
||||
DepositAccountData retrieveOneWithClosureTemplate(DepositAccountType depositAccountType, Long accountId);
|
||||
|
||||
DepositAccountData retrieveOneWithChartSlabs(DepositAccountType depositAccountType, Long productId);
|
||||
|
||||
Collection<SavingsAccountTransactionData> retrieveAllTransactions(DepositAccountType depositAccountType, Long accountId);
|
||||
|
||||
DepositAccountData retrieveTemplate(DepositAccountType depositAccountType, Long clientId, Long groupId, Long productId,
|
||||
boolean staffInSelectedOfficeOnly);
|
||||
|
||||
Collection<DepositAccountData> retrieveForMaturityUpdate();
|
||||
|
||||
SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(Long accountId);
|
||||
|
||||
Collection<AccountTransferDTO> retrieveDataForInterestTransfer();
|
||||
|
||||
Collection<Map<String, Object>> retriveDataForRDScheduleCreation();
|
||||
}
|
||||
|
||||
+2027
-1939
File diff suppressed because it is too large
Load Diff
+1582
-1533
File diff suppressed because it is too large
Load Diff
+4044
-4043
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user