Merge remote-tracking branch 'self-hosted/feature/account-charges' into feature/Backend-DSC-MFA

This commit is contained in:
Aditya
2026-05-21 16:15:28 +05:30
30 changed files with 14663 additions and 13696 deletions
@@ -1,108 +1,109 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.accounting.journalentry; package org.apache.fineract.accounting.journalentry;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType; import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.journalentry.data.JournalEntryData; import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
import org.apache.fineract.accounting.journalentry.domain.JournalEntry; import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType; import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig; import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.PortfolioProductType; import org.apache.fineract.portfolio.PortfolioProductType;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.Named; import org.mapstruct.Named;
@Mapper(config = MapstructMapperConfig.class) @Mapper(config = MapstructMapperConfig.class)
public interface JournalEntryMapper { public interface JournalEntryMapper {
@Mapping(target = "id", source = "id") @Mapping(target = "id", source = "id")
@Mapping(target = "officeId", source = "office.id") @Mapping(target = "officeId", source = "office.id")
@Mapping(target = "officeName", source = "office.name") @Mapping(target = "officeName", source = "office.name")
@Mapping(target = "glAccountId", source = "glAccount.id") @Mapping(target = "glAccountId", source = "glAccount.id")
@Mapping(target = "glAccountCode", source = "glAccount.glCode") @Mapping(target = "glAccountCode", source = "glAccount.glCode")
@Mapping(target = "glAccountName", source = "glAccount.name") @Mapping(target = "glAccountName", source = "glAccount.name")
@Mapping(target = "glAccountType", source = "glAccount.type", qualifiedByName = "glAccountType") @Mapping(target = "glAccountType", source = "glAccount.type", qualifiedByName = "glAccountType")
@Mapping(target = "transactionDate", source = "transactionDate") @Mapping(target = "transactionDate", source = "transactionDate")
@Mapping(target = "entryType", source = "type", qualifiedByName = "journalEntryType") @Mapping(target = "entryType", source = "type", qualifiedByName = "journalEntryType")
@Mapping(target = "amount", source = "amount") @Mapping(target = "amount", source = "amount")
@Mapping(target = "entityType", source = "entityType", qualifiedByName = "entityType") @Mapping(target = "entityType", source = "entityType", qualifiedByName = "entityType")
@Mapping(target = "entityId", source = "entityId") @Mapping(target = "entityId", source = "entityId")
@Mapping(target = "submittedOnDate", expression = "java(journalEntry.getCreatedDate().orElse(null))") @Mapping(target = "submittedOnDate", expression = "java(journalEntry.getCreatedDate().orElse(null))")
@Mapping(target = "transactionId", source = "transactionId") @Mapping(target = "transactionId", source = "transactionId")
@Mapping(target = "currency", source = "currencyCode") @Mapping(target = "currency", source = "currencyCode")
@Mapping(target = "manualEntry", source = "manualEntry") @Mapping(target = "manualEntry", source = "manualEntry")
@Mapping(target = "reversed", source = "reversed") @Mapping(target = "reversed", source = "reversed")
@Mapping(target = "referenceNumber", source = "referenceNumber") @Mapping(target = "referenceNumber", source = "referenceNumber")
@Mapping(target = "paymentTypeId", source = "paymentDetail.paymentType.id") @Mapping(target = "paymentTypeId", source = "paymentDetail.paymentType.id")
@Mapping(target = "accountNumber", source = "paymentDetail.accountNumber") @Mapping(target = "accountNumber", source = "paymentDetail.accountNumber")
@Mapping(target = "checkNumber", source = "paymentDetail.checkNumber") @Mapping(target = "checkNumber", source = "paymentDetail.checkNumber")
@Mapping(target = "routingCode", source = "paymentDetail.routingCode") @Mapping(target = "routingCode", source = "paymentDetail.routingCode")
@Mapping(target = "receiptNumber", source = "paymentDetail.receiptNumber") @Mapping(target = "receiptNumber", source = "paymentDetail.receiptNumber")
@Mapping(target = "bankNumber", source = "paymentDetail.bankNumber") @Mapping(target = "bankNumber", source = "paymentDetail.bankNumber")
@Mapping(target = "createdDate", ignore = true) @Mapping(target = "createdDate", ignore = true)
@Mapping(target = "createdByUserId", ignore = true) @Mapping(target = "createdByUserId", ignore = true)
@Mapping(target = "createdByUserName", ignore = true) @Mapping(target = "createdByUserName", ignore = true)
@Mapping(target = "comments", ignore = true) @Mapping(target = "comments", ignore = true)
@Mapping(target = "officeRunningBalance", ignore = true) @Mapping(target = "officeRunningBalance", ignore = true)
@Mapping(target = "organizationRunningBalance", ignore = true) @Mapping(target = "organizationRunningBalance", ignore = true)
@Mapping(target = "runningBalanceComputed", ignore = true) @Mapping(target = "runningBalanceComputed", ignore = true)
@Mapping(target = "rowIndex", ignore = true) @Mapping(target = "rowIndex", ignore = true)
@Mapping(target = "dateFormat", ignore = true) @Mapping(target = "dateFormat", ignore = true)
@Mapping(target = "locale", ignore = true) @Mapping(target = "locale", ignore = true)
@Mapping(target = "credits", ignore = true) @Mapping(target = "credits", ignore = true)
@Mapping(target = "debits", ignore = true) @Mapping(target = "debits", ignore = true)
@Mapping(target = "transactionDetails", ignore = true) @Mapping(target = "transactionDetails", ignore = true)
@Mapping(target = "savingTransactionId", ignore = true) @Mapping(target = "savingTransactionId", ignore = true)
@Mapping(target = "externalAssetOwner", ignore = true) @Mapping(target = "externalAssetOwner", ignore = true)
JournalEntryData map(JournalEntry journalEntry); @Mapping(target = "savingsAccountNo", ignore = true)
JournalEntryData map(JournalEntry journalEntry);
@Named("entityType")
default PortfolioProductType mapEntityType(Integer entityTypeId) { @Named("entityType")
return PortfolioProductType.fromInt(entityTypeId); default PortfolioProductType mapEntityType(Integer entityTypeId) {
} return PortfolioProductType.fromInt(entityTypeId);
}
@Named("glAccountType")
default GLAccountType mapGlAccountType(Integer typeId) { @Named("glAccountType")
return GLAccountType.fromInt(typeId); default GLAccountType mapGlAccountType(Integer typeId) {
} return GLAccountType.fromInt(typeId);
}
@Named("journalEntryType")
default JournalEntryType mapJournalEntryType(Integer typeId) { @Named("journalEntryType")
return JournalEntryType.fromInt(typeId); 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 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 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 EnumOptionData mapEntityType(PortfolioProductType entityType) {
} return new EnumOptionData((long) entityType.getValue(), entityType.getCode(), entityType.name());
}
default CurrencyData mapCurrency(String currencyCode) {
return new CurrencyData(currencyCode); default CurrencyData mapCurrency(String currencyCode) {
} return new CurrencyData(currencyCode);
} }
}
@@ -1,287 +1,291 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.accounting.journalentry.data; package org.apache.fineract.accounting.journalentry.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.List; import java.util.List;
import lombok.Data; import lombok.Data;
import org.apache.fineract.accounting.glaccount.data.GLAccountData; import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.data.CurrencyData;
/** /**
* Immutable object representing a General Ledger Account * Immutable object representing a General Ledger Account
* *
* Note: no getter/setters required as google will produce json from fields of object. * Note: no getter/setters required as google will produce json from fields of object.
*/ */
@Data @Data
public class JournalEntryData { public class JournalEntryData {
private Long id; private Long id;
private Long officeId; private Long officeId;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private String officeName; private String officeName;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private String glAccountName; private String glAccountName;
private Long glAccountId; private Long glAccountId;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private String glAccountCode; private String glAccountCode;
private EnumOptionData glAccountType; private EnumOptionData glAccountType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private LocalDate transactionDate; private LocalDate transactionDate;
private EnumOptionData entryType; private EnumOptionData entryType;
private BigDecimal amount; private BigDecimal amount;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private CurrencyData currency; private CurrencyData currency;
private String transactionId; private String transactionId;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private Boolean manualEntry; private Boolean manualEntry;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private EnumOptionData entityType; private EnumOptionData entityType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private Long entityId; private Long entityId;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private Long createdByUserId; private String savingsAccountNo;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private OffsetDateTime createdDate; private Long createdByUserId;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private String createdByUserName; private OffsetDateTime createdDate;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private String comments; private String createdByUserName;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private Boolean reversed; private String comments;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private String referenceNumber; private Boolean reversed;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private BigDecimal officeRunningBalance; private String referenceNumber;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private BigDecimal organizationRunningBalance; private BigDecimal officeRunningBalance;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private Boolean runningBalanceComputed; private BigDecimal organizationRunningBalance;
@SuppressWarnings("unused")
@SuppressWarnings("unused") private Boolean runningBalanceComputed;
private TransactionDetailData transactionDetails;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private OffsetDateTime submittedOnDate; private TransactionDetailData transactionDetails;
@SuppressWarnings("unused")
// import fields private OffsetDateTime submittedOnDate;
private transient Integer rowIndex;
private String dateFormat; // import fields
private String locale; private transient Integer rowIndex;
private List<CreditDebit> credits; private String dateFormat;
private List<CreditDebit> debits; private String locale;
private Long paymentTypeId; private List<CreditDebit> credits;
private String currencyCode; private List<CreditDebit> debits;
private String accountNumber; private Long paymentTypeId;
private String checkNumber; private String currencyCode;
private String routingCode; private String accountNumber;
private String receiptNumber; private String checkNumber;
private String bankNumber; private String routingCode;
private String externalAssetOwner; private String receiptNumber;
private transient Long savingTransactionId; private String bankNumber;
private String externalAssetOwner;
public JournalEntryData() {} private transient Long savingTransactionId;
// for opening bal bulk import public JournalEntryData() {}
public JournalEntryData(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
List<CreditDebit> debits, String locale, String dateFormat) { // for opening bal bulk import
this.officeId = officeId; public JournalEntryData(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
this.dateFormat = dateFormat; List<CreditDebit> debits, String locale, String dateFormat) {
this.locale = locale; this.officeId = officeId;
this.transactionDate = transactionDate; this.dateFormat = dateFormat;
this.currencyCode = currencyCode; this.locale = locale;
this.credits = credits; this.transactionDate = transactionDate;
this.debits = debits; this.currencyCode = currencyCode;
this.rowIndex = null; this.credits = credits;
this.paymentTypeId = null; this.debits = debits;
this.accountNumber = null; this.rowIndex = null;
this.checkNumber = null; this.paymentTypeId = null;
this.routingCode = null; this.accountNumber = null;
this.receiptNumber = null; this.checkNumber = null;
this.bankNumber = null; this.routingCode = null;
this.comments = null; this.receiptNumber = null;
this.id = null; this.bankNumber = null;
this.officeName = null; this.comments = null;
this.glAccountName = null; this.id = null;
this.glAccountId = null; this.officeName = null;
this.glAccountCode = null; this.glAccountName = null;
this.glAccountType = null; this.glAccountId = null;
this.entryType = null; this.glAccountCode = null;
this.amount = null; this.glAccountType = null;
this.currency = null; this.entryType = null;
this.transactionId = null; this.amount = null;
this.manualEntry = null; this.currency = null;
this.entityType = null; this.transactionId = null;
this.entityId = null; this.manualEntry = null;
this.createdByUserId = null; this.entityType = null;
this.createdDate = null; this.entityId = null;
this.createdByUserName = null; this.createdByUserId = null;
this.reversed = null; this.createdDate = null;
this.referenceNumber = null; this.createdByUserName = null;
this.officeRunningBalance = null; this.reversed = null;
this.organizationRunningBalance = null; this.referenceNumber = null;
this.runningBalanceComputed = null; this.officeRunningBalance = null;
this.transactionDetails = null; this.organizationRunningBalance = null;
this.submittedOnDate = 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) { private JournalEntryData(Long officeId, LocalDate transactionDate, String currencyCode, Long paymentTypeId, Integer rowIndex,
List<CreditDebit> credits, List<CreditDebit> debits, String accountNumber, String checkNumber, String routingCode,
this.officeId = officeId; String receiptNumber, String bankNumber, String comments, String locale, String dateFormat) {
this.dateFormat = dateFormat;
this.locale = locale; this.officeId = officeId;
this.transactionDate = transactionDate; this.dateFormat = dateFormat;
this.currencyCode = currencyCode; this.locale = locale;
this.rowIndex = rowIndex; this.transactionDate = transactionDate;
this.credits = credits; this.currencyCode = currencyCode;
this.debits = debits; this.rowIndex = rowIndex;
this.paymentTypeId = paymentTypeId; this.credits = credits;
this.accountNumber = accountNumber; this.debits = debits;
this.checkNumber = checkNumber; this.paymentTypeId = paymentTypeId;
this.routingCode = routingCode; this.accountNumber = accountNumber;
this.receiptNumber = receiptNumber; this.checkNumber = checkNumber;
this.bankNumber = bankNumber; this.routingCode = routingCode;
this.comments = comments; this.receiptNumber = receiptNumber;
this.id = null; this.bankNumber = bankNumber;
this.officeName = null; this.comments = comments;
this.glAccountName = null; this.id = null;
this.glAccountId = null; this.officeName = null;
this.glAccountCode = null; this.glAccountName = null;
this.glAccountType = null; this.glAccountId = null;
this.entryType = null; this.glAccountCode = null;
this.amount = null; this.glAccountType = null;
this.currency = null; this.entryType = null;
this.transactionId = null; this.amount = null;
this.manualEntry = null; this.currency = null;
this.entityType = null; this.transactionId = null;
this.entityId = null; this.manualEntry = null;
this.createdByUserId = null; this.entityType = null;
this.submittedOnDate = null; this.entityId = null;
this.createdDate = null; this.createdByUserId = null;
this.createdByUserName = null; this.submittedOnDate = null;
this.reversed = null; this.createdDate = null;
this.referenceNumber = null; this.createdByUserName = null;
this.officeRunningBalance = null; this.reversed = null;
this.organizationRunningBalance = null; this.referenceNumber = null;
this.runningBalanceComputed = null; this.officeRunningBalance = null;
this.transactionDetails = 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, public JournalEntryData(final Long id, final Long officeId, final String officeName, final String glAccountName, final Long glAccountId,
final EnumOptionData entityType, final Long entityId, final Long createdByUserId, final OffsetDateTime submittedOnDate, final String glAccountCode, final EnumOptionData glAccountClassification, final LocalDate transactionDate,
final String createdByUserName, final String comments, final Boolean reversed, final String referenceNumber, final EnumOptionData entryType, final BigDecimal amount, final String transactionId, final Boolean manualEntry,
final BigDecimal officeRunningBalance, final BigDecimal organizationRunningBalance, final Boolean runningBalanceComputed, final EnumOptionData entityType, final Long entityId, final Long createdByUserId, final OffsetDateTime submittedOnDate,
final TransactionDetailData transactionDetailData, final CurrencyData currency, final String externalAssetOwner) { final String createdByUserName, final String comments, final Boolean reversed, final String referenceNumber,
this.id = id; final BigDecimal officeRunningBalance, final BigDecimal organizationRunningBalance, final Boolean runningBalanceComputed,
this.officeId = officeId; final TransactionDetailData transactionDetailData, final CurrencyData currency, final String externalAssetOwner,
this.officeName = officeName; final String savingsAccountNo) {
this.glAccountName = glAccountName; this.id = id;
this.glAccountId = glAccountId; this.officeId = officeId;
this.glAccountCode = glAccountCode; this.officeName = officeName;
this.glAccountType = glAccountClassification; this.glAccountName = glAccountName;
this.transactionDate = transactionDate; this.glAccountId = glAccountId;
this.entryType = entryType; this.glAccountCode = glAccountCode;
this.amount = amount; this.glAccountType = glAccountClassification;
this.transactionId = transactionId; this.transactionDate = transactionDate;
this.manualEntry = manualEntry; this.entryType = entryType;
this.entityType = entityType; this.amount = amount;
this.entityId = entityId; this.transactionId = transactionId;
this.createdByUserId = createdByUserId; this.manualEntry = manualEntry;
this.createdDate = submittedOnDate; this.entityType = entityType;
this.submittedOnDate = submittedOnDate; this.entityId = entityId;
this.createdByUserName = createdByUserName; this.createdByUserId = createdByUserId;
this.comments = comments; this.createdDate = submittedOnDate;
this.reversed = reversed; this.submittedOnDate = submittedOnDate;
this.referenceNumber = referenceNumber; this.createdByUserName = createdByUserName;
this.officeRunningBalance = officeRunningBalance; this.comments = comments;
this.organizationRunningBalance = organizationRunningBalance; this.reversed = reversed;
this.runningBalanceComputed = runningBalanceComputed; this.referenceNumber = referenceNumber;
this.transactionDetails = transactionDetailData; this.officeRunningBalance = officeRunningBalance;
this.currency = currency; this.organizationRunningBalance = organizationRunningBalance;
this.externalAssetOwner = externalAssetOwner; this.runningBalanceComputed = runningBalanceComputed;
} this.transactionDetails = transactionDetailData;
this.currency = currency;
public static JournalEntryData importInstance(Long officeId, LocalDate transactionDate, String currencyCode, Long paymentTypeId, this.externalAssetOwner = externalAssetOwner;
Integer rowIndex, List<CreditDebit> credits, List<CreditDebit> debits, String accountNumber, String checkNumber, this.savingsAccountNo = savingsAccountNo;
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 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) {
public static JournalEntryData importInstance1(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits, return new JournalEntryData(officeId, transactionDate, currencyCode, paymentTypeId, rowIndex, credits, debits, accountNumber,
List<CreditDebit> debits, String locale, String dateFormat) { checkNumber, routingCode, receiptNumber, bankNumber, comments, locale, dateFormat);
return new JournalEntryData(officeId, transactionDate, currencyCode, credits, debits, locale, dateFormat); }
}
public static JournalEntryData importInstance1(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
public static JournalEntryData fromGLAccountData(final GLAccountData glAccountData) { List<CreditDebit> debits, String locale, String dateFormat) {
return new JournalEntryData(officeId, transactionDate, currencyCode, credits, debits, locale, dateFormat);
final Long id = null; }
final Long officeId = null;
final String officeName = null; public static JournalEntryData fromGLAccountData(final GLAccountData glAccountData) {
final String glAccountName = glAccountData.getName();
final Long glAccountId = glAccountData.getId(); final Long id = null;
final String glAccountCode = glAccountData.getGlCode(); final Long officeId = null;
final EnumOptionData glAccountClassification = glAccountData.getType(); final String officeName = null;
final LocalDate transactionDate = null; final String glAccountName = glAccountData.getName();
final EnumOptionData entryType = null; final Long glAccountId = glAccountData.getId();
final BigDecimal amount = null; final String glAccountCode = glAccountData.getGlCode();
final String transactionId = null; final EnumOptionData glAccountClassification = glAccountData.getType();
final Boolean manualEntry = null; final LocalDate transactionDate = null;
final EnumOptionData entityType = null; final EnumOptionData entryType = null;
final Long entityId = null; final BigDecimal amount = null;
final Long createdByUserId = null; final String transactionId = null;
final OffsetDateTime submittedOnDate = null; final Boolean manualEntry = null;
final String createdByUserName = null; final EnumOptionData entityType = null;
final String comments = null; final Long entityId = null;
final Boolean reversed = null; final Long createdByUserId = null;
final String referenceNumber = null; final OffsetDateTime submittedOnDate = null;
final BigDecimal officeRunningBalance = null; final String createdByUserName = null;
final BigDecimal organizationRunningBalance = null; final String comments = null;
final Boolean runningBalanceComputed = null; final Boolean reversed = null;
final TransactionDetailData transactionDetailData = null; final String referenceNumber = null;
final CurrencyData currency = null; final BigDecimal officeRunningBalance = null;
final String externalAssetOwner = null; final BigDecimal organizationRunningBalance = null;
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glAccountCode, glAccountClassification, final Boolean runningBalanceComputed = null;
transactionDate, entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate, final TransactionDetailData transactionDetailData = null;
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance, final CurrencyData currency = null;
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner); final String externalAssetOwner = null;
} return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glAccountCode, glAccountClassification,
transactionDate, entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
public Integer getRowIndex() { createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
return rowIndex; runningBalanceComputed, transactionDetailData, currency, externalAssetOwner, null);
} }
public LocalDate getTransactionDate() { public Integer getRowIndex() {
return transactionDate; return rowIndex;
} }
public void addDebits(CreditDebit debit) { public LocalDate getTransactionDate() {
this.debits.add(debit); return transactionDate;
} }
public void addCredits(CreditDebit credit) { public void addDebits(CreditDebit debit) {
this.credits.add(credit); this.debits.add(debit);
} }
}
public void addCredits(CreditDebit credit) {
this.credits.add(credit);
}
}
@@ -1,144 +1,161 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.infrastructure.core.service; package org.apache.fineract.infrastructure.core.service;
import lombok.AccessLevel; import java.time.LocalDate;
import lombok.AllArgsConstructor; import lombok.AccessLevel;
import lombok.Builder; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Builder;
import org.apache.commons.lang3.StringUtils; import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
@Builder
@Getter @Builder
@AllArgsConstructor @Getter
public class SearchParameters { @AllArgsConstructor
public class SearchParameters {
public static final int DEFAULT_MAX_LIMIT = 200;
public static final int DEFAULT_MAX_LIMIT = 200;
private Long officeId;
private String externalId; private Long officeId;
private String name; private String externalId;
private String hierarchy; private String name;
private String firstname; private String hierarchy;
private String lastname; private String firstname;
private String status; private String lastname;
private Integer offset; private String status;
@Getter(AccessLevel.NONE) private Integer offset;
private Integer limit; @Getter(AccessLevel.NONE)
private String orderBy; private Integer limit;
private String sortOrder; private String orderBy;
private String accountNo; private String sortOrder;
private String currencyCode; private String accountNo;
private Long staffId; private Integer depositType;
private Long loanId; private String currencyCode;
private Long clientId; private Long staffId;
private Long savingsId; private Long loanId;
@Getter(AccessLevel.NONE) private Long clientId;
private Boolean orphansOnly; private Long savingsId;
private Long provisioningEntryId; @Getter(AccessLevel.NONE)
private Long productId; private Boolean orphansOnly;
private Long categoryId; private Long provisioningEntryId;
@Getter(AccessLevel.NONE) private Long productId;
private Boolean isSelfUser; private Long categoryId;
private Integer legalForm; @Getter(AccessLevel.NONE)
private Boolean isSelfUser;
public Integer getLimit() { private Integer legalForm;
if (limit == null) { private LocalDate fromDate;
return DEFAULT_MAX_LIMIT; private LocalDate toDate;
} private LocalDate asOnDate;
if (limit > 0) { public boolean hasFromDate() {
return limit; return this.fromDate != null;
} }
return null; // unlimited (0 or less) public boolean hasToDate() {
} return this.toDate != null;
}
public Boolean getOrphansOnly() {
return Boolean.TRUE.equals(orphansOnly); public boolean hasAsOnDate() {
} return this.asOnDate != null;
}
public Boolean getIsSelfUser() {
return Boolean.TRUE.equals(isSelfUser); public Integer getLimit() {
} if (limit == null) {
return DEFAULT_MAX_LIMIT;
public boolean hasOrderBy() { }
return StringUtils.isNotBlank(this.orderBy);
} if (limit > 0) {
return limit;
public boolean hasSortOrder() { }
return StringUtils.isNotBlank(this.sortOrder);
} return null; // unlimited (0 or less)
}
public boolean hasOfficeId() {
return this.officeId != null && this.officeId != 0; public Boolean getOrphansOnly() {
} return Boolean.TRUE.equals(orphansOnly);
}
public boolean hasCurrencyCode() {
return StringUtils.isNotBlank(this.currencyCode); public Boolean getIsSelfUser() {
} return Boolean.TRUE.equals(isSelfUser);
}
public boolean hasLimit() {
return this.limit != null && this.limit > 0; public boolean hasOrderBy() {
} return StringUtils.isNotBlank(this.orderBy);
}
public boolean hasOffset() {
return this.offset != null; public boolean hasSortOrder() {
} return StringUtils.isNotBlank(this.sortOrder);
}
public boolean hasHierarchy() {
return StringUtils.isNotBlank(this.hierarchy); public boolean hasOfficeId() {
} return this.officeId != null && this.officeId != 0;
}
public boolean hasStaffId() {
return this.staffId != null && this.staffId != 0; public boolean hasCurrencyCode() {
} return StringUtils.isNotBlank(this.currencyCode);
}
public boolean hasLoanId() {
return this.loanId != null && this.loanId != 0; public boolean hasLimit() {
} return this.limit != null && this.limit > 0;
}
public boolean hasSavingsId() {
return this.savingsId != null && this.savingsId != 0; public boolean hasOffset() {
} return this.offset != null;
}
public boolean hasProvisioningEntryId() {
return this.provisioningEntryId != null && this.provisioningEntryId != 0; public boolean hasHierarchy() {
} return StringUtils.isNotBlank(this.hierarchy);
}
public boolean hasProductId() {
return this.productId != null && this.productId != 0; public boolean hasStaffId() {
} return this.staffId != null && this.staffId != 0;
}
public boolean hasCategoryId() {
return this.categoryId != null && this.categoryId != 0; public boolean hasLoanId() {
} return this.loanId != null && this.loanId != 0;
}
public boolean isPerson() {
return this.legalForm != null && this.legalForm == 1; public boolean hasSavingsId() {
} return this.savingsId != null && this.savingsId != 0;
}
public boolean isEntity() {
return this.legalForm != null && this.legalForm == 2; public boolean hasProvisioningEntryId() {
} return this.provisioningEntryId != null && this.provisioningEntryId != 0;
}
public boolean hasLegalForm() {
return this.legalForm != null; 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;
}
}
@@ -1,149 +1,193 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.currentaccount.domain; package org.apache.fineract.portfolio.currentaccount.domain;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
@Entity @Entity
@Table(name = "m_current_account_transaction") @Table(name = "m_current_account_transaction")
@Getter @Getter
@Setter @Setter
public class CurrentAccountTransaction extends AbstractPersistableCustom<Long> { public class CurrentAccountTransaction extends AbstractPersistableCustom<Long> {
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_account_id", nullable = false) @JoinColumn(name = "current_account_id", nullable = false)
private CurrentAccount currentAccount; private CurrentAccount currentAccount;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "office_id", nullable = false) @JoinColumn(name = "office_id", nullable = false)
private Office office; private Office office;
// 1=Deposit, 2=Withdrawal, 3=InterestPosting, 4=Charge, 5=ChargeWaiver, 6=Transfer, 7=Reversal // 1=Deposit, 2=Withdrawal, 3=InterestPosting, 4=Charge, 5=ChargeWaiver, 6=Transfer, 7=Reversal
@Column(name = "transaction_type_enum", nullable = false) @Column(name = "transaction_type_enum", nullable = false)
private Integer transactionTypeEnum; private Integer transactionTypeEnum;
@Column(name = "transaction_date", nullable = false) @Column(name = "transaction_date", nullable = false)
private LocalDate transactionDate; private LocalDate transactionDate;
@Column(name = "amount", nullable = false, precision = 19, scale = 6) @Column(name = "amount", nullable = false, precision = 19, scale = 6)
private BigDecimal amount; private BigDecimal amount;
@Column(name = "ref_no", length = 100) @Column(name = "ref_no", length = 100)
private String refNo; private String refNo;
@Column(name = "remarks", length = 500) @Column(name = "remarks", length = 500)
private String remarks; private String remarks;
@Column(name = "is_reversed", nullable = false) @Column(name = "is_reversed", nullable = false)
private boolean reversed = false; private boolean reversed = false;
@Column(name = "is_reversal", nullable = false) @Column(name = "is_reversal", nullable = false)
private boolean reversal = false; private boolean reversal = false;
@Column(name = "original_transaction_id") @Column(name = "original_transaction_id")
private Long originalTransactionId; private Long originalTransactionId;
@Column(name = "is_manual", nullable = false) @Column(name = "is_manual", nullable = false)
private boolean manual = false; private boolean manual = false;
@Column(name = "running_balance_derived", precision = 19, scale = 6) @Column(name = "running_balance_derived", precision = 19, scale = 6)
private BigDecimal runningBalanceDerived; private BigDecimal runningBalanceDerived;
@Column(name = "cumulative_balance_derived", precision = 19, scale = 6) @Column(name = "cumulative_balance_derived", precision = 19, scale = 6)
private BigDecimal cumulativeBalanceDerived; private BigDecimal cumulativeBalanceDerived;
@Column(name = "balance_end_date_derived") @Column(name = "balance_end_date_derived")
private LocalDate balanceEndDateDerived; private LocalDate balanceEndDateDerived;
@Column(name = "balance_number_of_days_derived") @Column(name = "balance_number_of_days_derived")
private Integer balanceNumberOfDaysDerived; private Integer balanceNumberOfDaysDerived;
@Column(name = "overdraft_amount_derived", precision = 19, scale = 6) @Column(name = "overdraft_amount_derived", precision = 19, scale = 6)
private BigDecimal overdraftAmountDerived; private BigDecimal overdraftAmountDerived;
@Column(name = "is_lien_transaction", nullable = false) @Column(name = "is_lien_transaction", nullable = false)
private boolean lienTransaction = false; private boolean lienTransaction = false;
@Column(name = "release_id_of_hold_amount_transaction") @Column(name = "release_id_of_hold_amount_transaction")
private Long releaseIdOfHoldAmountTransaction; private Long releaseIdOfHoldAmountTransaction;
@Column(name = "reason_for_block", length = 200) @Column(name = "reason_for_block", length = 200)
private String reasonForBlock; private String reasonForBlock;
@Column(name = "submitted_on_date") @Column(name = "submitted_on_date")
private LocalDate submittedOnDate; private LocalDate submittedOnDate;
@Column(name = "created_date") @Column(name = "created_date")
private LocalDateTime createdDate; private LocalDateTime createdDate;
@Column(name = "created_by") @Column(name = "created_by")
private Long createdBy; private Long createdBy;
protected CurrentAccountTransaction() {} protected CurrentAccountTransaction() {}
public static CurrentAccountTransaction deposit(CurrentAccount account, Office office, public static CurrentAccountTransaction deposit(CurrentAccount account, Office office,
BigDecimal amount, LocalDate transactionDate, String refNo) { BigDecimal amount, LocalDate transactionDate, String refNo) {
CurrentAccountTransaction txn = new CurrentAccountTransaction(); CurrentAccountTransaction txn = new CurrentAccountTransaction();
txn.currentAccount = account; txn.currentAccount = account;
txn.office = office; txn.office = office;
txn.transactionTypeEnum = 1; txn.transactionTypeEnum = 1;
txn.amount = amount; txn.amount = amount;
txn.transactionDate = transactionDate; txn.transactionDate = transactionDate;
txn.refNo = refNo; txn.refNo = refNo;
txn.submittedOnDate = transactionDate; txn.submittedOnDate = transactionDate;
return txn; return txn;
} }
public static CurrentAccountTransaction chargeDeduction(CurrentAccount account, Office office, public static CurrentAccountTransaction chargeDeduction(CurrentAccount account, Office office,
BigDecimal amount, LocalDate transactionDate) { BigDecimal amount, LocalDate transactionDate) {
CurrentAccountTransaction txn = new CurrentAccountTransaction(); CurrentAccountTransaction txn = new CurrentAccountTransaction();
txn.currentAccount = account; txn.currentAccount = account;
txn.office = office; txn.office = office;
txn.transactionTypeEnum = 7; // PAY_CHARGE txn.transactionTypeEnum = 7; // PAY_CHARGE
txn.amount = amount; txn.amount = amount;
txn.transactionDate = transactionDate; txn.transactionDate = transactionDate;
txn.submittedOnDate = transactionDate; txn.submittedOnDate = transactionDate;
return txn; return txn;
} }
public static CurrentAccountTransaction withdrawal(CurrentAccount account, Office office, public static CurrentAccountTransaction withdrawal(CurrentAccount account, Office office,
BigDecimal amount, LocalDate transactionDate, String refNo) { BigDecimal amount, LocalDate transactionDate, String refNo) {
CurrentAccountTransaction txn = new CurrentAccountTransaction(); CurrentAccountTransaction txn = new CurrentAccountTransaction();
txn.currentAccount = account; txn.currentAccount = account;
txn.office = office; txn.office = office;
txn.transactionTypeEnum = 2; txn.transactionTypeEnum = 2;
txn.amount = amount; txn.amount = amount;
txn.transactionDate = transactionDate; txn.transactionDate = transactionDate;
txn.refNo = refNo; txn.refNo = refNo;
txn.submittedOnDate = transactionDate; txn.submittedOnDate = transactionDate;
return txn; 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;
}
}
@@ -1,29 +1,35 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.currentaccount.service; package org.apache.fineract.portfolio.currentaccount.service;
import java.util.List; import java.time.LocalDate;
import java.util.Map; import java.util.List;
import java.util.Map;
public interface CurrentAccountReadPlatformService {
public interface CurrentAccountReadPlatformService {
Map<String, Object> retrieveOne(Long accountId);
Map<String, Object> retrieveOne(Long accountId);
List<Map<String, Object>> retrieveAll();
} 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);
}
@@ -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);
}
@@ -1,38 +1,39 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.accounting.journalentry.data; package org.apache.fineract.accounting.journalentry.data;
import java.util.List; import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@AllArgsConstructor @AllArgsConstructor
@Getter @Getter
@Setter @Setter
public class SavingsDTO { public class SavingsDTO {
private Long savingsId; private Long savingsId;
private Long savingsProductId; private Long savingsProductId;
private Long officeId; private Long officeId;
private String currencyCode; private String currencyCode;
private boolean cashBasedAccountingEnabled; private boolean cashBasedAccountingEnabled;
private boolean accrualBasedAccountingEnabled; private boolean accrualBasedAccountingEnabled;
private List<SavingsTransactionDTO> newSavingsTransactions; private List<SavingsTransactionDTO> newSavingsTransactions;
} private int depositAccountType;
}
@@ -1,285 +1,290 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.accounting.journalentry.service; package org.apache.fineract.accounting.journalentry.service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.closure.domain.GLClosure; import org.apache.fineract.accounting.closure.domain.GLClosure;
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForSavings; import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForSavings;
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity; import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO; import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
import org.apache.fineract.accounting.journalentry.data.SavingsDTO; import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO; import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO;
import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class AccrualBasedAccountingProcessorForSavings implements AccountingProcessorForSavings { public class AccrualBasedAccountingProcessorForSavings implements AccountingProcessorForSavings {
private final AccountingProcessorHelper helper; private final AccountingProcessorHelper helper;
@Override @Override
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) { public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId()); AccountingProcessorHelper.setDepositAccountTypeContext(savingsDTO.getDepositAccountType());
final Long savingsProductId = savingsDTO.getSavingsProductId(); try {
final Long savingsId = savingsDTO.getSavingsId(); final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
final String currencyCode = savingsDTO.getCurrencyCode(); final Long savingsProductId = savingsDTO.getSavingsProductId();
for (final SavingsTransactionDTO savingsTransactionDTO : savingsDTO.getNewSavingsTransactions()) { final Long savingsId = savingsDTO.getSavingsId();
final LocalDate transactionDate = savingsTransactionDTO.getTransactionDate(); final String currencyCode = savingsDTO.getCurrencyCode();
final String transactionId = savingsTransactionDTO.getTransactionId(); for (final SavingsTransactionDTO savingsTransactionDTO : savingsDTO.getNewSavingsTransactions()) {
final Office office = this.helper.getOfficeById(savingsTransactionDTO.getOfficeId()); final LocalDate transactionDate = savingsTransactionDTO.getTransactionDate();
final Long paymentTypeId = savingsTransactionDTO.getPaymentTypeId(); final String transactionId = savingsTransactionDTO.getTransactionId();
final boolean isReversal = savingsTransactionDTO.isReversed(); final Office office = this.helper.getOfficeById(savingsTransactionDTO.getOfficeId());
final BigDecimal amount = savingsTransactionDTO.getAmount(); final Long paymentTypeId = savingsTransactionDTO.getPaymentTypeId();
final BigDecimal overdraftAmount = savingsTransactionDTO.getOverdraftAmount(); final boolean isReversal = savingsTransactionDTO.isReversed();
final List<ChargePaymentDTO> feePayments = savingsTransactionDTO.getFeePayments(); final BigDecimal amount = savingsTransactionDTO.getAmount();
final List<ChargePaymentDTO> penaltyPayments = savingsTransactionDTO.getPenaltyPayments(); final BigDecimal overdraftAmount = savingsTransactionDTO.getOverdraftAmount();
final List<ChargePaymentDTO> feePayments = savingsTransactionDTO.getFeePayments();
this.helper.checkForBranchClosures(latestGLClosure, transactionDate); final List<ChargePaymentDTO> penaltyPayments = savingsTransactionDTO.getPenaltyPayments();
if (savingsTransactionDTO.getTransactionType().isWithdrawal() && savingsTransactionDTO.isOverdraftTransaction()) { this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
if (savingsTransactionDTO.isAccountTransfer()) { if (savingsTransactionDTO.getTransactionType().isWithdrawal() && savingsTransactionDTO.isOverdraftTransaction()) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), if (savingsTransactionDTO.isAccountTransfer()) {
FinancialActivity.LIABILITY_TRANSFER.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionDate, overdraftAmount, isReversal); AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
if (isPositive) { FinancialActivity.LIABILITY_TRANSFER.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, transactionDate, overdraftAmount, isReversal);
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), if (isPositive) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
amount.subtract(overdraftAmount), isReversal); AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
} savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
} else { amount.subtract(overdraftAmount), isReversal);
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, }
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), } else {
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, overdraftAmount, isReversal); AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
if (isPositive) { AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, transactionId, transactionDate, overdraftAmount, isReversal);
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), if (isPositive) {
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal); 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()) { else if (savingsTransactionDTO.getTransactionType().isDeposit() && savingsTransactionDTO.isOverdraftTransaction()) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
FinancialActivity.LIABILITY_TRANSFER.getValue(), if (savingsTransactionDTO.isAccountTransfer()) {
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, overdraftAmount, isReversal); FinancialActivity.LIABILITY_TRANSFER.getValue(),
if (isPositive) { AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, transactionId, transactionDate, overdraftAmount, isReversal);
FinancialActivity.LIABILITY_TRANSFER.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), if (isPositive) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
amount.subtract(overdraftAmount), isReversal); FinancialActivity.LIABILITY_TRANSFER.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
} savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
} else { amount.subtract(overdraftAmount), isReversal);
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, }
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), } else {
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, overdraftAmount, isReversal); AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(),
if (isPositive) { AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, transactionId, transactionDate, overdraftAmount, isReversal);
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), if (isPositive) {
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal); 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()) { /** Handle Deposits and reversals of deposits **/
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, else if (savingsTransactionDTO.getTransactionType().isDeposit()) {
FinancialActivity.LIABILITY_TRANSFER.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), if (savingsTransactionDTO.isAccountTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
} else { FinancialActivity.LIABILITY_TRANSFER.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), } else {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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, /** Handle Deposits and reversals of Dividend pay outs **/
FinancialActivity.PAYABLE_DIVIDENDS.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), else if (savingsTransactionDTO.getTransactionType().isDividendPayout()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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()) { /** Handle withdrawals and reversals of withdrawals **/
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, else if (savingsTransactionDTO.getTransactionType().isWithdrawal()) {
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), if (savingsTransactionDTO.isAccountTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
} else { AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), } else {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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(), else if (savingsTransactionDTO.getTransactionType().isEscheat()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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()) { * Handle Interest Applications and reversals of Interest Applications
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0; **/
// Post journal entry if earned interest amount is greater than else if (savingsTransactionDTO.getTransactionType().isInterestPosting() && savingsTransactionDTO.isOverdraftTransaction()) {
// zero boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) { // Post journal entry if earned interest amount is greater than
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, // zero
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, overdraftAmount, isReversal); AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
if (isPositive) { AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, transactionId, transactionDate, overdraftAmount, isReversal);
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), if (isPositive) {
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal); 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 else if (savingsTransactionDTO.getTransactionType().isInterestPosting()) {
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) { // Post journal entry if earned interest amount is greater than
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, // zero
AccrualAccountsForSavings.INTEREST_PAYABLE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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) { else if (savingsTransactionDTO.getTransactionType().isAccrual()) {
if (MathUtil.isGreaterThanZero(overdraftAmount)) { // Post journal entry for Accrual Recognition
this.helper.createAccrualBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode, if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), savingsProductId, paymentTypeId, savingsId, if (MathUtil.isGreaterThanZero(overdraftAmount)) {
transactionId, transactionDate, amount, isReversal); this.helper.createAccrualBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode,
this.helper.createAccrualBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode, AccrualAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), savingsProductId, paymentTypeId, savingsId,
AccrualAccountsForSavings.INTEREST_PAYABLE.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
transactionId, transactionDate, amount, isReversal); this.helper.createAccrualBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode,
} else { AccrualAccountsForSavings.INTEREST_PAYABLE.getValue(), savingsProductId, paymentTypeId, savingsId,
this.helper.createAccrualBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode, transactionId, transactionDate, amount, isReversal);
AccrualAccountsForSavings.INTEREST_RECEIVABLE.getValue(), savingsProductId, paymentTypeId, savingsId, } else {
transactionId, transactionDate, amount, isReversal); this.helper.createAccrualBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode,
this.helper.createAccrualBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode, AccrualAccountsForSavings.INTEREST_RECEIVABLE.getValue(), savingsProductId, paymentTypeId, savingsId,
AccrualAccountsForSavings.INCOME_FROM_INTEREST.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
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, else if (savingsTransactionDTO.getTransactionType().isWithholdTax()) {
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsTax(office, currencyCode,
savingsTransactionDTO.getTaxPayments()); 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; /** Handle Fees Deductions and reversals of Fees Deductions **/
// Is the Charge a penalty? else if (savingsTransactionDTO.getTransactionType().isFeeDeduction() && savingsTransactionDTO.isOverdraftTransaction()) {
if (penaltyPayments.size() > 0) { boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, // Is the Charge a penalty?
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES, if (penaltyPayments.size() > 0) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal, this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
penaltyPayments); AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES,
if (isPositive) { savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, penaltyPayments);
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES, if (isPositive) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
amount.subtract(overdraftAmount), isReversal, penaltyPayments); AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES,
} savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
} else { amount.subtract(overdraftAmount), isReversal, penaltyPayments);
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, }
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES, } else {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal, this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
feePayments); AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES,
if (isPositive) { savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, feePayments);
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId, if (isPositive) {
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal, this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
feePayments); 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) { else if (savingsTransactionDTO.getTransactionType().isFeeDeduction()) {
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, // Is the Charge a penalty?
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId, if (penaltyPayments.size() > 0) {
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, penaltyPayments); this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
} else { AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, penaltyPayments);
AccrualAccountsForSavings.SAVINGS_CONTROL, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId, } else {
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments); 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, /** Handle Transfers proposal **/
AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), AccrualAccountsForSavings.TRANSFERS_SUSPENSE.getValue(), else if (savingsTransactionDTO.getTransactionType().isInitiateTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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()) { /** Handle Transfer Withdrawal or Acceptance **/
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, else if (savingsTransactionDTO.getTransactionType().isWithdrawTransfer()
AccrualAccountsForSavings.TRANSFERS_SUSPENSE.getValue(), AccrualAccountsForSavings.SAVINGS_CONTROL.getValue(), || savingsTransactionDTO.getTransactionType().isApproveTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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, /** overdraft **/
AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), AccrualAccountsForSavings.INCOME_FROM_INTEREST.getValue(), else if (savingsTransactionDTO.getTransactionType().isOverdraftInterest()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
} else if (savingsTransactionDTO.getTransactionType().isWrittenoff()) { AccrualAccountsForSavings.SAVINGS_REFERENCE.getValue(), AccrualAccountsForSavings.INCOME_FROM_INTEREST.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
AccrualAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(), } else if (savingsTransactionDTO.getTransactionType().isWrittenoff()) {
AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, amount, isReversal); AccrualAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(),
} else if (savingsTransactionDTO.getTransactionType().isOverdraftFee()) { AccrualAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, transactionId, transactionDate, amount, isReversal);
AccrualAccountsForSavings.SAVINGS_REFERENCE, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId, } else if (savingsTransactionDTO.getTransactionType().isOverdraftFee()) {
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments); this.helper.createAccrualBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
} AccrualAccountsForSavings.SAVINGS_REFERENCE, AccrualAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
} paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
} }
} }
} finally {
AccountingProcessorHelper.clearDepositAccountTypeContext();
}
}
}
@@ -1,255 +1,260 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.accounting.journalentry.service; package org.apache.fineract.accounting.journalentry.service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.closure.domain.GLClosure; import org.apache.fineract.accounting.closure.domain.GLClosure;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings; import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity; import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO; import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
import org.apache.fineract.accounting.journalentry.data.SavingsDTO; import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO; import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class CashBasedAccountingProcessorForSavings implements AccountingProcessorForSavings { public class CashBasedAccountingProcessorForSavings implements AccountingProcessorForSavings {
private final AccountingProcessorHelper helper; private final AccountingProcessorHelper helper;
@Override @Override
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) { public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId()); AccountingProcessorHelper.setDepositAccountTypeContext(savingsDTO.getDepositAccountType());
final Long savingsProductId = savingsDTO.getSavingsProductId(); try {
final Long savingsId = savingsDTO.getSavingsId(); final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
final String currencyCode = savingsDTO.getCurrencyCode(); final Long savingsProductId = savingsDTO.getSavingsProductId();
for (final SavingsTransactionDTO savingsTransactionDTO : savingsDTO.getNewSavingsTransactions()) { final Long savingsId = savingsDTO.getSavingsId();
final LocalDate transactionDate = savingsTransactionDTO.getTransactionDate(); final String currencyCode = savingsDTO.getCurrencyCode();
final String transactionId = savingsTransactionDTO.getTransactionId(); for (final SavingsTransactionDTO savingsTransactionDTO : savingsDTO.getNewSavingsTransactions()) {
final Office office = this.helper.getOfficeById(savingsTransactionDTO.getOfficeId()); final LocalDate transactionDate = savingsTransactionDTO.getTransactionDate();
final Long paymentTypeId = savingsTransactionDTO.getPaymentTypeId(); final String transactionId = savingsTransactionDTO.getTransactionId();
final boolean isReversal = savingsTransactionDTO.isReversed(); final Office office = this.helper.getOfficeById(savingsTransactionDTO.getOfficeId());
final BigDecimal amount = savingsTransactionDTO.getAmount(); final Long paymentTypeId = savingsTransactionDTO.getPaymentTypeId();
final BigDecimal overdraftAmount = savingsTransactionDTO.getOverdraftAmount(); final boolean isReversal = savingsTransactionDTO.isReversed();
final List<ChargePaymentDTO> feePayments = savingsTransactionDTO.getFeePayments(); final BigDecimal amount = savingsTransactionDTO.getAmount();
final List<ChargePaymentDTO> penaltyPayments = savingsTransactionDTO.getPenaltyPayments(); final BigDecimal overdraftAmount = savingsTransactionDTO.getOverdraftAmount();
final List<ChargePaymentDTO> feePayments = savingsTransactionDTO.getFeePayments();
this.helper.checkForBranchClosures(latestGLClosure, transactionDate); final List<ChargePaymentDTO> penaltyPayments = savingsTransactionDTO.getPenaltyPayments();
if (savingsTransactionDTO.getTransactionType().isWithdrawal() && savingsTransactionDTO.isOverdraftTransaction()) { this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
if (savingsTransactionDTO.isAccountTransfer()) { if (savingsTransactionDTO.getTransactionType().isWithdrawal() && savingsTransactionDTO.isOverdraftTransaction()) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), if (savingsTransactionDTO.isAccountTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
if (isPositive) { CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal);
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), if (isPositive) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
amount.subtract(overdraftAmount), isReversal); CashAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
} savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
} else { amount.subtract(overdraftAmount), isReversal);
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, }
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), } else {
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionDate, overdraftAmount, isReversal); CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
if (isPositive) { CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), savingsProductId, paymentTypeId, savingsId, transactionId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, transactionDate, overdraftAmount, isReversal);
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), if (isPositive) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
amount.subtract(overdraftAmount), isReversal); 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()) { } else if (savingsTransactionDTO.getTransactionType().isDeposit() && savingsTransactionDTO.isOverdraftTransaction()) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), if (savingsTransactionDTO.isAccountTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
if (isPositive) { FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal);
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), if (isPositive) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
amount.subtract(overdraftAmount), isReversal); FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
} savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
} else { amount.subtract(overdraftAmount), isReversal);
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, }
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), } else {
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, overdraftAmount, isReversal); CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
if (isPositive) { CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, transactionId, transactionDate, overdraftAmount, isReversal);
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), if (isPositive) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
amount.subtract(overdraftAmount), isReversal); 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()) { /** Handle Deposits and reversals of deposits **/
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, else if (savingsTransactionDTO.getTransactionType().isDeposit()) {
FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), if (savingsTransactionDTO.isAccountTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
} else { FinancialActivity.LIABILITY_TRANSFER.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), } else {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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, /** Handle Deposits and reversals of Dividend pay outs **/
FinancialActivity.PAYABLE_DIVIDENDS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId, else if (savingsTransactionDTO.getTransactionType().isDividendPayout()) {
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
} FinancialActivity.PAYABLE_DIVIDENDS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), savingsProductId,
/** Handle withdrawals and reversals of withdrawals **/ paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
else if (savingsTransactionDTO.getTransactionType().isWithdrawal()) { }
if (savingsTransactionDTO.isAccountTransfer()) { /** Handle withdrawals and reversals of withdrawals **/
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, else if (savingsTransactionDTO.getTransactionType().isWithdrawal()) {
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), if (savingsTransactionDTO.isAccountTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
} else { CashAccountsForSavings.SAVINGS_CONTROL.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), } else {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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(), else if (savingsTransactionDTO.getTransactionType().isEscheat()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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()) { * Handle Interest Applications and reversals of Interest Applications
boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0; **/
// Post journal entry if earned interest amount is greater than else if (savingsTransactionDTO.getTransactionType().isInterestPosting() && savingsTransactionDTO.isOverdraftTransaction()) {
// zero boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) { // Post journal entry if earned interest amount is greater than
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, // zero
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
transactionId, transactionDate, overdraftAmount, isReversal); CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
if (isPositive) { CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, transactionId, transactionDate, overdraftAmount, isReversal);
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), if (isPositive) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
amount.subtract(overdraftAmount), isReversal); 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 else if (savingsTransactionDTO.getTransactionType().isInterestPosting()) {
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) { // Post journal entry if earned interest amount is greater than
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, // zero
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) > 0) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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, else if (savingsTransactionDTO.getTransactionType().isWithholdTax()) {
savingsId, transactionId, transactionDate, amount, isReversal, savingsTransactionDTO.getTaxPayments()); 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; /** Handle Fees Deductions and reversals of Fees Deductions **/
// Is the Charge a penalty? else if (savingsTransactionDTO.getTransactionType().isFeeDeduction() && savingsTransactionDTO.isOverdraftTransaction()) {
if (penaltyPayments.size() > 0) { boolean isPositive = amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) > 0;
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, // Is the Charge a penalty?
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, if (penaltyPayments.size() > 0) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal, this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
penaltyPayments); CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES,
if (isPositive) { savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, penaltyPayments);
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId, if (isPositive) {
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal, this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
penaltyPayments); CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
} paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal,
} else { penaltyPayments);
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, }
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId, } else {
paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal, feePayments); this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
if (isPositive) { CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal, feePayments);
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId, if (isPositive) {
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal, this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
feePayments); 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) { else if (savingsTransactionDTO.getTransactionType().isFeeDeduction()) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, // Is the Charge a penalty?
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId, if (penaltyPayments.size() > 0) {
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, penaltyPayments); this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
} else { CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_PENALTIES, savingsProductId,
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, penaltyPayments);
CashAccountsForSavings.SAVINGS_CONTROL, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId, } else {
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments); 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, /** Handle Transfers proposal **/
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue(), else if (savingsTransactionDTO.getTransactionType().isInitiateTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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()) { /** Handle Transfer Withdrawal or Acceptance **/
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, else if (savingsTransactionDTO.getTransactionType().isWithdrawTransfer()
CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(), || savingsTransactionDTO.getTransactionType().isApproveTransfer()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); 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, /** overdraft **/
CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.INCOME_FROM_INTEREST.getValue(), else if (savingsTransactionDTO.getTransactionType().isOverdraftInterest()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
} else if (savingsTransactionDTO.getTransactionType().isWrittenoff()) { CashAccountsForSavings.SAVINGS_REFERENCE.getValue(), CashAccountsForSavings.INCOME_FROM_INTEREST.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
CashAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(), CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), } else if (savingsTransactionDTO.getTransactionType().isWrittenoff()) {
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal); this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
} else if (savingsTransactionDTO.getTransactionType().isOverdraftFee()) { CashAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(), CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode, savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
CashAccountsForSavings.SAVINGS_REFERENCE, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId, paymentTypeId, } else if (savingsTransactionDTO.getTransactionType().isOverdraftFee()) {
savingsId, transactionId, transactionDate, amount, isReversal, feePayments); this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
} CashAccountsForSavings.SAVINGS_REFERENCE, CashAccountsForSavings.INCOME_FROM_FEES, savingsProductId, paymentTypeId,
} savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
} }
} }
} finally {
AccountingProcessorHelper.clearDepositAccountTypeContext();
}
}
}
@@ -1,285 +1,285 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.accounting.journalentry.service; package org.apache.fineract.accounting.journalentry.service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.accounting.common.AccountingEnumerations; import org.apache.fineract.accounting.common.AccountingEnumerations;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType; import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams; import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.apache.fineract.accounting.journalentry.data.JournalEntryData; import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
import org.apache.fineract.accounting.journalentry.data.JournalEntryDataValidator; import org.apache.fineract.accounting.journalentry.data.JournalEntryDataValidator;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType; import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper; import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntryRunningBalanceUpdateService { public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntryRunningBalanceUpdateService {
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final OfficeRepositoryWrapper officeRepositoryWrapper; private final OfficeRepositoryWrapper officeRepositoryWrapper;
private final JournalEntryDataValidator dataValidator; private final JournalEntryDataValidator dataValidator;
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseSpecificSQLGenerator sqlGenerator;
private final GLJournalEntryMapper entryMapper = new GLJournalEntryMapper(); private final GLJournalEntryMapper entryMapper = new GLJournalEntryMapper();
private final PlatformSecurityContext platformSecurityContext; private final PlatformSecurityContext platformSecurityContext;
@Override @Override
public void updateRunningBalance() { public void updateRunningBalance() {
String dateFinder = "select MIN(je.entry_date) as entityDate from acc_gl_journal_entry je " String dateFinder = "select MIN(je.entry_date) as entityDate from acc_gl_journal_entry je "
+ "where je.is_running_balance_calculated=false "; + "where je.is_running_balance_calculated=false ";
try { try {
LocalDate entityDate = this.jdbcTemplate.queryForObject(dateFinder, LocalDate.class); LocalDate entityDate = this.jdbcTemplate.queryForObject(dateFinder, LocalDate.class);
updateOrganizationRunningBalance(entityDate); updateOrganizationRunningBalance(entityDate);
} catch (EmptyResultDataAccessException e) { } catch (EmptyResultDataAccessException e) {
log.debug("No results found for updation of running balance "); log.debug("No results found for updation of running balance ");
} }
} }
@Override @Override
public CommandProcessingResult updateOfficeRunningBalance(JsonCommand command) { public CommandProcessingResult updateOfficeRunningBalance(JsonCommand command) {
this.dataValidator.validateForUpdateRunningBalance(command); this.dataValidator.validateForUpdateRunningBalance(command);
final Long officeId = this.fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue(), final Long officeId = this.fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue(),
command.parsedJson()); command.parsedJson());
CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder() CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder()
.withCommandId(command.commandId()); .withCommandId(command.commandId());
if (officeId == null) { if (officeId == null) {
updateRunningBalance(); updateRunningBalance();
} else { } else {
this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId); this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
String dateFinder = "select MIN(je.entry_date) as entityDate " + "from acc_gl_journal_entry je " 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=?"; + "where je.is_running_balance_calculated=false and je.office_id=?";
try { try {
LocalDate entityDate = this.jdbcTemplate.queryForObject(dateFinder, LocalDate.class, officeId); LocalDate entityDate = this.jdbcTemplate.queryForObject(dateFinder, LocalDate.class, officeId);
updateRunningBalance(officeId, entityDate); updateRunningBalance(officeId, entityDate);
} catch (EmptyResultDataAccessException e) { } catch (EmptyResultDataAccessException e) {
log.debug("No results found for updation of office running balance with office id: {}", officeId); log.debug("No results found for updation of office running balance with office id: {}", officeId);
} }
commandProcessingResultBuilder.withOfficeId(officeId); commandProcessingResultBuilder.withOfficeId(officeId);
} }
return commandProcessingResultBuilder.build(); return commandProcessingResultBuilder.build();
} }
private void updateOrganizationRunningBalance(LocalDate entityDate) { private void updateOrganizationRunningBalance(LocalDate entityDate) {
Map<Long, BigDecimal> runningBalanceMap = new HashMap<>(5); Map<Long, BigDecimal> runningBalanceMap = new HashMap<>(5);
Map<Long, Map<Long, BigDecimal>> officesRunningBalance = new HashMap<>(); 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 " 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(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 " + "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); + "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
List<Map<String, Object>> list = jdbcTemplate.queryForList(organizationRunningBalanceQuery, // NOSONAR List<Map<String, Object>> list = jdbcTemplate.queryForList(organizationRunningBalanceQuery, // NOSONAR
entityDate, entityDate); entityDate, entityDate);
for (Map<String, Object> entries : list) { for (Map<String, Object> entries : list) {
Long accountId = Long.parseLong(entries.get("accountId").toString()); // Drizzle Long accountId = Long.parseLong(entries.get("accountId").toString()); // Drizzle
// is // is
// returning // returning
// Big // Big
// Integer // Integer
// where // where
// as // as
// MySQL // MySQL
// returns // returns
// Long. // Long.
if (!runningBalanceMap.containsKey(accountId)) { if (!runningBalanceMap.containsKey(accountId)) {
runningBalanceMap.put(accountId, (BigDecimal) entries.get("runningBalance")); 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 " 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 " + "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(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 " + "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); + "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
List<Map<String, Object>> officesRunningBalanceList = jdbcTemplate.queryForList(offlineRunningBalanceQuery, // NOSONAR List<Map<String, Object>> officesRunningBalanceList = jdbcTemplate.queryForList(offlineRunningBalanceQuery, // NOSONAR
entityDate, entityDate); entityDate, entityDate);
for (Map<String, Object> entries : officesRunningBalanceList) { for (Map<String, Object> entries : officesRunningBalanceList) {
Long accountId = Long.parseLong(entries.get("accountId").toString()); Long accountId = Long.parseLong(entries.get("accountId").toString());
Long officeId = Long.parseLong(entries.get("officeId").toString()); Long officeId = Long.parseLong(entries.get("officeId").toString());
Map<Long, BigDecimal> runningBalance; Map<Long, BigDecimal> runningBalance;
if (officesRunningBalance.containsKey(officeId)) { if (officesRunningBalance.containsKey(officeId)) {
runningBalance = officesRunningBalance.get(officeId); runningBalance = officesRunningBalance.get(officeId);
} else { } else {
runningBalance = new HashMap<>(); runningBalance = new HashMap<>();
officesRunningBalance.put(officeId, runningBalance); officesRunningBalance.put(officeId, runningBalance);
} }
if (!runningBalance.containsKey(accountId)) { if (!runningBalance.containsKey(accountId)) {
runningBalance.put(accountId, (BigDecimal) entries.get("runningBalance")); runningBalance.put(accountId, (BigDecimal) entries.get("runningBalance"));
} }
} }
List<JournalEntryData> entryDataList = jdbcTemplate.query(entryMapper.organizationRunningBalanceSchema(), entryMapper, entityDate); List<JournalEntryData> entryDataList = jdbcTemplate.query(entryMapper.organizationRunningBalanceSchema(), entryMapper, entityDate);
if (entryDataList.size() > 0) { if (entryDataList.size() > 0) {
// run a batch update of 1000 SQL statements at a time // run a batch update of 1000 SQL statements at a time
final int batchUpdateSize = 1000; final int batchUpdateSize = 1000;
List<Object[]> params = new ArrayList<>(); List<Object[]> params = new ArrayList<>();
int batchIndex = 0; int batchIndex = 0;
String sql = "UPDATE acc_gl_journal_entry SET is_running_balance_calculated=?, organization_running_balance=?," 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=?"; + "office_running_balance=?, last_modified_by=?, last_modified_on_utc=? WHERE id=?";
for (int index = 0; index < entryDataList.size(); index++) { for (int index = 0; index < entryDataList.size(); index++) {
JournalEntryData entryData = entryDataList.get(index); JournalEntryData entryData = entryDataList.get(index);
Map<Long, BigDecimal> officeRunningBalanceMap; Map<Long, BigDecimal> officeRunningBalanceMap;
if (officesRunningBalance.containsKey(entryData.getOfficeId())) { if (officesRunningBalance.containsKey(entryData.getOfficeId())) {
officeRunningBalanceMap = officesRunningBalance.get(entryData.getOfficeId()); officeRunningBalanceMap = officesRunningBalance.get(entryData.getOfficeId());
} else { } else {
officeRunningBalanceMap = new HashMap<>(); officeRunningBalanceMap = new HashMap<>();
officesRunningBalance.put(entryData.getOfficeId(), officeRunningBalanceMap); officesRunningBalance.put(entryData.getOfficeId(), officeRunningBalanceMap);
} }
BigDecimal officeRunningBalance = calculateRunningBalance(entryData, officeRunningBalanceMap); BigDecimal officeRunningBalance = calculateRunningBalance(entryData, officeRunningBalanceMap);
BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap); BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap);
params.add(new Object[] { Boolean.TRUE, runningBalance, officeRunningBalance, params.add(new Object[] { Boolean.TRUE, runningBalance, officeRunningBalance,
platformSecurityContext.authenticatedUser().getId(), DateUtils.getAuditOffsetDateTime(), entryData.getId() }); platformSecurityContext.authenticatedUser().getId(), DateUtils.getAuditOffsetDateTime(), entryData.getId() });
batchIndex++; batchIndex++;
if (batchIndex == batchUpdateSize || index == entryDataList.size() - 1) { if (batchIndex == batchUpdateSize || index == entryDataList.size() - 1) {
this.jdbcTemplate.batchUpdate(sql, params); this.jdbcTemplate.batchUpdate(sql, params);
// reset counter and string array // reset counter and string array
batchIndex = 0; batchIndex = 0;
params.clear(); params.clear();
} }
} }
} }
} }
private void updateRunningBalance(Long officeId, LocalDate entityDate) { private void updateRunningBalance(Long officeId, LocalDate entityDate) {
Map<Long, BigDecimal> runningBalanceMap = new HashMap<>(5); 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 " 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(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 " + "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); + "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0);
List<Map<String, Object>> list = jdbcTemplate.queryForList(offlineRunningBalanceQuery, // NOSONAR List<Map<String, Object>> list = jdbcTemplate.queryForList(offlineRunningBalanceQuery, // NOSONAR
officeId, entityDate, officeId, entityDate); officeId, entityDate, officeId, entityDate);
for (Map<String, Object> entries : list) { for (Map<String, Object> entries : list) {
Long accountId = (Long) entries.get("accountId"); Long accountId = (Long) entries.get("accountId");
if (!runningBalanceMap.containsKey(accountId)) { if (!runningBalanceMap.containsKey(accountId)) {
runningBalanceMap.put(accountId, (BigDecimal) entries.get("runningBalance")); runningBalanceMap.put(accountId, (BigDecimal) entries.get("runningBalance"));
} }
} }
List<JournalEntryData> entryDataList = jdbcTemplate.query(entryMapper.officeRunningBalanceSchema(), entryMapper, officeId, List<JournalEntryData> entryDataList = jdbcTemplate.query(entryMapper.officeRunningBalanceSchema(), entryMapper, officeId,
entityDate); entityDate);
List<Object[]> params = new ArrayList<>(); 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=?"; String sql = "UPDATE acc_gl_journal_entry SET office_running_balance=?, last_modified_by=?, last_modified_on_utc=? WHERE id=?";
for (JournalEntryData entryData : entryDataList) { for (JournalEntryData entryData : entryDataList) {
BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap); BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap);
params.add(new Object[] { runningBalance, platformSecurityContext.authenticatedUser().getId(), params.add(new Object[] { runningBalance, platformSecurityContext.authenticatedUser().getId(),
DateUtils.getAuditOffsetDateTime(), entryData.getId() }); DateUtils.getAuditOffsetDateTime(), entryData.getId() });
} }
this.jdbcTemplate.batchUpdate(sql, params); this.jdbcTemplate.batchUpdate(sql, params);
} }
private BigDecimal calculateRunningBalance(JournalEntryData entry, Map<Long, BigDecimal> runningBalanceMap) { private BigDecimal calculateRunningBalance(JournalEntryData entry, Map<Long, BigDecimal> runningBalanceMap) {
BigDecimal runningBalance = BigDecimal.ZERO; BigDecimal runningBalance = BigDecimal.ZERO;
if (runningBalanceMap.containsKey(entry.getGlAccountId())) { if (runningBalanceMap.containsKey(entry.getGlAccountId())) {
runningBalance = runningBalanceMap.get(entry.getGlAccountId()); runningBalance = runningBalanceMap.get(entry.getGlAccountId());
} }
GLAccountType accountType = GLAccountType.fromInt(entry.getGlAccountType().getId().intValue()); GLAccountType accountType = GLAccountType.fromInt(entry.getGlAccountType().getId().intValue());
JournalEntryType entryType = JournalEntryType.fromInt(entry.getEntryType().getId().intValue()); JournalEntryType entryType = JournalEntryType.fromInt(entry.getEntryType().getId().intValue());
boolean isIncrease = false; boolean isIncrease = false;
switch (accountType) { switch (accountType) {
case ASSET: case ASSET:
case EXPENSE: case EXPENSE:
if (entryType.isDebitType()) { if (entryType.isDebitType()) {
isIncrease = true; isIncrease = true;
} }
break; break;
case EQUITY: case EQUITY:
case INCOME: case INCOME:
case LIABILITY: case LIABILITY:
if (entryType.isCreditType()) { if (entryType.isCreditType()) {
isIncrease = true; isIncrease = true;
} }
break; break;
} }
if (isIncrease) { if (isIncrease) {
runningBalance = runningBalance.add(entry.getAmount()); runningBalance = runningBalance.add(entry.getAmount());
} else { } else {
runningBalance = runningBalance.subtract(entry.getAmount()); runningBalance = runningBalance.subtract(entry.getAmount());
} }
runningBalanceMap.put(entry.getGlAccountId(), runningBalance); runningBalanceMap.put(entry.getGlAccountId(), runningBalance);
return runningBalance; return runningBalance;
} }
private static final class GLJournalEntryMapper implements RowMapper<JournalEntryData> { private static final class GLJournalEntryMapper implements RowMapper<JournalEntryData> {
public String officeRunningBalanceSchema() { public String officeRunningBalanceSchema() {
return "select je.id as id,je.account_id as glAccountId,je.type_enum as entryType,je.amount as amount, " 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 " + "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 " + "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"; + "and je.office_id=? and je.entry_date >= ? order by je.entry_date,je.id";
} }
public String organizationRunningBalanceSchema() { public String organizationRunningBalanceSchema() {
return "select je.id as id,je.account_id as glAccountId," + "je.type_enum as entryType,je.amount as amount, " 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 " + "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 " + "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"; + "and je.entry_date >= ? order by je.entry_date,je.id";
} }
@Override @Override
public JournalEntryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { public JournalEntryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id"); final Long id = rs.getLong("id");
final Long glAccountId = rs.getLong("glAccountId"); final Long glAccountId = rs.getLong("glAccountId");
final Long officeId = rs.getLong("officeId"); final Long officeId = rs.getLong("officeId");
final int accountTypeId = JdbcSupport.getInteger(rs, "classification"); final int accountTypeId = JdbcSupport.getInteger(rs, "classification");
final EnumOptionData accountType = AccountingEnumerations.gLAccountType(accountTypeId); final EnumOptionData accountType = AccountingEnumerations.gLAccountType(accountTypeId);
final BigDecimal amount = rs.getBigDecimal("amount"); final BigDecimal amount = rs.getBigDecimal("amount");
final int entryTypeId = JdbcSupport.getInteger(rs, "entryType"); final int entryTypeId = JdbcSupport.getInteger(rs, "entryType");
final EnumOptionData entryType = AccountingEnumerations.journalEntryType(entryTypeId); final EnumOptionData entryType = AccountingEnumerations.journalEntryType(entryTypeId);
return new JournalEntryData(id, officeId, null, null, glAccountId, null, accountType, null, entryType, amount, null, null, null, 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, null, null, null, null, null, null, null, null, null, null, null, null, null);
} }
} }
} }
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.portfolio.savings.api; 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.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse; 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.Context;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo; 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 lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.commands.domain.CommandWrapper; 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.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; 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.DepositsApiConstants;
import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService; import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/v1/currentaccounts/{accountId}/transactions") @Path("/v1/currentaccounts/{accountId}/transactions")
@@ -55,11 +65,58 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class CurrentAccountTransactionsApiResource { public class CurrentAccountTransactionsApiResource {
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final DefaultToApiJsonSerializer<SavingsAccountTransactionData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<SavingsAccountTransactionData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper; private final ApiRequestParameterHelper apiRequestParameterHelper;
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; 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 @GET
@Path("{transactionId}") @Path("{transactionId}")
@@ -1,214 +1,225 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.savings.api; package org.apache.fineract.portfolio.savings.api;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE; import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT; import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import java.util.List; import java.time.LocalDate;
import java.util.Map; import java.util.List;
import lombok.RequiredArgsConstructor; import java.util.Map;
import org.apache.commons.lang3.StringUtils; import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.savings.DepositsApiConstants; import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData; import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService; import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService; import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService; import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
import org.springframework.stereotype.Component; import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
import org.springframework.stereotype.Component;
@Path("/v1/currentaccounts")
@Component @Path("/v1/currentaccounts")
@Tag(name = "Current Account", description = "Current accounts are instances of a particular current product created for an individual or group.") @Component
@RequiredArgsConstructor @Tag(name = "Current Account", description = "Current accounts are instances of a particular current product created for an individual or group.")
public class CurrentAccountsApiResource { @RequiredArgsConstructor
public class CurrentAccountsApiResource {
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
private final CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService;
private final PlatformSecurityContext context; private final CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService;
private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer; private final PlatformSecurityContext context;
private final ApiRequestParameterHelper apiRequestParameterHelper; private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer;
private final SqlValidator sqlValidator; private final ApiRequestParameterHelper apiRequestParameterHelper;
private final CurrentAccountWritePlatformService currentAccountWritePlatformService; private final SqlValidator sqlValidator;
private final CurrentAccountReadPlatformService currentAccountReadPlatformService; private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
private final CurrentAccountReadPlatformService currentAccountReadPlatformService;
@GET
@Path("template") @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Path("template")
@Produces({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Current Account Template") @Produces({ MediaType.APPLICATION_JSON })
@ApiResponse(responseCode = "200", description = "OK") @Operation(summary = "Retrieve Current Account Template")
public String template(@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId, @ApiResponse(responseCode = "200", description = "OK")
@QueryParam("groupId") @Parameter(description = "groupId") final Long groupId, public String template(@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
@QueryParam("productId") @Parameter(description = "productId") final Long productId, @QueryParam("groupId") @Parameter(description = "groupId") final Long groupId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, @QueryParam("productId") @Parameter(description = "productId") final Long productId,
@Context final UriInfo uriInfo) { @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, context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
staffInSelectedOfficeOnly); final SavingsAccountData savingsAccount = currentAccountTemplateReadPlatformService.retrieveTemplate(clientId, groupId, productId,
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); staffInSelectedOfficeOnly);
return toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiSetConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
} return toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiSetConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON }) @GET
@Produces({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Operation(summary = "List current accounts") @Produces({ MediaType.APPLICATION_JSON })
@ApiResponse(responseCode = "200", description = "OK") @Operation(summary = "List current accounts")
public String retrieveAll(@Context final UriInfo uriInfo, @ApiResponse(responseCode = "200", description = "OK")
@QueryParam("externalId") final String externalId, public String retrieveAll(@Context final UriInfo uriInfo,
@QueryParam("offset") final Integer offset, @QueryParam("externalId") final String externalId,
@QueryParam("limit") final Integer limit, @QueryParam("offset") final Integer offset,
@QueryParam("orderBy") final String orderBy, @QueryParam("limit") final Integer limit,
@QueryParam("sortOrder") final String sortOrder) { @QueryParam("orderBy") final String orderBy,
@QueryParam("sortOrder") final String sortOrder,
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME); @QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo,
final List<Map<String, Object>> accounts = currentAccountReadPlatformService.retrieveAll(); @QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
return GSON.toJson(accounts); @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,
@POST @QueryParam("asOnDate") @Parameter(description = "asOnDate (YYYY-MM-DD)") final String asOnDate) {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
@Operation(summary = "Submit new current account application") final List<Map<String, Object>> accounts = currentAccountReadPlatformService.retrieveAll(offset, limit, accountNo, clientName,
@ApiResponse(responseCode = "200", description = "OK") status,
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) { fromDate != null ? LocalDate.parse(fromDate) : null,
final org.apache.fineract.infrastructure.core.api.JsonCommand command = toDate != null ? LocalDate.parse(toDate) : null,
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null, asOnDate != null ? LocalDate.parse(asOnDate) : null);
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}")); return GSON.toJson(accounts);
final CommandProcessingResult result = currentAccountWritePlatformService.submitApplication(command); }
return toApiJsonSerializer.serialize(result);
} @POST
@Consumes({ MediaType.APPLICATION_JSON })
@GET @Produces({ MediaType.APPLICATION_JSON })
@Path("{accountId}") @Operation(summary = "Submit new current account application")
@Consumes({ MediaType.APPLICATION_JSON }) @ApiResponse(responseCode = "200", description = "OK")
@Produces({ MediaType.APPLICATION_JSON }) public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
@Operation(summary = "Retrieve a current account") final org.apache.fineract.infrastructure.core.api.JsonCommand command =
@ApiResponse(responseCode = "200", description = "OK") org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
public String retrieveOne(@PathParam("accountId") final Long accountId, JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, final CommandProcessingResult result = currentAccountWritePlatformService.submitApplication(command);
@DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus, return toApiJsonSerializer.serialize(result);
@Context final UriInfo uriInfo) { }
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME); @GET
final Map<String, Object> account = currentAccountReadPlatformService.retrieveOne(accountId); @Path("{accountId}")
return GSON.toJson(account); @Consumes({ MediaType.APPLICATION_JSON })
} @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a current account")
@PUT @ApiResponse(responseCode = "200", description = "OK")
@Path("{accountId}") public String retrieveOne(@PathParam("accountId") final Long accountId,
@Consumes({ MediaType.APPLICATION_JSON }) @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
@Produces({ MediaType.APPLICATION_JSON }) @DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus,
@Operation(summary = "Modify a current account application") @Context final UriInfo uriInfo) {
@ApiResponse(responseCode = "200", description = "OK")
public String update(@PathParam("accountId") final Long accountId, context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
@Parameter(hidden = true) final String apiRequestBodyAsJson, final Map<String, Object> account = currentAccountReadPlatformService.retrieveOne(accountId);
@QueryParam("command") final String commandParam) { return GSON.toJson(account);
}
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null, @PUT
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}")); @Path("{accountId}")
final CommandProcessingResult result = currentAccountWritePlatformService.modifyApplication(accountId, command); @Consumes({ MediaType.APPLICATION_JSON })
return toApiJsonSerializer.serialize(result); @Produces({ MediaType.APPLICATION_JSON })
} @Operation(summary = "Modify a current account application")
@ApiResponse(responseCode = "200", description = "OK")
@POST public String update(@PathParam("accountId") final Long accountId,
@Path("{accountId}") @Parameter(hidden = true) final String apiRequestBodyAsJson,
@Consumes({ MediaType.APPLICATION_JSON }) @QueryParam("command") final String commandParam) {
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Approve/Reject/Activate/Close/Deposit/Withdrawal and other current account commands") final org.apache.fineract.infrastructure.core.api.JsonCommand command =
@ApiResponse(responseCode = "200", description = "OK") org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
public String handleCommands(@PathParam("accountId") final Long accountId, JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
@QueryParam("command") final String commandParam, final CommandProcessingResult result = currentAccountWritePlatformService.modifyApplication(accountId, command);
@Parameter(hidden = true) final String apiRequestBodyAsJson) { return toApiJsonSerializer.serialize(result);
}
String jsonApiRequest = apiRequestBodyAsJson;
if (StringUtils.isBlank(jsonApiRequest)) { @POST
jsonApiRequest = "{}"; @Path("{accountId}")
} @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
final org.apache.fineract.infrastructure.core.api.JsonCommand command = @Operation(summary = "Approve/Reject/Activate/Close/Deposit/Withdrawal and other current account commands")
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null, @ApiResponse(responseCode = "200", description = "OK")
JsonParser.parseString(jsonApiRequest)); public String handleCommands(@PathParam("accountId") final Long accountId,
@QueryParam("command") final String commandParam,
CommandProcessingResult result = null; @Parameter(hidden = true) final String apiRequestBodyAsJson) {
if (is(commandParam, "reject")) {
result = currentAccountWritePlatformService.rejectApplication(accountId, command); String jsonApiRequest = apiRequestBodyAsJson;
} else if (is(commandParam, "withdrawnByApplicant")) { if (StringUtils.isBlank(jsonApiRequest)) {
result = currentAccountWritePlatformService.withdrawApplication(accountId, command); jsonApiRequest = "{}";
} else if (is(commandParam, "approve")) { }
result = currentAccountWritePlatformService.approveApplication(accountId, command);
} else if (is(commandParam, "undoapproval")) { final org.apache.fineract.infrastructure.core.api.JsonCommand command =
result = currentAccountWritePlatformService.undoApproval(accountId, command); org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
} else if (is(commandParam, "activate")) { JsonParser.parseString(jsonApiRequest));
result = currentAccountWritePlatformService.activate(accountId, command);
} else if (is(commandParam, "close")) { CommandProcessingResult result = null;
result = currentAccountWritePlatformService.closeAccount(accountId, command); if (is(commandParam, "reject")) {
} result = currentAccountWritePlatformService.rejectApplication(accountId, command);
} else if (is(commandParam, "withdrawnByApplicant")) {
if (result == null) { result = currentAccountWritePlatformService.withdrawApplication(accountId, command);
throw new UnrecognizedQueryParamException("command", commandParam, } else if (is(commandParam, "approve")) {
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "close" }); result = currentAccountWritePlatformService.approveApplication(accountId, command);
} } else if (is(commandParam, "undoapproval")) {
result = currentAccountWritePlatformService.undoApproval(accountId, command);
return toApiJsonSerializer.serialize(result); } else if (is(commandParam, "activate")) {
} result = currentAccountWritePlatformService.activate(accountId, command);
} else if (is(commandParam, "close")) {
@DELETE result = currentAccountWritePlatformService.closeAccount(accountId, command);
@Path("{accountId}") }
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) if (result == null) {
@Operation(summary = "Delete a current account application") throw new UnrecognizedQueryParamException("command", commandParam,
@ApiResponse(responseCode = "200", description = "OK") new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "close" });
public String delete(@PathParam("accountId") final Long accountId) { }
final CommandProcessingResult result = currentAccountWritePlatformService.deleteApplication(accountId);
return toApiJsonSerializer.serialize(result); return toApiJsonSerializer.serialize(result);
} }
private boolean is(final String commandParam, final String commandValue) { @DELETE
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); @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);
}
}
@@ -1,470 +1,481 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.savings.api; package org.apache.fineract.portfolio.savings.api;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE; import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT; import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays; import java.time.LocalDate;
import java.util.Collection; import java.util.Arrays;
import java.util.HashMap; import java.util.Collection;
import java.util.HashSet; import java.util.HashMap;
import java.util.Set; import java.util.HashSet;
import lombok.RequiredArgsConstructor; import java.util.Set;
import org.apache.commons.lang3.StringUtils; import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.infrastructure.bulkimport.data.GlobalEntityType; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookPopulatorService; import org.apache.fineract.infrastructure.bulkimport.data.GlobalEntityType;
import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookService; import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookPopulatorService;
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper; import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
import org.apache.fineract.infrastructure.core.api.JsonQuery; import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.api.JsonQuery;
import org.apache.fineract.infrastructure.core.data.PaginationParameters; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.UploadRequest; import org.apache.fineract.infrastructure.core.data.PaginationParameters;
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException; import org.apache.fineract.infrastructure.core.data.UploadRequest;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.portfolio.savings.DepositsApiConstants; import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.SavingsApiConstants; import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.savings.data.DepositAccountData; import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.data.RecurringDepositAccountData; import org.apache.fineract.portfolio.savings.data.DepositAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData; import org.apache.fineract.portfolio.savings.data.RecurringDepositAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData; import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
import org.apache.fineract.portfolio.savings.service.DepositAccountPreMatureCalculationPlatformService; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformService; import org.apache.fineract.portfolio.savings.service.DepositAccountPreMatureCalculationPlatformService;
import org.apache.fineract.portfolio.savings.service.RecurringDepositAccountInterestCalculationService; import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService; import org.apache.fineract.portfolio.savings.service.RecurringDepositAccountInterestCalculationService;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.springframework.stereotype.Component; import org.glassfish.jersey.media.multipart.FormDataParam;
import org.springframework.util.CollectionUtils; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@Path("/v1/recurringdepositaccounts")
@Component @Path("/v1/recurringdepositaccounts")
@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.") @Component
@RequiredArgsConstructor @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.")
public class RecurringDepositAccountsApiResource { @RequiredArgsConstructor
public class RecurringDepositAccountsApiResource {
private final DepositAccountReadPlatformService depositAccountReadPlatformService;
private final PlatformSecurityContext context; private final DepositAccountReadPlatformService depositAccountReadPlatformService;
private final DefaultToApiJsonSerializer<DepositAccountData> toApiJsonSerializer; private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final DefaultToApiJsonSerializer<DepositAccountData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService; private final ApiRequestParameterHelper apiRequestParameterHelper;
private final FromJsonHelper fromJsonHelper; private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
private final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService; private final FromJsonHelper fromJsonHelper;
private final BulkImportWorkbookService bulkImportWorkbookService; private final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService;
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService; private final BulkImportWorkbookService bulkImportWorkbookService;
private final RecurringDepositAccountInterestCalculationService recurringDepositAccountInterestCalculationService; private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
private final SqlValidator sqlValidator; private final RecurringDepositAccountInterestCalculationService recurringDepositAccountInterestCalculationService;
private final SqlValidator sqlValidator;
@GET
@Path("template") @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Path("template")
@Produces({ MediaType.APPLICATION_JSON }) @Consumes({ 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" @Produces({ MediaType.APPLICATION_JSON })
+ "\n" + "Field Defaults\n" + "Allowed Value Lists\n\n" + "Example Requests:\n" + "\n" @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"
+ "recurringdepositaccounts/template?clientId=1\n" + "\n" + "\n" + "recurringdepositaccounts/template?clientId=1&productId=1") + "\n" + "Field Defaults\n" + "Allowed Value Lists\n\n" + "Example Requests:\n" + "\n"
@ApiResponses({ + "recurringdepositaccounts/template?clientId=1\n" + "\n" + "\n" + "recurringdepositaccounts/template?clientId=1&productId=1")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsTemplateResponse.class))) }) @ApiResponses({
public String template(@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId, @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsTemplateResponse.class))) })
@QueryParam("groupId") @Parameter(description = "groupId") final Long groupId, public String template(@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
@QueryParam("productId") @Parameter(description = "productId") final Long productId, @QueryParam("groupId") @Parameter(description = "groupId") final Long groupId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, @QueryParam("productId") @Parameter(description = "productId") final Long productId,
@Context final UriInfo uriInfo) { @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
final DepositAccountData account = this.depositAccountReadPlatformService.retrieveTemplate(DepositAccountType.RECURRING_DEPOSIT,
clientId, groupId, productId, staffInSelectedOfficeOnly); 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, final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); return this.toApiJsonSerializer.serialize(settings, account,
} DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON }) @GET
@Produces({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Recurring deposit applications/accounts", description = "Lists Recurring deposit applications/accounts\n\n" @Produces({ MediaType.APPLICATION_JSON })
+ "Example Requests:\n" + "\n" + "recurringdepositaccounts\n" + "\n" + "\n" + "recurringdepositaccounts?fields=name") @Operation(summary = "List Recurring deposit applications/accounts", description = "Lists Recurring deposit applications/accounts\n\n"
@ApiResponses({ + "Example Requests:\n" + "\n" + "recurringdepositaccounts\n" + "\n" + "\n" + "recurringdepositaccounts?fields=name")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsResponse.class)))) }) @ApiResponses({
public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("paged") @Parameter(description = "paged") final Boolean paged, @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsResponse.class)))) })
@QueryParam("offset") @Parameter(description = "offset") final Integer offset, public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("paged") @Parameter(description = "paged") final Boolean paged,
@QueryParam("limit") @Parameter(description = "limit") final Integer limit, @QueryParam("offset") @Parameter(description = "offset") final Integer offset,
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, @QueryParam("limit") @Parameter(description = "limit") final Integer limit,
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) { @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); @QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo,
sqlValidator.validate(orderBy); @QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
sqlValidator.validate(sortOrder); @QueryParam("status") @Parameter(description = "status") final String status,
final PaginationParameters paginationParameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit) @QueryParam("fromDate") @Parameter(description = "fromDate (YYYY-MM-DD)") final String fromDate,
.offset(offset).orderBy(orderBy).sortOrder(sortOrder).build(); @QueryParam("toDate") @Parameter(description = "toDate (YYYY-MM-DD)") final String toDate,
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); @QueryParam("asOnDate") @Parameter(description = "asOnDate (YYYY-MM-DD)") final String asOnDate) {
if (paginationParameters.isPaged()) { this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
final Page<DepositAccountData> account = this.depositAccountReadPlatformService sqlValidator.validate(orderBy);
.retrieveAllPaged(DepositAccountType.RECURRING_DEPOSIT, paginationParameters); sqlValidator.validate(sortOrder);
return this.toApiJsonSerializer.serialize(settings, account, final PaginationParameters paginationParameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit)
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); .offset(offset).orderBy(orderBy).sortOrder(sortOrder).build();
} final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final Collection<DepositAccountData> account = this.depositAccountReadPlatformService if (paginationParameters.isPaged()) {
.retrieveAll(DepositAccountType.RECURRING_DEPOSIT, paginationParameters); final Page<DepositAccountData> account = this.depositAccountReadPlatformService
.retrieveAllPaged(DepositAccountType.RECURRING_DEPOSIT, paginationParameters, accountNo, clientName,
return this.toApiJsonSerializer.serialize(settings, account, status,
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); fromDate != null ? LocalDate.parse(fromDate) : null,
} toDate != null ? LocalDate.parse(toDate) : null,
asOnDate != null ? LocalDate.parse(asOnDate) : null);
@POST return this.toApiJsonSerializer.serialize(settings, account,
@Consumes({ MediaType.APPLICATION_JSON }) DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
@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" final Collection<DepositAccountData> account = this.depositAccountReadPlatformService
+ "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" .retrieveAll(DepositAccountType.RECURRING_DEPOSIT, paginationParameters);
+ "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))) return this.toApiJsonSerializer.serialize(settings, account,
@ApiResponses({ DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsResponse.class))) }) }
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
@POST
final CommandWrapper commandRequest = new CommandWrapperBuilder().createRecurringDepositAccount().withJson(apiRequestBodyAsJson) @Consumes({ MediaType.APPLICATION_JSON })
.build(); @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Submit new recurring deposit application", description = "Submits new recurring deposit application\n\n"
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + "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"
return this.toApiJsonSerializer.serialize(result); + "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({
@GET @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsResponse.class))) })
@Path("{accountId}") public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) final CommandWrapper commandRequest = new CommandWrapperBuilder().createRecurringDepositAccount().withJson(apiRequestBodyAsJson)
@Operation(summary = "Retrieve a recurring deposit application/account", description = "Retrieves a recurring deposit application/account\n\n" .build();
+ "Example Requests :\n" + "\n" + "recurringdepositaccounts/1\n" + "\n" + "\n" + "recurringdepositaccounts/1?associations=all")
@ApiResponses({ final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.GetRecurringDepositAccountsAccountIdResponse.class))) })
public String retrieveOne(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId, return this.toApiJsonSerializer.serialize(result);
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, }
@DefaultValue("all") @QueryParam("chargeStatus") @Parameter(description = "chargeStatus") final String chargeStatus,
@Context final UriInfo uriInfo) { @GET
@Path("{accountId}")
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) { @Operation(summary = "Retrieve a recurring deposit application/account", description = "Retrieves a recurring deposit application/account\n\n"
throw new UnrecognizedQueryParamException("status", chargeStatus, new Object[] { "all", "active", "inactive" }); + "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))) })
final RecurringDepositAccountData account = (RecurringDepositAccountData) this.depositAccountReadPlatformService public String retrieveOne(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
.retrieveOneWithChartSlabs(DepositAccountType.RECURRING_DEPOSIT, accountId); @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
@DefaultValue("all") @QueryParam("chargeStatus") @Parameter(description = "chargeStatus") final String chargeStatus,
final Set<String> mandatoryResponseParameters = new HashSet<>(); @Context final UriInfo uriInfo) {
final RecurringDepositAccountData accountTemplate = populateTemplateAndAssociations(accountId, account, staffInSelectedOfficeOnly,
chargeStatus, uriInfo, mandatoryResponseParameters); this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(), if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) {
mandatoryResponseParameters); throw new UnrecognizedQueryParamException("status", chargeStatus, new Object[] { "all", "active", "inactive" });
return this.toApiJsonSerializer.serialize(settings, accountTemplate, }
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
} final RecurringDepositAccountData account = (RecurringDepositAccountData) this.depositAccountReadPlatformService
.retrieveOneWithChartSlabs(DepositAccountType.RECURRING_DEPOSIT, accountId);
private RecurringDepositAccountData populateTemplateAndAssociations(final Long accountId,
final RecurringDepositAccountData savingsAccount, final boolean staffInSelectedOfficeOnly, final String chargeStatus, final Set<String> mandatoryResponseParameters = new HashSet<>();
final UriInfo uriInfo, final Set<String> mandatoryResponseParameters) { final RecurringDepositAccountData accountTemplate = populateTemplateAndAssociations(accountId, account, staffInSelectedOfficeOnly,
chargeStatus, uriInfo, mandatoryResponseParameters);
Collection<SavingsAccountTransactionData> transactions = null;
Collection<SavingsAccountChargeData> charges = null; final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, accountTemplate,
if (!associationParameters.isEmpty()) { DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
}
if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList(SavingsApiConstants.transactions, SavingsApiConstants.charges)); private RecurringDepositAccountData populateTemplateAndAssociations(final Long accountId,
} final RecurringDepositAccountData savingsAccount, final boolean staffInSelectedOfficeOnly, final String chargeStatus,
final UriInfo uriInfo, final Set<String> mandatoryResponseParameters) {
if (associationParameters.contains(SavingsApiConstants.transactions)) {
mandatoryResponseParameters.add(SavingsApiConstants.transactions); Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsAccountTransactionData> currentTransactions = this.depositAccountReadPlatformService Collection<SavingsAccountChargeData> charges = null;
.retrieveAllTransactions(DepositAccountType.RECURRING_DEPOSIT, accountId);
if (!CollectionUtils.isEmpty(currentTransactions)) { final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
transactions = currentTransactions; if (!associationParameters.isEmpty()) {
}
} if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList(SavingsApiConstants.transactions, SavingsApiConstants.charges));
if (associationParameters.contains(SavingsApiConstants.charges)) { }
mandatoryResponseParameters.add(SavingsApiConstants.charges);
final Collection<SavingsAccountChargeData> currentCharges = this.savingsAccountChargeReadPlatformService if (associationParameters.contains(SavingsApiConstants.transactions)) {
.retrieveSavingsAccountCharges(accountId, chargeStatus); mandatoryResponseParameters.add(SavingsApiConstants.transactions);
if (!CollectionUtils.isEmpty(currentCharges)) { final Collection<SavingsAccountTransactionData> currentTransactions = this.depositAccountReadPlatformService
charges = currentCharges; .retrieveAllTransactions(DepositAccountType.RECURRING_DEPOSIT, accountId);
} if (!CollectionUtils.isEmpty(currentTransactions)) {
} transactions = currentTransactions;
} }
}
RecurringDepositAccountData templateData = null;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); if (associationParameters.contains(SavingsApiConstants.charges)) {
mandatoryResponseParameters.add(SavingsApiConstants.charges);
if (settings.isTemplate()) { final Collection<SavingsAccountChargeData> currentCharges = this.savingsAccountChargeReadPlatformService
templateData = (RecurringDepositAccountData) this.depositAccountReadPlatformService.retrieveTemplate( .retrieveSavingsAccountCharges(accountId, chargeStatus);
DepositAccountType.RECURRING_DEPOSIT, savingsAccount.getClientId(), savingsAccount.getGroupId(), if (!CollectionUtils.isEmpty(currentCharges)) {
savingsAccount.getDepositProductId(), staffInSelectedOfficeOnly); charges = currentCharges;
} }
}
return RecurringDepositAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges); }
}
RecurringDepositAccountData templateData = null;
@PUT final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@Path("{accountId}")
@Consumes({ MediaType.APPLICATION_JSON }) if (settings.isTemplate()) {
@Produces({ MediaType.APPLICATION_JSON }) templateData = (RecurringDepositAccountData) this.depositAccountReadPlatformService.retrieveTemplate(
@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") DepositAccountType.RECURRING_DEPOSIT, savingsAccount.getClientId(), savingsAccount.getGroupId(),
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PutRecurringDepositAccountsAccountIdRequest.class))) savingsAccount.getDepositProductId(), staffInSelectedOfficeOnly);
@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, return RecurringDepositAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges);
@Parameter(hidden = true) final String apiRequestBodyAsJson) { }
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRecurringDepositAccount(accountId) @PUT
.withJson(apiRequestBodyAsJson).build(); @Path("{accountId}")
@Consumes({ MediaType.APPLICATION_JSON })
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); @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")
return this.toApiJsonSerializer.serialize(result); @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))) })
@POST public String update(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
@Path("{accountId}") @Parameter(hidden = true) final String apiRequestBodyAsJson) {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRecurringDepositAccount(accountId)
@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" .withJson(apiRequestBodyAsJson).build();
+ "Approves recurring deposit application so long as its in 'Submitted and pending approval' state.\n\n"
+ "Undo approval recurring deposit application:\n\n" final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ "Will move 'approved' recurring deposit application back to 'Submitted and pending approval' state.\n\n"
+ "Reject recurring deposit application\n\n" return this.toApiJsonSerializer.serialize(result);
+ "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" @POST
+ "Activate a recurring deposit account:\n\n" @Path("{accountId}")
+ "Results in an approved recurring deposit application being converted into an 'active' recurring deposit account.\n\n" @Consumes({ MediaType.APPLICATION_JSON })
+ "Update the recommended deposit amount for a recurring deposit account:\n\n" @Produces({ MediaType.APPLICATION_JSON })
+ "Updates the recommended deposit amount for a RD account as on the effective date.\n\n" @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"
+ "Close a recurring deposit account\n\n" + "Approves recurring deposit application so long as its in 'Submitted and pending approval' state.\n\n"
+ "Results in a Matured recurring deposit account being converted into a 'closed' recurring deposit account.\n" + "\n" + "Undo approval recurring deposit application:\n\n"
+ "On account close allowed actions are." + "Premature Close a recurring deposit account:\n\n" + "Will move 'approved' recurring deposit application back to 'Submitted and pending approval' state.\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" + "Reject recurring deposit application\n\n"
+ "\n" + "On account premature closure allowed actions are.\n\n" + "Rejects recurring deposit application so long as its in 'Submitted and pending approval' state.\n\n"
+ "Calculate Premature amount on Recurring deposit account:\n\n" + "Withdraw recurring deposit application:\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" + "Used when an applicant withdraws from the recurring deposit application. It must be in 'Submitted and pending approval' state.\n\n"
+ "Calculate Interest on recurring Deposit Account:\n\n" + "Activate a 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" + "Results in an approved recurring deposit application being converted into an 'active' recurring deposit account.\n\n"
+ "Post Interest on recurring Deposit Account:\n\n" + "Update the recommended deposit amount for a 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" + "Updates the recommended deposit amount for a RD account as on the effective date.\n\n"
+ "Showing request/response for 'Post Interest on recurring Deposit Account'") + "Close a recurring deposit account\n\n"
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsAccountIdRequest.class))) + "Results in a Matured recurring deposit account being converted into a 'closed' recurring deposit account.\n" + "\n"
@ApiResponses({ + "On account close allowed actions are." + "Premature Close a recurring deposit account:\n\n"
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsAccountIdResponse.class))) }) + "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"
public String handleCommands(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId, + "\n" + "On account premature closure allowed actions are.\n\n"
@QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo, + "Calculate Premature amount on Recurring deposit account:\n\n"
@Parameter(hidden = true) final String apiRequestBodyAsJson) { + "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"
String jsonApiRequest = apiRequestBodyAsJson; + "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"
if (StringUtils.isBlank(jsonApiRequest)) { + "Post Interest on recurring Deposit Account:\n\n"
jsonApiRequest = "{}"; + "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)))
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest); @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.PostRecurringDepositAccountsAccountIdResponse.class))) })
CommandProcessingResult result = null; public String handleCommands(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
if (is(commandParam, "reject")) { @QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo,
final CommandWrapper commandRequest = builder.rejectRecurringDepositAccountApplication(accountId).build(); @Parameter(hidden = true) final String apiRequestBodyAsJson) {
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "withdrawnByApplicant")) { String jsonApiRequest = apiRequestBodyAsJson;
final CommandWrapper commandRequest = builder.withdrawRecurringDepositAccountApplication(accountId).build(); if (StringUtils.isBlank(jsonApiRequest)) {
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); jsonApiRequest = "{}";
} else if (is(commandParam, "approve")) { }
final CommandWrapper commandRequest = builder.approveRecurringDepositAccountApplication(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest);
} else if (is(commandParam, "undoapproval")) {
final CommandWrapper commandRequest = builder.undoRecurringDepositAccountApplication(accountId).build(); CommandProcessingResult result = null;
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); if (is(commandParam, "reject")) {
} else if (is(commandParam, "activate")) { final CommandWrapper commandRequest = builder.rejectRecurringDepositAccountApplication(accountId).build();
final CommandWrapper commandRequest = builder.recurringDepositAccountActivation(accountId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } else if (is(commandParam, "withdrawnByApplicant")) {
} else if (is(commandParam, "calculateInterest")) { final CommandWrapper commandRequest = builder.withdrawRecurringDepositAccountApplication(accountId).build();
final CommandWrapper commandRequest = builder.withNoJsonBody().recurringDepositAccountInterestCalculation(accountId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } else if (is(commandParam, "approve")) {
} else if (is(commandParam, DepositsApiConstants.UPDATE_DEPOSIT_AMOUNT)) { final CommandWrapper commandRequest = builder.approveRecurringDepositAccountApplication(accountId).build();
final CommandWrapper commandRequest = builder.updateDepositAmountForRecurringDepositAccount(accountId) result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
.withJson(apiRequestBodyAsJson).build(); } else if (is(commandParam, "undoapproval")) {
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandWrapper commandRequest = builder.undoRecurringDepositAccountApplication(accountId).build();
} else if (is(commandParam, "postInterest")) { result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandWrapper commandRequest = builder.recurringDepositAccountInterestPosting(accountId).build(); } else if (is(commandParam, "activate")) {
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandWrapper commandRequest = builder.recurringDepositAccountActivation(accountId).build();
} else if (is(commandParam, "close")) { result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandWrapper commandRequest = builder.closeRecurringDepositAccount(accountId).build(); } else if (is(commandParam, "calculateInterest")) {
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandWrapper commandRequest = builder.withNoJsonBody().recurringDepositAccountInterestCalculation(accountId).build();
} else if (is(commandParam, "prematureClose")) { result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandWrapper commandRequest = builder.prematureCloseRecurringDepositAccount(accountId).build(); } else if (is(commandParam, DepositsApiConstants.UPDATE_DEPOSIT_AMOUNT)) {
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandWrapper commandRequest = builder.updateDepositAmountForRecurringDepositAccount(accountId)
} else if (is(commandParam, "calculatePrematureAmount")) { .withJson(apiRequestBodyAsJson).build();
final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper); } else if (is(commandParam, "postInterest")) {
final DepositAccountData account = this.accountPreMatureCalculationPlatformService.calculatePreMatureAmount(accountId, query, final CommandWrapper commandRequest = builder.recurringDepositAccountInterestPosting(accountId).build();
DepositAccountType.RECURRING_DEPOSIT); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); } else if (is(commandParam, "close")) {
return this.toApiJsonSerializer.serialize(settings, account, final CommandWrapper commandRequest = builder.closeRecurringDepositAccount(accountId).build();
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} } else if (is(commandParam, "prematureClose")) {
final CommandWrapper commandRequest = builder.prematureCloseRecurringDepositAccount(accountId).build();
if (result == null) { result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
throw new UnrecognizedQueryParamException("command", commandParam, } else if (is(commandParam, "calculatePrematureAmount")) {
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest", final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
"postInterest", "close", "prematureClose", "calculatePrematureAmount" }); final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
} final DepositAccountData account = this.accountPreMatureCalculationPlatformService.calculatePreMatureAmount(accountId, query,
DepositAccountType.RECURRING_DEPOSIT);
return this.toApiJsonSerializer.serialize(result); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
} return this.toApiJsonSerializer.serialize(settings, account,
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
@POST }
@Path("calculate-rd-interest")
@Consumes({ MediaType.APPLICATION_JSON }) if (result == null) {
@Produces({ MediaType.APPLICATION_JSON }) throw new UnrecognizedQueryParamException("command", commandParam,
public String calculateRecurringDepositInterest(@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) { new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest",
JsonElement jsonElement = fromJsonHelper.parse(apiRequestBodyAsJson); "postInterest", "close", "prematureClose", "calculatePrematureAmount" });
HashMap<String, Object> result = recurringDepositAccountInterestCalculationService }
.calculateInterest(new JsonQuery(apiRequestBodyAsJson, jsonElement, fromJsonHelper));
return this.toApiJsonSerializer.serialize(result);
return toApiJsonSerializer.serializeResult(result); }
}
@POST
private boolean is(final String commandParam, final String commandValue) { @Path("calculate-rd-interest")
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); @Consumes({ MediaType.APPLICATION_JSON })
} @Produces({ MediaType.APPLICATION_JSON })
public String calculateRecurringDepositInterest(@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
@DELETE JsonElement jsonElement = fromJsonHelper.parse(apiRequestBodyAsJson);
@Path("{accountId}") HashMap<String, Object> result = recurringDepositAccountInterestCalculationService
@Consumes({ MediaType.APPLICATION_JSON }) .calculateInterest(new JsonQuery(apiRequestBodyAsJson, jsonElement, fromJsonHelper));
@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.") return toApiJsonSerializer.serializeResult(result);
@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) { private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteRecurringDepositAccount(accountId).build(); }
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); @DELETE
@Path("{accountId}")
return this.toApiJsonSerializer.serialize(result); @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.")
@GET @ApiResponses({
@Path("{accountId}/template") @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = RecurringDepositAccountsApiResourceSwagger.DeleteRecurringDepositAccountsResponse.class))) })
@Consumes({ MediaType.APPLICATION_JSON }) public String delete(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId) {
@Produces({ MediaType.APPLICATION_JSON })
public String accountClosureTemplate(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId, final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteRecurringDepositAccount(accountId).build();
@QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo) {
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
DepositAccountData account = null; return this.toApiJsonSerializer.serialize(result);
if (is(commandParam, "close")) { }
account = this.depositAccountReadPlatformService.retrieveOneWithClosureTemplate(DepositAccountType.RECURRING_DEPOSIT,
accountId); @GET
@Path("{accountId}/template")
} @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); public String accountClosureTemplate(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
return this.toApiJsonSerializer.serialize(settings, account, @QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo) {
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
} this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
DepositAccountData account = null;
@GET if (is(commandParam, "close")) {
@Path("downloadtemplate") account = this.depositAccountReadPlatformService.retrieveOneWithClosureTemplate(DepositAccountType.RECURRING_DEPOSIT,
@Produces("application/vnd.ms-excel") accountId);
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); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
} return this.toApiJsonSerializer.serialize(settings, account,
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
@POST }
@Path("uploadtemplate")
@Consumes(MediaType.MULTIPART_FORM_DATA) @GET
@RequestBody(description = "Upload recurring deposit template", content = { @Path("downloadtemplate")
@Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) }) @Produces("application/vnd.ms-excel")
public String postRecurringDepositTemplate(@FormDataParam("file") InputStream uploadedInputStream, public Response getRecurringDepositTemplate(@QueryParam("officeId") final Long officeId, @QueryParam("staffId") final Long staffId,
@FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale, @QueryParam("dateFormat") final String dateFormat) {
@FormDataParam("dateFormat") final String dateFormat) { return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS.toString(), officeId, staffId,
final Long importDocumentId = this.bulkImportWorkbookService.importWorkbook(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS.toString(), dateFormat);
uploadedInputStream, fileDetail, locale, dateFormat); }
return this.toApiJsonSerializer.serialize(importDocumentId);
} @POST
@Path("uploadtemplate")
@GET @Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("transactions/downloadtemplate") @RequestBody(description = "Upload recurring deposit template", content = {
@Produces("application/vnd.ms-excel") @Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) })
public Response getRecurringDepositTransactionTemplate(@QueryParam("officeId") final Long officeId, public String postRecurringDepositTemplate(@FormDataParam("file") InputStream uploadedInputStream,
@QueryParam("dateFormat") final String dateFormat) { @FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale,
return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS_TRANSACTIONS.toString(), officeId, @FormDataParam("dateFormat") final String dateFormat) {
null, dateFormat); final Long importDocumentId = this.bulkImportWorkbookService.importWorkbook(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS.toString(),
} uploadedInputStream, fileDetail, locale, dateFormat);
return this.toApiJsonSerializer.serialize(importDocumentId);
@POST }
@Path("transactions/uploadtemplate")
@Consumes(MediaType.MULTIPART_FORM_DATA) @GET
@RequestBody(description = "Upload recurring deposit transactions template", content = { @Path("transactions/downloadtemplate")
@Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) }) @Produces("application/vnd.ms-excel")
public String postRecurringDepositTransactionsTemplate(@FormDataParam("file") InputStream uploadedInputStream, public Response getRecurringDepositTransactionTemplate(@QueryParam("officeId") final Long officeId,
@FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale, @QueryParam("dateFormat") final String dateFormat) {
@FormDataParam("dateFormat") final String dateFormat) { return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS_TRANSACTIONS.toString(), officeId,
final Long importDocumentId = this.bulkImportWorkbookService.importWorkbook( null, dateFormat);
GlobalEntityType.RECURRING_DEPOSIT_ACCOUNTS_TRANSACTIONS.toString(), uploadedInputStream, fileDetail, locale, dateFormat); }
return this.toApiJsonSerializer.serialize(importDocumentId);
} @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);
}
}
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTACCOUNT", action = "ADJUSTTRANSACTION") @CommandType(entity = "CURRENTACCOUNT", action = "ADJUSTTRANSACTION")
public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCommandSourceHandler { public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService; private final CurrentAccountTransactionWritePlatformService writePlatformService;
@Autowired @Autowired
public CurrentAccountTransactionAdjustmentCommandHandler(final SavingsAccountWritePlatformService writePlatformService) { public CurrentAccountTransactionAdjustmentCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService; this.writePlatformService = writePlatformService;
} }
@@ -42,6 +42,6 @@ public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCom
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { public CommandProcessingResult processCommand(final JsonCommand command) {
final Long transactionId = Long.valueOf(command.getTransactionId()); final Long transactionId = Long.valueOf(command.getTransactionId());
return this.writePlatformService.adjustSavingsTransaction(command.getSavingsId(), transactionId, command); return this.writePlatformService.adjustTransaction(command.getSavingsId(), transactionId, command);
} }
} }
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTACCOUNT", action = "DEPOSIT") @CommandType(entity = "CURRENTACCOUNT", action = "DEPOSIT")
public class DepositCurrentAccountCommandHandler implements NewCommandSourceHandler { public class DepositCurrentAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService; private final CurrentAccountTransactionWritePlatformService writePlatformService;
@Autowired @Autowired
public DepositCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) { public DepositCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService; this.writePlatformService = writePlatformService;
} }
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTACCOUNT", action = "HOLDAMOUNT") @CommandType(entity = "CURRENTACCOUNT", action = "HOLDAMOUNT")
public class HoldAmountCurrentAccountCommandHandler implements NewCommandSourceHandler { public class HoldAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService; private final CurrentAccountTransactionWritePlatformService writePlatformService;
@Autowired @Autowired
public HoldAmountCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) { public HoldAmountCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService; this.writePlatformService = writePlatformService;
} }
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTACCOUNT", action = "RELEASEAMOUNT") @CommandType(entity = "CURRENTACCOUNT", action = "RELEASEAMOUNT")
public class ReleaseAmountCurrentAccountCommandHandler implements NewCommandSourceHandler { public class ReleaseAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService; private final CurrentAccountTransactionWritePlatformService writePlatformService;
@Autowired @Autowired
public ReleaseAmountCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) { public ReleaseAmountCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService; this.writePlatformService = writePlatformService;
} }
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTACCOUNT", action = "REVERSETRANSACTION") @CommandType(entity = "CURRENTACCOUNT", action = "REVERSETRANSACTION")
public class ReverseTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler { public class ReverseTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService; private final CurrentAccountTransactionWritePlatformService writePlatformService;
@Autowired @Autowired
public ReverseTransactionCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) { public ReverseTransactionCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService; this.writePlatformService = writePlatformService;
} }
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTACCOUNT", action = "UNDOTRANSACTION") @CommandType(entity = "CURRENTACCOUNT", action = "UNDOTRANSACTION")
public class UndoTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler { public class UndoTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService; private final CurrentAccountTransactionWritePlatformService writePlatformService;
@Autowired @Autowired
public UndoTransactionCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) { public UndoTransactionCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService; this.writePlatformService = writePlatformService;
} }
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
@CommandType(entity = "CURRENTACCOUNT", action = "WITHDRAWAL") @CommandType(entity = "CURRENTACCOUNT", action = "WITHDRAWAL")
public class WithdrawCurrentAccountCommandHandler implements NewCommandSourceHandler { public class WithdrawCurrentAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService; private final CurrentAccountTransactionWritePlatformService writePlatformService;
@Autowired @Autowired
public WithdrawCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) { public WithdrawCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService; this.writePlatformService = writePlatformService;
} }
@@ -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);
}
}
@@ -1,56 +1,63 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.savings.service; package org.apache.fineract.portfolio.savings.service;
import java.util.Collection; import java.time.LocalDate;
import java.util.Map; import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.PaginationParameters; import java.util.Map;
import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.data.PaginationParameters;
import org.apache.fineract.portfolio.account.data.AccountTransferDTO; import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
import org.apache.fineract.portfolio.savings.data.DepositAccountData; import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData; import org.apache.fineract.portfolio.savings.data.DepositAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
public interface DepositAccountReadPlatformService {
public interface DepositAccountReadPlatformService {
Collection<DepositAccountData> retrieveAll(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
Collection<DepositAccountData> retrieveAll(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
Collection<DepositAccountData> retrieveAllForLookup(DepositAccountType depositAccountType);
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters,
DepositAccountData retrieveOne(DepositAccountType depositAccountType, Long accountId); String accountNo, String clientName);
DepositAccountData retrieveOneWithClosureTemplate(DepositAccountType depositAccountType, Long accountId); Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters,
String accountNo, String clientName, String status, LocalDate fromDate, LocalDate toDate, LocalDate asOnDate);
DepositAccountData retrieveOneWithChartSlabs(DepositAccountType depositAccountType, Long productId);
Collection<DepositAccountData> retrieveAllForLookup(DepositAccountType depositAccountType);
Collection<SavingsAccountTransactionData> retrieveAllTransactions(DepositAccountType depositAccountType, Long accountId);
DepositAccountData retrieveOne(DepositAccountType depositAccountType, Long accountId);
DepositAccountData retrieveTemplate(DepositAccountType depositAccountType, Long clientId, Long groupId, Long productId,
boolean staffInSelectedOfficeOnly); DepositAccountData retrieveOneWithClosureTemplate(DepositAccountType depositAccountType, Long accountId);
Collection<DepositAccountData> retrieveForMaturityUpdate(); DepositAccountData retrieveOneWithChartSlabs(DepositAccountType depositAccountType, Long productId);
SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(Long accountId); Collection<SavingsAccountTransactionData> retrieveAllTransactions(DepositAccountType depositAccountType, Long accountId);
Collection<AccountTransferDTO> retrieveDataForInterestTransfer(); DepositAccountData retrieveTemplate(DepositAccountType depositAccountType, Long clientId, Long groupId, Long productId,
boolean staffInSelectedOfficeOnly);
Collection<Map<String, Object>> retriveDataForRDScheduleCreation();
} Collection<DepositAccountData> retrieveForMaturityUpdate();
SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(Long accountId);
Collection<AccountTransferDTO> retrieveDataForInterestTransfer();
Collection<Map<String, Object>> retriveDataForRDScheduleCreation();
}