MifosX-1466-Flexible Savings Interest Posting

This commit is contained in:
avikganguly01
2014-10-20 13:43:31 +05:30
parent 21c4cdf002
commit c04cebc5b6
21 changed files with 793 additions and 185 deletions
+2
View File
@@ -13145,6 +13145,8 @@ Request Body:
<li><b>allow-transactions-on-non_workingday</b> - defaults to false - if true allows transactions on non-working days</li>
<li><b>reschedule-repayments-on-holidays</b> - defaults to false - if true reschedules repayemnts which falls on a non-working day to defined <a href="#holidays">reschedule date</a></li>
<li><b>allow-transactions-on-holiday</b> - defaults to false - if true allows transactions on <a href="#holidays">holidays</a></li>
<li><b>savings-interest-posting-current-period-end</b> - Set it at the database level before any savings interest is posted. When set as false(default), interest will be posted on the first date of next period. If set as true, interest will be posted on last date of current period. There is no difference in the interest amount posted.</li>
<li><b>financial-year-beginning-month</b> - Set it at the database level before any savings interest is posted. Allowed values 1 - 12 (January - December). Interest posting periods are evaluated based on this configuration.</li>
</ol>
</div>
</div>
@@ -0,0 +1,148 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.integrationtests;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mifosplatform.integrationtests.common.ClientHelper;
import org.mifosplatform.integrationtests.common.CommonConstants;
import org.mifosplatform.integrationtests.common.GlobalConfigurationHelper;
import org.mifosplatform.integrationtests.common.Utils;
import org.mifosplatform.integrationtests.common.savings.SavingsAccountHelper;
import org.mifosplatform.integrationtests.common.savings.SavingsProductHelper;
import org.mifosplatform.integrationtests.common.savings.SavingsStatusChecker;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
@SuppressWarnings({ "rawtypes", "unused", "unchecked" })
public class FlexibleSavingsInterestPostingIntegrationTest {
public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
private ResponseSpecification responseSpec;
private RequestSpecification requestSpec;
private SavingsProductHelper savingsProductHelper;
private SavingsAccountHelper savingsAccountHelper;
@Before
public void setup() {
Utils.initializeRESTAssured();
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
this.savingsProductHelper = new SavingsProductHelper();
}
@Test
public void testSavingsInterestPostingAtPeriodEnd() {
//client activation, savings activation and 1st transaction date
final String startDate = "01 December 2013";
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate);
Assert.assertNotNull(clientID);
// Configuring global config flags
configureInterestPosting(true, 4);
final Integer savingsId = createSavingsAccount(clientID, startDate);
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "1000",
startDate, CommonConstants.RESPONSE_RESOURCE_ID);
/***
* Perform Post interest transaction and verify the posted transaction date
*/
this.savingsAccountHelper.postInterestForSavings(savingsId);
HashMap accountDetails = this.savingsAccountHelper.getSavingsDetails(savingsId);
ArrayList<HashMap<String, Object>> transactions = (ArrayList<HashMap<String, Object>>)
accountDetails.get("transactions");
HashMap<String, Object> interestPostingTransaction = transactions.get(0);
for(Entry<String, Object> entry : interestPostingTransaction.entrySet())
System.out.println(entry.getKey() + "-" + entry.getValue().toString());
//1st Dec 13 to 31st March 14 - 365 days, daily compounding using daily balance
//33.7016 obtained from formula in excel provided by Subramanya
assertEquals("Equality check for interest posted amount",
"33.7016", interestPostingTransaction.get("amount").toString());
assertEquals("Date check for Interest Posting transaction",
"[2014, 3, 31]", interestPostingTransaction.get("date").toString());
}
private Integer createSavingsAccount(final Integer clientID, final String startDate) {
final Integer savingsProductID = createSavingsProduct();
Assert.assertNotNull(savingsProductID);
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID,
savingsProductID, ACCOUNT_TYPE_INDIVIDUAL, startDate);
Assert.assertNotNull(savingsId);
HashMap savingsStatusHashMap = this.savingsAccountHelper.approveSavingsOnDate(savingsId, startDate);
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.activateSavingsAccount(savingsId, startDate);
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
return savingsId;
}
private void configureInterestPosting(final Boolean periodEndEnable,
final Integer financialYearBeginningMonth) {
final ArrayList<HashMap> globalConfig = GlobalConfigurationHelper.getAllGlobalConfigurations(this.requestSpec,
this.responseSpec);
Assert.assertNotNull(globalConfig);
// Updating flag for interest posting at period end
Integer periodEndConfigId = (Integer) globalConfig.get(10).get("id");
Assert.assertNotNull(periodEndConfigId);
HashMap periodEndConfigData = GlobalConfigurationHelper.getGlobalConfigurationById(this.requestSpec, this.responseSpec,
periodEndConfigId.toString());
Assert.assertNotNull(periodEndConfigData);
Boolean enabled = (Boolean) globalConfig.get(10).get("enabled");
if (enabled != periodEndEnable)
periodEndConfigId = GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec,
periodEndConfigId.toString(), periodEndEnable);
//Updating value for financial year beginning month
Integer financialYearBeginningConfigId = (Integer) globalConfig.get(11).get("id");
Assert.assertNotNull(financialYearBeginningConfigId);
HashMap financialYearBeginningConfigData = GlobalConfigurationHelper.getGlobalConfigurationById(this.requestSpec, this.responseSpec,
financialYearBeginningConfigId.toString());
Assert.assertNotNull(financialYearBeginningConfigData);
financialYearBeginningConfigId = GlobalConfigurationHelper.updateValueForGlobalConfiguration(this.requestSpec, this.responseSpec,
financialYearBeginningConfigId.toString(), financialYearBeginningMonth.toString());
Assert.assertNotNull(financialYearBeginningConfigId);
}
private Integer createSavingsProduct() {
final String savingsProductJSON = this.savingsProductHelper
.withInterestCompoundingPeriodTypeAsDaily()
.withInterestPostingPeriodTypeAsAnnual()
.withInterestCalculationPeriodTypeAsDailyBalance()
.build();
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
}
//Reset configuration fields
@After
public void tearDown() {
configureInterestPosting(false, 1);
}
}
@@ -59,9 +59,14 @@ public class SavingsAccountHelper {
}
public Integer applyForSavingsApplication(final Integer ID, final Integer savingsProductID, final String accountType) {
System.out.println("--------------------------------APPLYING FOR SAVINGS APPLICATION--------------------------------");
return applyForSavingsApplicationOnDate(ID, savingsProductID, accountType, CREATED_DATE);
}
public Integer applyForSavingsApplicationOnDate(final Integer ID, final Integer savingsProductID, final String accountType,
final String submittedOnDate) {
System.out.println("--------------------------------APPLYING FOR SAVINGS APPLICATION--------------------------------");
final String savingsApplicationJSON = new SavingsApplicationTestBuilder() //
.withSubmittedOnDate(CREATED_DATE) //
.withSubmittedOnDate(submittedOnDate) //
.build(ID.toString(), savingsProductID.toString(), accountType);
return Utils.performServerPost(this.requestSpec, this.responseSpec, SAVINGS_ACCOUNT_URL + "?" + Utils.TENANT_IDENTIFIER,
savingsApplicationJSON, "savingsId");
@@ -77,8 +82,15 @@ public class SavingsAccountHelper {
}
public HashMap approveSavings(final Integer savingsID) {
System.out.println("--------------------------------- APPROVING SAVINGS APPLICATION ------------------------------------");
return performSavingApplicationActions(createSavingsOperationURL(APPROVE_SAVINGS_COMMAND, savingsID), getApproveSavingsAsJSON());
return approveSavingsOnDate(savingsID, null);
}
public HashMap approveSavingsOnDate(final Integer savingsID, final String approvalDate) {
System.out.println("--------------------------------- APPROVING SAVINGS APPLICATION ------------------------------------");
final String savingsOperationURL = createSavingsOperationURL(APPROVE_SAVINGS_COMMAND, savingsID);
if(approvalDate == null || approvalDate == "")
return performSavingApplicationActions(savingsOperationURL, getApproveSavingsAsJSON());
return performSavingApplicationActions(savingsOperationURL, getApproveSavingsAsJsonOnDate(approvalDate));
}
public HashMap undoApproval(final Integer savingsID) {
@@ -185,10 +197,14 @@ public class SavingsAccountHelper {
}
private String getApproveSavingsAsJSON() {
final HashMap<String, String> map = new HashMap<>();
return getApproveSavingsAsJsonOnDate(CREATED_DATE_PLUS_ONE);
}
private String getApproveSavingsAsJsonOnDate(final String approvalDate) {
final HashMap<String, String> map = new HashMap<>();
map.put("locale", CommonConstants.locale);
map.put("dateFormat", CommonConstants.dateFormat);
map.put("approvedOnDate", CREATED_DATE_PLUS_ONE);
map.put("approvedOnDate", approvalDate);
map.put("note", "Approval NOTE");
String savingsAccountApproveJson = new Gson().toJson(map);
System.out.println(savingsAccountApproveJson);
@@ -18,18 +18,24 @@ public class GlobalConfigurationPropertyData {
private final Long value;
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final String description;
public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value) {
public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value,
final String description) {
this.name = name;
this.enabled = enabled;
this.value = value;
this.id = null;
this.description = description;
}
public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, final Long id) {
public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value,
final Long id, final String description) {
this.name = name;
this.enabled = enabled;
this.value = value;
this.id = id;
this.description = description;
}
}
@@ -35,4 +35,8 @@ public interface ConfigurationDomainService {
Long retrieveGraceOnPenaltyPostingPeriod();
boolean isSavingsInterestPostingAtCurrentPeriodEnd();
Integer retrieveFinancialYearBeginningMonth();
}
@@ -135,5 +135,21 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
return property.getValue();
}
@Override
public boolean isSavingsInterestPostingAtCurrentPeriodEnd() {
final String propertyName = "savings-interest-posting-current-period-end";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
return property.isEnabled();
}
@Override
public Integer retrieveFinancialYearBeginningMonth() {
final String propertyName = "financial-year-beginning-month";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
if(property.isEnabled())
return property.getValue().intValue();
return 1;
}
}
@@ -27,17 +27,23 @@ public class GlobalConfigurationProperty extends AbstractPersistable<Long> {
@Column(name = "value", nullable = true)
private Long value;
@Column(name = "description", nullable = true)
private final String description;
protected GlobalConfigurationProperty() {
this.name = null;
this.enabled = false;
this.value = null;
this.description = null;
}
public GlobalConfigurationProperty(final String name, final boolean enabled, final Long value) {
public GlobalConfigurationProperty(final String name, final boolean enabled, final Long value,
final String description) {
this.name = name;
this.enabled = enabled;
this.value = value;
this.description = description;
}
public boolean isEnabled() {
@@ -78,7 +84,7 @@ public class GlobalConfigurationProperty extends AbstractPersistable<Long> {
public static GlobalConfigurationProperty newSurveyConfiguration(final String name)
{
return new GlobalConfigurationProperty(name,false,null);
return new GlobalConfigurationProperty(name, false, null, null);
}
}
@@ -39,7 +39,7 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl
this.context.authenticatedUser();
String sql = "SELECT c.id, c.name, c.enabled, c.value FROM c_configuration c ";
String sql = "SELECT c.id, c.name, c.enabled, c.value, c.description FROM c_configuration c ";
if(survey)
{
@@ -59,7 +59,8 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl
this.context.authenticatedUser();
final String sql = "SELECT c.id, c.name, c.enabled, c.value FROM c_configuration c where c.id=? order by c.id";
final String sql = "SELECT c.id, c.name, c.enabled, c.value, c.description FROM "
+ "c_configuration c where c.id=? order by c.id";
final GlobalConfigurationPropertyData globalConfiguration = this.jdbcTemplate.queryForObject(sql, this.rm, new Object[] {configId});
return globalConfiguration;
@@ -76,9 +77,10 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl
final String name = rs.getString("name");
final boolean enabled = rs.getBoolean("enabled");
final Long value = rs.getLong("value");
final String description = rs.getString("description");
final Long id = rs.getLong("id");
return new GlobalConfigurationPropertyData(name, enabled, value, id);
return new GlobalConfigurationPropertyData(name, enabled, value, id, description);
}
}
@@ -50,7 +50,7 @@ public class GlobalConfigurationWritePlatformServiceJpaRepositoryImpl implements
this.globalConfigurationDataValidator.validateForUpdate(command);
final GlobalConfigurationProperty configItemForUpdate = this.repository.findOneWithNotFoundDetection(configId);
final Map<String, Object> changes = configItemForUpdate.update(command);
if (!changes.isEmpty()) {
@@ -66,7 +66,6 @@ public class GlobalConfigurationWritePlatformServiceJpaRepositoryImpl implements
}
@Transactional
@Override
public void addSurveyConfig(final String name)
@@ -20,6 +20,7 @@ import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
@@ -53,6 +54,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
private final DepositAccountAssembler depositAccountAssembler;
private final SavingsAccountDomainService savingsAccountDomainService;
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
private final ConfigurationDomainService configurationDomainService;
@Autowired
public DepositAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository,
@@ -60,7 +62,8 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final JournalEntryWritePlatformService journalEntryWritePlatformService,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final DepositAccountAssembler depositAccountAssembler,
final SavingsAccountDomainService savingsAccountDomainService,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService) {
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final ConfigurationDomainService configurationDomainService) {
this.savingsAccountRepository = savingsAccountRepository;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
@@ -68,6 +71,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
this.depositAccountAssembler = depositAccountAssembler;
this.savingsAccountDomainService = savingsAccountDomainService;
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
this.configurationDomainService = configurationDomainService;
}
@Transactional
@@ -100,6 +104,12 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
public SavingsAccountTransaction handleRDDeposit(final RecurringDepositAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final boolean isRegularTransaction) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
boolean isAccountTransfer = false;
final boolean isPreMatureClosure = false;
final MathContext mc = MathContext.DECIMAL64;
@@ -107,7 +117,8 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction);
account.handleScheduleInstallments(deposit);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.updateOverduePayments(DateUtils.getLocalDateOfTenant());
return deposit;
}
@@ -116,6 +127,12 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Override
public Long handleFDAccountClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
boolean isRegularTransaction = false;
boolean isAccountTransfer = false;
final boolean isPreMatureClosure = false;
@@ -140,13 +157,15 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
Long savingsTransactionId = null;
account.postMaturityInterest();
account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName);
final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId);
if (onClosureType.isReinvest()) {
FixedDepositAccount reinvestedDeposit = account.reInvest(account.getAccountBalance());
this.depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit);
reinvestedDeposit.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure);
reinvestedDeposit.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
this.savingsAccountRepository.save(reinvestedDeposit);
autoGenerateAccountNumber(reinvestedDeposit);
final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(),
@@ -185,6 +204,12 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Override
public Long handleRDAccountClosure(final RecurringDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
boolean isRegularTransaction = false;
boolean isAccountTransfer = false;
final boolean isPreMatureClosure = false;
@@ -197,16 +222,19 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
Long savingsTransactionId = null;
account.postMaturityInterest();
account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
final BigDecimal transactionAmount = account.getAccountBalance();
final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName);
final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId);
if (onClosureType.isReinvest()) {
RecurringDepositAccount reinvestedDeposit = account.reInvest(transactionAmount);
depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
reinvestedDeposit.processAccountUponActivation(fmt);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
this.savingsAccountRepository.save(reinvestedDeposit);
Money amountForDeposit = reinvestedDeposit.activateWithBalance();
@@ -214,7 +242,8 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
handleRDDeposit(reinvestedDeposit, fmt, reinvestedDeposit.getActivationLocalDate(), amountForDeposit.getAmount(),
paymentDetail, isRegularTransaction);
}
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
this.savingsAccountRepository.save(reinvestedDeposit);
autoGenerateAccountNumber(reinvestedDeposit);
@@ -265,6 +294,12 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Override
public Long handleFDAccountPreMatureClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
boolean isAccountTransfer = false;
boolean isRegularTransaction = false;
final boolean isPreMatureClosure = true;
@@ -277,7 +312,8 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
Long savingsTransactionId = null;
// post interest
account.postPreMaturityInterest(closedDate, isPreMatureClosure);
account.postPreMaturityInterest(closedDate, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
final Integer closureTypeValue = command.integerValueOfParameterNamed(DepositsApiConstants.onAccountClosureIdParamName);
DepositAccountOnClosureType closureType = DepositAccountOnClosureType.fromInt(closureTypeValue);
@@ -315,7 +351,13 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Override
public Long handleRDAccountPreMatureClosure(final RecurringDepositAccount account, final PaymentDetail paymentDetail,
final AppUser user, final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) {
boolean isAccountTransfer = false;
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
boolean isAccountTransfer = false;
final boolean isPreMatureClosure = true;
boolean isRegularTransaction = false;
final Set<Long> existingTransactionIds = new HashSet<>();
@@ -327,7 +369,8 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
Long savingsTransactionId = null;
// post interest
account.postPreMaturityInterest(closedDate, isPreMatureClosure);
account.postPreMaturityInterest(closedDate, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
final Integer closureTypeValue = command.integerValueOfParameterNamed(DepositsApiConstants.onAccountClosureIdParamName);
DepositAccountOnClosureType closureType = DepositAccountOnClosureType.fromInt(closureTypeValue);
@@ -174,7 +174,9 @@ public class FixedDepositAccount extends SavingsAccount {
return applicableInterestRate.divide(BigDecimal.valueOf(100l), mc);
}
public void updateMaturityDateAndAmountBeforeAccountActivation(final MathContext mc, final boolean isPreMatureClosure) {
public void updateMaturityDateAndAmountBeforeAccountActivation(final MathContext mc,
final boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
List<SavingsAccountTransaction> allTransactions = new ArrayList<>();
final Money transactionAmountMoney = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount());
final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null,
@@ -182,15 +184,20 @@ public class FixedDepositAccount extends SavingsAccount {
transaction.updateRunningBalance(transactionAmountMoney);
transaction.updateCumulativeBalanceAndDates(this.getCurrency(), interestCalculatedUpto());
allTransactions.add(transaction);
updateMaturityDateAndAmount(mc, allTransactions, isPreMatureClosure);
updateMaturityDateAndAmount(mc, allTransactions, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
}
public void updateMaturityDateAndAmount(final MathContext mc, final boolean isPreMatureClosure) {
updateMaturityDateAndAmount(mc, retreiveOrderedNonInterestPostingTransactions(), isPreMatureClosure);
public void updateMaturityDateAndAmount(final MathContext mc, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
updateMaturityDateAndAmount(mc, retreiveOrderedNonInterestPostingTransactions(), isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
}
public void updateMaturityDateAndAmount(final MathContext mc, final List<SavingsAccountTransaction> transactions,
final boolean isPreMatureClosure) {
final boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final LocalDate maturityDate = calculateMaturityDate();
final LocalDate interestCalculationUpto = maturityDate.minusDays(1);
@@ -198,7 +205,9 @@ public class FixedDepositAccount extends SavingsAccount {
// calculation
this.resetAccountTransactionsEndOfDayBalances(transactions, maturityDate);
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, interestCalculationUpto, transactions, isPreMatureClosure);
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, interestCalculationUpto,
transactions, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
// reset end of day balance back to today's date
this.resetAccountTransactionsEndOfDayBalances(transactions, DateUtils.getLocalDateOfTenant());
@@ -213,7 +222,8 @@ public class FixedDepositAccount extends SavingsAccount {
this.accountTermAndPreClosure.updateMaturityDetails(maturityAmount.getAmount(), maturityDate);
}
public void updateMaturityStatus() {
public void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.updateMaturityDetailsAction);
@@ -228,7 +238,8 @@ public class FixedDepositAccount extends SavingsAccount {
if (!this.maturityDate().isAfter(todayDate)) {
// update account status
this.status = SavingsAccountStatusType.MATURED.getValue();
postMaturityInterest();
postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
}
@@ -258,7 +269,9 @@ public class FixedDepositAccount extends SavingsAccount {
}
private List<PostingPeriod> calculateInterestPayable(final MathContext mc, final LocalDate maturityDate,
final List<SavingsAccountTransaction> transactions, final boolean isPreMatureClosure) {
final List<SavingsAccountTransaction> transactions, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final SavingsPostingInterestPeriodType postingPeriodType = SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType);
@@ -269,7 +282,8 @@ public class FixedDepositAccount extends SavingsAccount {
.fromInt(this.interestCalculationDaysInYearType);
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(
accountSubmittedOrActivationDate(), maturityDate, postingPeriodType);
accountSubmittedOrActivationDate(), maturityDate, postingPeriodType,
financialYearBeginningMonth);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -284,7 +298,8 @@ public class FixedDepositAccount extends SavingsAccount {
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions,
this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation);
maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
isSavingsInterestPostingAtCurrentPeriodEnd);
periodStartingBalance = postingPeriod.closingBalance();
@@ -455,11 +470,14 @@ public class FixedDepositAccount extends SavingsAccount {
// this.savingsAccountTransactionSummaryWrapper, this.transactions);
}
public void postMaturityInterest() {
public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final LocalDate interestPostingUpToDate = maturityDate();
final MathContext mc = MathContext.DECIMAL64;
final boolean isInterestTransfer = false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate,
isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
Money interestPostedToDate = Money.zero(this.currency);
@@ -503,13 +521,17 @@ public class FixedDepositAccount extends SavingsAccount {
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
}
public void postPreMaturityInterest(final LocalDate accountCloseDate, final boolean isPreMatureClosure) {
public void postPreMaturityInterest(final LocalDate accountCloseDate, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
Money interestPostedToDate = totalInterestPosted();
// calculate interest before one day of closure date
final LocalDate interestCalculatedToDate = accountCloseDate.minusDays(1);
final Money interestOnMaturity = calculatePreMatureInterest(interestCalculatedToDate,
retreiveOrderedNonInterestPostingTransactions(), isPreMatureClosure);
retreiveOrderedNonInterestPostingTransactions(), isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
boolean recalucateDailyBalance = false;
// post remaining interest
@@ -531,12 +553,15 @@ public class FixedDepositAccount extends SavingsAccount {
}
public BigDecimal calculatePreMatureAmount(final LocalDate preMatureDate, final boolean isPreMatureClosure) {
public BigDecimal calculatePreMatureAmount(final LocalDate preMatureDate, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final Money interestPostedToDate = totalInterestPosted().copy();
final Money interestEarnedTillDate = calculatePreMatureInterest(preMatureDate, retreiveOrderedNonInterestPostingTransactions(),
isPreMatureClosure);
isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
final Money accountBalance = Money.of(getCurrency(), getAccountBalance());
final Money maturityAmount = accountBalance.minus(interestPostedToDate).plus(interestEarnedTillDate);
@@ -545,9 +570,12 @@ public class FixedDepositAccount extends SavingsAccount {
}
private Money calculatePreMatureInterest(final LocalDate preMatureDate, final List<SavingsAccountTransaction> transactions,
final boolean isPreMatureClosure) {
final boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final MathContext mc = MathContext.DECIMAL64;
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions, isPreMatureClosure);
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions,
isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
Money interestOnMaturity = Money.zero(this.currency);
@@ -560,15 +588,23 @@ public class FixedDepositAccount extends SavingsAccount {
}
@Override
public void postInterest(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer) {
public void postInterest(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer);
}
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
@Override
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer) {
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate,
boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
@@ -206,13 +206,16 @@ public class RecurringDepositAccount extends SavingsAccount {
return applicableInterestRate.divide(BigDecimal.valueOf(100l), mc);
}
public void updateMaturityDateAndAmount(final MathContext mc, final boolean isPreMatureClosure) {
public void updateMaturityDateAndAmount(final MathContext mc, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final LocalDate maturityDate = calculateMaturityDate();
final LocalDate interestCalculationUpto = maturityDate.minusDays(1);
final List<SavingsAccountTransaction> allTransactions = generateIncludingFutureTransactions(interestCalculationUpto);
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, interestCalculationUpto, allTransactions,
isPreMatureClosure);
isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
Money totalInterestPayable = Money.zero(getCurrency());
Money totalDepositAmount = Money.zero(getCurrency());
for (PostingPeriod postingPeriod : postingPeriods) {
@@ -223,7 +226,8 @@ public class RecurringDepositAccount extends SavingsAccount {
this.accountTermAndPreClosure.updateMaturityDetails(totalDepositAmount.getAmount(), totalInterestPayable.getAmount(), maturityDate);
}
public void updateMaturityStatus() {
public void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.updateMaturityDetailsAction);
@@ -238,7 +242,8 @@ public class RecurringDepositAccount extends SavingsAccount {
if (!this.maturityDate().isAfter(todayDate)) {
// update account status
this.status = SavingsAccountStatusType.MATURED.getValue();
postMaturityInterest();
postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
}
@@ -268,7 +273,9 @@ public class RecurringDepositAccount extends SavingsAccount {
}
private List<PostingPeriod> calculateInterestPayable(final MathContext mc, final LocalDate maturityDate,
final List<SavingsAccountTransaction> transactions, final boolean isPreMatureClosure) {
final List<SavingsAccountTransaction> transactions, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
// 1. default to calculate interest based on entire history OR
// 2. determine latest 'posting period' and find interest credited to
@@ -284,7 +291,7 @@ public class RecurringDepositAccount extends SavingsAccount {
.fromInt(this.interestCalculationDaysInYearType);
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(depositStartDate(),
maturityDate, postingPeriodType);
maturityDate, postingPeriodType, financialYearBeginningMonth);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -298,7 +305,8 @@ public class RecurringDepositAccount extends SavingsAccount {
for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions,
this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation);
maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
isSavingsInterestPostingAtCurrentPeriodEnd);
periodStartingBalance = postingPeriod.closingBalance();
@@ -560,11 +568,14 @@ public class RecurringDepositAccount extends SavingsAccount {
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
}
public void postMaturityInterest() {
public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final LocalDate interestPostingUpToDate = maturityDate();
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate,
isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
Money interestPostedToDate = Money.zero(this.currency);
@@ -606,13 +617,17 @@ public class RecurringDepositAccount extends SavingsAccount {
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
}
public void postPreMaturityInterest(final LocalDate accountCloseDate, final boolean isPreMatureClosure) {
public void postPreMaturityInterest(final LocalDate accountCloseDate, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final Money interestPostedToDate = totalInterestPosted();
// calculate interest before one day of closure date
final LocalDate interestCalculatedToDate = accountCloseDate.minusDays(1);
final Money interestOnMaturity = calculatePreMatureInterest(interestCalculatedToDate,
retreiveOrderedNonInterestPostingTransactions(), isPreMatureClosure);
retreiveOrderedNonInterestPostingTransactions(), isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
boolean recalucateDailyBalance = false;
@@ -635,12 +650,15 @@ public class RecurringDepositAccount extends SavingsAccount {
this.accountTermAndPreClosure.updateMaturityDetails(this.getAccountBalance(), accountCloseDate);
}
public BigDecimal calculatePreMatureAmount(final LocalDate preMatureDate, final boolean isPreMatureClosure) {
public BigDecimal calculatePreMatureAmount(final LocalDate preMatureDate, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final Money interestPostedToDate = totalInterestPosted().copy();
final Money interestEarnedTillDate = calculatePreMatureInterest(preMatureDate, retreiveOrderedNonInterestPostingTransactions(),
isPreMatureClosure);
isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
final Money accountBalance = Money.of(getCurrency(), getAccountBalance());
final Money maturityAmount = accountBalance.minus(interestPostedToDate).plus(interestEarnedTillDate);
@@ -649,9 +667,12 @@ public class RecurringDepositAccount extends SavingsAccount {
}
private Money calculatePreMatureInterest(final LocalDate preMatureDate, final List<SavingsAccountTransaction> transactions,
final boolean isPreMatureClosure) {
final boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final MathContext mc = MathContext.DECIMAL64;
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions, isPreMatureClosure);
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions,
isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
Money interestOnMaturity = Money.zero(this.currency);
@@ -664,15 +685,20 @@ public class RecurringDepositAccount extends SavingsAccount {
}
@Override
public void postInterest(final MathContext mc, final LocalDate postingDate, final boolean isInterestTransfer) {
public void postInterest(final MathContext mc, final LocalDate postingDate, final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
}
@Override
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer) {
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate,
boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
}
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
@@ -417,9 +417,14 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return SavingsAccountStatusType.fromInt(this.status).isClosed();
}
public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer) {
public void postInterest(final MathContext mc,
final LocalDate interestPostingUpToDate,
final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
Money interestPostedToDate = Money.zero(this.currency);
@@ -514,7 +519,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
* TODO
*/
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate,
boolean isInterestTransfer) {
boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
// no openingBalance concept supported yet but probably will to allow
// for migrations.
@@ -538,7 +544,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
.fromInt(this.interestCalculationDaysInYearType);
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(
getStartInterestCalculationDate(), upToInterestCalculationDate, postingPeriodType);
getStartInterestCalculationDate(), upToInterestCalculationDate, postingPeriodType,
financialYearBeginningMonth);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -576,7 +583,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance,
retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType,
interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate, interestPostTransactions,
isInterestTransfer, minBalanceForInterestCalculation);
isInterestTransfer, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd);
periodStartingBalance = postingPeriod.closingBalance();
@@ -1825,7 +1832,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return actualChanges;
}
public void processAccountUponActivation() {
public void processAccountUponActivation(
final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
// update annual fee due date
for (SavingsAccountCharge charge : this.charges()) {
@@ -1833,7 +1842,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
// auto pay the activation time charges
this.payActivationCharges();
this.payActivationCharges(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
// TODO : AA add activation charges to actual changes list
}
@@ -1856,7 +1866,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
this.lockedInUntilDate = calculateDateAccountIsLockedUntil(getActivationLocalDate());
}
private void payActivationCharges() {
private void payActivationCharges(final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final Integer financialYearBeginningMonth) {
boolean isSavingsChargeApplied = false;
for (SavingsAccountCharge savingsAccountCharge : this.charges()) {
if (savingsAccountCharge.isSavingsActivation()) {
@@ -1870,10 +1881,12 @@ public class SavingsAccount extends AbstractPersistable<Long> {
boolean isInterestTransfer = false;
if (this.isBeforeLastPostingPeriod(getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
this.postInterest(mc, today, isInterestTransfer);
this.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
this.calculateInterestUsing(mc, today, isInterestTransfer);
this.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
}
}
}
@@ -15,6 +15,7 @@ import java.util.Set;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
@@ -34,16 +35,19 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final ConfigurationDomainService configurationDomainService;
@Autowired
public SavingsAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository,
final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
final JournalEntryWritePlatformService journalEntryWritePlatformService,
final ConfigurationDomainService configurationDomainService) {
this.savingsAccountRepository = savingsAccountRepository;
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.configurationDomainService = configurationDomainService;
}
@Transactional
@@ -51,6 +55,11 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final SavingsTransactionBooleanValues transactionBooleanValues) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
if (transactionBooleanValues.isRegularTransaction() && !account.allowWithdrawal()) { throw new DepositAccountTransactionNotAllowedException(
account.getId(), "withdraw", account.depositAccountType()); }
@@ -64,10 +73,14 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer());
account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer(),
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer());
account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer(),
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isWithdrawBalance());
saveTransactionToGenerateTransactionId(withdrawal);
@@ -83,6 +96,11 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final boolean isAccountTransfer, final boolean isRegularTransaction) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
if (isRegularTransaction && !account.allowDeposit()) { throw new DepositAccountTransactionNotAllowedException(account.getId(),
"deposit", account.depositAccountType()); }
@@ -98,10 +116,13 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
saveTransactionToGenerateTransactionId(deposit);
@@ -7,9 +7,9 @@ package org.mifosplatform.portfolio.savings.domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
@@ -30,7 +30,8 @@ public final class SavingsHelper {
private final CompoundInterestHelper compoundInterestHelper = new CompoundInterestHelper();
public List<LocalDateInterval> determineInterestPostingPeriods(final LocalDate startInterestCalculationLocalDate,
final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType) {
final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType,
final Integer financialYearBeginningMonth) {
final List<LocalDateInterval> postingPeriods = new ArrayList<>();
@@ -40,14 +41,10 @@ public final class SavingsHelper {
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType,
interestPostingUpToDate);
interestPostingUpToDate, financialYearBeginningMonth);
periodEndDate = interestPostingLocalDate.minusDays(1);
if (!interestPostingLocalDate.isAfter(interestPostingUpToDate)) {
postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
} else {
postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
}
postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
periodEndDate = interestPostingLocalDate;
periodStartDate = interestPostingLocalDate;
@@ -57,46 +54,76 @@ public final class SavingsHelper {
}
private LocalDate determineInterestPostingPeriodEndDateFrom(final LocalDate periodStartDate,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final LocalDate interestPostingUpToDate) {
final SavingsPostingInterestPeriodType interestPostingPeriodType, final LocalDate interestPostingUpToDate,
Integer financialYearBeginningMonth) {
LocalDate periodEndDate = interestPostingUpToDate;
switch (interestPostingPeriodType) {
case INVALID:
break;
case MONTHLY:
// produce period end date on last day of current month
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
break;
case QUATERLY:
// jan 1st to mar 31st, 1st apr to jun 30, jul 1st to sept 30,
// oct 1st to dec 31
final int year = periodStartDate.getYearOfEra();
final int monthofYear = periodStartDate.getMonthOfYear();
if (monthofYear <= 3) {
periodEndDate = new DateTime().withDate(year, 3, 31).toLocalDate();
} else if (monthofYear <= 6) {
periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate();
} else if (monthofYear <= 9) {
periodEndDate = new DateTime().withDate(year, 9, 30).toLocalDate();
} else if (monthofYear <= 12) {
periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
}
break;
case BIANNUAL:
final int byear = periodStartDate.getYearOfEra();
final int bmonthofYear = periodStartDate.getMonthOfYear();
if (bmonthofYear <= 6) {
periodEndDate = new DateTime().withDate(byear, 6, 30).toLocalDate();
} else if (bmonthofYear <= 12) {
periodEndDate = new DateTime().withDate(byear, 12, 31).toLocalDate();
}
break;
case ANNUAL:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
}
final Integer monthOfYear = periodStartDate.getMonthOfYear();
financialYearBeginningMonth--;
if(financialYearBeginningMonth == 0)
financialYearBeginningMonth = 12;
final ArrayList<LocalDate> quarterlyDates = new ArrayList<>();
quarterlyDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).dayOfMonth().withMaximumValue());
quarterlyDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth)
.plusMonths(3).withYear(periodStartDate.getYear()).dayOfMonth().withMaximumValue());
quarterlyDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth)
.plusMonths(6).withYear(periodStartDate.getYear()).dayOfMonth().withMaximumValue());
quarterlyDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth)
.plusMonths(9).withYear(periodStartDate.getYear()).dayOfMonth().withMaximumValue());
Collections.sort(quarterlyDates);
final ArrayList<LocalDate> biannualDates = new ArrayList<>();
biannualDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).dayOfMonth().withMaximumValue());
biannualDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).plusMonths(6)
.withYear(periodStartDate.getYear()).dayOfMonth().withMaximumValue());
Collections.sort(biannualDates);
boolean isEndDateSet = false;
switch (interestPostingPeriodType) {
case INVALID:
break;
case MONTHLY:
// produce period end date on last day of current month
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
break;
case QUATERLY:
for(LocalDate quarterlyDate : quarterlyDates) {
if(quarterlyDate.isAfter(periodStartDate)) {
periodEndDate = quarterlyDate;
isEndDateSet = true;
break;
}
}
if(!isEndDateSet)
periodEndDate = quarterlyDates.get(0).plusYears(1).dayOfMonth().withMaximumValue();
break;
case BIANNUAL:
for(LocalDate biannualDate : biannualDates) {
if(biannualDate.isAfter(periodStartDate)) {
periodEndDate = biannualDate;
isEndDateSet = true;
break;
}
}
if(!isEndDateSet)
periodEndDate = biannualDates.get(0).plusYears(1).dayOfMonth().withMaximumValue();
break;
case ANNUAL:
if (financialYearBeginningMonth < monthOfYear) {
periodEndDate = periodStartDate
.withMonthOfYear(financialYearBeginningMonth);
periodEndDate = periodEndDate.plusYears(1);
} else {
periodEndDate = periodStartDate
.withMonthOfYear(financialYearBeginningMonth);
}
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
}
// interest posting always occurs on next day after the period end date.
periodEndDate = periodEndDate.plusDays(1);
@@ -52,7 +52,7 @@ public class PostingPeriod {
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final LocalDate upToInterestCalculationDate, Collection<Long> interestPostTransactions, boolean isInterestTransfer,
final Money minBalanceForInterestCalculation) {
final Money minBalanceForInterestCalculation, final boolean isSavingsInterestPostingAtCurrentPeriodEnd) {
final List<EndOfDayBalance> accountEndOfDayBalances = new ArrayList<>();
boolean interestTransfered = false;
@@ -113,13 +113,14 @@ public class PostingPeriod {
return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType,
interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods, interestTransfered,
minBalanceForInterestCalculation);
minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd);
}
private PostingPeriod(final LocalDateInterval periodInterval, final MonetaryCurrency currency, final Money openingBalance,
final Money closingBalance, final SavingsCompoundingInterestPeriodType interestCompoundingType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final List<CompoundingPeriod> compoundingPeriods, boolean interestTransfered, final Money minBalanceForInterestCalculation) {
final List<CompoundingPeriod> compoundingPeriods, boolean interestTransfered, final Money minBalanceForInterestCalculation,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd) {
this.periodInterval = periodInterval;
this.currency = currency;
this.openingBalance = openingBalance;
@@ -129,8 +130,11 @@ public class PostingPeriod {
this.interestRateAsFraction = interestRateAsFraction;
this.daysInYear = daysInYear;
this.compoundingPeriods = compoundingPeriods;
this.dateOfPostingTransaction = periodInterval.endDate().plusDays(1);
if(isSavingsInterestPostingAtCurrentPeriodEnd)
this.dateOfPostingTransaction = periodInterval.endDate();
else
this.dateOfPostingTransaction = periodInterval.endDate().plusDays(1);
this.interestTransfered = interestTransfered;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
}
@@ -12,6 +12,7 @@ import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonQuery;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
@@ -41,17 +42,20 @@ public class DepositAccountPreMatureCalculationPlatformServiceImpl implements De
private final DepositAccountAssembler depositAccountAssembler;
private final CodeValueReadPlatformService codeValueReadPlatformService;
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
private final ConfigurationDomainService configurationDomainService;
@Autowired
public DepositAccountPreMatureCalculationPlatformServiceImpl(final FromJsonHelper fromJsonHelper,
final DepositAccountTransactionDataValidator depositAccountTransactionDataValidator,
final DepositAccountAssembler depositAccountAssembler, final CodeValueReadPlatformService codeValueReadPlatformService,
final SavingsAccountReadPlatformService savingsAccountReadPlatformService) {
final SavingsAccountReadPlatformService savingsAccountReadPlatformService,
final ConfigurationDomainService configurationDomainService) {
this.fromJsonHelper = fromJsonHelper;
this.depositAccountTransactionDataValidator = depositAccountTransactionDataValidator;
this.depositAccountAssembler = depositAccountAssembler;
this.codeValueReadPlatformService = codeValueReadPlatformService;
this.savingsAccountReadPlatformService = savingsAccountReadPlatformService;
this.configurationDomainService = configurationDomainService;
}
@@ -59,6 +63,12 @@ public class DepositAccountPreMatureCalculationPlatformServiceImpl implements De
@Override
public DepositAccountData calculatePreMatureAmount(final Long accountId, final JsonQuery query,
final DepositAccountType depositAccountType) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
this.depositAccountTransactionDataValidator.validatePreMatureAmountCalculation(query.json(), depositAccountType);
final SavingsAccount account = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType);
@@ -79,13 +89,15 @@ public class DepositAccountPreMatureCalculationPlatformServiceImpl implements De
if (depositAccountType.isFixedDeposit()) {
final FixedDepositAccount fd = (FixedDepositAccount) account;
accountData = FixedDepositAccountData.preClosureDetails(account.getId(),
fd.calculatePreMatureAmount(interestCalculatedToDate, isPreMatureClosure), onAccountClosureOptions, paymentTypeOptions,
savingsAccountDatas);
fd.calculatePreMatureAmount(interestCalculatedToDate, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth),
onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas);
} else if (depositAccountType.isRecurringDeposit()) {
final RecurringDepositAccount rd = (RecurringDepositAccount) account;
accountData = RecurringDepositAccountData.preClosureDetails(account.getId(),
rd.calculatePreMatureAmount(interestCalculatedToDate, isPreMatureClosure), onAccountClosureOptions, paymentTypeOptions,
savingsAccountDatas);
rd.calculatePreMatureAmount(interestCalculatedToDate, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth),
onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas);
}
return accountData;
@@ -27,6 +27,7 @@ import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -121,6 +122,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
private final DepositAccountReadPlatformService depositAccountReadPlatformService;
private final CalendarInstanceRepository calendarInstanceRepository;
private final ConfigurationDomainService configurationDomainService;
@Autowired
public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -140,7 +142,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final DepositAccountReadPlatformService depositAccountReadPlatformService,
final CalendarInstanceRepository calendarInstanceRepository) {
final CalendarInstanceRepository calendarInstanceRepository,
final ConfigurationDomainService configurationDomainService) {
this.context = context;
this.savingAccountRepository = savingAccountRepository;
@@ -162,6 +165,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
this.depositAccountReadPlatformService = depositAccountReadPlatformService;
this.calendarInstanceRepository = calendarInstanceRepository;
this.configurationDomainService = configurationDomainService;
}
@Transactional
@@ -169,6 +173,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
public CommandProcessingResult activateFDAccount(final Long savingsId, final JsonCommand command) {
final AppUser user = this.context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
this.depositAccountTransactionDataValidator.validateActivation(command);
final MathContext mc = MathContext.DECIMAL64;
@@ -208,17 +217,20 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final boolean isInterestTransfer = false;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
}
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
}
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name());
this.savingAccountRepository.save(account);
}
@@ -241,6 +253,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
boolean isRegularTransaction = false;
final AppUser user = this.context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
this.depositAccountTransactionDataValidator.validateActivation(command);
@@ -290,7 +307,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence());
frequency = frequency == -1 ? 1 : frequency;
account.generateSchedule(frequencyType, frequency, calendar);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
final LocalDate overdueUptoDate = account.maturityDate().isAfter(DateUtils.getLocalDateOfTenant()) ? DateUtils
@@ -299,10 +318,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final boolean isInterestTransfer = false;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name());
@@ -407,6 +430,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override
public CommandProcessingResult calculateInterest(final Long savingsId, final DepositAccountType depositAccountType) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount account = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType);
checkClientOrGroupActive(account);
@@ -414,7 +442,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN);
boolean isInterestTransfer = false;
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingAccountRepository.save(account);
@@ -445,13 +475,21 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional
private void postInterest(final SavingsAccount account) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
boolean isInterestTransfer = false;
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingAccountRepository.save(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -487,6 +525,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override
public CommandProcessingResult undoRDTransaction(final Long savingsId, final Long transactionId,
final boolean allowAccountTransferModification) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId,
DepositAccountType.RECURRING_DEPOSIT);
@@ -514,14 +557,20 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction);
// account.activateAccountBasedOnBalance();
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
final LocalDate overdueUptoDate = account.maturityDate().isAfter(DateUtils.getLocalDateOfTenant()) ? DateUtils
.getLocalDateOfTenant() : account.maturityDate();
@@ -553,6 +602,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override
public CommandProcessingResult adjustRDTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
this.depositAccountTransactionDataValidator.validate(command, DepositAccountType.RECURRING_DEPOSIT);
final SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository
@@ -601,9 +655,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
boolean isInterestTransfer = false;
if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction);
@@ -788,6 +846,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override
public SavingsAccountTransaction initiateSavingsTransfer(final Long accountId, final LocalDate transferDate,
final DepositAccountType depositAccountType) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType);
final Set<Long> existingTransactionIds = new HashSet<>();
@@ -800,7 +864,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -813,6 +879,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override
public SavingsAccountTransaction withdrawSavingsTransfer(final Long accountId, final LocalDate transferDate,
final DepositAccountType depositAccountType) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType);
final Set<Long> existingTransactionIds = new HashSet<>();
@@ -825,7 +897,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -845,7 +919,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override
public SavingsAccountTransaction acceptSavingsTransfer(final Long accountId, final LocalDate transferDate,
final Office acceptedInOffice, final Staff fieldOfficer, final DepositAccountType depositAccountType) {
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType);
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType);
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
@@ -860,7 +940,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
}
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -985,6 +1067,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@SuppressWarnings("unused") final DepositAccountType depositAccountType) {
this.context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
@@ -1002,10 +1089,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction);
@@ -1109,6 +1200,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional
private void payCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, final BigDecimal amountPaid,
final DateTimeFormatter formatter) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
// Get Savings account from savings charge
final SavingsAccount account = savingsAccountCharge.savingsAccount();
this.depositAccountAssembler.assignSavingAccountHelpers(account);
@@ -1120,10 +1217,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode());
@@ -1136,15 +1237,23 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional
@Override
public void updateMaturityDetails(Long depositAccountId, DepositAccountType depositAccountType) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount account = this.depositAccountAssembler.assembleFrom(depositAccountId, depositAccountType);
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
if (depositAccountType.isFixedDeposit()) {
((FixedDepositAccount) account).updateMaturityStatus();
((FixedDepositAccount) account).updateMaturityStatus(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else if (depositAccountType.isRecurringDeposit()) {
((RecurringDepositAccount) account).updateMaturityStatus();
((RecurringDepositAccount) account).updateMaturityStatus(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -19,6 +19,7 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -100,6 +101,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
private final AccountAssociationsRepository accountAssociationsRepository;
private final FromJsonHelper fromJsonHelper;
private final CalendarInstanceRepository calendarInstanceRepository;
private final ConfigurationDomainService configurationDomainService;
@Autowired
public DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -113,7 +115,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final FixedDepositAccountRepository fixedDepositAccountRepository,
final RecurringDepositAccountRepository recurringDepositAccountRepository,
final AccountAssociationsRepository accountAssociationsRepository, final FromJsonHelper fromJsonHelper,
final CalendarInstanceRepository calendarInstanceRepository) {
final CalendarInstanceRepository calendarInstanceRepository,
final ConfigurationDomainService configurationDomainService) {
this.context = context;
this.savingAccountRepository = savingAccountRepository;
this.depositAccountAssembler = depositAccountAssembler;
@@ -131,6 +134,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
this.accountAssociationsRepository = accountAssociationsRepository;
this.fromJsonHelper = fromJsonHelper;
this.calendarInstanceRepository = calendarInstanceRepository;
this.configurationDomainService = configurationDomainService;
}
/*
@@ -167,6 +171,11 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
try {
this.depositAccountDataValidator.validateFixedDepositForSubmit(command.json());
final AppUser submittedBy = this.context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final FixedDepositAccount account = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(command, submittedBy,
DepositAccountType.FIXED_DEPOSIT);
@@ -174,7 +183,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final MathContext mc = MathContext.DECIMAL64;
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure);
account.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
this.fixedDepositAccountRepository.save(account);
if (account.isAccountNumberRequiresAutoGeneration()) {
@@ -217,6 +227,11 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
try {
this.depositAccountDataValidator.validateRecurringDepositForSubmit(command.json());
final AppUser submittedBy = this.context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(command,
submittedBy, DepositAccountType.RECURRING_DEPOSIT);
@@ -242,7 +257,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
frequency = frequency == -1 ? 1 : frequency;
account.generateSchedule(frequencyType, frequency, calendar);
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.validateApplicableInterestRate();
this.savingAccountRepository.save(account);
@@ -318,6 +334,11 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
public CommandProcessingResult modifyFDApplication(final Long accountId, final JsonCommand command) {
try {
this.depositAccountDataValidator.validateFixedDepositForUpdate(command.json());
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final Map<String, Object> changes = new LinkedHashMap<>(20);
@@ -331,7 +352,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
updateFDAndRDCommonChanges(changes, command, account);
final MathContext mc = MathContext.DECIMAL64;
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure);
account.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
this.savingAccountRepository.save(account);
}
@@ -355,6 +377,11 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
public CommandProcessingResult modifyRDApplication(final Long accountId, final JsonCommand command) {
try {
this.depositAccountDataValidator.validateRecurringDepositForUpdate(command.json());
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final Map<String, Object> changes = new LinkedHashMap<>(20);
@@ -375,7 +402,9 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
frequency = frequency == -1 ? 1 : frequency;
account.generateSchedule(frequencyType, frequency, calendar);
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
account.validateApplicableInterestRate();
this.savingAccountRepository.save(account);
@@ -28,6 +28,7 @@ import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -113,6 +114,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
private final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository;
private final HolidayWritePlatformService holidayWritePlatformService;
private final WorkingDaysWritePlatformService workingDaysWritePlatformService;
private final ConfigurationDomainService configurationDomainService;
@Autowired
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -131,7 +133,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final HolidayWritePlatformService holidayWritePlatformService,
final WorkingDaysWritePlatformService workingDaysWritePlatformService,
final SavingsAccountDataValidator fromApiJsonDeserializer, final SavingsAccountRepositoryWrapper savingsRepository,
final StaffRepositoryWrapper staffRepository) {
final StaffRepositoryWrapper staffRepository,
final ConfigurationDomainService configurationDomainService) {
this.context = context;
this.savingAccountRepository = savingAccountRepository;
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
@@ -152,6 +155,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.savingsRepository = savingsRepository;
this.staffRepository = staffRepository;
this.configurationDomainService = configurationDomainService;
}
@Transactional
@@ -192,6 +196,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public void processPostActiveActions(final SavingsAccount account, final DateTimeFormatter fmt, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
Money amountForDeposit = account.activateWithBalance();
boolean isRegularTransaction = false;
if (amountForDeposit.isGreaterThanZero()) {
@@ -200,7 +210,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
null, isAccountTransfer, isRegularTransaction);
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
}
account.processAccountUponActivation();
account.processAccountUponActivation(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name());
}
@@ -304,6 +315,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional
@Override
public CommandProcessingResult calculateInterest(final Long savingsId) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account);
@@ -311,7 +327,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN);
boolean isInterestTransfer = false;
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingAccountRepository.save(account);
@@ -342,6 +360,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional
private void postInterest(final SavingsAccount account) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
if (account.getNominalAnnualInterestRate().compareTo(BigDecimal.ZERO) == 1) {
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
@@ -349,7 +373,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
boolean isInterestTransfer = false;
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
// for generating transaction id's
List<SavingsAccountTransaction> transactions = account.getTransactions();
@@ -379,7 +404,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId,
final boolean allowAccountTransferModification) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
@@ -419,9 +449,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction);
account.activateAccountBasedOnBalance();
@@ -438,6 +471,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public CommandProcessingResult adjustSavingsTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository
.findOneByIdAndSavingsAccountId(transactionId, savingsId);
@@ -495,9 +533,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction);
account.activateAccountBasedOnBalance();
@@ -597,6 +638,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public SavingsAccountTransaction initiateSavingsTransfer(final Long accountId, final LocalDate transferDate) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId);
final Set<Long> existingTransactionIds = new HashSet<>();
@@ -609,7 +656,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -621,6 +670,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public SavingsAccountTransaction withdrawSavingsTransfer(final Long accountId, final LocalDate transferDate) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId);
final Set<Long> existingTransactionIds = new HashSet<>();
@@ -633,7 +688,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -653,6 +710,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public SavingsAccountTransaction acceptSavingsTransfer(final Long accountId, final LocalDate transferDate,
final Office acceptedInOffice, final Staff fieldOfficer) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId);
final Set<Long> existingTransactionIds = new HashSet<>();
@@ -668,7 +731,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
}
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -792,6 +857,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
this.context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
@@ -809,10 +879,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction);
@@ -914,6 +987,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional
private void payCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, final BigDecimal amountPaid,
final DateTimeFormatter formatter) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService
.retrieveFinancialYearBeginningMonth();
// Get Savings account from savings charge
final SavingsAccount account = savingsAccountCharge.savingsAccount();
this.savingAccountAssembler.assignSavingAccountHelpers(account);
@@ -925,10 +1004,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer);
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode());
@@ -0,0 +1,7 @@
ALTER TABLE c_configuration ADD `description` varchar(300) DEFAULT NULL;
INSERT INTO `c_configuration` (`id`, `name`, `enabled`, `description`)
VALUES (NULL, 'savings-interest-posting-current-period-end', '0', "Recommended to be changed only once during start of production. When set as false(default), interest will be posted on the first date of next period. If set as true, interest will be posted on last date of current period. There is no difference in the interest amount posted.");
INSERT INTO `c_configuration` (`id`, `name`, `value`, `enabled`, `description`)
VALUES (NULL, 'financial-year-beginning-month', '1', '1', "Recommended to be changed only once during start of production. Allowed values 1 - 12 (January - December). Interest posting periods are evaluated based on this configuration.");