Repository: incubator-fineract Updated Branches: refs/heads/develop a2d794a34 -> 9d4d90121
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java index 5cc2065..760ac7c 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java @@ -114,6 +114,7 @@ import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.springframework.data.jpa.domain.AbstractPersistable; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import com.google.gson.JsonArray; @@ -154,6 +155,9 @@ public class SavingsAccount extends AbstractPersistable<Long> { @Column(name = "status_enum", nullable = false) protected Integer status; + @Column(name = "sub_status_enum", nullable = false) + protected Integer sub_status = 0; + @Column(name = "account_type_enum", nullable = false) protected Integer accountType; @@ -901,6 +905,11 @@ public class SavingsAccount extends AbstractPersistable<Long> { this.transactions.add(transaction); this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); + + if(this.sub_status.equals(SavingsAccountSubStatusEnum.INACTIVE.getValue()) + || this.sub_status.equals(SavingsAccountSubStatusEnum.DORMANT.getValue())){ + this.sub_status = SavingsAccountSubStatusEnum.NONE.getValue(); + } return transaction; } @@ -987,6 +996,10 @@ public class SavingsAccount extends AbstractPersistable<Long> { // auto pay withdrawal fee payWithdrawalFee(transactionDTO.getTransactionAmount(), transactionDTO.getTransactionDate(), transactionDTO.getAppUser()); } + if(this.sub_status.equals(SavingsAccountSubStatusEnum.INACTIVE.getValue()) + || this.sub_status.equals(SavingsAccountSubStatusEnum.DORMANT.getValue())){ + this.sub_status = SavingsAccountSubStatusEnum.NONE.getValue(); + } return transaction; } @@ -2744,4 +2757,37 @@ public class SavingsAccount extends AbstractPersistable<Long> { return recalucateDailyBalance; } + + public void setSubStatusInactive(AppUser appUser) { + this.sub_status = SavingsAccountSubStatusEnum.INACTIVE.getValue(); + LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); + for (SavingsAccountCharge charge : this.charges()) { + if (charge.isSavingsNoActivity() && charge.isActive()) { + charge.updateWithdralFeeAmount(this.getAccountBalance()); + this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, appUser); + } + } + recalculateDailyBalances(Money.zero(this.currency), transactionDate); + this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); + } + + + public void setSubStatusDormant() { + this.sub_status = SavingsAccountSubStatusEnum.DORMANT.getValue(); + } + + public void escheat(AppUser appUser) { + this.status = SavingsAccountStatusType.CLOSED.getValue(); + this.sub_status = SavingsAccountSubStatusEnum.ESCHEAT.getValue(); + this.closedOnDate = DateUtils.getDateOfTenant(); + this.closedBy = appUser; + + LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); + if(this.getSummary().getAccountBalance(this.getCurrency()).isGreaterThanZero()){ + SavingsAccountTransaction transaction = SavingsAccountTransaction.escheat(this, transactionDate, appUser); + this.transactions.add(transaction); + } + recalculateDailyBalances(Money.zero(this.currency), transactionDate); + this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java index 395797c..fe40f3b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java @@ -656,6 +656,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> { public boolean isSavingsActivation() { return ChargeTimeType.fromInt(this.chargeTime).isSavingsActivation(); } + + public boolean isSavingsNoActivity(){ + return ChargeTimeType.fromInt(this.chargeTime).isSavingsNoActivityFee(); + } public boolean isSavingsClosure() { return ChargeTimeType.fromInt(this.chargeTime).isSavingsClosure(); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSubStatusEnum.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSubStatusEnum.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSubStatusEnum.java new file mode 100644 index 0000000..aefe183 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSubStatusEnum.java @@ -0,0 +1,85 @@ +/** + * 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.domain; + +/** + * Enum representation of {@link SavingsAccount} sub-status states. + */ +public enum SavingsAccountSubStatusEnum { + + NONE(0, "SavingsAccountSubStatusEnum.none"), // + INACTIVE(100, "SavingsAccountSubStatusEnum.inactive"), // + DORMANT(200, "SavingsAccountSubStatusEnum.dormant"), + ESCHEAT(300,"SavingsAccountSubStatusEnum.escheat"); + + private final Integer value; + private final String code; + + public static SavingsAccountSubStatusEnum fromInt(final Integer type) { + + SavingsAccountSubStatusEnum enumeration = SavingsAccountSubStatusEnum.NONE; + if(null != type){ + switch (type) { + case 100: + enumeration = SavingsAccountSubStatusEnum.INACTIVE; + break; + case 200: + enumeration = SavingsAccountSubStatusEnum.DORMANT; + break; + case 300: + enumeration = SavingsAccountSubStatusEnum.ESCHEAT; + break; + } + } + return enumeration; + } + + private SavingsAccountSubStatusEnum(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public boolean hasStateOf(final SavingsAccountSubStatusEnum state) { + return this.value.equals(state.getValue()); + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isSubStatusInactive() { + return this.value.equals(SavingsAccountSubStatusEnum.INACTIVE.getValue()); + } + + public boolean isSubStatusDormant() { + return this.value.equals(SavingsAccountSubStatusEnum.DORMANT.getValue()); + } + + public boolean isSubStatusNone() { + return this.value.equals(SavingsAccountSubStatusEnum.NONE.getValue()); + } + + public boolean isSubStatusEscheat() { + return this.value.equals(SavingsAccountSubStatusEnum.ESCHEAT.getValue()); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java index 2e6abcc..8c4fc27 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java @@ -228,6 +228,15 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> { return accountTransaction; } + public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date, + final AppUser appUser) { + final boolean isReversed = false; + final PaymentDetail paymentDetail = null; + return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail, + SavingsAccountTransactionType.ESCHEAT.getValue(), date, new Date(), savingsAccount.getSummary() + .getAccountBalance(), isReversed, appUser); + } + public static void updateTaxDetails(final Map<TaxComponent, BigDecimal> taxDetails, final SavingsAccountTransaction accountTransaction) { if (taxDetails != null) { for (Map.Entry<TaxComponent, BigDecimal> mapEntry : taxDetails.entrySet()) { http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java index 1b100a0..4415c71 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java @@ -46,6 +46,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNam import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName; import java.math.BigDecimal; import java.util.ArrayList; @@ -191,6 +195,18 @@ public class SavingsProduct extends AbstractPersistable<Long> { @JoinColumn(name = "tax_group_id") private TaxGroup taxGroup; + @Column(name = "is_dormancy_tracking_active") + private Boolean isDormancyTrackingActive; + + @Column(name = "days_to_inactive") + private Long daysToInactive; + + @Column(name = "days_to_dormancy") + private Long daysToDormancy; + + @Column(name = "days_to_escheat") + private Long daysToEscheat; + public static SavingsProduct createNew(final String name, final String shortName, final String description, final MonetaryCurrency currency, final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, @@ -201,13 +217,15 @@ public class SavingsProduct extends AbstractPersistable<Long> { final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, final BigDecimal minRequiredBalance, final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, - boolean withHoldTax, TaxGroup taxGroup) { + boolean withHoldTax, TaxGroup taxGroup, + final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) { return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation, - nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup); + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, + isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat); } protected SavingsProduct() { @@ -226,7 +244,7 @@ public class SavingsProduct extends AbstractPersistable<Long> { this(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, - false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup); + false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup, null, null, null, null); } protected SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency, @@ -238,7 +256,8 @@ public class SavingsProduct extends AbstractPersistable<Long> { final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, final BigDecimal minRequiredBalance, BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, - final boolean withHoldTax, final TaxGroup taxGroup) { + final boolean withHoldTax, final TaxGroup taxGroup, + final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) { this.name = name; this.shortName = shortName; @@ -283,6 +302,11 @@ public class SavingsProduct extends AbstractPersistable<Long> { this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; this.withHoldTax = withHoldTax; this.taxGroup = taxGroup; + + this.isDormancyTrackingActive = isDormancyTrackingActive; + this.daysToInactive = daysToInactive; + this.daysToDormancy = daysToDormancy; + this.daysToEscheat = daysToEscheat; } /** @@ -541,6 +565,36 @@ public class SavingsProduct extends AbstractPersistable<Long> { } else { this.taxGroup = null; } + + if(command.isChangeInBooleanParameterNamed(isDormancyTrackingActiveParamName, this.isDormancyTrackingActive)){ + final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isDormancyTrackingActiveParamName); + actualChanges.put(isDormancyTrackingActiveParamName, newValue); + this.isDormancyTrackingActive = newValue; + } + + if(command.isChangeInLongParameterNamed(daysToInactiveParamName, this.daysToInactive)){ + final Long newValue = command.longValueOfParameterNamed(daysToInactiveParamName); + actualChanges.put(daysToInactiveParamName, newValue); + this.daysToInactive = newValue; + } + + if(command.isChangeInLongParameterNamed(daysToDormancyParamName, this.daysToDormancy)){ + final Long newValue = command.longValueOfParameterNamed(daysToDormancyParamName); + actualChanges.put(daysToDormancyParamName, newValue); + this.daysToDormancy = newValue; + } + + if(command.isChangeInLongParameterNamed(daysToEscheatParamName, this.daysToEscheat)){ + final Long newValue = command.longValueOfParameterNamed(daysToEscheatParamName); + actualChanges.put(daysToEscheatParamName, newValue); + this.daysToEscheat = newValue; + } + + if(!this.isDormancyTrackingActive){ + this.daysToInactive = null; + this.daysToDormancy = null; + this.daysToEscheat = null; + } validateLockinDetails(); esnureOverdraftLimitsSetForOverdraftAccounts(); @@ -676,4 +730,20 @@ public class SavingsProduct extends AbstractPersistable<Long> { return this.withHoldTax; } + public boolean isDormancyTrackingActive() { + return null == this.isDormancyTrackingActive? false: this.isDormancyTrackingActive; + } + + public Long getDaysToInactive() { + return this.daysToInactive; + } + + public Long getDaysToDormancy() { + return this.daysToDormancy; + } + + public Long getDaysToEscheat() { + return this.daysToEscheat; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java index dcbfe93..e52e6dc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java @@ -44,6 +44,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraf import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName; import java.math.BigDecimal; import java.util.HashSet; @@ -173,12 +177,18 @@ public class SavingsProductAssembler { boolean withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName); final TaxGroup taxGroup = assembleTaxGroup(command); + + final Boolean isDormancyTrackingActive = command.booleanObjectValueOfParameterNamed(isDormancyTrackingActiveParamName); + final Long daysToInactive = command.longValueOfParameterNamed(daysToInactiveParamName); + final Long daysToDormancy = command.longValueOfParameterNamed(daysToDormancyParamName); + final Long daysToEscheat = command.longValueOfParameterNamed(daysToEscheatParamName); return SavingsProduct.createNew(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation, - nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup); + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, + isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat); } public Set<Charge> assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) { http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java index b9835f1..0637c46 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java @@ -19,12 +19,14 @@ package org.apache.fineract.portfolio.savings.service; import java.util.Collection; +import java.util.List; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.data.SavingsAccountData; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData; +import org.joda.time.LocalDate; public interface SavingsAccountReadPlatformService { @@ -50,4 +52,10 @@ public interface SavingsAccountReadPlatformService { SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId, DepositAccountType depositAccountType); Collection<SavingsAccountData> retrieveForLookup(Long clientId, Boolean overdraft); + + List<Long> retrieveSavingsIdsPendingInactive(LocalDate tenantLocalDate); + + List<Long> retrieveSavingsIdsPendingDormant(LocalDate tenantLocalDate); + + List<Long> retrieveSavingsIdsPendingEscheat(LocalDate tenantLocalDate); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 022f61e..8d93f46 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -24,6 +24,8 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; +import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.fineract.infrastructure.core.data.EnumOptionData; @@ -38,6 +40,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.staff.data.StaffData; import org.apache.fineract.organisation.staff.service.StaffReadPlatformService; import org.apache.fineract.portfolio.account.data.AccountTransferData; +import org.apache.fineract.portfolio.calendar.service.CalendarUtils; import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService; import org.apache.fineract.portfolio.client.data.ClientData; @@ -56,15 +59,22 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimel import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData; import org.apache.fineract.portfolio.savings.data.SavingsAccountData; import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData; +import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData; import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData; import org.apache.fineract.portfolio.savings.data.SavingsProductData; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum; import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException; import org.apache.fineract.portfolio.tax.data.TaxGroupData; import org.apache.fineract.useradministration.domain.AppUser; +import org.joda.time.Days; import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -82,6 +92,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead private final StaffReadPlatformService staffReadPlatformService; private final SavingsDropdownReadPlatformService dropdownReadPlatformService; private final ChargeReadPlatformService chargeReadPlatformService; + private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd"); // mappers private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper; @@ -217,7 +228,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sp.id as productId, sp.name as productName, "); sqlBuilder.append("s.id fieldOfficerId, s.display_name as fieldOfficerName, "); sqlBuilder.append("sa.status_enum as statusEnum, "); - + sqlBuilder.append("sa.sub_status_enum as subStatusEnum, "); sqlBuilder.append("sa.submittedon_date as submittedOnDate,"); sqlBuilder.append("sbu.username as submittedByUsername,"); sqlBuilder.append("sbu.firstname as submittedByFirstname, sbu.lastname as submittedByLastname,"); @@ -282,8 +293,16 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds, "); sqlBuilder.append("sa.withhold_tax as withHoldTax,"); sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax,"); - sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName "); - + sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, "); + sqlBuilder.append("(select IFNULL(max(sat.transaction_date),sa.activatedon_date) "); + sqlBuilder.append("from m_savings_account_transaction as sat "); + sqlBuilder.append("where sat.is_reversed = 0 "); + sqlBuilder.append("and sat.transaction_type_enum in (1,2) "); + sqlBuilder.append("and sat.savings_account_id = sa.id) as lastActiveTransactionDate, "); + sqlBuilder.append("sp.is_dormancy_tracking_active as isDormancyTrackingActive, "); + sqlBuilder.append("sp.days_to_inactive as daysToInactive, "); + sqlBuilder.append("sp.days_to_dormancy as daysToDormancy, "); + sqlBuilder.append("sp.days_to_escheat as daysToEscheat "); sqlBuilder.append("from m_savings_account sa "); sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id "); sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); @@ -328,6 +347,31 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum"); final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusEnum); + final Integer subStatusEnum = JdbcSupport.getInteger(rs, "subStatusEnum"); + final SavingsAccountSubStatusEnumData subStatus = SavingsEnumerations.subStatus(subStatusEnum); + + final LocalDate lastActiveTransactionDate = JdbcSupport.getLocalDate(rs, "lastActiveTransactionDate"); + final boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive"); + final Integer numDaysToInactive = JdbcSupport.getInteger(rs, "daysToInactive"); + final Integer numDaysToDormancy = JdbcSupport.getInteger(rs, "daysToDormancy"); + final Integer numDaysToEscheat = JdbcSupport.getInteger(rs, "daysToEscheat"); + Integer daysToInactive = null; + Integer daysToDormancy = null; + Integer daysToEscheat = null; + + LocalDate localTenantDate = DateUtils.getLocalDateOfTenant(); + if(isDormancyTrackingActive && statusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())){ + if(subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()){ + daysToEscheat = Days.daysBetween(localTenantDate,lastActiveTransactionDate.plusDays(numDaysToEscheat)).getDays(); + } + if(subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()){ + daysToDormancy = Days.daysBetween(localTenantDate,lastActiveTransactionDate.plusDays(numDaysToDormancy)).getDays(); + } + if(subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()){ + daysToInactive = Days.daysBetween(localTenantDate,lastActiveTransactionDate.plusDays(numDaysToInactive)).getDays(); + } + } + final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate"); final String submittedByUsername = rs.getString("submittedByUsername"); final String submittedByFirstname = rs.getString("submittedByFirstname"); @@ -469,11 +513,12 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead } return SavingsAccountData.instance(id, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId, - productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, - interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, - minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, - allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, - onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData); + productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, + nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, + interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, + summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, + minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, + taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat); } } @@ -996,15 +1041,23 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead // final LocalDate annualFeeNextDueDate = null; final SavingsAccountSummaryData summary = null; final BigDecimal onHoldFunds = null; + + final SavingsAccountSubStatusEnumData subStatus = null; + final LocalDate lastActiveTransactionDate = null; + final boolean isDormancyTrackingActive = false; + final Integer daysToInactive = null; + final Integer daysToDormancy = null; + final Integer daysToEscheat = null; final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault(); final EnumOptionData depositType = null; return SavingsAccountData.instance(null, null, depositType, null, groupId, groupName, clientId, clientName, productId, - productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualIterestRate, - interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, - minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, - allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, - onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData); + productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, + nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, + interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, + summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, + minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, + taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat); } } @@ -1025,6 +1078,84 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead } + @Override + public List<Long> retrieveSavingsIdsPendingInactive( + LocalDate tenantLocalDate) { + List<Long> ret = null; + StringBuilder sql = new StringBuilder("select sa.id "); + sql.append(" from m_savings_account as sa "); + sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) "); + sql.append(" where sa.status_enum = 300 "); + sql.append(" and sa.sub_status_enum = 0 "); + sql.append(" and DATEDIFF(?,(select IFNULL(max(sat.transaction_date),sa.activatedon_date) "); + sql.append(" from m_savings_account_transaction as sat "); + sql.append(" where sat.is_reversed = 0 "); + sql.append(" and sat.transaction_type_enum in (1,2) "); + sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_inactive "); + + try { + ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] {formatter.print(tenantLocalDate)}); + } catch (EmptyResultDataAccessException e) { + // ignore empty result scenario + } catch (DataAccessException e) { + throw e; + } + + return ret; + } + + @Override + public List<Long> retrieveSavingsIdsPendingDormant( + LocalDate tenantLocalDate) { + List<Long> ret = null; + StringBuilder sql = new StringBuilder("select sa.id "); + sql.append(" from m_savings_account as sa "); + sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) "); + sql.append(" where sa.status_enum = 300 "); + sql.append(" and sa.sub_status_enum = 100 "); + sql.append(" and DATEDIFF(?,(select IFNULL(max(sat.transaction_date),sa.activatedon_date) "); + sql.append(" from m_savings_account_transaction as sat "); + sql.append(" where sat.is_reversed = 0 "); + sql.append(" and sat.transaction_type_enum in (1,2) "); + sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_dormancy "); + + try { + ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] {formatter.print(tenantLocalDate)}); + } catch (EmptyResultDataAccessException e) { + // ignore empty result scenario + } catch (DataAccessException e) { + throw e; + } + + return ret; + } + + @Override + public List<Long> retrieveSavingsIdsPendingEscheat( + LocalDate tenantLocalDate) { + List<Long> ret = null; + StringBuilder sql = new StringBuilder("select sa.id "); + sql.append(" from m_savings_account as sa "); + sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) "); + sql.append(" where sa.status_enum = 300 "); + sql.append(" and sa.sub_status_enum = 200 "); + sql.append(" and DATEDIFF(?,(select IFNULL(max(sat.transaction_date),sa.activatedon_date) "); + sql.append(" from m_savings_account_transaction as sat "); + sql.append(" where sat.is_reversed = 0 "); + sql.append(" and sat.transaction_type_enum in (1,2) "); + sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_escheat "); + + try { + ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] {formatter.print(tenantLocalDate)}); + } catch (EmptyResultDataAccessException e) { + // ignore empty result scenario + } catch (DataAccessException e) { + throw e; + } + + return ret; + } + /* * private static final class SavingsAccountAnnualFeeMapper implements * RowMapper<SavingsAccountAnnualFeeData> { http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java index 3fe8aa9..d428d42 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java @@ -81,4 +81,10 @@ public interface SavingsAccountWritePlatformService { void postInterest(SavingsAccount account); CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, JsonCommand command); + + void setSubStatusInactive(Long savingsId); + + void setSubStatusDormant(Long savingsId); + + void escheat(Long savingsId); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index f1b338b..6612c88 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -78,6 +78,7 @@ import org.apache.fineract.portfolio.savings.exception.SavingsOfficerAssignmentE import org.apache.fineract.portfolio.savings.exception.SavingsOfficerUnassignmentException; import org.apache.fineract.portfolio.savings.exception.TransactionUpdateNotAllowedException; import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; @@ -130,6 +131,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi private final WorkingDaysRepositoryWrapper workingDaysRepository; private final ConfigurationDomainService configurationDomainService; private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository; + private final AppUserRepositoryWrapper appuserRepository; @Autowired public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -148,7 +150,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final ChargeRepositoryWrapper chargeRepository, final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository, final SavingsAccountDataValidator fromApiJsonDeserializer, final SavingsAccountRepositoryWrapper savingsRepository, final StaffRepositoryWrapper staffRepository, final ConfigurationDomainService configurationDomainService, - final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository) { + final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository, + final AppUserRepositoryWrapper appuserRepository) { this.context = context; this.savingAccountRepository = savingAccountRepository; this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; @@ -171,6 +174,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi this.staffRepository = staffRepository; this.configurationDomainService = configurationDomainService; this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository; + this.appuserRepository = appuserRepository; } @Transactional @@ -1223,7 +1227,39 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } - private AppUser getAppUserIfPresent() { + @Override + @Transactional + public void setSubStatusInactive(Long savingsId){ + final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); + final Set<Long> existingTransactionIds = new HashSet<>(); + final Set<Long> existingReversedTransactionIds = new HashSet<>(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + account.setSubStatusInactive(appuserRepository.fetchSystemUser()); + this.savingAccountRepository.save(account); + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + } + + @Override + @Transactional + public void setSubStatusDormant(Long savingsId){ + final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); + account.setSubStatusDormant(); + this.savingAccountRepository.save(account); + } + + @Override + @Transactional + public void escheat(Long savingsId){ + final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); + final Set<Long> existingTransactionIds = new HashSet<>(); + final Set<Long> existingReversedTransactionIds = new HashSet<>(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + account.escheat(appuserRepository.fetchSystemUser()); + this.savingAccountRepository.save(account); + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + } + + private AppUser getAppUserIfPresent() { AppUser user = null; if (this.context != null) { user = this.context.getAuthenticatedUserIfPresent(); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java index 270562e..9bc4566 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java @@ -36,8 +36,10 @@ import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType; import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType; import org.apache.fineract.portfolio.savings.SavingsWithdrawalFeesType; import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData; +import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData; import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum; public class SavingsEnumerations { @@ -187,6 +189,10 @@ public class SavingsEnumerations { optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.DIVIDEND_PAYOUT.getValue().longValue(), SavingsAccountTransactionType.DIVIDEND_PAYOUT.getCode(), "Dividend Payout"); break; + case ESCHEAT: + optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.ESCHEAT.getValue().longValue(), + SavingsAccountTransactionType.ESCHEAT.getCode(), "Escheat"); + break; } return optionData; } @@ -278,6 +284,39 @@ public class SavingsEnumerations { return optionData; } + public static SavingsAccountSubStatusEnumData subStatus(final Integer subStatusEnum) { + return subStatus(SavingsAccountSubStatusEnum.fromInt(subStatusEnum)); + } + + public static SavingsAccountSubStatusEnumData subStatus(final SavingsAccountSubStatusEnum type) { + + final boolean none = type.isSubStatusNone(); + final boolean inactive = type.isSubStatusInactive(); + final boolean dormant = type.isSubStatusDormant(); + final boolean escheat = type.isSubStatusEscheat(); + + SavingsAccountSubStatusEnumData optionData = new SavingsAccountSubStatusEnumData(SavingsAccountSubStatusEnum.NONE.getValue().longValue(), + SavingsAccountSubStatusEnum.NONE.getCode(), "None", true, inactive, dormant, escheat); + + switch (type) { + case INACTIVE: + optionData = new SavingsAccountSubStatusEnumData(SavingsAccountSubStatusEnum.INACTIVE.getValue().longValue(), + SavingsAccountSubStatusEnum.INACTIVE.getCode(), "Inactive", none, inactive, dormant, escheat); + break; + case DORMANT: + optionData = new SavingsAccountSubStatusEnumData(SavingsAccountSubStatusEnum.DORMANT.getValue().longValue(), + SavingsAccountSubStatusEnum.DORMANT.getCode(), "Dormant", none, inactive, dormant, escheat); + break; + case ESCHEAT: + optionData = new SavingsAccountSubStatusEnumData(SavingsAccountSubStatusEnum.ESCHEAT.getValue().longValue(), + SavingsAccountSubStatusEnum.ESCHEAT.getCode(), "Escheat", none, inactive, dormant, escheat); + break; + default: + break; + } + return optionData; + } + public static EnumOptionData interestPostingPeriodType(final Integer type) { return interestPostingPeriodType(SavingsPostingInterestPeriodType.fromInt(type)); } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index 1fc785e..454cbe3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -134,7 +134,11 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation,"); sqlBuilder.append("sp.accounting_type as accountingType, "); sqlBuilder.append("sp.withhold_tax as withHoldTax,"); - sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName "); + sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, "); + sqlBuilder.append("sp.is_dormancy_tracking_active as isDormancyTrackingActive,"); + sqlBuilder.append("sp.days_to_inactive as daysToInactive,"); + sqlBuilder.append("sp.days_to_dormancy as daysToDormancy,"); + sqlBuilder.append("sp.days_to_escheat as daysToEscheat "); sqlBuilder.append("from m_savings_product sp "); sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); sqlBuilder.append("left join m_tax_group tg on tg.id = sp.tax_group_id "); @@ -210,13 +214,18 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead if (taxGroupId != null) { taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName); } - + + final Boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive"); + final Long daysToInactive = JdbcSupport.getLong(rs, "daysToInactive"); + final Long daysToDormancy = JdbcSupport.getLong(rs, "daysToDormancy"); + final Long daysToEscheat = JdbcSupport.getLong(rs, "daysToEscheat"); + return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate, compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, - taxGroupData); + taxGroupData, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat); } } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java index 83cfb8a..c864dc1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java @@ -145,11 +145,11 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi try { this.context.authenticatedUser(); - this.fromApiJsonDataValidator.validateForUpdate(command.json()); - final SavingsProduct product = this.savingProductRepository.findOne(productId); if (product == null) { throw new SavingsProductNotFoundException(productId); } + this.fromApiJsonDataValidator.validateForUpdate(command.json(), product); + final Map<String, Object> changes = product.update(command); if (changes.containsKey(chargesParamName)) { http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularService.java index 13c3471..b2117fa 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularService.java @@ -24,5 +24,7 @@ import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; public interface SavingsSchedularService { void postInterestForAccounts() throws JobExecutionException; + + void updateSavingsDormancyStatus() throws JobExecutionException; } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java index 6e8e619..24aefa6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java @@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.savings.service; import java.util.List; +import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; import org.apache.fineract.infrastructure.jobs.service.JobName; @@ -27,6 +28,7 @@ import org.apache.fineract.portfolio.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType; +import org.joda.time.LocalDate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -36,14 +38,17 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService { private final SavingsAccountAssembler savingAccountAssembler; private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; private final SavingsAccountRepository savingAccountRepository; + private final SavingsAccountReadPlatformService savingAccountReadPlatformService; @Autowired public SavingsSchedularServiceImpl(final SavingsAccountAssembler savingAccountAssembler, final SavingsAccountWritePlatformService savingsAccountWritePlatformService, - final SavingsAccountRepository savingAccountRepository) { + final SavingsAccountRepository savingAccountRepository, + final SavingsAccountReadPlatformService savingAccountReadPlatformService) { this.savingAccountAssembler = savingAccountAssembler; this.savingsAccountWritePlatformService = savingsAccountWritePlatformService; this.savingAccountRepository = savingAccountRepository; + this.savingAccountReadPlatformService = savingAccountReadPlatformService; } @CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS) @@ -68,4 +73,34 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService { if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } } + + @CronTarget(jobName = JobName.UPDATE_SAVINGS_DORMANT_ACCOUNTS) + @Override + public void updateSavingsDormancyStatus() throws JobExecutionException { + final LocalDate tenantLocalDate = DateUtils.getLocalDateOfTenant(); + + final List<Long> savingsPendingInactive = this.savingAccountReadPlatformService + .retrieveSavingsIdsPendingInactive(tenantLocalDate); + if(null != savingsPendingInactive && savingsPendingInactive.size() > 0){ + for(Long savingsId : savingsPendingInactive){ + this.savingsAccountWritePlatformService.setSubStatusInactive(savingsId); + } + } + + final List<Long> savingsPendingDormant = this.savingAccountReadPlatformService + .retrieveSavingsIdsPendingDormant(tenantLocalDate); + if(null != savingsPendingDormant && savingsPendingDormant.size() > 0){ + for(Long savingsId : savingsPendingDormant){ + this.savingsAccountWritePlatformService.setSubStatusDormant(savingsId); + } + } + + final List<Long> savingsPendingEscheat = this.savingAccountReadPlatformService + .retrieveSavingsIdsPendingEscheat(tenantLocalDate); + if(null != savingsPendingEscheat && savingsPendingEscheat.size() > 0){ + for(Long savingsId : savingsPendingEscheat){ + this.savingsAccountWritePlatformService.escheat(savingsId); + } + } + } } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/9d4d9012/fineract-provider/src/main/resources/sql/migrations/core_db/V303__Savings_Account_Dormancy.sql ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V303__Savings_Account_Dormancy.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V303__Savings_Account_Dormancy.sql new file mode 100644 index 0000000..6820148 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V303__Savings_Account_Dormancy.sql @@ -0,0 +1,18 @@ +ALTER TABLE `m_savings_product` +ADD COLUMN `is_dormancy_tracking_active` SMALLINT(1) NULL, +ADD COLUMN `days_to_inactive` INT(11) NULL, +ADD COLUMN `days_to_dormancy` INT(11) NULL, +ADD COLUMN `days_to_escheat` INT(11) NULL; + +ALTER TABLE `m_savings_account` +ADD COLUMN `sub_status_enum` SMALLINT(5) NOT NULL DEFAULT '0' AFTER `status_enum`; + +INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`) VALUES ('Update Savings Dormant Accounts', 'Update Savings Dormant Accounts', '0 0 0 1/1 * ? *', now()); + +INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Savings Accounts Dormancy Report', 'Table', 'Savings', 'select cl.display_name as \'Client Display Name\',\r\nsa.account_no as \'Account Number\',\r\ncl.mobile_no as \'Mobile Number\',\r\n@lastdate:=(select IFNULL(max(sat.transaction_date),sa.activatedon_date) \r\n from m_savings_account_transaction as sat \r\n where sat.is_reversed = 0 \r\n and sat.transaction_type_enum in (1,2) \r\n and sat.savings_account_id = sa.id) as \'Date of Last Activity\',\r\nDATEDIFF(now(), @lastdate) as \'Days Since Last Activity\'\r\nfrom m_savings_account as sa \r\ninner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) \r\nleft join m_client as cl on sa.client_id = cl.id \r\nwhere sa.sub_status_enum = ${subStatus}\r\nand cl.office_id = ${officeId}', NULL, 1, 1); + +INSERT INTO `stretchy_parameter` (`parameter_name`, `parameter_variable`, `parameter_label`, `parameter_displayType`, `parameter_FormatType`, `parameter_default`, `parameter_sql`) VALUES ('SavingsAccountSubStatus', 'subStatus', 'SavingsAccountDormancyStatus', 'select', 'number', '100', 'select * from\r\n(select 100 as id, "Inactive" as name union all\r\nselect 200 as id, "Dormant" as name union all \r\nselect 300 as id, "Escheat" as name) x\r\norder by x.`id`'); + +INSERT INTO stretchy_report_parameter (report_id, parameter_id) VALUES ((select sr.id From stretchy_report sr where sr.report_name='Savings Accounts Dormancy Report'),(select sp.id from stretchy_parameter sp where sp.parameter_name='SavingsAccountSubStatus')); + +INSERT INTO stretchy_report_parameter (report_id, parameter_id) VALUES ((select sr.id From stretchy_report sr where sr.report_name='Savings Accounts Dormancy Report'),(select sp.id from stretchy_parameter sp where sp.parameter_name='OfficeIdSelectOne'));
