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 WITHDRAWN_BY_CLIENT_SAVINGS_COMMAND = "withdrawnByApplicant";
private static final String CALCULATE_INTEREST_SAVINGS_COMMAND = "calculateInterest"; private static final String CALCULATE_INTEREST_SAVINGS_COMMAND = "calculateInterest";
private static final String POST_INTEREST_SAVINGS_COMMAND = "postInterest"; 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 CLOSE_SAVINGS_COMMAND = "close";
private static final String UPDATE_WITHHOLD_TAX_STATUS = "updateWithHoldTax"; private static final String UPDATE_WITHHOLD_TAX_STATUS = "updateWithHoldTax";
@@ -196,6 +197,12 @@ public class SavingsAccountHelper {
getCalculatedInterestForSavingsApplicationAsJSON(), ""); 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) { public Integer addChargesForSavings(final Integer savingsId, final Integer chargeId, boolean addDueDate) {
System.out.println("--------------------------------- ADD CHARGES FOR SAVINGS --------------------------------"); System.out.println("--------------------------------- ADD CHARGES FOR SAVINGS --------------------------------");
return (Integer) performSavingActions(SAVINGS_ACCOUNT_URL + "/" + savingsId + "/charges?" + Utils.TENANT_IDENTIFIER, return (Integer) performSavingActions(SAVINGS_ACCOUNT_URL + "/" + savingsId + "/charges?" + Utils.TENANT_IDENTIFIER,
@@ -287,6 +294,16 @@ public class SavingsAccountHelper {
return 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) { private String getSavingsPayChargeJSON(final String amount, final String dueDate) {
final HashMap<String, Object> map = new HashMap<>(); final HashMap<String, Object> map = new HashMap<>();
map.put("locale", CommonConstants.locale); map.put("locale", CommonConstants.locale);
@@ -321,6 +338,20 @@ public class SavingsAccountHelper {
map.put("closedOnDate", closedOnDate); map.put("closedOnDate", closedOnDate);
map.put("withdrawBalance", withdrawBalance); map.put("withdrawBalance", withdrawBalance);
map.put("note", "Close Test"); 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); String josn = new Gson().toJson(map);
return josn; return josn;
} }
@@ -342,6 +373,10 @@ public class SavingsAccountHelper {
return SAVINGS_ACCOUNT_URL + "/" + savingsID + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER; 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) { private String createChargesURL(final String command, final Integer savingsID, final Integer chargeId) {
return SAVINGS_ACCOUNT_URL + "/" + savingsID + "/charges/" + chargeId + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER; return SAVINGS_ACCOUNT_URL + "/" + savingsID + "/charges/" + chargeId + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
} }
@@ -419,6 +454,13 @@ public class SavingsAccountHelper {
getCloseAccountJSON(withdrawBalance, closedOnDate), jsonAttributeToGetBack); 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) { private String getPeriodChargeRequestJSON(Integer chargeId, boolean addDueDate) {
final HashMap<String, Object> map = new HashMap<>(); final HashMap<String, Object> map = new HashMap<>();
map.put("chargeId", chargeId); map.put("chargeId", chargeId);
@@ -2249,6 +2249,15 @@ public class CommandWrapperBuilder {
return this; 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) { public CommandWrapperBuilder createLoanRescheduleRequest(final String entityName) {
this.actionName = "CREATE"; this.actionName = "CREATE";
this.entityName = entityName; this.entityName = entityName;
@@ -34,6 +34,7 @@ public class SavingsApiConstants {
public static final String SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME = "savingsaccountcharge"; public static final String SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME = "savingsaccountcharge";
// actions // actions
public static final String postInterestValidationOnClosure = "postInterestValidationOnClosure";
public static String summitalAction = ".summital"; public static String summitalAction = ".summital";
public static String approvalAction = ".approval"; public static String approvalAction = ".approval";
public static String undoApprovalAction = ".undoApproval"; public static String undoApprovalAction = ".undoApproval";
@@ -245,12 +246,16 @@ public class SavingsApiConstants {
Arrays.asList(idParamName, "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName, Arrays.asList(idParamName, "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName,
runningBalanceParamName, reversedParamName)); 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, public static final Set<String> SAVINGS_ACCOUNT_ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, activatedOnDateParamName)); dateFormatParamName, activatedOnDateParamName));
public static final Set<String> SAVINGS_ACCOUNT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, public static final Set<String> SAVINGS_ACCOUNT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, closedOnDateParamName, noteParamName, paymentTypeIdParamName, withdrawBalanceParamName, 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, public static final Set<String> SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(chargeIdParamName,
savingsAccountChargeIdParamName, chargeNameParamName, penaltyParamName, chargeTimeTypeParamName, dueAsOfDateParamName, savingsAccountChargeIdParamName, chargeNameParamName, penaltyParamName, chargeTimeTypeParamName, dueAsOfDateParamName,
@@ -142,6 +142,9 @@ public class SavingsAccountTransactionsApiResource {
} else if (is(commandParam, "withdrawal")) { } else if (is(commandParam, "withdrawal")) {
final CommandWrapper commandRequest = builder.savingsAccountWithdrawal(savingsId).build(); final CommandWrapper commandRequest = builder.savingsAccountWithdrawal(savingsId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); 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) { if (result == null) {
@@ -49,6 +49,7 @@ public class SavingsAccountTransactionData {
private final boolean reversed; private final boolean reversed;
private final AccountTransferData transfer; private final AccountTransferData transfer;
private final LocalDate submittedOnDate; private final LocalDate submittedOnDate;
private final boolean interestedPostedAsOn;
// templates // templates
final Collection<PaymentTypeData> paymentTypeOptions; final Collection<PaymentTypeData> paymentTypeOptions;
@@ -56,19 +57,19 @@ public class SavingsAccountTransactionData {
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType, public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, 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 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; final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, 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, public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, 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 CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer,final LocalDate submittedOnDate) { final AccountTransferData transfer, final LocalDate submittedOnDate, final boolean interestedPostedAsOn) {
final Collection<PaymentTypeData> paymentTypeOptions = null; final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, 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, public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo,
@@ -80,8 +81,9 @@ public class SavingsAccountTransactionData {
final boolean reversed = false; final boolean reversed = false;
final PaymentDetailData paymentDetailData = null; final PaymentDetailData paymentDetailData = null;
final Collection<CodeValueData> paymentTypeOptions = null; final Collection<CodeValueData> paymentTypeOptions = null;
final boolean interestedPostedAsOn = false;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, defaultLocalDate, 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, public static SavingsAccountTransactionData templateOnTop(final SavingsAccountTransactionData savingsAccountTransactionData,
@@ -90,24 +92,23 @@ public class SavingsAccountTransactionData {
savingsAccountTransactionData.paymentDetailData, savingsAccountTransactionData.accountId, savingsAccountTransactionData.paymentDetailData, savingsAccountTransactionData.accountId,
savingsAccountTransactionData.accountNo, savingsAccountTransactionData.date, savingsAccountTransactionData.currency, savingsAccountTransactionData.accountNo, savingsAccountTransactionData.date, savingsAccountTransactionData.currency,
savingsAccountTransactionData.amount, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed, savingsAccountTransactionData.amount, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed,
savingsAccountTransactionData.transfer, paymentTypeOptions); savingsAccountTransactionData.transfer, paymentTypeOptions, savingsAccountTransactionData.interestedPostedAsOn);
} }
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType, private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, 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 CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions) { final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions, final boolean interestedPostedAsOn) {
this(id,transactionType,paymentDetailData,savingsId, savingsAccountNo,date,
currency,amount,runningBalance, reversed,
transfer, paymentTypeOptions,null);
this(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, amount, runningBalance, reversed,
transfer, paymentTypeOptions, null, interestedPostedAsOn);
} }
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType, private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, 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 CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed,
final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,final LocalDate submittedOnDate) { final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions, final LocalDate submittedOnDate,
final boolean interestedPostedAsOn) {
this.id = id; this.id = id;
this.transactionType = transactionType; this.transactionType = transactionType;
this.paymentDetailData = paymentDetailData; this.paymentDetailData = paymentDetailData;
@@ -121,6 +122,7 @@ public class SavingsAccountTransactionData {
this.transfer = transfer; this.transfer = transfer;
this.paymentTypeOptions = paymentTypeOptions; this.paymentTypeOptions = paymentTypeOptions;
this.submittedOnDate = submittedOnDate; this.submittedOnDate = submittedOnDate;
this.interestedPostedAsOn = interestedPostedAsOn;
} }
public static SavingsAccountTransactionData withWithDrawalTransactionDetails( public static SavingsAccountTransactionData withWithDrawalTransactionDetails(
@@ -135,6 +137,6 @@ public class SavingsAccountTransactionData {
savingsAccountTransactionData.accountNo, currentDate, savingsAccountTransactionData.currency, savingsAccountTransactionData.accountNo, currentDate, savingsAccountTransactionData.currency,
savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.runningBalance,
savingsAccountTransactionData.reversed, savingsAccountTransactionData.transfer, 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, public void updateMaturityDateAndAmountBeforeAccountActivation(final MathContext mc, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
List<SavingsAccountTransaction> allTransactions = new ArrayList<>(); List<SavingsAccountTransaction> allTransactions = new ArrayList<>();
final boolean postInterestAsOn = false;
final Money transactionAmountMoney = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount()); final Money transactionAmountMoney = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount());
final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null, final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null,
this.accountSubmittedOrActivationDate(), transactionAmountMoney, new Date(), null); // TODO: this.accountSubmittedOrActivationDate(), transactionAmountMoney, new Date(), null, postInterestAsOn); // TODO:
// verify // verify
// if // if
// it // it
@@ -297,9 +298,11 @@ public class FixedDepositAccount extends SavingsAccount {
final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType
.fromInt(this.interestCalculationDaysInYearType); .fromInt(this.interestCalculationDaysInYearType);
List<LocalDate> postedAsOnTransactionDates= getManualPostingDates();
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods( final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(
accountSubmittedOrActivationDate(), maturityDate, postingPeriodType, financialYearBeginningMonth); accountSubmittedOrActivationDate(), maturityDate, postingPeriodType, financialYearBeginningMonth,
postedAsOnTransactionDates);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>(); final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -311,11 +314,14 @@ public class FixedDepositAccount extends SavingsAccount {
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
final Money minBalanceForInterestCalculation = Money.of(getCurrency(), minBalanceForInterestCalculation()); final Money minBalanceForInterestCalculation = Money.of(getCurrency(), minBalanceForInterestCalculation());
for (final LocalDateInterval periodInterval : postingPeriodIntervals) { for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
boolean isUserPosting = false;
if (postedAsOnTransactionDates.contains(periodInterval.endDate())) {
isUserPosting = true;
}
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions, final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions,
this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(), this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation, maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
isSavingsInterestPostingAtCurrentPeriodEnd); isSavingsInterestPostingAtCurrentPeriodEnd, isUserPosting);
periodStartingBalance = postingPeriod.closingBalance(); periodStartingBalance = postingPeriod.closingBalance();
@@ -490,8 +496,9 @@ public class FixedDepositAccount extends SavingsAccount {
final LocalDate interestPostingUpToDate = maturityDate(); final LocalDate interestPostingUpToDate = maturityDate();
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
final boolean isInterestTransfer = false; final boolean isInterestTransfer = false;
final boolean postInterestAsOn = false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestAsOn);
Money interestPostedToDate = Money.zero(this.currency); Money interestPostedToDate = Money.zero(this.currency);
@@ -511,7 +518,7 @@ public class FixedDepositAccount extends SavingsAccount {
final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate);
if (postingTransaction == null) { if (postingTransaction == null) {
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod); interestPostingTransactionDate, interestEarnedToBePostedForPeriod, postInterestAsOn);
this.transactions.add(newPostingTransaction); this.transactions.add(newPostingTransaction);
recalucateDailyBalanceDetails = true; recalucateDailyBalanceDetails = true;
} else { } else {
@@ -519,7 +526,7 @@ public class FixedDepositAccount extends SavingsAccount {
if (correctionRequired) { if (correctionRequired) {
postingTransaction.reverse(); postingTransaction.reverse();
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod); interestPostingTransactionDate, interestEarnedToBePostedForPeriod, postInterestAsOn);
this.transactions.add(newPostingTransaction); this.transactions.add(newPostingTransaction);
recalucateDailyBalanceDetails = true; recalucateDailyBalanceDetails = true;
} }
@@ -549,8 +556,9 @@ public class FixedDepositAccount extends SavingsAccount {
// post remaining interest // post remaining interest
final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate); final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate);
if (!remainigInterestToBePosted.isZero()) { if (!remainigInterestToBePosted.isZero()) {
final boolean postInterestAsOn = false;
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
accountCloseDate, remainigInterestToBePosted); accountCloseDate, remainigInterestToBePosted, postInterestAsOn);
this.transactions.add(newPostingTransaction); this.transactions.add(newPostingTransaction);
recalucateDailyBalance = true; recalucateDailyBalance = true;
} }
@@ -600,18 +608,21 @@ public class FixedDepositAccount extends SavingsAccount {
@Override @Override
public void postInterest(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer, public void postInterest(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
final boolean postAsInterestOn) {
final boolean postInterestAsOn=false;
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, super.postInterest(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
@Override @Override
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer, public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
final boolean postAsInterestOn) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postAsInterestOn);
} }
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) { private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
@@ -331,9 +331,9 @@ public class RecurringDepositAccount extends SavingsAccount {
final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType
.fromInt(this.interestCalculationDaysInYearType); .fromInt(this.interestCalculationDaysInYearType);
List<LocalDate> PostedAsOnDates = getManualPostingDates();
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(depositStartDate(), final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(depositStartDate(),
maturityDate, postingPeriodType, financialYearBeginningMonth); maturityDate, postingPeriodType, financialYearBeginningMonth, PostedAsOnDates);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>(); final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -345,10 +345,14 @@ public class RecurringDepositAccount extends SavingsAccount {
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
final Money minBalanceForInterestCalculation = Money.of(getCurrency(), minBalanceForInterestCalculation()); final Money minBalanceForInterestCalculation = Money.of(getCurrency(), minBalanceForInterestCalculation());
for (final LocalDateInterval periodInterval : postingPeriodIntervals) { for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
boolean isUserPosting = false;
if (PostedAsOnDates.contains(periodInterval.endDate())) {
isUserPosting = true;
}
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions, final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions,
this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(), this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation, maturityDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
isSavingsInterestPostingAtCurrentPeriodEnd); isSavingsInterestPostingAtCurrentPeriodEnd, isUserPosting);
periodStartingBalance = postingPeriod.closingBalance(); periodStartingBalance = postingPeriod.closingBalance();
@@ -369,9 +373,10 @@ public class RecurringDepositAccount extends SavingsAccount {
if (generateFutureTransactions) { if (generateFutureTransactions) {
for (RecurringDepositScheduleInstallment installment : depositScheduleInstallments()) { for (RecurringDepositScheduleInstallment installment : depositScheduleInstallments()) {
if (installment.isPrincipalNotCompleted(getCurrency())) { if (installment.isPrincipalNotCompleted(getCurrency())) {
boolean postInterestAsOn = false;
final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null, final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null,
installment.dueDate(), installment.getDepositAmountOutstanding(getCurrency()), installment.dueDate().toDate(), installment.dueDate(), installment.getDepositAmountOutstanding(getCurrency()), installment.dueDate().toDate(),
null); null, postInterestAsOn);
allTransactions.add(transaction); allTransactions.add(transaction);
} }
} }
@@ -598,8 +603,9 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn =false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate.minusDays(1), isInterestTransfer, final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate.minusDays(1), isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestAsOn);
Money interestPostedToDate = Money.zero(this.currency); Money interestPostedToDate = Money.zero(this.currency);
@@ -617,7 +623,7 @@ public class RecurringDepositAccount extends SavingsAccount {
final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate);
if (postingTransaction == null) { if (postingTransaction == null) {
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod); interestPostingTransactionDate, interestEarnedToBePostedForPeriod, postInterestAsOn);
addTransaction(newPostingTransaction); addTransaction(newPostingTransaction);
recalucateDailyBalanceDetails = true; recalucateDailyBalanceDetails = true;
} else { } else {
@@ -625,7 +631,7 @@ public class RecurringDepositAccount extends SavingsAccount {
if (correctionRequired) { if (correctionRequired) {
postingTransaction.reverse(); postingTransaction.reverse();
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod); interestPostingTransactionDate, interestEarnedToBePostedForPeriod, postInterestAsOn);
addTransaction(newPostingTransaction); addTransaction(newPostingTransaction);
recalucateDailyBalanceDetails = true; recalucateDailyBalanceDetails = true;
} }
@@ -656,8 +662,9 @@ public class RecurringDepositAccount extends SavingsAccount {
// post remaining interest // post remaining interest
final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate); final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate);
if (!remainigInterestToBePosted.isZero()) { if (!remainigInterestToBePosted.isZero()) {
final boolean postInterestAsOn = false;
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
accountCloseDate, remainigInterestToBePosted); accountCloseDate, remainigInterestToBePosted, postInterestAsOn);
addTransaction(newPostingTransaction); addTransaction(newPostingTransaction);
recalucateDailyBalance = true; recalucateDailyBalance = true;
} }
@@ -707,18 +714,18 @@ public class RecurringDepositAccount extends SavingsAccount {
@Override @Override
public void postInterest(final MathContext mc, final LocalDate postingDate, final boolean isInterestTransfer, public void postInterest(final MathContext mc, final LocalDate postingDate, final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,final boolean postInterestAson) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, super.postInterest(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAson);
} }
@Override @Override
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer, public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,final boolean postAsInterestOn) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postAsInterestOn);
} }
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) { private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
@@ -235,6 +235,7 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
@Column(name = "interest_calculation_type_enum", nullable = false) @Column(name = "interest_calculation_type_enum", nullable = false)
protected Integer interestCalculationType; protected Integer interestCalculationType;
/** /**
* A value from the {@link SavingsInterestCalculationDaysInYearType} * A value from the {@link SavingsInterestCalculationDaysInYearType}
* enumeration. * enumeration.
@@ -462,13 +463,10 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
public boolean isClosed() { public boolean isClosed() {
return SavingsAccountStatusType.fromInt(this.status).isClosed(); return SavingsAccountStatusType.fromInt(this.status).isClosed();
} }
public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer, public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,final boolean postInterestAsOn) {
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestAsOn);
Money interestPostedToDate = Money.zero(this.currency); Money interestPostedToDate = Money.zero(this.currency);
boolean recalucateDailyBalanceDetails = false; boolean recalucateDailyBalanceDetails = false;
@@ -482,18 +480,18 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned(); final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned();
if (!interestPostingTransactionDate.isAfter(interestPostingUpToDate)) { if (!interestPostingTransactionDate.isAfter(interestPostingUpToDate)) {
interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod); interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod);
final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate);
if (postingTransaction == null) { if (postingTransaction == null) {
SavingsAccountTransaction newPostingTransaction; SavingsAccountTransaction newPostingTransaction;
if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) { if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) {
newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), interestPostingTransactionDate, newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), interestPostingTransactionDate,
interestEarnedToBePostedForPeriod); interestEarnedToBePostedForPeriod, interestPostingPeriod.isUserPosting());
} else { } else {
newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), interestPostingTransactionDate, newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), interestPostingTransactionDate,
interestEarnedToBePostedForPeriod.negated()); interestEarnedToBePostedForPeriod.negated(), interestPostingPeriod.isUserPosting());
} }
addTransaction(newPostingTransaction); addTransaction(newPostingTransaction);
if (applyWithHoldTax) { if (applyWithHoldTax) {
@@ -519,10 +517,12 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
SavingsAccountTransaction newPostingTransaction; SavingsAccountTransaction newPostingTransaction;
if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) { if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) {
newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod); interestPostingTransactionDate, interestEarnedToBePostedForPeriod,
interestPostingPeriod.isUserPosting());
} else { } else {
newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated()); interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated(),
interestPostingPeriod.isUserPosting());
} }
addTransaction(newPostingTransaction); addTransaction(newPostingTransaction);
if (applyWithHoldTaxForOldTransaction) { if (applyWithHoldTaxForOldTransaction) {
@@ -641,6 +641,16 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
return savingsTransaction; 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. * All interest calculation based on END-OF-DAY-BALANCE.
* *
@@ -658,8 +668,9 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
* @param isInterestTransfer * @param isInterestTransfer
* TODO * TODO
*/ */
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate, 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 // no openingBalance concept supported yet but probably will to allow
// for migrations. // for migrations.
@@ -681,9 +692,13 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType 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( final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(
getStartInterestCalculationDate(), upToInterestCalculationDate, postingPeriodType, financialYearBeginningMonth); getStartInterestCalculationDate(), upToInterestCalculationDate, postingPeriodType, financialYearBeginningMonth,
postedAsOnDates);
final List<PostingPeriod> allPostingPeriods = new ArrayList<>(); final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
@@ -720,11 +735,16 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
for (final LocalDateInterval periodInterval : postingPeriodIntervals) { for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
boolean isUserPosting = false;
if(postedAsOnDates.contains(periodInterval.endDate().plusDays(1))){
isUserPosting = true;
}
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance,
retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType, retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType,
interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate, interestPostTransactions, interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate, interestPostTransactions,
isInterestTransfer, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd, isInterestTransfer, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd,
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); overdraftInterestRateAsFraction, minOverdraftForInterestCalculation, isUserPosting);
periodStartingBalance = postingPeriod.closingBalance(); periodStartingBalance = postingPeriod.closingBalance();
@@ -2133,13 +2153,15 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
if (isSavingsChargeApplied) { if (isSavingsChargeApplied) {
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOnDate = false;
if (this.isBeforeLastPostingPeriod(getActivationLocalDate())) { if (this.isBeforeLastPostingPeriod(getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
this.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); this.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOnDate);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
this.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, 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.sub_status = SavingsAccountSubStatusEnum.ESCHEAT.getValue();
this.closedOnDate = DateUtils.getDateOfTenant(); this.closedOnDate = DateUtils.getDateOfTenant();
this.closedBy = appUser; this.closedBy = appUser;
boolean postInterestAsOnDate = false;
LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
if(this.getSummary().getAccountBalance(this.getCurrency()).isGreaterThanZero()){ 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); this.transactions.add(transaction);
} }
recalculateDailyBalances(Money.zero(this.currency), transactionDate); recalculateDailyBalances(Money.zero(this.currency), transactionDate);
@@ -86,6 +86,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
if (transactionBooleanValues.isRegularTransaction() && !account.allowWithdrawal()) { throw new DepositAccountTransactionNotAllowedException( if (transactionBooleanValues.isRegularTransaction() && !account.allowWithdrawal()) { throw new DepositAccountTransactionNotAllowedException(
account.getId(), "withdraw", account.depositAccountType()); } account.getId(), "withdraw", account.depositAccountType()); }
final Set<Long> existingTransactionIds = new HashSet<>(); final Set<Long> existingTransactionIds = new HashSet<>();
final boolean interestPostAsOn = false;
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
@@ -96,11 +97,11 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
if (account.isBeforeLastPostingPeriod(transactionDate)) { if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer(), isSavingsInterestPostingAtCurrentPeriodEnd, account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer(), isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, interestPostAsOn);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer(), account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer(),
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, interestPostAsOn);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) { if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -154,15 +155,16 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, new Date(), user); paymentDetail, new Date(), user);
final SavingsAccountTransaction deposit = account.deposit(transactionDTO, savingsAccountTransactionType); final SavingsAccountTransaction deposit = account.deposit(transactionDTO, savingsAccountTransactionType);
final boolean interestPostAsOn = false;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) { if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
interestPostAsOn);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, interestPostAsOn);
} }
saveTransactionToGenerateTransactionId(deposit); saveTransactionToGenerateTransactionId(deposit);
@@ -115,6 +115,9 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
@JoinColumn(name = "appuser_id", nullable = true) @JoinColumn(name = "appuser_id", nullable = true)
private AppUser appUser; private AppUser appUser;
@Column(name = "is_manual", length = 1, nullable = true)
private boolean isManualTransaction;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.EAGER) @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.EAGER)
@JoinColumn(name = "savings_transaction_id", referencedColumnName = "id", nullable = false) @JoinColumn(name = "savings_transaction_id", referencedColumnName = "id", nullable = false)
private List<SavingsAccountTransactionTaxDetails> taxDetails = new ArrayList<>(); private List<SavingsAccountTransactionTaxDetails> taxDetails = new ArrayList<>();
@@ -126,112 +129,123 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
} }
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office, 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; final boolean isReversed = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date, 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, 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 SavingsAccountTransactionType savingsAccountTransactionType) { final SavingsAccountTransactionType savingsAccountTransactionType) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date, 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, 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 PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(), 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, 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; final boolean isReversed = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date, 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, 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; final boolean isReversed = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date, 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, public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount, 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, public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount, 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, public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount, 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, public static SavingsAccountTransaction waiver(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount, 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, public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final AppUser appUser) { final LocalDate date, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, new Date(), savingsAccount.getSummary() 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, public static SavingsAccountTransaction approveTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final AppUser appUser) { final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, new Date(), savingsAccount.getSummary() 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, public static SavingsAccountTransaction withdrawTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final AppUser appUser) { final LocalDate date, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, new Date(), savingsAccount.getSummary() 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, public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) { final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false;
SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office, 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); updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction; return accountTransaction;
} }
public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date, public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date,
final AppUser appUser) { final AppUser appUser,final boolean accountTransaction) {
final boolean isReversed = false; final boolean isReversed = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail, return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail,
SavingsAccountTransactionType.ESCHEAT.getValue(), date, new Date(), savingsAccount.getSummary() 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) { 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) { public static SavingsAccountTransaction copyTransaction(SavingsAccountTransaction accountTransaction) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office,
accountTransaction.paymentDetail, accountTransaction.typeOf, accountTransaction.transactionLocalDate(), 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, private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final Integer typeOf,
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final AppUser 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); this(savingsAccount, office, null, typeOf, transactionLocalDate, new Date(), amount, isReversed, appUser, isManualTransaction);
} }
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail, private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final Money amount, final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final Money amount,
final boolean isReversed, final AppUser appUser) { final boolean isReversed, final AppUser appUser, final boolean isManualTransaction) {
this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, createdDate, amount.getAmount(), isReversed, appUser); this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, createdDate, amount.getAmount(), isReversed, appUser,
isManualTransaction);
} }
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail, private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final BigDecimal amount, 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.savingsAccount = savingsAccount;
this.office = office; this.office = office;
this.typeOf = typeOf; this.typeOf = typeOf;
@@ -271,6 +287,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
this.paymentDetail = paymentDetail; this.paymentDetail = paymentDetail;
this.createdDate = createdDate; this.createdDate = createdDate;
this.appUser = appUser; this.appUser = appUser;
this.isManualTransaction = isManualTransaction;
} }
public LocalDate transactionLocalDate() { 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(); this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
} }
private LocalDate getTransactionLocalDate() { public LocalDate getTransactionLocalDate() {
return new LocalDate(this.dateOf); return new LocalDate(this.dateOf);
} }
@@ -471,6 +488,14 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
return getTransactionLocalDate().isAfter(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) { public EndOfDayBalance toEndOfDayBalance(final LocalDateInterval periodInterval, final MonetaryCurrency currency) {
final Money endOfDayBalance = Money.of(currency, this.runningBalance); final Money endOfDayBalance = Money.of(currency, this.runningBalance);
@@ -705,4 +730,21 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
this.amount = amount.getAmount(); 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;
}
} }
@@ -44,30 +44,49 @@ public final class SavingsHelper {
public List<LocalDateInterval> determineInterestPostingPeriods(final LocalDate startInterestCalculationLocalDate, public List<LocalDateInterval> determineInterestPostingPeriods(final LocalDate startInterestCalculationLocalDate,
final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType, final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType,
final Integer financialYearBeginningMonth) { final Integer financialYearBeginningMonth,List<LocalDate> postInterestAsOn) {
final List<LocalDateInterval> postingPeriods = new ArrayList<>(); final List<LocalDateInterval> postingPeriods = new ArrayList<>();
LocalDate periodStartDate = startInterestCalculationLocalDate; LocalDate periodStartDate = startInterestCalculationLocalDate;
LocalDate periodEndDate = periodStartDate; LocalDate periodEndDate = periodStartDate;
LocalDate actualPeriodStartDate = periodStartDate;
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) { while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType, final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType,
interestPostingUpToDate, financialYearBeginningMonth); interestPostingUpToDate, financialYearBeginningMonth);
periodEndDate = interestPostingLocalDate.minusDays(1); 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)); postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
if (actualPeriodStartDate.isEqual(periodEndDate))
{
periodEndDate = actualPeriodStartDate.plusDays(1);
periodStartDate = actualPeriodStartDate.plusDays(1);
}else{
periodEndDate = interestPostingLocalDate; periodEndDate = interestPostingLocalDate;
periodStartDate = interestPostingLocalDate; periodStartDate = interestPostingLocalDate;
}
} }
return postingPeriods; return postingPeriods;
} }
private LocalDate determineInterestPostingPeriodEndDateFrom(final LocalDate periodStartDate, private LocalDate determineInterestPostingPeriodEndDateFrom(final LocalDate periodStartDate,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType interestPostingPeriodType, final LocalDate interestPostingUpToDate,
Integer financialYearBeginningMonth) { Integer financialYearBeginningMonth) {
LocalDate periodEndDate = interestPostingUpToDate; LocalDate periodEndDate = interestPostingUpToDate;
@@ -90,7 +109,6 @@ public final class SavingsHelper {
biannualDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).plusMonths(6).withYear(periodStartDate.getYear()) biannualDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).plusMonths(6).withYear(periodStartDate.getYear())
.dayOfMonth().withMaximumValue()); .dayOfMonth().withMaximumValue());
Collections.sort(biannualDates); Collections.sort(biannualDates);
boolean isEndDateSet = false; boolean isEndDateSet = false;
switch (interestPostingPeriodType) { switch (interestPostingPeriodType) {
@@ -132,10 +150,8 @@ public final class SavingsHelper {
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue(); periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break; break;
} }
// interest posting always occurs on next day after the period end date. // interest posting always occurs on next day after the period end date.
periodEndDate = periodEndDate.plusDays(1); periodEndDate = periodEndDate.plusDays(1);
return periodEndDate; return periodEndDate;
} }
@@ -54,6 +54,7 @@ public class PostingPeriod {
// include in compounding interest // include in compounding interest
private boolean interestTransfered = false; private boolean interestTransfered = false;
private boolean isUserPosting = false;
// minimum balance for interest calculation // minimum balance for interest calculation
private final Money minBalanceForInterestCalculation; private final Money minBalanceForInterestCalculation;
@@ -65,7 +66,7 @@ public class PostingPeriod {
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear, final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final LocalDate upToInterestCalculationDate, Collection<Long> interestPostTransactions, boolean isInterestTransfer, 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 BigDecimal overdraftInterestRateAsFraction = BigDecimal.ZERO;
final Money minOverdraftForInterestCalculation = Money.zero(currency); final Money minOverdraftForInterestCalculation = Money.zero(currency);
@@ -74,7 +75,7 @@ public class PostingPeriod {
interestCompoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYear, interestCompoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYear,
upToInterestCalculationDate, interestPostTransactions, isInterestTransfer, upToInterestCalculationDate, interestPostTransactions, isInterestTransfer,
minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd,
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); overdraftInterestRateAsFraction, minOverdraftForInterestCalculation, isUserPosting);
} }
// isInterestTransfer boolean is to identify newly created transaction is // 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 SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final LocalDate upToInterestCalculationDate, Collection<Long> interestPostTransactions, boolean isInterestTransfer, final LocalDate upToInterestCalculationDate, Collection<Long> interestPostTransactions, boolean isInterestTransfer,
final Money minBalanceForInterestCalculation, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, 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<>(); final List<EndOfDayBalance> accountEndOfDayBalances = new ArrayList<>();
boolean interestTransfered = false; boolean interestTransfered = false;
@@ -147,7 +148,7 @@ public class PostingPeriod {
return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType, return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType,
interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods, interestTransfered, interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods, interestTransfered,
minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd,
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); overdraftInterestRateAsFraction, minOverdraftForInterestCalculation, isUserPosting);
} }
private PostingPeriod(final LocalDateInterval periodInterval, final MonetaryCurrency currency, final Money openingBalance, 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 SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final List<CompoundingPeriod> compoundingPeriods, boolean interestTransfered, final Money minBalanceForInterestCalculation, final List<CompoundingPeriod> compoundingPeriods, boolean interestTransfered, final Money minBalanceForInterestCalculation,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final BigDecimal overdraftInterestRateAsFraction, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final BigDecimal overdraftInterestRateAsFraction,
final Money minOverdraftForInterestCalculation) { final Money minOverdraftForInterestCalculation, boolean isUserPosting) {
this.periodInterval = periodInterval; this.periodInterval = periodInterval;
this.currency = currency; this.currency = currency;
this.openingBalance = openingBalance; this.openingBalance = openingBalance;
@@ -174,6 +175,7 @@ public class PostingPeriod {
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.overdraftInterestRateAsFraction = overdraftInterestRateAsFraction; this.overdraftInterestRateAsFraction = overdraftInterestRateAsFraction;
this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation; this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation;
this.isUserPosting = isUserPosting;
} }
public Money interest() { public Money interest() {
@@ -410,4 +412,12 @@ public class PostingPeriod {
public boolean isInterestTransfered() { public boolean isInterestTransfered() {
return this.interestTransfered; 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 @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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, transfer = AccountTransferData.transferBasicDetails(toTransferId, currency, toTransferAmount, toTransferDate,
toTransferDescription, toTransferReversed); toTransferDescription, toTransferReversed);
} }
final boolean postInterestAsOn = false;
return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency, 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 PaymentDetailData paymentDetailData = null;
final AccountTransferData transfer = null; final AccountTransferData transfer = null;
final BigDecimal runningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "runningBalance"); final BigDecimal runningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "runningBalance");
; final boolean postInterestAsOn = false;
return SavingsAccountTransactionData.create(savingsId, transactionType, paymentDetailData, savingsId, accountNo, duedate, 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 WorkingDaysRepositoryWrapper workingDaysRepository;
private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository; private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
@Autowired @Autowired
public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final SavingsAccountRepositoryWrapper savingAccountRepositoryWrapper, final SavingsAccountRepositoryWrapper savingAccountRepositoryWrapper,
@@ -185,7 +184,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.depositAccountReadPlatformService = depositAccountReadPlatformService; this.depositAccountReadPlatformService = depositAccountReadPlatformService;
this.calendarInstanceRepository = calendarInstanceRepository; this.calendarInstanceRepository = calendarInstanceRepository;
this.configurationDomainService = configurationDomainService; this.configurationDomainService = configurationDomainService;
this.depositAccountOnHoldTransactionRepository =depositAccountOnHoldTransactionRepository; this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository;
} }
@Transactional @Transactional
@@ -235,14 +234,15 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
} }
final boolean isInterestTransfer = false; final boolean isInterestTransfer = false;
final boolean postInterestAsOn = false;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) { if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
@@ -252,10 +252,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth);
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){ if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account); depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
} }
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions); account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),
depositAccountOnHoldTransactions);
this.savingAccountRepositoryWrapper.saveAndFlush(account); this.savingAccountRepositoryWrapper.saveAndFlush(account);
} }
@@ -338,21 +340,24 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant(); final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant();
account.updateOverduePayments(overdueUptoDate); account.updateOverduePayments(overdueUptoDate);
final boolean isInterestTransfer = false; final boolean isInterestTransfer = false;
final boolean postInterestAsOn = false;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) { if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){ if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account); depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
} }
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions); account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),
depositAccountOnHoldTransactions);
this.savingAccountRepositoryWrapper.saveAndFlush(account); this.savingAccountRepositoryWrapper.saveAndFlush(account);
} }
@@ -391,8 +396,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate depositAmountUpdateEffectiveFromDate = command final LocalDate depositAmountUpdateEffectiveFromDate = command
.localDateValueOfParameterNamed(DepositsApiConstants.effectiveDateParamName); .localDateValueOfParameterNamed(DepositsApiConstants.effectiveDateParamName);
final RecurringDepositAccount recurringDepositAccount = (RecurringDepositAccount) this.depositAccountAssembler final RecurringDepositAccount recurringDepositAccount = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(
.assembleFrom(savingsId, DepositAccountType.RECURRING_DEPOSIT); savingsId, DepositAccountType.RECURRING_DEPOSIT);
DepositAccountRecurringDetail recurringDetail = recurringDepositAccount.getRecurringDetail(); DepositAccountRecurringDetail recurringDetail = recurringDepositAccount.getRecurringDetail();
Map<String, Object> changes = recurringDetail.updateMandatoryRecommendedDepositAmount(mandatoryRecommendedDepositAmount, Map<String, Object> changes = recurringDetail.updateMandatoryRecommendedDepositAmount(mandatoryRecommendedDepositAmount,
depositAmountUpdateEffectiveFromDate, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); depositAmountUpdateEffectiveFromDate, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
@@ -446,8 +451,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional @Transactional
@Override @Override
public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command, public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command, final DepositAccountType depositAccountType) {
final DepositAccountType depositAccountType) {
boolean isRegularTransaction = true; boolean isRegularTransaction = true;
@@ -493,9 +497,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
this.savingAccountRepositoryWrapper.save(account); this.savingAccountRepositoryWrapper.save(account);
@@ -537,7 +541,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false; 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); this.savingAccountRepositoryWrapper.saveAndFlush(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -588,11 +594,10 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.findOneByIdAndSavingsAccountId(transactionId, savingsId); .findOneByIdAndSavingsAccountId(transactionId, savingsId);
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!allowAccountTransferModification && this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, if (!allowAccountTransferModification
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException( && this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.recurring.deposit.account.transfer.transaction.update.not.allowed", "error.msg.recurring.deposit.account.transfer.transaction.update.not.allowed", "Recurring deposit account transaction:"
"Recurring deposit account transaction:" + transactionId + " update not allowed as it involves in account transfer", + transactionId + " update not allowed as it involves in account transfer", transactionId); }
transactionId); }
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
@@ -602,24 +607,26 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
} }
account.undoTransaction(transactionId); account.undoTransaction(transactionId);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired() if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else { } else {
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){ if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account); depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
} }
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction,depositAccountOnHoldTransactions); account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction, depositAccountOnHoldTransactions);
// account.activateAccountBasedOnBalance(); // account.activateAccountBasedOnBalance();
final boolean isPreMatureClosure = false; final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
financialYearBeginningMonth);
final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant(); final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant();
@@ -663,14 +670,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.findOneByIdAndSavingsAccountId(transactionId, savingsId); .findOneByIdAndSavingsAccountId(transactionId, savingsId);
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(
|| savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(savingsId, transactionId); } savingsId, transactionId); }
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException( "error.msg.saving.account.transfer.transaction.update.not.allowed", "Deposit account transaction:" + transactionId
"error.msg.saving.account.transfer.transaction.update.not.allowed", + " update not allowed as it involves in account transfer", transactionId); }
"Deposit account transaction:" + transactionId + " update not allowed as it involves in account transfer",
transactionId); }
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
@@ -705,19 +710,22 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
} }
final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction); final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
final boolean postInterestAsOn = false;
if (account.isBeforeLastPostingPeriod(transactionDate) if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { || account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else { } else {
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){ if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account); depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
} }
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction,depositAccountOnHoldTransactions); account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction, depositAccountOnHoldTransactions);
account.activateAccountBasedOnBalance(); account.activateAccountBasedOnBalance();
if (savingsAccountTransaction.isDeposit()) { if (savingsAccountTransaction.isDeposit()) {
@@ -913,7 +921,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType); final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(accountId, depositAccountType);
final boolean postInterestAsOn = false;
final Set<Long> existingTransactionIds = new HashSet<>(); final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
@@ -925,7 +933,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(newTransferTransaction); this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount); this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -957,8 +965,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction); this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount); this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -999,9 +1008,10 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
savingsAccount.reassignSavingsOfficer(fieldOfficer, transferDate); savingsAccount.reassignSavingsOfficer(fieldOfficer, transferDate);
} }
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(acceptTransferTransaction); this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount); this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -1131,8 +1141,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId); savingsAccountChargeId, savingsAccountId);
// Get Savings account from savings charge // Get Savings account from savings charge
final SavingsAccount account = savingsAccountCharge.savingsAccount(); final SavingsAccount account = savingsAccountCharge.savingsAccount();
@@ -1144,21 +1154,25 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.waiveCharge(savingsAccountChargeId, user); account.waiveCharge(savingsAccountChargeId, user);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){ if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account); depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
} }
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,depositAccountOnHoldTransactions); account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,
depositAccountOnHoldTransactions);
this.savingAccountRepositoryWrapper.saveAndFlush(account); this.savingAccountRepositoryWrapper.saveAndFlush(account);
@@ -1181,8 +1195,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsAccountId, depositAccountType); final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsAccountId, depositAccountType);
checkClientOrGroupActive(savingsAccount); checkClientOrGroupActive(savingsAccount);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId); savingsAccountChargeId, savingsAccountId);
savingsAccount.removeCharge(savingsAccountCharge); savingsAccount.removeCharge(savingsAccountCharge);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount); this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -1208,8 +1222,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(amountParamName); final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(amountParamName);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName); final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId); savingsAccountChargeId, savingsAccountId);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
@@ -1247,8 +1261,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@SuppressWarnings("unused") final DepositAccountType depositAccountType) { @SuppressWarnings("unused") final DepositAccountType depositAccountType) {
// always use current date as transaction date for batch job // always use current date as transaction date for batch job
final LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId); savingsAccountChargeId, accountId);
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy"); final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
@@ -1275,21 +1289,25 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, user); account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, user);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) { if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){ if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account); depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
} }
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode(),depositAccountOnHoldTransactions); account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode(),
depositAccountOnHoldTransactions);
this.savingAccountRepositoryWrapper.saveAndFlush(account); this.savingAccountRepositoryWrapper.saveAndFlush(account);
@@ -1340,8 +1358,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override @Override
public SavingsAccountTransaction mandatorySavingsAccountDeposit(final SavingsAccountTransactionDTO accountTransactionDTO) { public SavingsAccountTransaction mandatorySavingsAccountDeposit(final SavingsAccountTransactionDTO accountTransactionDTO) {
boolean isRegularTransaction = false; boolean isRegularTransaction = false;
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(
.assembleFrom(accountTransactionDTO.getSavingsAccountId(), DepositAccountType.RECURRING_DEPOSIT); accountTransactionDTO.getSavingsAccountId(), DepositAccountType.RECURRING_DEPOSIT);
final PaymentDetail paymentDetail = accountTransactionDTO.getPaymentDetail(); final PaymentDetail paymentDetail = accountTransactionDTO.getPaymentDetail();
if (paymentDetail != null && paymentDetail.getId() == null) { if (paymentDetail != null && paymentDetail.getId() == null) {
this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail); 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, "); .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.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); 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("from m_savings_account sa ");
sqlBuilder.append("join m_savings_account_transaction tr on tr.savings_account_id = sa.id "); 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 "); 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 Long savingsId = rs.getLong("savingsId");
final String accountNo = rs.getString("accountNo"); final String accountNo = rs.getString("accountNo");
final boolean postInterestAsOn = rs.getBoolean("postInterestAsOn");
PaymentDetailData paymentDetailData = null; PaymentDetailData paymentDetailData = null;
if (transactionType.isDepositOrWithdrawal()) { if (transactionType.isDepositOrWithdrawal()) {
@@ -840,12 +842,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final boolean toTransferReversed = rs.getBoolean("toTransferReversed"); final boolean toTransferReversed = rs.getBoolean("toTransferReversed");
final String toTransferDescription = rs.getString("toTransferDescription"); final String toTransferDescription = rs.getString("toTransferDescription");
transfer = AccountTransferData.transferBasicDetails(toTransferId, currency, toTransferAmount, toTransferDate, transfer = AccountTransferData.transferBasicDetails(toTransferId, currency, toTransferAmount, toTransferDate,
toTransferDescription, toTransferReversed); toTransferDescription, toTransferReversed);
} }
return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency, 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 calculateInterest(Long savingsId);
CommandProcessingResult postInterest(Long savingsId);
CommandProcessingResult undoTransaction(Long savingsId, Long transactionId, boolean allowAccountTransferModification); CommandProcessingResult undoTransaction(Long savingsId, Long transactionId, boolean allowAccountTransferModification);
CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command); CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command);
@@ -78,7 +76,6 @@ public interface SavingsAccountWritePlatformService {
void processPostActiveActions(SavingsAccount account, DateTimeFormatter fmt, Set<Long> existingTransactionIds, void processPostActiveActions(SavingsAccount account, DateTimeFormatter fmt, Set<Long> existingTransactionIds,
Set<Long> existingReversedTransactionIds); Set<Long> existingReversedTransactionIds);
void postInterest(SavingsAccount account);
CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, JsonCommand command); CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, JsonCommand command);
@@ -87,4 +84,8 @@ public interface SavingsAccountWritePlatformService {
void setSubStatusDormant(Long savingsId); void setSubStatusDormant(Long savingsId);
void escheat(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 java.util.Set;
import org.apache.commons.lang.StringUtils; 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.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand; 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.SavingsAccountStatusType;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction; import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository; 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.SavingsAccountClosingNotAllowedException;
import org.apache.fineract.portfolio.savings.exception.SavingsAccountTransactionNotFoundException; import org.apache.fineract.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
import org.apache.fineract.portfolio.savings.exception.SavingsOfficerAssignmentException; import org.apache.fineract.portfolio.savings.exception.SavingsOfficerAssignmentException;
import org.apache.fineract.portfolio.savings.exception.SavingsOfficerUnassignmentException; import org.apache.fineract.portfolio.savings.exception.SavingsOfficerUnassignmentException;
import org.apache.fineract.portfolio.savings.exception.TransactionUpdateNotAllowedException; 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.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper; import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
@@ -255,7 +260,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
@@ -268,7 +272,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
boolean isRegularTransaction = true; boolean isRegularTransaction = true;
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(account, fmt, transactionDate, final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(account, fmt, transactionDate,
transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction); transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withEntityId(deposit.getId()) // .withEntityId(deposit.getId()) //
.withOfficeId(account.officeId()) // .withOfficeId(account.officeId()) //
@@ -277,6 +280,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.withSavingsId(savingsId) // .withSavingsId(savingsId) //
.with(changes) // .with(changes) //
.build(); .build();
} }
private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) { private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) {
@@ -301,7 +305,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
final boolean isAccountTransfer = false; final boolean isAccountTransfer = false;
final boolean isRegularTransaction = true; final boolean isRegularTransaction = true;
final boolean isApplyWithdrawFee = true; final boolean isApplyWithdrawFee = true;
@@ -359,7 +362,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth,false);
this.savingAccountRepositoryWrapper.save(account); this.savingAccountRepositoryWrapper.save(account);
@@ -371,13 +374,32 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.withSavingsId(savingsId) // .withSavingsId(savingsId) //
.build(); .build();
} }
@Override @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); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account); 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() // return new CommandProcessingResultBuilder() //
.withEntityId(savingsId) // .withEntityId(savingsId) //
.withOfficeId(account.officeId()) // .withOfficeId(account.officeId()) //
@@ -389,7 +411,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional @Transactional
@Override @Override
public void postInterest(final SavingsAccount account) { public void postInterest(final SavingsAccount account,final boolean postInterestAs,final LocalDate transactionDate) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -403,14 +425,18 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false; 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 // for generating transaction id's
List<SavingsAccountTransaction> transactions = account.getTransactions(); List<SavingsAccountTransaction> transactions = account.getTransactions();
for (SavingsAccountTransaction accountTransaction : transactions) { for (SavingsAccountTransaction accountTransaction : transactions) {
if (accountTransaction.getId() == null) { if (accountTransaction.getId() == null) {
this.savingsAccountTransactionRepository.save(accountTransaction); this.savingsAccountTransactionRepository.save(accountTransaction);
} }
} }
this.savingAccountRepositoryWrapper.saveAndFlush(account); this.savingAccountRepositoryWrapper.saveAndFlush(account);
@@ -466,10 +492,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired() if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,false);
} else { } else {
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth,false);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) { if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -555,10 +581,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (account.isBeforeLastPostingPeriod(transactionDate) if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { || account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,false);
} else { } else {
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth,false);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) { if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -607,6 +633,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsAccountTransactionDataValidator.validateClosing(command); this.savingsAccountTransactionDataValidator.validateClosing(command);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final boolean isLinkedWithAnyActiveLoan = this.accountAssociationsReadPlatformService.isLinkedWithAnyActiveAccount(savingsId); final boolean isLinkedWithAnyActiveLoan = this.accountAssociationsReadPlatformService.isLinkedWithAnyActiveAccount(savingsId);
if (isLinkedWithAnyActiveLoan) { if (isLinkedWithAnyActiveLoan) {
@@ -615,17 +642,35 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
throw new SavingsAccountClosingNotAllowedException("linked", defaultUserMessage, savingsId); throw new SavingsAccountClosingNotAllowedException("linked", defaultUserMessage, savingsId);
} }
final boolean isWithdrawBalance = command.booleanPrimitiveValueOfParameterNamed(withdrawBalanceParamName); final boolean isWithdrawBalance = command.booleanPrimitiveValueOfParameterNamed(withdrawBalanceParamName);
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); 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<>(); final Map<String, Object> changes = new LinkedHashMap<>();
if (isWithdrawBalance && account.getSummary().getAccountBalance(account.getCurrency()).isGreaterThanZero()) { if (isWithdrawBalance && account.getSummary().getAccountBalance(account.getCurrency()).isGreaterThanZero()) {
final BigDecimal transactionAmount = account.getSummary().getAccountBalance(); final BigDecimal transactionAmount = account.getSummary().getAccountBalance();
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final boolean isAccountTransfer = false; final boolean isAccountTransfer = false;
@@ -685,8 +730,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(newTransferTransaction); this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount); this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -715,8 +761,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn =false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction); this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount); this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -756,8 +803,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
} }
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean postInterestAsOn = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
this.savingsAccountTransactionRepository.save(acceptTransferTransaction); this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount); this.savingAccountRepositoryWrapper.saveAndFlush(savingsAccount);
@@ -906,14 +954,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
account.waiveCharge(savingsAccountChargeId, user); account.waiveCharge(savingsAccountChargeId, user);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) { if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -1038,14 +1088,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, user); account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, user);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
boolean postInterestAsOn = false;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) { if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestAsOn);
} else { } else {
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth, postInterestAsOn);
} }
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) { if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
@@ -60,7 +60,9 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
for (final SavingsAccount savingsAccount : savingsAccounts) { for (final SavingsAccount savingsAccount : savingsAccounts) {
try { try {
this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount); this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount);
this.savingsAccountWritePlatformService.postInterest(savingsAccount); boolean postInterestAsOn = false;
LocalDate transactionDate = null;
this.savingsAccountWritePlatformService.postInterest(savingsAccount, postInterestAsOn, transactionDate);
} catch (Exception e) { } catch (Exception e) {
Throwable realCause = e; Throwable realCause = e;
if (e.getCause() != null) { 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