complete fd account module
This commit is contained in:
+52
-5
@@ -70,7 +70,7 @@ export class CreateFixedDepositAccountComponent {
|
||||
/** Fixed Deposit Account Product Template */
|
||||
fixedDepositsAccountProductTemplate: any;
|
||||
/** Tenure selected from the interest rate chart step */
|
||||
selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number } | null = null;
|
||||
selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number } | null = null;
|
||||
|
||||
/**
|
||||
* Fetches FD account template from `resolve`
|
||||
@@ -94,7 +94,7 @@ export class CreateFixedDepositAccountComponent {
|
||||
this.fixedDepositsAccountProductTemplate = $event;
|
||||
}
|
||||
|
||||
onTenureSelected($event: { depositPeriod: number; depositPeriodFrequencyId: number }) {
|
||||
onTenureSelected($event: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number }) {
|
||||
this.selectedTenure = $event;
|
||||
}
|
||||
|
||||
@@ -129,12 +129,59 @@ export class CreateFixedDepositAccountComponent {
|
||||
const locale = this.settingsService.language.code;
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
const monthDayFormat = 'dd MMMM';
|
||||
const pt = this.fixedDepositsAccountProductTemplate;
|
||||
|
||||
// Terms — pulled from product template
|
||||
const terms: any = {};
|
||||
if (pt?.nominalAnnualInterestRate != null) terms.nominalAnnualInterestRate = pt.nominalAnnualInterestRate;
|
||||
if (pt?.interestCompoundingPeriodType?.id != null)
|
||||
terms.interestCompoundingPeriodType = pt.interestCompoundingPeriodType.id;
|
||||
if (pt?.interestPostingPeriodType?.id != null) terms.interestPostingPeriodType = pt.interestPostingPeriodType.id;
|
||||
if (pt?.interestCalculationType?.id != null) terms.interestCalculationType = pt.interestCalculationType.id;
|
||||
if (pt?.interestCalculationDaysInYearType?.id != null)
|
||||
terms.interestCalculationDaysInYearType = pt.interestCalculationDaysInYearType.id;
|
||||
|
||||
// Deposit amount: Prefer user input from Rate Calculator, fallback to product template
|
||||
if (this.selectedTenure?.depositAmount != null) {
|
||||
terms.depositAmount = this.selectedTenure.depositAmount;
|
||||
} else if (pt?.depositAmount != null) {
|
||||
terms.depositAmount = pt.depositAmount;
|
||||
}
|
||||
|
||||
if (pt?.minDepositAmount != null) terms.minDepositAmount = pt.minDepositAmount;
|
||||
if (pt?.maxDepositAmount != null) terms.maxDepositAmount = pt.maxDepositAmount;
|
||||
|
||||
// Settings — pulled from product template
|
||||
const settings: any = {};
|
||||
if (pt?.lockinPeriodFrequency != null) settings.lockinPeriodFrequency = pt.lockinPeriodFrequency;
|
||||
if (pt?.lockinPeriodFrequencyType?.id != null) settings.lockinPeriodFrequencyType = pt.lockinPeriodFrequencyType.id;
|
||||
if (pt?.minDepositTerm != null) settings.minDepositTerm = pt.minDepositTerm;
|
||||
if (pt?.minDepositTermType?.id != null) settings.minDepositTermTypeId = pt.minDepositTermType.id;
|
||||
if (pt?.inMultiplesOfDepositTerm != null) settings.inMultiplesOfDepositTerm = pt.inMultiplesOfDepositTerm;
|
||||
if (pt?.inMultiplesOfDepositTermType?.id != null)
|
||||
settings.inMultiplesOfDepositTermTypeId = pt.inMultiplesOfDepositTermType.id;
|
||||
if (pt?.maxDepositTerm != null) settings.maxDepositTerm = pt.maxDepositTerm;
|
||||
if (pt?.maxDepositTermType?.id != null) settings.maxDepositTermTypeId = pt.maxDepositTermType.id;
|
||||
if (pt?.preClosurePenalApplicable != null) settings.preClosurePenalApplicable = pt.preClosurePenalApplicable;
|
||||
if (pt?.preClosurePenalInterest != null) settings.preClosurePenalInterest = pt.preClosurePenalInterest;
|
||||
if (pt?.preClosurePenalInterestOnType?.id != null)
|
||||
settings.preClosurePenalInterestOnTypeId = pt.preClosurePenalInterestOnType.id;
|
||||
if (pt?.maturityInstructionId != null) settings.maturityInstructionId = pt.maturityInstructionId;
|
||||
if (pt?.transferInterestToSavings != null) settings.transferInterestToSavings = pt.transferInterestToSavings;
|
||||
if (pt?.withHoldTax != null) settings.withHoldTax = pt.withHoldTax;
|
||||
|
||||
// Charges — from product template if not overridden in details
|
||||
const rawCharges: any[] =
|
||||
(this.fixedDepositAccount.charges?.length ? this.fixedDepositAccount.charges : pt?.charges) ?? [];
|
||||
|
||||
const fixedDepositAccount = {
|
||||
...this.fixedDepositAccount,
|
||||
...terms,
|
||||
...settings,
|
||||
...(this.selectedTenure ?? {}),
|
||||
clientId: this.fixedDepositsAccountTemplate.clientId,
|
||||
charges: this.fixedDepositAccount.charges.map((charge: any) => ({
|
||||
chargeId: charge.id,
|
||||
charges: rawCharges.map((charge: any) => ({
|
||||
chargeId: charge.chargeId ?? charge.id,
|
||||
amount: charge.amount,
|
||||
dueDate: charge.dueDate && this.dateUtils.formatDate(charge.dueDate, dateFormat),
|
||||
feeOnMonthDay:
|
||||
@@ -142,7 +189,7 @@ export class CreateFixedDepositAccountComponent {
|
||||
feeInterval: charge.feeInterval
|
||||
})),
|
||||
submittedOnDate: this.dateUtils.formatDate(this.fixedDepositAccount.submittedOnDate, dateFormat),
|
||||
charts: [{ chartSlabs: this.fixedDepositsAccountProductTemplate.accountChart?.chartSlabs ?? [] }],
|
||||
charts: [{ chartSlabs: pt?.accountChart?.chartSlabs ?? [] }],
|
||||
dateFormat,
|
||||
monthDayFormat,
|
||||
locale
|
||||
|
||||
+2
-4
@@ -55,11 +55,9 @@
|
||||
<h4>Nomination Facility</h4>
|
||||
<p>Allow account holders to designate individuals to receive funds in the event of account holder's death.</p>
|
||||
</div>
|
||||
<span class="facc-yn" [class.facc-yn--yes]="fixedDepositsAccountProductTemplate?.nominationFacility">
|
||||
{{ fixedDepositsAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }}
|
||||
</span>
|
||||
<mat-slide-toggle [(ngModel)]="nominationFacility" color="primary"></mat-slide-toggle>
|
||||
</div>
|
||||
@if (fixedDepositsAccountProductTemplate?.nominationFacility) {
|
||||
@if (nominationFacility) {
|
||||
<mifosx-nominee-form (nomineeChange)="onNomineeChange($event)"></mifosx-nominee-form>
|
||||
}
|
||||
</div>
|
||||
|
||||
+3
@@ -31,6 +31,7 @@ export class FixedDepositAccountCurrencyStepComponent implements OnChanges {
|
||||
nomineeData: any = null;
|
||||
|
||||
autoDebitFacility = false;
|
||||
nominationFacility = false;
|
||||
|
||||
// Editable feature toggles (pre-filled from product, overridable by user)
|
||||
|
||||
@@ -38,6 +39,7 @@ export class FixedDepositAccountCurrencyStepComponent implements OnChanges {
|
||||
if (changes['fixedDepositsAccountProductTemplate']?.currentValue) {
|
||||
const pt = changes['fixedDepositsAccountProductTemplate'].currentValue;
|
||||
this.autoDebitFacility = !!pt.autoDebitFacility;
|
||||
this.nominationFacility = !!pt.nominationFacility;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +51,7 @@ export class FixedDepositAccountCurrencyStepComponent implements OnChanges {
|
||||
get currencyStepData() {
|
||||
return {
|
||||
autoDebitFacility: this.autoDebitFacility,
|
||||
nominationFacility: this.nominationFacility,
|
||||
...(this.nomineeData ? { nominee: this.nomineeData } : {})
|
||||
};
|
||||
}
|
||||
|
||||
+62
-31
@@ -145,24 +145,23 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Principal Amount (SPECIAL only) -->
|
||||
@if (isSpecial) {
|
||||
<div class="fdrc-calc-field">
|
||||
<label class="fdrc-calc-label"
|
||||
>Principal Amount <span class="fdrc-calc-optional">(optional — for monthly payout estimate)</span></label
|
||||
>
|
||||
<div class="fdrc-amount-row">
|
||||
<span class="fdrc-amount-prefix">₹</span>
|
||||
<input
|
||||
type="number"
|
||||
class="fdrc-tenure-input fdrc-tenure-input--amount"
|
||||
[(ngModel)]="principalAmount"
|
||||
[min]="1"
|
||||
placeholder="e.g. 100000"
|
||||
/>
|
||||
</div>
|
||||
<!-- Principal Amount (all FD types) -->
|
||||
<div class="fdrc-calc-field">
|
||||
<label class="fdrc-calc-label">
|
||||
Deposit Amount
|
||||
<span class="fdrc-calc-optional">(optional — to calculate maturity amount)</span>
|
||||
</label>
|
||||
<div class="fdrc-amount-row">
|
||||
<span class="fdrc-amount-prefix">₹</span>
|
||||
<input
|
||||
type="number"
|
||||
class="fdrc-tenure-input fdrc-tenure-input--amount"
|
||||
[(ngModel)]="principalAmount"
|
||||
[min]="1"
|
||||
placeholder="e.g. 100000"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="fdrc-calc-actions">
|
||||
<button type="button" class="fdrc-calc-btn" (click)="calculateRate()">
|
||||
@@ -237,25 +236,57 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Monthly Payout (SPECIAL) -->
|
||||
@if (isSpecial && matchResult.estimatedMonthlyPayout != null) {
|
||||
<div class="fdrc-payout-card">
|
||||
<i class="fas fa-wallet"></i>
|
||||
<div class="fdrc-payout-card__content">
|
||||
<span class="fdrc-payout-card__label">Estimated Monthly Payout</span>
|
||||
<span class="fdrc-payout-card__amount"
|
||||
>₹ {{ matchResult.estimatedMonthlyPayout | number: '1.2-2' }}</span
|
||||
>
|
||||
<span class="fdrc-payout-card__formula"
|
||||
>= ₹{{ principalAmount | number }} × {{ matchResult.appliedRate }}% ÷ 12</span
|
||||
>
|
||||
<!-- Maturity Summary (all FD types) -->
|
||||
@if (matchResult.maturityAmount != null) {
|
||||
<div class="fdrc-maturity-card">
|
||||
<div class="fdrc-maturity-card__header">
|
||||
<i class="fas fa-coins"></i>
|
||||
<span>Maturity Summary</span>
|
||||
</div>
|
||||
<div class="fdrc-maturity-card__body">
|
||||
<div class="fdrc-maturity-row">
|
||||
<span class="fdrc-maturity-row__label">Deposit Amount</span>
|
||||
<span class="fdrc-maturity-row__value">₹ {{ principalAmount | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="fdrc-maturity-row">
|
||||
<span class="fdrc-maturity-row__label">Tenure</span>
|
||||
<span class="fdrc-maturity-row__value">{{ tenureValue }} {{ tenurePeriodUnit }}</span>
|
||||
</div>
|
||||
<div class="fdrc-maturity-row">
|
||||
<span class="fdrc-maturity-row__label">Interest Rate</span>
|
||||
<span class="fdrc-maturity-row__value">{{ matchResult.appliedRate | number: '1.2-2' }}% p.a.</span>
|
||||
</div>
|
||||
<div class="fdrc-maturity-row fdrc-maturity-row--interest">
|
||||
<span class="fdrc-maturity-row__label">Interest Earned</span>
|
||||
<span class="fdrc-maturity-row__value">₹ {{ matchResult.interestEarned | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
@if (isSpecial && matchResult.estimatedMonthlyPayout != null) {
|
||||
<div class="fdrc-maturity-row">
|
||||
<span class="fdrc-maturity-row__label">Monthly Payout</span>
|
||||
<span class="fdrc-maturity-row__value fdrc-maturity-row__value--highlight">
|
||||
₹ {{ matchResult.estimatedMonthlyPayout | number: '1.2-2' }} / month
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
<div class="fdrc-maturity-row fdrc-maturity-row--total">
|
||||
<span class="fdrc-maturity-row__label">
|
||||
@if (isSpecial) {
|
||||
Principal at Maturity
|
||||
} @else {
|
||||
Maturity Amount
|
||||
}
|
||||
</span>
|
||||
<span class="fdrc-maturity-row__value fdrc-maturity-row__value--big">
|
||||
₹ {{ (isSpecial ? principalAmount : matchResult.maturityAmount) | number: '1.2-2' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (isSpecial && !matchResult.estimatedMonthlyPayout) {
|
||||
@if (!matchResult.maturityAmount) {
|
||||
<div class="fdrc-payout-hint">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
Enter a principal amount above to see estimated monthly interest payout.
|
||||
Enter a deposit amount above to see the maturity calculation.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
+76
@@ -869,3 +869,79 @@ $red-light: #fee2e2;
|
||||
margin-top: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
// ── Maturity Card ─────────────────────────────────────────────────────────
|
||||
|
||||
.fdrc-maturity-card {
|
||||
margin-top: 14px;
|
||||
border: 1.5px solid #86efac;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #dcfce7;
|
||||
padding: 8px 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #166534;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
|
||||
i {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
background: #f0fdf4;
|
||||
}
|
||||
}
|
||||
|
||||
.fdrc-maturity-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 14px;
|
||||
border-bottom: 1px solid #d1fae5;
|
||||
font-size: 13px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&__label {
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
&--interest {
|
||||
.fdrc-maturity-row__value {
|
||||
color: #16a34a;
|
||||
}
|
||||
}
|
||||
|
||||
&--total {
|
||||
background: #dcfce7;
|
||||
border-top: 1.5px solid #86efac;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
&__value--big {
|
||||
font-size: 17px;
|
||||
font-weight: 800;
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
&__value--highlight {
|
||||
color: #0369a1;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
+37
-6
@@ -33,6 +33,9 @@ export interface SlabMatchResult {
|
||||
slabIndex: number;
|
||||
appliedRate: number;
|
||||
estimatedMonthlyPayout?: number;
|
||||
maturityAmount?: number;
|
||||
interestEarned?: number;
|
||||
tenureDays?: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -69,7 +72,11 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
|
||||
@Input() fixedDepositsAccountTemplate: any;
|
||||
@Input() fixedDepositsAccountProductTemplate: any;
|
||||
|
||||
@Output() tenureSelected = new EventEmitter<{ depositPeriod: number; depositPeriodFrequencyId: number }>();
|
||||
@Output() tenureSelected = new EventEmitter<{
|
||||
depositPeriod: number;
|
||||
depositPeriodFrequencyId: number;
|
||||
depositAmount?: number;
|
||||
}>();
|
||||
|
||||
interestRateChartData: any[] = [];
|
||||
|
||||
@@ -99,6 +106,10 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
|
||||
tenurePeriodUnit: FdPeriodUnit = 'Days';
|
||||
principalAmount: number | null = null;
|
||||
|
||||
get showPrincipalInput(): boolean {
|
||||
return true; // shown for all FD types
|
||||
}
|
||||
|
||||
matchResult: SlabMatchResult | null = null;
|
||||
validationWarning: string | null = null;
|
||||
|
||||
@@ -219,19 +230,38 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
|
||||
const result: SlabMatchResult = {
|
||||
slab: matchedSlab,
|
||||
slabIndex: matchedIndex,
|
||||
appliedRate: rate
|
||||
appliedRate: rate,
|
||||
tenureDays: enteredDays
|
||||
};
|
||||
|
||||
if (this.isSpecial && this.principalAmount && this.principalAmount > 0) {
|
||||
result.estimatedMonthlyPayout = (this.principalAmount * rate) / 1200;
|
||||
if (this.principalAmount && this.principalAmount > 0) {
|
||||
const p = this.principalAmount;
|
||||
const r = rate / 100;
|
||||
const years = enteredDays / 365;
|
||||
const n = 4; // Quarterly compounding
|
||||
|
||||
if (this.isSpecial) {
|
||||
// Monthly payout: simple interest paid monthly, principal returned at maturity
|
||||
result.estimatedMonthlyPayout = (p * rate) / 1200;
|
||||
result.interestEarned = result.estimatedMonthlyPayout * (enteredDays / 30);
|
||||
result.maturityAmount = p; // principal returned at maturity
|
||||
} else {
|
||||
// Compound interest (Standard Quarterly)
|
||||
result.maturityAmount = p * Math.pow(1 + r / n, n * years);
|
||||
result.interestEarned = result.maturityAmount - p;
|
||||
}
|
||||
}
|
||||
|
||||
this.matchResult = result;
|
||||
|
||||
// Emit selected tenure to parent for backend submission
|
||||
// Emit selected tenure and principal amount to parent for backend submission
|
||||
const frequencyId = this.resolveFrequencyId(this.tenurePeriodUnit);
|
||||
if (frequencyId !== null) {
|
||||
this.tenureSelected.emit({ depositPeriod: this.tenureValue, depositPeriodFrequencyId: frequencyId });
|
||||
this.tenureSelected.emit({
|
||||
depositPeriod: this.tenureValue,
|
||||
depositPeriodFrequencyId: frequencyId,
|
||||
depositAmount: this.principalAmount || undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +280,7 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
|
||||
this.principalAmount = null;
|
||||
this.matchResult = null;
|
||||
this.validationWarning = null;
|
||||
this.tenurePeriodUnit = this.allowedPeriodUnits[0];
|
||||
}
|
||||
|
||||
onPeriodUnitChange() {
|
||||
|
||||
+39
@@ -408,6 +408,45 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ══ 4. Maturity Summary ═══════════════════════════════════ -->
|
||||
@if (maturitySummary) {
|
||||
<div class="preview-section">
|
||||
<div class="preview-section__header">
|
||||
<i class="fas fa-piggy-bank preview-section__icon"></i>
|
||||
<span class="preview-section__title">Maturity Summary</span>
|
||||
<span class="preview-section__badge preview-section__badge--active">Projected</span>
|
||||
</div>
|
||||
<div class="preview-body">
|
||||
<div class="preview-grid preview-grid--three">
|
||||
<div class="preview-field">
|
||||
<span class="preview-label">Deposit Amount</span>
|
||||
<span class="preview-value">{{ maturitySummary.depositAmount | formatNumber }} {{ currencyName }}</span>
|
||||
</div>
|
||||
<div class="preview-field">
|
||||
<span class="preview-label">Applicable Interest Rate</span>
|
||||
<span class="preview-value">{{ maturitySummary.interestRate | formatNumber }}%</span>
|
||||
</div>
|
||||
<div class="preview-field">
|
||||
<span class="preview-label">Tenure</span>
|
||||
<span class="preview-value">{{ maturitySummary.tenureLabel }}</span>
|
||||
</div>
|
||||
<div class="preview-field">
|
||||
<span class="preview-label">Estimated Interest Earned</span>
|
||||
<span class="preview-value" style="color: var(--theme-success, #28a745); font-weight: 500">
|
||||
+ {{ maturitySummary.totalInterest | formatNumber }} {{ currencyName }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="preview-field" style="grid-column: span 2">
|
||||
<span class="preview-label">Projected Maturity Amount</span>
|
||||
<span class="preview-value preview-mono" style="font-weight: 600; font-size: 1.25rem">
|
||||
{{ maturitySummary.maturityAmount | formatNumber }} {{ currencyName }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ══ 5. Interest Rate Chart ═══════════════════════════════════ -->
|
||||
<div class="preview-section">
|
||||
<div class="preview-section__header">
|
||||
|
||||
+133
-7
@@ -77,7 +77,8 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
|
||||
/** Fixed Deposits Account Data */
|
||||
@Input() fixedDepositAccountData: any;
|
||||
/** Tenure selected from the interest rate chart step */
|
||||
@Input() selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number } | null = null;
|
||||
@Input() selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number } | null =
|
||||
null;
|
||||
|
||||
/** Charges Displayed Columns */
|
||||
chargesDisplayedColumns: string[] = [
|
||||
@@ -146,10 +147,27 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
|
||||
}
|
||||
|
||||
get matchedSlabIndex(): number {
|
||||
if (!this.selectedTenure || !this.interestRateChartData?.length) return -1;
|
||||
const { depositPeriod, depositPeriodFrequencyId } = this.selectedTenure;
|
||||
if (!this.interestRateChartData?.length) return -1;
|
||||
|
||||
let depositPeriod: number;
|
||||
let depositPeriodFrequencyId: number;
|
||||
|
||||
if (this.selectedTenure) {
|
||||
depositPeriod = this.selectedTenure.depositPeriod;
|
||||
depositPeriodFrequencyId = this.selectedTenure.depositPeriodFrequencyId;
|
||||
} else if (
|
||||
this.fixedDepositsAccountProductTemplate?.minDepositTerm != null &&
|
||||
this.fixedDepositsAccountProductTemplate?.minDepositTermType?.id != null
|
||||
) {
|
||||
depositPeriod = this.fixedDepositsAccountProductTemplate.minDepositTerm;
|
||||
depositPeriodFrequencyId = this.fixedDepositsAccountProductTemplate.minDepositTermType.id;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert selected tenure to days for comparison
|
||||
const toDays = (v: number, id: number) => {
|
||||
if (id === 1) return v * 7; // Weeks
|
||||
if (id === 2) return v * 30; // Months
|
||||
if (id === 3) return v * 365; // Years
|
||||
return v; // Days (id=0)
|
||||
@@ -158,7 +176,13 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
|
||||
|
||||
return this.interestRateChartData.findIndex((slab: any) => {
|
||||
const periodTypeValue = (slab.periodType?.value || '').toLowerCase();
|
||||
const slabId = periodTypeValue.includes('month') ? 2 : periodTypeValue.includes('year') ? 3 : 0;
|
||||
const slabId = periodTypeValue.includes('month')
|
||||
? 2
|
||||
: periodTypeValue.includes('year')
|
||||
? 3
|
||||
: periodTypeValue.includes('week')
|
||||
? 1
|
||||
: 0;
|
||||
const fromDays = toDays(slab.fromPeriod ?? 0, slabId);
|
||||
const toDaysVal = slab.toPeriod != null ? toDays(slab.toPeriod, slabId) : Infinity;
|
||||
return enteredDays >= fromDays && enteredDays <= toDaysVal;
|
||||
@@ -166,9 +190,111 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
|
||||
}
|
||||
|
||||
get selectedTenureLabel(): string {
|
||||
if (!this.selectedTenure) return '';
|
||||
const { depositPeriod, depositPeriodFrequencyId } = this.selectedTenure;
|
||||
const unitMap: Record<number, string> = { 0: 'Days', 2: 'Months', 3: 'Years' };
|
||||
let depositPeriod: number;
|
||||
let depositPeriodFrequencyId: number;
|
||||
|
||||
if (this.selectedTenure) {
|
||||
depositPeriod = this.selectedTenure.depositPeriod;
|
||||
depositPeriodFrequencyId = this.selectedTenure.depositPeriodFrequencyId;
|
||||
} else if (
|
||||
this.fixedDepositsAccountProductTemplate?.minDepositTerm != null &&
|
||||
this.fixedDepositsAccountProductTemplate?.minDepositTermType?.id != null
|
||||
) {
|
||||
depositPeriod = this.fixedDepositsAccountProductTemplate.minDepositTerm;
|
||||
depositPeriodFrequencyId = this.fixedDepositsAccountProductTemplate.minDepositTermType.id;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
const unitMap: Record<number, string> = { 0: 'Days', 1: 'Weeks', 2: 'Months', 3: 'Years' };
|
||||
return `${depositPeriod} ${unitMap[depositPeriodFrequencyId] ?? ''}`;
|
||||
}
|
||||
|
||||
get maturitySummary(): {
|
||||
depositAmount: number;
|
||||
maturityAmount: number;
|
||||
totalInterest: number;
|
||||
interestRate: number;
|
||||
tenureLabel: string;
|
||||
} {
|
||||
let depositPeriod = 0;
|
||||
let depositPeriodFrequencyId = 0;
|
||||
|
||||
if (this.selectedTenure) {
|
||||
depositPeriod = this.selectedTenure.depositPeriod;
|
||||
depositPeriodFrequencyId = this.selectedTenure.depositPeriodFrequencyId;
|
||||
} else if (
|
||||
this.fixedDepositsAccountProductTemplate?.minDepositTerm != null &&
|
||||
this.fixedDepositsAccountProductTemplate?.minDepositTermType?.id != null
|
||||
) {
|
||||
depositPeriod = this.fixedDepositsAccountProductTemplate.minDepositTerm;
|
||||
depositPeriodFrequencyId = this.fixedDepositsAccountProductTemplate.minDepositTermType.id;
|
||||
}
|
||||
|
||||
const depositAmount =
|
||||
this.selectedTenure?.depositAmount ??
|
||||
this.fixedDepositAccountData?.depositAmount ??
|
||||
this.fixedDepositsAccountProductTemplate?.depositAmount ??
|
||||
0;
|
||||
|
||||
const chartRate =
|
||||
this.matchedSlabIndex >= 0 ? this.interestRateChartData[this.matchedSlabIndex]?.annualInterestRate : null;
|
||||
const interestRate = chartRate ?? this.fixedDepositsAccountProductTemplate?.nominalAnnualInterestRate ?? 0;
|
||||
const r = interestRate / 100;
|
||||
|
||||
// Tenure in years (t)
|
||||
let t = 0;
|
||||
const daysInYear =
|
||||
this.fixedDepositsAccountProductTemplate?.interestCalculationDaysInYearType?.id === 360 ? 360 : 365;
|
||||
|
||||
if (depositPeriodFrequencyId === 0) {
|
||||
// Days
|
||||
t = depositPeriod / daysInYear;
|
||||
} else if (depositPeriodFrequencyId === 1) {
|
||||
// Weeks
|
||||
t = (depositPeriod * 7) / daysInYear;
|
||||
} else if (depositPeriodFrequencyId === 2) {
|
||||
// Months
|
||||
t = depositPeriod / 12;
|
||||
} else if (depositPeriodFrequencyId === 3) {
|
||||
// Years
|
||||
t = depositPeriod;
|
||||
}
|
||||
|
||||
// Compounding frequency (n)
|
||||
const compPeriodValue = (
|
||||
this.fixedDepositsAccountProductTemplate?.interestCompoundingPeriodType?.value || ''
|
||||
).toLowerCase();
|
||||
let n = 1;
|
||||
if (compPeriodValue.includes('daily')) n = daysInYear;
|
||||
else if (compPeriodValue.includes('monthly')) n = 12;
|
||||
else if (compPeriodValue.includes('quarterly')) n = 4;
|
||||
else if (compPeriodValue.includes('half')) n = 2;
|
||||
else if (compPeriodValue.includes('biannual')) n = 2;
|
||||
else if (compPeriodValue.includes('annual')) n = 1;
|
||||
|
||||
// Interest Calculation Type
|
||||
const calcTypeValue = (
|
||||
this.fixedDepositsAccountProductTemplate?.interestCalculationType?.value || ''
|
||||
).toLowerCase();
|
||||
const isSimple = calcTypeValue.includes('simple');
|
||||
|
||||
let maturityAmount = 0;
|
||||
if (isSimple) {
|
||||
maturityAmount = depositAmount + depositAmount * r * t;
|
||||
} else {
|
||||
// Compound interest: A = P(1 + r/n)^(nt)
|
||||
maturityAmount = depositAmount * Math.pow(1 + r / n, n * t);
|
||||
}
|
||||
|
||||
const totalInterest = maturityAmount - depositAmount;
|
||||
|
||||
return {
|
||||
depositAmount,
|
||||
maturityAmount,
|
||||
totalInterest,
|
||||
interestRate,
|
||||
tenureLabel: this.selectedTenureLabel
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN!
|
||||
/* tslint:disable */
|
||||
export default {
|
||||
'mifos_x': {
|
||||
'version': '260507',
|
||||
'hash': 'b0cffd38'
|
||||
'hash': 'a12ba20d'
|
||||
},
|
||||
'allow_switching_backend_instance': true
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user