interest posting on specified date

This commit is contained in:
Koustav Muhuri
2016-11-03 15:39:29 +05:30
parent b3c4f75f11
commit 580840c35e
24 changed files with 1648 additions and 226 deletions
@@ -47,6 +47,7 @@ public class SavingsAccountHelper {
private static final String WITHDRAWN_BY_CLIENT_SAVINGS_COMMAND = "withdrawnByApplicant";
private static final String CALCULATE_INTEREST_SAVINGS_COMMAND = "calculateInterest";
private static final String POST_INTEREST_SAVINGS_COMMAND = "postInterest";
private static final String POST_INTEREST_AS_ON_SAVINGS_COMMAND = "postInterestAsOn";
private static final String CLOSE_SAVINGS_COMMAND = "close";
private static final String UPDATE_WITHHOLD_TAX_STATUS = "updateWithHoldTax";
@@ -195,6 +196,12 @@ public class SavingsAccountHelper {
performSavingActions(createSavingsCalculateInterestURL(POST_INTEREST_SAVINGS_COMMAND, savingsId),
getCalculatedInterestForSavingsApplicationAsJSON(), "");
}
public void postInterestAsOnSavings(final Integer savingsId, final String today) {
System.out.println("--------------------------------- POST INTEREST AS ON FOR SAVINGS --------------------------------");
performSavingActions(createSavingsPostInterestAsOnURL(POST_INTEREST_AS_ON_SAVINGS_COMMAND, savingsId),
getCalculatedInterestForSavingsApplicationAsJSON(today), CommonConstants.RESPONSE_RESOURCE_ID);
}
public Integer addChargesForSavings(final Integer savingsId, final Integer chargeId, boolean addDueDate) {
System.out.println("--------------------------------- ADD CHARGES FOR SAVINGS --------------------------------");
@@ -286,6 +293,16 @@ public class SavingsAccountHelper {
System.out.println(savingsAccountCalculatedInterestJson);
return savingsAccountCalculatedInterestJson;
}
private String getCalculatedInterestForSavingsApplicationAsJSON(final String today) {
final HashMap<String, String> map = new HashMap<>();
map.put("locale", CommonConstants.locale);
map.put("dateFormat", CommonConstants.dateFormat);
map.put("transactionDate", today);
map.put("postInterestManualOrAutomatic", "true");
String savingsAccountCalculatedInterestJson = new Gson().toJson(map);
return savingsAccountCalculatedInterestJson;
}
private String getSavingsPayChargeJSON(final String amount, final String dueDate) {
final HashMap<String, Object> map = new HashMap<>();
@@ -321,6 +338,20 @@ public class SavingsAccountHelper {
map.put("closedOnDate", closedOnDate);
map.put("withdrawBalance", withdrawBalance);
map.put("note", "Close Test");
String josn = new Gson().toJson(map);
return josn;
}
private String getCloseAccountPostInterestJSON(String withdrawBalance, String closedOnDate) {
final HashMap<String, Object> map = new HashMap<>();
map.put("locale", CommonConstants.locale);
map.put("dateFormat", CommonConstants.dateFormat);
map.put("closedOnDate", closedOnDate);
map.put("withdrawBalance", withdrawBalance);
map.put("note", "Close Test");
map.put("postInterestValidationOnClosure", "true");
String josn = new Gson().toJson(map);
return josn;
}
@@ -341,6 +372,10 @@ public class SavingsAccountHelper {
private String createSavingsCalculateInterestURL(final String command, final Integer savingsID) {
return SAVINGS_ACCOUNT_URL + "/" + savingsID + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
}
private String createSavingsPostInterestAsOnURL(final String command, final Integer savingsID) {
return SAVINGS_ACCOUNT_URL + "/" + savingsID + "/transactions/" + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
}
private String createChargesURL(final String command, final Integer savingsID, final Integer chargeId) {
return SAVINGS_ACCOUNT_URL + "/" + savingsID + "/charges/" + chargeId + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
@@ -418,6 +453,13 @@ public class SavingsAccountHelper {
return performSavingActions(createSavingsOperationURL(CLOSE_SAVINGS_COMMAND, savingsID),
getCloseAccountJSON(withdrawBalance, closedOnDate), jsonAttributeToGetBack);
}
public Object closeSavingsAccountPostInterestAndGetBackRequiredField(final Integer savingsID, String withdrawBalance,
final String jsonAttributeToGetBack, final String closedOnDate) {
System.out.println("---------------------------------- CLOSE SAVINGS APPLICATION ----------------------------------");
return performSavingActions(createSavingsOperationURL(CLOSE_SAVINGS_COMMAND, savingsID),
getCloseAccountPostInterestJSON(withdrawBalance, closedOnDate), jsonAttributeToGetBack);
}
private String getPeriodChargeRequestJSON(Integer chargeId, boolean addDueDate) {
final HashMap<String, Object> map = new HashMap<>();
@@ -2248,6 +2248,15 @@ public class CommandWrapperBuilder {
this.href = "/savingsaccounts/" + accountId + "?commad=unassignSavingsOfficer";
return this;
}
public CommandWrapperBuilder SavingsInterestPostingAsOnDate(final Long accountId) {
this.actionName = "POSTINTERESTASONDATE";
this.entityName = "SAVINGSACCOUNT";
this.savingsId = accountId;
this.entityId = null;
this.href = "/savingsaccounts/" + accountId + "?command=postInterestAsOn";
return this;
}
public CommandWrapperBuilder createLoanRescheduleRequest(final String entityName) {
this.actionName = "CREATE";
@@ -34,6 +34,7 @@ public class SavingsApiConstants {
public static final String SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME = "savingsaccountcharge";
// actions
public static final String postInterestValidationOnClosure = "postInterestValidationOnClosure";
public static String summitalAction = ".summital";
public static String approvalAction = ".approval";
public static String undoApprovalAction = ".undoApproval";
@@ -244,13 +245,17 @@ public class SavingsApiConstants {
public static final Set<String> SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(idParamName, "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName,
runningBalanceParamName, reversedParamName));
public static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName,
accountNoParamName));
public static final Set<String> SAVINGS_ACCOUNT_ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, activatedOnDateParamName));
public static final Set<String> SAVINGS_ACCOUNT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, closedOnDateParamName, noteParamName, paymentTypeIdParamName, withdrawBalanceParamName,
transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName));
transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName,
postInterestValidationOnClosure));
public static final Set<String> SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(chargeIdParamName,
savingsAccountChargeIdParamName, chargeNameParamName, penaltyParamName, chargeTimeTypeParamName, dueAsOfDateParamName,
@@ -91,8 +91,8 @@ public class SavingsAccountTransactionsApiResource {
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@PathParam("savingsId") final Long savingsId,
// @QueryParam("command") final String commandParam,
@Context final UriInfo uriInfo) {
@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
// FIXME - KW - for now just send back generic default information for
@@ -104,7 +104,7 @@ public class SavingsAccountTransactionsApiResource {
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, savingsAccount,
SavingsApiConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS);
SavingsApiConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS);
}
@GET
@@ -142,6 +142,9 @@ public class SavingsAccountTransactionsApiResource {
} else if (is(commandParam, "withdrawal")) {
final CommandWrapper commandRequest = builder.savingsAccountWithdrawal(savingsId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "postInterestAsOn")) {
final CommandWrapper commandRequest = builder.savingsAccountInterestPosting(savingsId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
if (result == null) {
@@ -49,6 +49,7 @@ public class SavingsAccountTransactionData {
private final boolean reversed;
private final AccountTransferData transfer;
private final LocalDate submittedOnDate;
private final boolean interestedPostedAsOn;
// templates
final Collection<PaymentTypeData> paymentTypeOptions;
@@ -56,19 +57,19 @@ public class SavingsAccountTransactionData {
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer) {
final AccountTransferData transfer, final boolean interestedPostedAsOn) {
final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
amount, runningBalance, reversed, transfer, paymentTypeOptions);
amount, runningBalance, reversed, transfer, paymentTypeOptions, interestedPostedAsOn);
}
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer,final LocalDate submittedOnDate) {
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer, final LocalDate submittedOnDate, final boolean interestedPostedAsOn) {
final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
amount, runningBalance, reversed, transfer, paymentTypeOptions,submittedOnDate);
amount, runningBalance, reversed, transfer, paymentTypeOptions, submittedOnDate, interestedPostedAsOn);
}
public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo,
@@ -80,8 +81,9 @@ public class SavingsAccountTransactionData {
final boolean reversed = false;
final PaymentDetailData paymentDetailData = null;
final Collection<CodeValueData> paymentTypeOptions = null;
final boolean interestedPostedAsOn = false;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, defaultLocalDate,
currency, amount, runningBalance, reversed, null, null);
currency, amount, runningBalance, reversed, null, null, interestedPostedAsOn);
}
public static SavingsAccountTransactionData templateOnTop(final SavingsAccountTransactionData savingsAccountTransactionData,
@@ -90,24 +92,23 @@ public class SavingsAccountTransactionData {
savingsAccountTransactionData.paymentDetailData, savingsAccountTransactionData.accountId,
savingsAccountTransactionData.accountNo, savingsAccountTransactionData.date, savingsAccountTransactionData.currency,
savingsAccountTransactionData.amount, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed,
savingsAccountTransactionData.transfer, paymentTypeOptions);
savingsAccountTransactionData.transfer, paymentTypeOptions, savingsAccountTransactionData.interestedPostedAsOn);
}
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions) {
this(id,transactionType,paymentDetailData,savingsId, savingsAccountNo,date,
currency,amount,runningBalance, reversed,
transfer, paymentTypeOptions,null);
final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions, final boolean interestedPostedAsOn) {
this(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, amount, runningBalance, reversed,
transfer, paymentTypeOptions, null, interestedPostedAsOn);
}
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,final LocalDate submittedOnDate) {
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions, final LocalDate submittedOnDate,
final boolean interestedPostedAsOn) {
this.id = id;
this.transactionType = transactionType;
this.paymentDetailData = paymentDetailData;
@@ -121,6 +122,7 @@ public class SavingsAccountTransactionData {
this.transfer = transfer;
this.paymentTypeOptions = paymentTypeOptions;
this.submittedOnDate = submittedOnDate;
this.interestedPostedAsOn = interestedPostedAsOn;
}
public static SavingsAccountTransactionData withWithDrawalTransactionDetails(
@@ -135,6 +137,6 @@ public class SavingsAccountTransactionData {
savingsAccountTransactionData.accountNo, currentDate, savingsAccountTransactionData.currency,
savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.runningBalance,
savingsAccountTransactionData.reversed, savingsAccountTransactionData.transfer,
savingsAccountTransactionData.paymentTypeOptions);
savingsAccountTransactionData.paymentTypeOptions, savingsAccountTransactionData.interestedPostedAsOn);
}
}
@@ -190,9 +190,10 @@ public class FixedDepositAccount extends SavingsAccount {
public void updateMaturityDateAndAmountBeforeAccountActivation(final MathContext mc, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
List<SavingsAccountTransaction> allTransactions = new ArrayList<>();
final boolean postInterestAsOn = false;
final Money transactionAmountMoney = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount());
final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null,
this.accountSubmittedOrActivationDate(), transactionAmountMoney, new Date(), null); // TODO:
this.accountSubmittedOrActivationDate(), transactionAmountMoney, new Date(), null, postInterestAsOn); // TODO:
// verify
// if
// it
@@ -297,9 +298,11 @@ public class FixedDepositAccount extends SavingsAccount {
final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType
.fromInt(this.interestCalculationDaysInYearType);
List<LocalDate> postedAsOnTransactionDates= getManualPostingDates();
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(
accountSubmittedOrActivationDate(), maturityDate, postingPeriodType, financialYearBeginningMonth);
accountSubmittedOrActivationDate(), maturityDate, postingPeriodType, financialYearBeginningMonth,
postedAsOnTransactionDates);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -311,11 +314,14 @@ public class FixedDepositAccount extends SavingsAccount {
boolean isInterestTransfer = false;
final Money minBalanceForInterestCalculation = Money.of(getCurrency(), minBalanceForInterestCalculation());
for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
boolean isUserPosting = false;
if (postedAsOnTransactionDates.contains(periodInterval.endDate())) {
isUserPosting = true;
}
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions,
this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
isSavingsInterestPostingAtCurrentPeriodEnd);
isSavingsInterestPostingAtCurrentPeriodEnd, isUserPosting);
periodStartingBalance = postingPeriod.closingBalance();
@@ -490,8 +496,9 @@ public class FixedDepositAccount extends SavingsAccount {
final LocalDate interestPostingUpToDate = maturityDate();
final MathContext mc = MathContext.DECIMAL64;
final boolean isInterestTransfer = false;
final boolean postInterestAsOn = false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestAsOn);
Money interestPostedToDate = Money.zero(this.currency);
@@ -511,7 +518,7 @@ public class FixedDepositAccount extends SavingsAccount {
final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate);
if (postingTransaction == null) {
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod);
interestPostingTransactionDate, interestEarnedToBePostedForPeriod, postInterestAsOn);
this.transactions.add(newPostingTransaction);
recalucateDailyBalanceDetails = true;
} else {
@@ -519,7 +526,7 @@ public class FixedDepositAccount extends SavingsAccount {
if (correctionRequired) {
postingTransaction.reverse();
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod);
interestPostingTransactionDate, interestEarnedToBePostedForPeriod, postInterestAsOn);
this.transactions.add(newPostingTransaction);
recalucateDailyBalanceDetails = true;
}
@@ -549,8 +556,9 @@ public class FixedDepositAccount extends SavingsAccount {
// post remaining interest
final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate);
if (!remainigInterestToBePosted.isZero()) {
final boolean postInterestAsOn = false;
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
accountCloseDate, remainigInterestToBePosted);
accountCloseDate, remainigInterestToBePosted, postInterestAsOn);
this.transactions.add(newPostingTransaction);
recalucateDailyBalance = true;
}
@@ -600,18 +608,21 @@ public class FixedDepositAccount extends SavingsAccount {
@Override
public void postInterest(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
final boolean postAsInterestOn) {
final boolean postInterestAsOn=false;
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
@Override
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
final boolean postAsInterestOn) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postAsInterestOn);
}
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
@@ -331,9 +331,9 @@ public class RecurringDepositAccount extends SavingsAccount {
final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType
.fromInt(this.interestCalculationDaysInYearType);
List<LocalDate> PostedAsOnDates = getManualPostingDates();
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(depositStartDate(),
maturityDate, postingPeriodType, financialYearBeginningMonth);
maturityDate, postingPeriodType, financialYearBeginningMonth, PostedAsOnDates);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -345,10 +345,14 @@ public class RecurringDepositAccount extends SavingsAccount {
boolean isInterestTransfer = false;
final Money minBalanceForInterestCalculation = Money.of(getCurrency(), minBalanceForInterestCalculation());
for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
boolean isUserPosting = false;
if (PostedAsOnDates.contains(periodInterval.endDate())) {
isUserPosting = true;
}
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions,
this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
isSavingsInterestPostingAtCurrentPeriodEnd);
isSavingsInterestPostingAtCurrentPeriodEnd, isUserPosting);
periodStartingBalance = postingPeriod.closingBalance();
@@ -369,9 +373,10 @@ public class RecurringDepositAccount extends SavingsAccount {
if (generateFutureTransactions) {
for (RecurringDepositScheduleInstallment installment : depositScheduleInstallments()) {
if (installment.isPrincipalNotCompleted(getCurrency())) {
boolean postInterestAsOn = false;
final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null,
installment.dueDate(), installment.getDepositAmountOutstanding(getCurrency()), installment.dueDate().toDate(),
null);
null, postInterestAsOn);
allTransactions.add(transaction);
}
}
@@ -598,8 +603,9 @@ public class RecurringDepositAccount extends SavingsAccount {
}
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
boolean postInterestAsOn =false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate.minusDays(1), isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestAsOn);
Money interestPostedToDate = Money.zero(this.currency);
@@ -617,7 +623,7 @@ public class RecurringDepositAccount extends SavingsAccount {
final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate);
if (postingTransaction == null) {
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod);
interestPostingTransactionDate, interestEarnedToBePostedForPeriod, postInterestAsOn);
addTransaction(newPostingTransaction);
recalucateDailyBalanceDetails = true;
} else {
@@ -625,7 +631,7 @@ public class RecurringDepositAccount extends SavingsAccount {
if (correctionRequired) {
postingTransaction.reverse();
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod);
interestPostingTransactionDate, interestEarnedToBePostedForPeriod, postInterestAsOn);
addTransaction(newPostingTransaction);
recalucateDailyBalanceDetails = true;
}
@@ -656,8 +662,9 @@ public class RecurringDepositAccount extends SavingsAccount {
// post remaining interest
final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate);
if (!remainigInterestToBePosted.isZero()) {
final boolean postInterestAsOn = false;
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
accountCloseDate, remainigInterestToBePosted);
accountCloseDate, remainigInterestToBePosted, postInterestAsOn);
addTransaction(newPostingTransaction);
recalucateDailyBalance = true;
}
@@ -707,18 +714,18 @@ public class RecurringDepositAccount extends SavingsAccount {
@Override
public void postInterest(final MathContext mc, final LocalDate postingDate, final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,final boolean postInterestAson) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAson);
}
@Override
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,final boolean postAsInterestOn) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postAsInterestOn);
}
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
@@ -235,6 +235,7 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
@Column(name = "interest_calculation_type_enum", nullable = false)
protected Integer interestCalculationType;
/**
* A value from the {@link SavingsInterestCalculationDaysInYearType}
* enumeration.
@@ -462,13 +463,10 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
public boolean isClosed() {
return SavingsAccountStatusType.fromInt(this.status).isClosed();
}
public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,final boolean postInterestAsOn) {
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestAsOn);
Money interestPostedToDate = Money.zero(this.currency);
boolean recalucateDailyBalanceDetails = false;
@@ -477,23 +475,23 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
withholdTransactions.addAll(findWithHoldTransactions());
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
final LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction();
final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned();
if (!interestPostingTransactionDate.isAfter(interestPostingUpToDate)) {
interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod);
final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate);
if (postingTransaction == null) {
SavingsAccountTransaction newPostingTransaction;
if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) {
newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), interestPostingTransactionDate,
interestEarnedToBePostedForPeriod);
interestEarnedToBePostedForPeriod, interestPostingPeriod.isUserPosting());
} else {
newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), interestPostingTransactionDate,
interestEarnedToBePostedForPeriod.negated());
interestEarnedToBePostedForPeriod.negated(), interestPostingPeriod.isUserPosting());
}
addTransaction(newPostingTransaction);
if (applyWithHoldTax) {
@@ -519,10 +517,12 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
SavingsAccountTransaction newPostingTransaction;
if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) {
newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod);
interestPostingTransactionDate, interestEarnedToBePostedForPeriod,
interestPostingPeriod.isUserPosting());
} else {
newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated());
interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated(),
interestPostingPeriod.isUserPosting());
}
addTransaction(newPostingTransaction);
if (applyWithHoldTaxForOldTransaction) {
@@ -641,6 +641,16 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
return savingsTransaction;
}
public List<LocalDate> getManualPostingDates() {
List<LocalDate> transactions = new ArrayList<>();
for (SavingsAccountTransaction trans : this.transactions) {
if (trans.isManualTransaction()) {
transactions.add(trans.getTransactionLocalDate());
}
}
return transactions;
}
/**
* All interest calculation based on END-OF-DAY-BALANCE.
*
@@ -657,9 +667,10 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
*
* @param isInterestTransfer
* TODO
*/
*/
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate,
boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,final boolean postInterestAsOn) {
// no openingBalance concept supported yet but probably will to allow
// for migrations.
@@ -680,10 +691,14 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
.fromInt(this.interestCompoundingPeriodType);
final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType
.fromInt(this.interestCalculationDaysInYearType);
.fromInt(this.interestCalculationDaysInYearType);
List<LocalDate> postedAsOnDates= getManualPostingDates();
if(postInterestAsOn){
postedAsOnDates.add(upToInterestCalculationDate);
}
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(
getStartInterestCalculationDate(), upToInterestCalculationDate, postingPeriodType, financialYearBeginningMonth);
getStartInterestCalculationDate(), upToInterestCalculationDate, postingPeriodType, financialYearBeginningMonth,
postedAsOnDates);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -719,12 +734,17 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
final Money minOverdraftForInterestCalculation = Money.of(getCurrency(), this.minOverdraftForInterestCalculation);
for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
boolean isUserPosting = false;
if(postedAsOnDates.contains(periodInterval.endDate().plusDays(1))){
isUserPosting = true;
}
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance,
retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType,
interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate, interestPostTransactions,
isInterestTransfer, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd,
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation);
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation, isUserPosting);
periodStartingBalance = postingPeriod.closingBalance();
@@ -2133,13 +2153,15 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
if (isSavingsChargeApplied) {
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
boolean postInterestAsOnDate = false;
if (this.isBeforeLastPostingPeriod(getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
this.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
this.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOnDate);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
this.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOnDate);
}
}
}
@@ -2781,10 +2803,10 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
this.sub_status = SavingsAccountSubStatusEnum.ESCHEAT.getValue();
this.closedOnDate = DateUtils.getDateOfTenant();
this.closedBy = appUser;
boolean postInterestAsOnDate = false;
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
if(this.getSummary().getAccountBalance(this.getCurrency()).isGreaterThanZero()){
SavingsAccountTransaction transaction = SavingsAccountTransaction.escheat(this, transactionDate, appUser);
SavingsAccountTransaction transaction = SavingsAccountTransaction.escheat(this, transactionDate, appUser, postInterestAsOnDate);
this.transactions.add(transaction);
}
recalculateDailyBalances(Money.zero(this.currency), transactionDate);
@@ -86,6 +86,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
if (transactionBooleanValues.isRegularTransaction() && !account.allowWithdrawal()) { throw new DepositAccountTransactionNotAllowedException(
account.getId(), "withdraw", account.depositAccountType()); }
final Set<Long> existingTransactionIds = new HashSet<>();
final boolean interestPostAsOn = false;
final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
@@ -96,11 +97,11 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer(), isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, interestPostAsOn);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer(),
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, interestPostAsOn);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -154,15 +155,16 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, new Date(), user);
final SavingsAccountTransaction deposit = account.deposit(transactionDTO, savingsAccountTransactionType);
final boolean interestPostAsOn = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
interestPostAsOn);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, interestPostAsOn);
}
saveTransactionToGenerateTransactionId(deposit);
@@ -114,6 +114,9 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
@ManyToOne
@JoinColumn(name = "appuser_id", nullable = true)
private AppUser appUser;
@Column(name = "is_manual", length = 1, nullable = true)
private boolean isManualTransaction;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.EAGER)
@JoinColumn(name = "savings_transaction_id", referencedColumnName = "id", nullable = false)
@@ -126,112 +129,123 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
}
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser,
final boolean isManualTransaction) {
final boolean isReversed = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date,
createdDate, amount, isReversed, appUser);
createdDate, amount, isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser,
final SavingsAccountTransactionType savingsAccountTransactionType) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date,
createdDate, amount, isReversed, appUser);
createdDate, amount, isReversed, appUser,isManualTransaction);
}
public static SavingsAccountTransaction withdrawal(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(),
date, createdDate, amount, isReversed, appUser);
date, createdDate, amount, isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction interestPosting(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount) {
final Money amount,final boolean isManualTransaction) {
final boolean isReversed = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date,
amount, isReversed, null);
amount, isReversed, null, isManualTransaction);
}
public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final Money amount) {
final LocalDate date, final Money amount,final boolean isManualTransaction) {
final boolean isReversed = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date,
amount, isReversed, null);
amount, isReversed, null, isManualTransaction);
}
public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount,
isReversed, appUser);
isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount,
isReversed, appUser);
isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount,
isReversed, appUser);
isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction waiver(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount,
isReversed, appUser);
isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, new Date(), savingsAccount.getSummary()
.getAccountBalance(), isReversed, appUser);
.getAccountBalance(), isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction approveTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, new Date(), savingsAccount.getSummary()
.getAccountBalance(), isReversed, appUser);
.getAccountBalance(), isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction withdrawTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, new Date(), savingsAccount.getSummary()
.getAccountBalance(), isReversed, appUser);
.getAccountBalance(), isReversed, appUser, isManualTransaction);
}
public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, null);
SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, null, isManualTransaction);
updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction;
}
public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date,
final AppUser appUser) {
final AppUser appUser,final boolean accountTransaction) {
final boolean isReversed = false;
final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail,
SavingsAccountTransactionType.ESCHEAT.getValue(), date, new Date(), savingsAccount.getSummary()
.getAccountBalance(), isReversed, appUser);
.getAccountBalance(), isReversed, appUser,accountTransaction);
}
public static void updateTaxDetails(final Map<TaxComponent, BigDecimal> taxDetails, final SavingsAccountTransaction accountTransaction) {
@@ -245,23 +259,25 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
public static SavingsAccountTransaction copyTransaction(SavingsAccountTransaction accountTransaction) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office,
accountTransaction.paymentDetail, accountTransaction.typeOf, accountTransaction.transactionLocalDate(),
accountTransaction.createdDate, accountTransaction.amount, accountTransaction.reversed, accountTransaction.appUser);
accountTransaction.createdDate, accountTransaction.amount, accountTransaction.reversed, accountTransaction.appUser,
accountTransaction.isManualTransaction);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final Integer typeOf,
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final AppUser appUser) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, new Date(), amount, isReversed, appUser);
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final AppUser appUser,final boolean isManualTransaction) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, new Date(), amount, isReversed, appUser, isManualTransaction);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final Money amount,
final boolean isReversed, final AppUser appUser) {
this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, createdDate, amount.getAmount(), isReversed, appUser);
final boolean isReversed, final AppUser appUser, final boolean isManualTransaction) {
this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, createdDate, amount.getAmount(), isReversed, appUser,
isManualTransaction);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final BigDecimal amount,
final boolean isReversed, final AppUser appUser) {
final boolean isReversed, final AppUser appUser, final boolean isManualTransaction) {
this.savingsAccount = savingsAccount;
this.office = office;
this.typeOf = typeOf;
@@ -271,6 +287,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
this.paymentDetail = paymentDetail;
this.createdDate = createdDate;
this.appUser = appUser;
this.isManualTransaction = isManualTransaction;
}
public LocalDate transactionLocalDate() {
@@ -389,7 +406,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
}
private LocalDate getTransactionLocalDate() {
public LocalDate getTransactionLocalDate() {
return new LocalDate(this.dateOf);
}
@@ -470,6 +487,14 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
public boolean isAfter(final LocalDate transactionDate) {
return getTransactionLocalDate().isAfter(transactionDate);
}
public boolean isManualTransaction() {
return this.isManualTransaction;
}
public void setPostInterestAsOn(boolean isManualTransaction) {
this.isManualTransaction = isManualTransaction;
}
public EndOfDayBalance toEndOfDayBalance(final LocalDateInterval periodInterval, final MonetaryCurrency currency) {
@@ -704,5 +729,22 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
public void updateAmount(final Money amount) {
this.amount = amount.getAmount();
}
public Integer getTypeOf() {
return this.typeOf;
}
public SavingsAccount getSavingsAccount() {
return this.savingsAccount;
}
public void setSavingsAccount(SavingsAccount savingsAccount) {
this.savingsAccount = savingsAccount;
}
public Date getDateOf() {
return this.dateOf;
}
}
@@ -33,9 +33,9 @@ import org.apache.fineract.portfolio.savings.domain.interest.PostingPeriod;
import org.joda.time.LocalDate;
public final class SavingsHelper {
AccountTransfersReadPlatformService accountTransfersReadPlatformService = null;
public SavingsHelper(AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
}
@@ -44,30 +44,49 @@ public final class SavingsHelper {
public List<LocalDateInterval> determineInterestPostingPeriods(final LocalDate startInterestCalculationLocalDate,
final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType,
final Integer financialYearBeginningMonth) {
final Integer financialYearBeginningMonth,List<LocalDate> postInterestAsOn) {
final List<LocalDateInterval> postingPeriods = new ArrayList<>();
LocalDate periodStartDate = startInterestCalculationLocalDate;
LocalDate periodEndDate = periodStartDate;
LocalDate actualPeriodStartDate = periodStartDate;
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType,
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType,
interestPostingUpToDate, financialYearBeginningMonth);
periodEndDate = interestPostingLocalDate.minusDays(1);
if (!postInterestAsOn.isEmpty()) {
for (LocalDate transactiondate : postInterestAsOn) {
if (periodStartDate.isBefore(transactiondate)
&& (periodEndDate.isAfter(transactiondate) || periodEndDate
.isEqual(transactiondate))) {
periodEndDate = transactiondate.minusDays(1);
actualPeriodStartDate = periodEndDate;
break;
}
}
}
postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
if (actualPeriodStartDate.isEqual(periodEndDate))
{
periodEndDate = actualPeriodStartDate.plusDays(1);
periodStartDate = actualPeriodStartDate.plusDays(1);
}else{
periodEndDate = interestPostingLocalDate;
periodStartDate = interestPostingLocalDate;
}
}
return postingPeriods;
}
private LocalDate determineInterestPostingPeriodEndDateFrom(final LocalDate periodStartDate,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final LocalDate interestPostingUpToDate,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final LocalDate interestPostingUpToDate,
Integer financialYearBeginningMonth) {
LocalDate periodEndDate = interestPostingUpToDate;
@@ -90,7 +109,6 @@ public final class SavingsHelper {
biannualDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).plusMonths(6).withYear(periodStartDate.getYear())
.dayOfMonth().withMaximumValue());
Collections.sort(biannualDates);
boolean isEndDateSet = false;
switch (interestPostingPeriodType) {
@@ -131,11 +149,9 @@ public final class SavingsHelper {
}
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
}
}
// interest posting always occurs on next day after the period end date.
periodEndDate = periodEndDate.plusDays(1);
periodEndDate = periodEndDate.plusDays(1);
return periodEndDate;
}
@@ -54,6 +54,7 @@ public class PostingPeriod {
// include in compounding interest
private boolean interestTransfered = false;
private boolean isUserPosting = false;
// minimum balance for interest calculation
private final Money minBalanceForInterestCalculation;
@@ -65,7 +66,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 boolean isSavingsInterestPostingAtCurrentPeriodEnd) {
final Money minBalanceForInterestCalculation, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,final boolean isUserPosting) {
final BigDecimal overdraftInterestRateAsFraction = BigDecimal.ZERO;
final Money minOverdraftForInterestCalculation = Money.zero(currency);
@@ -74,7 +75,7 @@ public class PostingPeriod {
interestCompoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYear,
upToInterestCalculationDate, interestPostTransactions, isInterestTransfer,
minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd,
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation);
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation, isUserPosting);
}
// isInterestTransfer boolean is to identify newly created transaction is
@@ -85,7 +86,7 @@ public class PostingPeriod {
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final LocalDate upToInterestCalculationDate, Collection<Long> interestPostTransactions, boolean isInterestTransfer,
final Money minBalanceForInterestCalculation, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
final BigDecimal overdraftInterestRateAsFraction, final Money minOverdraftForInterestCalculation) {
final BigDecimal overdraftInterestRateAsFraction, final Money minOverdraftForInterestCalculation, boolean isUserPosting) {
final List<EndOfDayBalance> accountEndOfDayBalances = new ArrayList<>();
boolean interestTransfered = false;
@@ -147,7 +148,7 @@ public class PostingPeriod {
return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType,
interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods, interestTransfered,
minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd,
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation);
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation, isUserPosting);
}
private PostingPeriod(final LocalDateInterval periodInterval, final MonetaryCurrency currency, final Money openingBalance,
@@ -155,7 +156,7 @@ public class PostingPeriod {
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final List<CompoundingPeriod> compoundingPeriods, boolean interestTransfered, final Money minBalanceForInterestCalculation,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final BigDecimal overdraftInterestRateAsFraction,
final Money minOverdraftForInterestCalculation) {
final Money minOverdraftForInterestCalculation, boolean isUserPosting) {
this.periodInterval = periodInterval;
this.currency = currency;
this.openingBalance = openingBalance;
@@ -174,6 +175,7 @@ public class PostingPeriod {
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.overdraftInterestRateAsFraction = overdraftInterestRateAsFraction;
this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation;
this.isUserPosting = isUserPosting;
}
public Money interest() {
@@ -410,4 +412,12 @@ public class PostingPeriod {
public boolean isInterestTransfered() {
return this.interestTransfered;
}
public LocalDateInterval getPeriodInterval() {
return this.periodInterval;
}
public boolean isUserPosting() {
return this.isUserPosting;
}
}
@@ -0,0 +1,61 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.savings.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class PostInterestAsOnDateException extends AbstractPlatformDomainRuleException{
public static enum PostInterestAsOnException_TYPE {
FUTURE_DATE, VALID_DATE, ACTIVATION_DATE, LAST_TRANSACTION_DATE;
public String errorMessage() {
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) {
return "Cannot Post Interest in future Dates";
} else if (name().toString().equalsIgnoreCase("VALID_DATE")) {
return "Please Pass a valid date";
} else if (name().toString().equalsIgnoreCase("ACTIVATION_DATE")) {
return "Post Interest Date must be after the Activation date";
} else if (name().toString().equalsIgnoreCase("LAST_TRANSACTION_DATE")) {
return "Cannot Post Interest before last transaction date";
}
return name().toString();
}
public String errorCode() {
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) {
return "error.msg.futureDate";
} else if (name().toString().equalsIgnoreCase("VALID_DATE")) {
return "error.msg.nullDatePassed";
} else if (name().toString().equalsIgnoreCase("ACTIVATION_DATE")) {
return "error.msg.before activation date";
} else if (name().toString().equalsIgnoreCase("LAST_TRANSACTION_DATE")) {
return "error.msg.countInterest";
}
return name().toString();
}
}
public PostInterestAsOnDateException(final PostInterestAsOnException_TYPE reason) {
super(reason.errorCode(), reason.errorMessage());
}
}
@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.savings.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class PostInterestClosingDateException extends AbstractPlatformDomainRuleException {
public PostInterestClosingDateException() {
super("error.msg.postInterest.notDone", "Please do a post interest on the closing date");
}
}
@@ -41,6 +41,6 @@ public class PostInterestSavingsAccountCommandHandler implements NewCommandSourc
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.postInterest(command.getSavingsId());
return this.writePlatformService.postInterest(command);
}
}
@@ -0,0 +1,47 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.savings.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SAVINGSACCOUNT", action = "POSTINTERESTASONDATE")
public class PostSavingsAccountInterestAsOnDateCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService;
@Autowired
public PostSavingsAccountInterestAsOnDateCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.postInterest(command);
}
}
@@ -1049,9 +1049,9 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
transfer = AccountTransferData.transferBasicDetails(toTransferId, currency, toTransferAmount, toTransferDate,
toTransferDescription, toTransferReversed);
}
final boolean postInterestAsOn = false;
return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency,
amount, runningBalance, reversed, transfer);
amount, runningBalance, reversed, transfer, postInterestAsOn);
}
}
@@ -1437,9 +1437,9 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
final PaymentDetailData paymentDetailData = null;
final AccountTransferData transfer = null;
final BigDecimal runningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "runningBalance");
;
final boolean postInterestAsOn = false;
return SavingsAccountTransactionData.create(savingsId, transactionType, paymentDetailData, savingsId, accountNo, duedate,
currency, dueamount, runningBalance, false, transfer);
currency, dueamount, runningBalance, false, transfer, postInterestAsOn);
}
}
@@ -143,7 +143,6 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
@Autowired
public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final SavingsAccountRepositoryWrapper savingAccountRepositoryWrapper,
@@ -185,7 +184,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.depositAccountReadPlatformService = depositAccountReadPlatformService;
this.calendarInstanceRepository = calendarInstanceRepository;
this.configurationDomainService = configurationDomainService;
this.depositAccountOnHoldTransactionRepository =depositAccountOnHoldTransactionRepository;
this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository;
}
@Transactional
@@ -235,14 +234,15 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
}
final boolean isInterestTransfer = false;
final boolean postInterestAsOn = false;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
@@ -252,10 +252,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions);
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),
depositAccountOnHoldTransactions);
this.savingAccountRepositoryWrapper.saveAndFlush(account);
}
@@ -338,21 +340,24 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant();
account.updateOverduePayments(overdueUptoDate);
final boolean isInterestTransfer = false;
final boolean postInterestAsOn = false;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions);
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),
depositAccountOnHoldTransactions);
this.savingAccountRepositoryWrapper.saveAndFlush(account);
}
@@ -391,8 +396,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate depositAmountUpdateEffectiveFromDate = command
.localDateValueOfParameterNamed(DepositsApiConstants.effectiveDateParamName);
final RecurringDepositAccount recurringDepositAccount = (RecurringDepositAccount) this.depositAccountAssembler
.assembleFrom(savingsId, DepositAccountType.RECURRING_DEPOSIT);
final RecurringDepositAccount recurringDepositAccount = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(
savingsId, DepositAccountType.RECURRING_DEPOSIT);
DepositAccountRecurringDetail recurringDetail = recurringDepositAccount.getRecurringDetail();
Map<String, Object> changes = recurringDetail.updateMandatoryRecommendedDepositAmount(mandatoryRecommendedDepositAmount,
depositAmountUpdateEffectiveFromDate, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
@@ -446,8 +451,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional
@Override
public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command,
final DepositAccountType depositAccountType) {
public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command, final DepositAccountType depositAccountType) {
boolean isRegularTransaction = true;
@@ -493,9 +497,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
this.savingAccountRepositoryWrapper.save(account);
@@ -537,7 +541,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false;
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
boolean postInterestAsOn = false;
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
this.savingAccountRepositoryWrapper.saveAndFlush(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -588,11 +594,10 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.findOneByIdAndSavingsAccountId(transactionId, savingsId);
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!allowAccountTransferModification && this.accountTransfersReadPlatformService.isAccountTransfer(transactionId,
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.recurring.deposit.account.transfer.transaction.update.not.allowed",
"Recurring deposit account transaction:" + transactionId + " update not allowed as it involves in account transfer",
transactionId); }
if (!allowAccountTransferModification
&& this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.recurring.deposit.account.transfer.transaction.update.not.allowed", "Recurring deposit account transaction:"
+ transactionId + " update not allowed as it involves in account transfer", transactionId); }
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = MathContext.DECIMAL64;
@@ -602,24 +607,26 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
}
account.undoTransaction(transactionId);
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction,depositAccountOnHoldTransactions);
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction, depositAccountOnHoldTransactions);
// account.activateAccountBasedOnBalance();
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant();
@@ -663,14 +670,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.findOneByIdAndSavingsAccountId(transactionId, savingsId);
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal())
|| savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(savingsId, transactionId); }
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(
savingsId, transactionId); }
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId,
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transfer.transaction.update.not.allowed",
"Deposit account transaction:" + transactionId + " update not allowed as it involves in account transfer",
transactionId); }
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Deposit account transaction:" + transactionId
+ " update not allowed as it involves in account transfer", transactionId); }
final LocalDate today = DateUtils.getLocalDateOfTenant();
@@ -705,19 +710,22 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
}
final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction);
boolean isInterestTransfer = false;
final boolean postInterestAsOn = false;
if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction,depositAccountOnHoldTransactions);
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction, depositAccountOnHoldTransactions);
account.activateAccountBasedOnBalance();
if (savingsAccountTransaction.isDeposit()) {
@@ -913,7 +921,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType);
final boolean postInterestAsOn = false;
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
@@ -925,7 +933,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -957,8 +965,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -999,9 +1008,10 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
savingsAccount.reassignSavingsOfficer(fieldOfficer, transferDate);
}
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -1131,8 +1141,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
// Get Savings account from savings charge
final SavingsAccount account = savingsAccountCharge.savingsAccount();
@@ -1144,21 +1154,25 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.waiveCharge(savingsAccountChargeId, user);
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,depositAccountOnHoldTransactions);
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,
depositAccountOnHoldTransactions);
this.savingAccountRepositoryWrapper.saveAndFlush(account);
@@ -1181,8 +1195,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsAccountId, depositAccountType);
checkClientOrGroupActive(savingsAccount);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
savingsAccount.removeCharge(savingsAccountCharge);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -1208,8 +1222,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(amountParamName);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
@@ -1247,8 +1261,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@SuppressWarnings("unused") final DepositAccountType depositAccountType) {
// always use current date as transaction date for batch job
final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, accountId);
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
@@ -1275,21 +1289,25 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, user);
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
}
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode(),depositAccountOnHoldTransactions);
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode(),
depositAccountOnHoldTransactions);
this.savingAccountRepositoryWrapper.saveAndFlush(account);
@@ -1340,8 +1358,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override
public SavingsAccountTransaction mandatorySavingsAccountDeposit(final SavingsAccountTransactionDTO accountTransactionDTO) {
boolean isRegularTransaction = false;
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler
.assembleFrom(accountTransactionDTO.getSavingsAccountId(), DepositAccountType.RECURRING_DEPOSIT);
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(
accountTransactionDTO.getSavingsAccountId(), DepositAccountType.RECURRING_DEPOSIT);
final PaymentDetail paymentDetail = accountTransactionDTO.getPaymentDetail();
if (paymentDetail != null && paymentDetail.getId() == null) {
this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
@@ -767,7 +767,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("pt.value as paymentTypeName ");
sqlBuilder.append("pt.value as paymentTypeName, ");
sqlBuilder.append("tr.is_manual as postInterestAsOn ");
sqlBuilder.append("from m_savings_account sa ");
sqlBuilder.append("join m_savings_account_transaction tr on tr.savings_account_id = sa.id ");
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
@@ -797,6 +798,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final Long savingsId = rs.getLong("savingsId");
final String accountNo = rs.getString("accountNo");
final boolean postInterestAsOn = rs.getBoolean("postInterestAsOn");
PaymentDetailData paymentDetailData = null;
if (transactionType.isDepositOrWithdrawal()) {
@@ -840,12 +842,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final boolean toTransferReversed = rs.getBoolean("toTransferReversed");
final String toTransferDescription = rs.getString("toTransferDescription");
transfer = AccountTransferData.transferBasicDetails(toTransferId, currency, toTransferAmount, toTransferDate,
toTransferDescription, toTransferReversed);
}
return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency,
amount, runningBalance, reversed, transfer, submittedOnDate);
amount, runningBalance, reversed, transfer, submittedOnDate, postInterestAsOn);
}
}
@@ -41,8 +41,6 @@ public interface SavingsAccountWritePlatformService {
CommandProcessingResult calculateInterest(Long savingsId);
CommandProcessingResult postInterest(Long savingsId);
CommandProcessingResult undoTransaction(Long savingsId, Long transactionId, boolean allowAccountTransferModification);
CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command);
@@ -78,7 +76,6 @@ public interface SavingsAccountWritePlatformService {
void processPostActiveActions(SavingsAccount account, DateTimeFormatter fmt, Set<Long> existingTransactionIds,
Set<Long> existingReversedTransactionIds);
void postInterest(SavingsAccount account);
CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, JsonCommand command);
@@ -87,4 +84,8 @@ public interface SavingsAccountWritePlatformService {
void setSubStatusDormant(Long savingsId);
void escheat(Long savingsId);
CommandProcessingResult postInterest(JsonCommand command);
void postInterest(SavingsAccount account, boolean postInterestAs, LocalDate transactionDate);
}
@@ -39,6 +39,8 @@ import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.accounting.journalentry.exception.JournalEntryInvalidException;
import org.apache.fineract.accounting.journalentry.exception.JournalEntryInvalidException.GL_JOURNAL_ENTRY_INVALID_REASON;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
@@ -95,11 +97,14 @@ import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrap
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
import org.apache.fineract.portfolio.savings.exception.PostInterestAsOnDateException;
import org.apache.fineract.portfolio.savings.exception.PostInterestClosingDateException;
import org.apache.fineract.portfolio.savings.exception.SavingsAccountClosingNotAllowedException;
import org.apache.fineract.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
import org.apache.fineract.portfolio.savings.exception.SavingsOfficerAssignmentException;
import org.apache.fineract.portfolio.savings.exception.SavingsOfficerUnassignmentException;
import org.apache.fineract.portfolio.savings.exception.TransactionUpdateNotAllowedException;
import org.apache.fineract.portfolio.savings.exception.PostInterestAsOnDateException.PostInterestAsOnException_TYPE;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
import org.joda.time.LocalDate;
@@ -255,7 +260,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
@@ -268,7 +272,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
boolean isRegularTransaction = true;
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(account, fmt, transactionDate,
transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction);
return new CommandProcessingResultBuilder() //
.withEntityId(deposit.getId()) //
.withOfficeId(account.officeId()) //
@@ -277,6 +280,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) {
@@ -300,8 +304,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account);
checkClientOrGroupActive(account);
final boolean isAccountTransfer = false;
final boolean isRegularTransaction = true;
final boolean isApplyWithdrawFee = true;
@@ -359,7 +362,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false;
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth,false);
this.savingAccountRepositoryWrapper.save(account);
@@ -371,13 +374,32 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.withSavingsId(savingsId) //
.build();
}
@Override
public CommandProcessingResult postInterest(final Long savingsId) {
public CommandProcessingResult postInterest(final JsonCommand command) {
Long savingsId=command.getSavingsId();
final boolean postInterestAs =command.booleanPrimitiveValueOfParameterNamed("isPostInterestAsOn");
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account);
postInterest(account);
if (postInterestAs == true) {
if (transactionDate == null) {
throw new PostInterestAsOnDateException(PostInterestAsOnException_TYPE.VALID_DATE); }
if (transactionDate.isBefore(account.accountSubmittedOrActivationDate())) { throw new PostInterestAsOnDateException(
PostInterestAsOnException_TYPE.ACTIVATION_DATE); }
List<SavingsAccountTransaction> savingTransactions = account.getTransactions();
for (SavingsAccountTransaction savingTransaction : savingTransactions) {
if (transactionDate.toDate().before(savingTransaction.getDateOf())) { throw new PostInterestAsOnDateException(
PostInterestAsOnException_TYPE.LAST_TRANSACTION_DATE); }
}
LocalDate today = DateUtils.getLocalDateOfTenant();
if (transactionDate.isAfter(today)) { throw new PostInterestAsOnDateException(PostInterestAsOnException_TYPE.FUTURE_DATE); }
}
postInterest(account,postInterestAs,transactionDate);
return new CommandProcessingResultBuilder() //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
@@ -389,7 +411,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional
@Override
public void postInterest(final SavingsAccount account) {
public void postInterest(final SavingsAccount account,final boolean postInterestAs,final LocalDate transactionDate) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -403,14 +425,18 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false;
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
if(postInterestAs==false){
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,postInterestAs);
}
else{
account.postInterest(mc, transactionDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,postInterestAs);
}
// for generating transaction id's
List<SavingsAccountTransaction> transactions = account.getTransactions();
for (SavingsAccountTransaction accountTransaction : transactions) {
if (accountTransaction.getId() == null) {
this.savingsAccountTransactionRepository.save(accountTransaction);
}
}
}
this.savingAccountRepositoryWrapper.saveAndFlush(account);
@@ -418,7 +444,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
}
}
@Override
public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId,
final boolean allowAccountTransferModification) {
@@ -466,10 +492,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,false);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth,false);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -555,10 +581,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,false);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth,false);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -604,9 +630,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public CommandProcessingResult close(final Long savingsId, final JsonCommand command) {
final AppUser user = this.context.authenticatedUser();
this.savingsAccountTransactionDataValidator.validateClosing(command);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final boolean isLinkedWithAnyActiveLoan = this.accountAssociationsReadPlatformService.isLinkedWithAnyActiveAccount(savingsId);
if (isLinkedWithAnyActiveLoan) {
@@ -614,18 +641,36 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
+ " is not allowed, since it is linked with one of the active accounts";
throw new SavingsAccountClosingNotAllowedException("linked", defaultUserMessage, savingsId);
}
final boolean isWithdrawBalance = command.booleanPrimitiveValueOfParameterNamed(withdrawBalanceParamName);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
final boolean isPostInterest = command.booleanPrimitiveValueOfParameterNamed(SavingsApiConstants.postInterestValidationOnClosure);
// postInterest(account,closedDate,flag);
if (isPostInterest) {
boolean postInterestOnClosingDate = false;
List<SavingsAccountTransaction> savingTransactions = account.getTransactions();
for (SavingsAccountTransaction savingTransaction : savingTransactions) {
if (closedDate.toDate().equals(savingTransaction.getDateOf())) if (savingTransaction.getTypeOf() == SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue()) {
postInterestOnClosingDate = true;
}
}
if (postInterestOnClosingDate == false) { throw new PostInterestClosingDateException(); }
}
final Map<String, Object> changes = new LinkedHashMap<>();
if (isWithdrawBalance && account.getSummary().getAccountBalance(account.getCurrency()).isGreaterThanZero()) {
final BigDecimal transactionAmount = account.getSummary().getAccountBalance();
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final boolean isAccountTransfer = false;
@@ -685,8 +730,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -715,8 +761,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
boolean postInterestAsOn =false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -756,8 +803,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
}
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
boolean postInterestAsOn = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -906,14 +954,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
account.waiveCharge(savingsAccountChargeId, user);
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -1038,14 +1088,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, user);
boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
financialYearBeginningMonth, postInterestAsOn);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -60,7 +60,9 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
for (final SavingsAccount savingsAccount : savingsAccounts) {
try {
this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount);
this.savingsAccountWritePlatformService.postInterest(savingsAccount);
boolean postInterestAsOn = false;
LocalDate transactionDate = null;
this.savingsAccountWritePlatformService.postInterest(savingsAccount, postInterestAsOn, transactionDate);
} catch (Exception e) {
Throwable realCause = e;
if (e.getCause() != null) {
@@ -0,0 +1,21 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership. The ASF licenses this file
-- to you under the Apache License, Version 2.0 (the
-- "License"); you may not use this file except in compliance
-- with the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing,
-- software distributed under the License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
--
ALTER TABLE m_savings_account_transaction
ADD is_manual boolean default 0