feat(deposits): RBI premature closure compliance, simplified to always use the contracted rate
Adds a Closure Reason to premature FD closure (Normal, Premature Extension, Conversion to Another TD, Deceased Settlement, Nominee/Legal-Heir), persisted as an RBI audit trail on m_deposit_account_term_and_preclosure. RBI-mandated reasons auto-waive the pre-closure penalty regardless of the maker's own checkbox; the maker can also waive it explicitly for any other reason. Interest rate for a premature closure is always the deposit's own contracted rate -- the same one its regular scheduled postings use throughout its life -- rather than a slab re-lookup against the actual period held. An earlier "actual-period vs contracted" toggle was built, tested, then deliberately removed per explicit request: simpler, and avoids disagreeing with interest the account was already accruing all along. Also fixes a real bug found while testing this: a renewed FD term's premature- closure interest was including the previous term's closing transaction a second time (it's dated the same day the new term starts), understating the first day's balance by the whole prior term's payout. Scoped the interest calculation to transactions strictly after the current term's start date. Known open issue: a renewed/auto-renewed account can still show a materially wrong (sometimes negative) premature-closure interest figure in some cases (seen on account 10, a QUARTERLY-posting auto-renewed FD) -- root cause not yet confirmed; needs further investigation with debug instrumentation before this is considered fully correct for every renewal shape. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+7
-2
@@ -674,7 +674,7 @@ public final class FixedDepositAccountData extends DepositAccountData {
|
||||
|
||||
public static FixedDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount,
|
||||
final Collection<EnumOptionData> onAccountClosureOptions, final Collection<PaymentTypeData> paymentTypeOptions,
|
||||
final Collection<SavingsAccountData> savingsAccountDatas) {
|
||||
final Collection<SavingsAccountData> savingsAccountDatas, final BigDecimal effectiveInterestRate) {
|
||||
|
||||
final Long groupId = null;
|
||||
final String groupName = null;
|
||||
@@ -689,7 +689,12 @@ public final class FixedDepositAccountData extends DepositAccountData {
|
||||
final SavingsAccountStatusEnumData status = null;
|
||||
final SavingsAccountApplicationTimelineData timeline = null;
|
||||
final CurrencyData currency = null;
|
||||
final BigDecimal nominalAnnualInterestRate = null;
|
||||
// Not the account's stored contracted rate -- the rate actually resolved for THIS premature
|
||||
// closure preview (see FixedDepositAccount#getEffectiveInterestRateAsFraction, read back
|
||||
// straight after the calculation by the caller), so a maker can see which of the RBI-default
|
||||
// actual-period rate, a bank's "use contracted rate" override, and any penalty deduction ended
|
||||
// up applying, rather than only the final rupee amount.
|
||||
final BigDecimal nominalAnnualInterestRate = effectiveInterestRate;
|
||||
final EnumOptionData interestPeriodType = null;
|
||||
final EnumOptionData interestPostingPeriodType = null;
|
||||
final EnumOptionData interestCalculationType = null;
|
||||
|
||||
+15
@@ -45,6 +45,7 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType;
|
||||
import org.apache.fineract.portfolio.savings.PrematureClosureReasonType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
|
||||
@@ -118,6 +119,20 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom<L
|
||||
this.fdAccountNumber = fdAccountNumber;
|
||||
}
|
||||
|
||||
// RBI audit evidence: why THIS closure happened, set once by FixedDepositAccount#prematureClosure
|
||||
// and never changed afterward -- see PrematureClosureReasonType. Null on an account that was never
|
||||
// prematurely closed (including one still active), or on one closed before this column existed.
|
||||
@Column(name = "premature_closure_reason_enum")
|
||||
private Integer prematureClosureReason;
|
||||
|
||||
public PrematureClosureReasonType getPrematureClosureReason() {
|
||||
return PrematureClosureReasonType.fromInt(this.prematureClosureReason);
|
||||
}
|
||||
|
||||
public void updatePrematureClosureReason(final PrematureClosureReasonType reason) {
|
||||
this.prematureClosureReason = (reason == null || reason.isInvalid()) ? null : reason.getValue();
|
||||
}
|
||||
|
||||
// Which renewal cycle of one logical FD this account represents: 0 = originally opened account,
|
||||
// 1 = first renewal, and so on. Lets the UI present "one FD, many terms" over what is physically
|
||||
// a chain of per-term account rows.
|
||||
|
||||
Reference in New Issue
Block a user