rd account and rd product code update
This commit is contained in:
@@ -108,7 +108,7 @@ export class CkycReportsComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.parseAndStore(SAMPLE_CKYC_FILE);
|
this.parseAndStore(SAMPLE_CKYC_FILE);
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(
|
console.log(
|
||||||
'[CKYC] Sample loaded —',
|
'[CKYC] Sample loaded —',
|
||||||
this.customers.length,
|
this.customers.length,
|
||||||
|
|||||||
+5
-2
@@ -74,7 +74,8 @@
|
|||||||
<mifosx-recurring-deposits-account-interest-rate-chart-step
|
<mifosx-recurring-deposits-account-interest-rate-chart-step
|
||||||
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
|
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
|
||||||
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
||||||
[rdInterestRates]="rdInterestRates"
|
[submittedOnDate]="submittedOnDate"
|
||||||
|
(tenureSelected)="handleTenureSelected($event)"
|
||||||
>
|
>
|
||||||
</mifosx-recurring-deposits-account-interest-rate-chart-step>
|
</mifosx-recurring-deposits-account-interest-rate-chart-step>
|
||||||
|
|
||||||
@@ -97,11 +98,13 @@
|
|||||||
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
|
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
|
||||||
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
||||||
[recurringDepositAccountData]="recurringDepositAccountData"
|
[recurringDepositAccountData]="recurringDepositAccountData"
|
||||||
[rdInterestRates]="rdInterestRates"
|
|
||||||
[rdNominee]="rdNominee"
|
[rdNominee]="rdNominee"
|
||||||
[rdCurrencyFlags]="rdCurrencyFlags"
|
[rdCurrencyFlags]="rdCurrencyFlags"
|
||||||
[rdTenureDays]="rdTenureDays"
|
[rdTenureDays]="rdTenureDays"
|
||||||
[rdApplicableSlab]="rdApplicableSlab"
|
[rdApplicableSlab]="rdApplicableSlab"
|
||||||
|
[rdMaturityAmount]="rdMaturityAmount"
|
||||||
|
[rdInterestEarned]="rdInterestEarned"
|
||||||
|
[rdDaysInYear]="rdDaysInYear"
|
||||||
(submitEvent)="submit()"
|
(submitEvent)="submit()"
|
||||||
>
|
>
|
||||||
</mifosx-recurring-deposits-account-preview-step>
|
</mifosx-recurring-deposits-account-preview-step>
|
||||||
|
|||||||
+102
-28
@@ -75,8 +75,10 @@ export class CreateRecurringDepositsAccountComponent {
|
|||||||
recurringDepositsAccountTemplate: any;
|
recurringDepositsAccountTemplate: any;
|
||||||
/** Recurring Deposit Account Product Template */
|
/** Recurring Deposit Account Product Template */
|
||||||
recurringDepositsAccountProductTemplate: any;
|
recurringDepositsAccountProductTemplate: any;
|
||||||
/** Active CBS base interest rate chart */
|
/** Selected tenure value and frequency from calculator */
|
||||||
rdInterestRates: any[] = [];
|
selectedTenureValue: number | null = null;
|
||||||
|
selectedTenureFrequencyId: number | null = null;
|
||||||
|
selectedDepositAmount: number | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.route.data.subscribe((data: { recurringDepositsAccountTemplate: any }) => {
|
this.route.data.subscribe((data: { recurringDepositsAccountTemplate: any }) => {
|
||||||
@@ -90,14 +92,13 @@ export class CreateRecurringDepositsAccountComponent {
|
|||||||
*/
|
*/
|
||||||
setTemplate($event: any) {
|
setTemplate($event: any) {
|
||||||
this.recurringDepositsAccountProductTemplate = $event;
|
this.recurringDepositsAccountProductTemplate = $event;
|
||||||
this.productsService.getRdBaseInterestRates().subscribe({
|
}
|
||||||
next: (rates: any[]) => {
|
|
||||||
this.rdInterestRates = rates || [];
|
/** Captures tenure selection from calculator */
|
||||||
},
|
handleTenureSelected($event: any) {
|
||||||
error: () => {
|
this.selectedTenureValue = $event.depositPeriod;
|
||||||
this.rdInterestRates = [];
|
this.selectedTenureFrequencyId = $event.depositPeriodFrequencyId;
|
||||||
}
|
this.selectedDepositAmount = $event.depositAmount;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get Recurring Deposit Account Details Form Data */
|
/** Get Recurring Deposit Account Details Form Data */
|
||||||
@@ -123,17 +124,31 @@ export class CreateRecurringDepositsAccountComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get rdTenureDays(): number | null {
|
get rdTenureDays(): number | null {
|
||||||
return this.recurringDepositsAccountChartStep?.tenureDays ?? null;
|
return this.recurringDepositsAccountChartStep?.matchResult?.tenureDays ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get rdApplicableSlab(): any | null {
|
get rdApplicableSlab(): any | null {
|
||||||
return this.recurringDepositsAccountChartStep?.applicableSlab ?? null;
|
return this.recurringDepositsAccountChartStep?.matchResult?.slab ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rdMaturityAmount(): number | null {
|
||||||
|
return this.recurringDepositsAccountChartStep?.matchResult?.maturityAmount ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rdInterestEarned(): number | null {
|
||||||
|
return this.recurringDepositsAccountChartStep?.matchResult?.interestEarned ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rdDaysInYear(): number | null {
|
||||||
|
return this.recurringDepositsAccountChartStep?.matchResult?.daysInYear ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get submittedOnDate(): Date | null {
|
||||||
|
return this.recurringDepositAccountDetailsForm?.value?.submittedOnDate ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get rdCurrencyFlags() {
|
get rdCurrencyFlags() {
|
||||||
return {
|
return {
|
||||||
balanceSwapOption: this.recurringDepositsAccountCurrencyStep?.balanceSwapOption ?? false,
|
|
||||||
msme: this.recurringDepositsAccountCurrencyStep?.msme ?? false,
|
|
||||||
autoDebitFacility: this.recurringDepositsAccountCurrencyStep?.autoDebitFacility ?? false
|
autoDebitFacility: this.recurringDepositsAccountCurrencyStep?.autoDebitFacility ?? false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -145,34 +160,93 @@ export class CreateRecurringDepositsAccountComponent {
|
|||||||
const locale = this.settingsService.language.code;
|
const locale = this.settingsService.language.code;
|
||||||
const dateFormat = this.settingsService.dateFormat;
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
const monthDayFormat = 'dd MMMM';
|
const monthDayFormat = 'dd MMMM';
|
||||||
|
const pt = this.recurringDepositsAccountProductTemplate;
|
||||||
|
|
||||||
const details = this.recurringDepositAccount;
|
const details = this.recurringDepositAccount;
|
||||||
const currencyStep = this.recurringDepositsAccountCurrencyStep;
|
const currencyStep = this.recurringDepositsAccountCurrencyStep;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Flags and other product defaults
|
||||||
|
const productDefaults: any = {
|
||||||
|
isCalendarInherited: pt?.isCalendarInherited ?? false,
|
||||||
|
isMandatoryDeposit: pt?.isMandatoryDeposit ?? false,
|
||||||
|
allowWithdrawal: pt?.allowWithdrawal ?? false,
|
||||||
|
adjustAdvanceTowardsFuturePayments: pt?.adjustAdvanceTowardsFuturePayments ?? false,
|
||||||
|
autoDebitFacility: currencyStep?.autoDebitFacility ?? pt?.autoDebitFacility ?? false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Charges from product template
|
||||||
|
const rawCharges: any[] = pt?.charges ?? [];
|
||||||
|
|
||||||
const recurringDepositAccount: any = {
|
const recurringDepositAccount: any = {
|
||||||
...details,
|
...details,
|
||||||
|
...terms,
|
||||||
|
...settings,
|
||||||
|
...productDefaults,
|
||||||
clientId: this.recurringDepositsAccountTemplate.clientId,
|
clientId: this.recurringDepositsAccountTemplate.clientId,
|
||||||
charges: [],
|
mandatoryRecommendedDepositAmount: pt?.minDepositAmount ?? 0,
|
||||||
isCalendarInherited: false,
|
recurringFrequency: pt?.recurringFrequency ?? 1,
|
||||||
mandatoryRecommendedDepositAmount: 0,
|
recurringFrequencyType: pt?.recurringFrequencyType ?? 2, // Monthly
|
||||||
recurringFrequency: 1,
|
|
||||||
recurringFrequencyType: 2,
|
|
||||||
submittedOnDate: this.dateUtils.formatDate(details.submittedOnDate, dateFormat),
|
submittedOnDate: this.dateUtils.formatDate(details.submittedOnDate, dateFormat),
|
||||||
expectedFirstDepositOnDate: this.dateUtils.formatDate(details.expectedFirstDepositOnDate, dateFormat),
|
expectedFirstDepositOnDate: this.dateUtils.formatDate(details.expectedFirstDepositOnDate, dateFormat),
|
||||||
dateFormat,
|
dateFormat,
|
||||||
monthDayFormat,
|
monthDayFormat,
|
||||||
locale,
|
locale,
|
||||||
// Currency step overrides
|
charts: [
|
||||||
balanceSwapOption: currencyStep?.balanceSwapOption ?? details.balanceSwapOption ?? false,
|
{
|
||||||
msme: currencyStep?.msme ?? details.msme ?? false,
|
chartSlabs:
|
||||||
autoDebitFacility: currencyStep?.autoDebitFacility ?? details.autoDebitFacility ?? false
|
pt?.accountChart?.chartSlabs ?? pt?.activeChart?.chartSlabs ?? pt?.interestRateCharts?.[0]?.chartSlabs ?? []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
charges: rawCharges.map((charge: any) => ({
|
||||||
|
chargeId: charge.chargeId ?? charge.id,
|
||||||
|
amount: charge.amount,
|
||||||
|
dueDate: charge.dueDate && this.dateUtils.formatDate(charge.dueDate, dateFormat),
|
||||||
|
feeOnMonthDay: charge.feeOnMonthDay,
|
||||||
|
feeInterval: charge.feeInterval
|
||||||
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tenure from interest rate chart step (depositPeriod in days)
|
// Tenure override from interest rate chart calculator
|
||||||
const tenureDays = this.rdTenureDays;
|
if (this.selectedTenureValue && this.selectedTenureFrequencyId !== null) {
|
||||||
if (tenureDays && tenureDays > 0) {
|
recurringDepositAccount.depositPeriod = this.selectedTenureValue;
|
||||||
recurringDepositAccount.depositPeriod = tenureDays;
|
recurringDepositAccount.depositPeriodFrequencyId = this.selectedTenureFrequencyId;
|
||||||
recurringDepositAccount.depositPeriodFrequencyId = 0; // 0 = Days
|
} else if (this.rdTenureDays) {
|
||||||
|
recurringDepositAccount.depositPeriod = this.rdTenureDays;
|
||||||
|
recurringDepositAccount.depositPeriodFrequencyId = 0; // Days
|
||||||
|
}
|
||||||
|
|
||||||
|
// Amount override from calculator if provided
|
||||||
|
if (this.selectedDepositAmount) {
|
||||||
|
recurringDepositAccount.mandatoryRecommendedDepositAmount = this.selectedDepositAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nominee from currency step
|
// Nominee from currency step
|
||||||
|
|||||||
+6
-2
@@ -71,7 +71,7 @@
|
|||||||
<mifosx-recurring-deposits-account-interest-rate-chart-step
|
<mifosx-recurring-deposits-account-interest-rate-chart-step
|
||||||
[recurringDepositsAccountTemplate]="recurringDepositsAccountAndTemplate"
|
[recurringDepositsAccountTemplate]="recurringDepositsAccountAndTemplate"
|
||||||
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
||||||
[rdInterestRates]="rdInterestRates"
|
[submittedOnDate]="submittedOnDate"
|
||||||
>
|
>
|
||||||
</mifosx-recurring-deposits-account-interest-rate-chart-step>
|
</mifosx-recurring-deposits-account-interest-rate-chart-step>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
@@ -94,7 +94,11 @@
|
|||||||
[recurringDepositsAccountTemplate]="recurringDepositsAccountAndTemplate"
|
[recurringDepositsAccountTemplate]="recurringDepositsAccountAndTemplate"
|
||||||
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
||||||
[recurringDepositAccountData]="recurringDepositAccountData"
|
[recurringDepositAccountData]="recurringDepositAccountData"
|
||||||
[rdInterestRates]="rdInterestRates"
|
[rdTenureDays]="rdTenureDays"
|
||||||
|
[rdApplicableSlab]="rdApplicableSlab"
|
||||||
|
[rdMaturityAmount]="rdMaturityAmount"
|
||||||
|
[rdInterestEarned]="rdInterestEarned"
|
||||||
|
[rdDaysInYear]="rdDaysInYear"
|
||||||
(submitEvent)="submit()"
|
(submitEvent)="submit()"
|
||||||
>
|
>
|
||||||
</mifosx-recurring-deposits-account-preview-step>
|
</mifosx-recurring-deposits-account-preview-step>
|
||||||
|
|||||||
+36
-10
@@ -67,12 +67,37 @@ export class EditRecurringDepositAccountComponent {
|
|||||||
@ViewChild(RecurringDepositsAccountChargesStepComponent, { static: true })
|
@ViewChild(RecurringDepositsAccountChargesStepComponent, { static: true })
|
||||||
recurringDepositAccountChargesStep: RecurringDepositsAccountChargesStepComponent;
|
recurringDepositAccountChargesStep: RecurringDepositsAccountChargesStepComponent;
|
||||||
|
|
||||||
|
@ViewChild(RecurringDepositsAccountInterestRateChartStepComponent)
|
||||||
|
recurringDepositsAccountChartStep: RecurringDepositsAccountInterestRateChartStepComponent;
|
||||||
|
|
||||||
|
get rdTenureDays(): number | null {
|
||||||
|
return this.recurringDepositsAccountChartStep?.matchResult?.tenureDays ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rdApplicableSlab(): any | null {
|
||||||
|
return this.recurringDepositsAccountChartStep?.matchResult?.slab ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rdMaturityAmount(): number | null {
|
||||||
|
return this.recurringDepositsAccountChartStep?.matchResult?.maturityAmount ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rdInterestEarned(): number | null {
|
||||||
|
return this.recurringDepositsAccountChartStep?.matchResult?.interestEarned ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rdDaysInYear(): number | null {
|
||||||
|
return this.recurringDepositsAccountChartStep?.matchResult?.daysInYear ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get submittedOnDate(): Date | null {
|
||||||
|
return this.recurringDepositAccountDetailsForm?.value?.submittedOnDate ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Recurring Deposits Account And Template */
|
/** Recurring Deposits Account And Template */
|
||||||
recurringDepositsAccountAndTemplate: any;
|
recurringDepositsAccountAndTemplate: any;
|
||||||
/** Recurring Deposit Account Product Template */
|
/** Recurring Deposit Account Product Template */
|
||||||
recurringDepositsAccountProductTemplate: any;
|
recurringDepositsAccountProductTemplate: any;
|
||||||
/** Active CBS base interest rate chart */
|
|
||||||
rdInterestRates: any[] = [];
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.route.data.subscribe((data: { recurringDepositsAccountAndTemplate: any }) => {
|
this.route.data.subscribe((data: { recurringDepositsAccountAndTemplate: any }) => {
|
||||||
@@ -86,14 +111,6 @@ export class EditRecurringDepositAccountComponent {
|
|||||||
*/
|
*/
|
||||||
setTemplate($event: any) {
|
setTemplate($event: any) {
|
||||||
this.recurringDepositsAccountProductTemplate = $event;
|
this.recurringDepositsAccountProductTemplate = $event;
|
||||||
this.productsService.getRdBaseInterestRates().subscribe({
|
|
||||||
next: (rates: any[]) => {
|
|
||||||
this.rdInterestRates = rates || [];
|
|
||||||
},
|
|
||||||
error: () => {
|
|
||||||
this.rdInterestRates = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get Recurring Deposit Account Details Form Data */
|
/** Get Recurring Deposit Account Details Form Data */
|
||||||
@@ -169,6 +186,15 @@ export class EditRecurringDepositAccountComponent {
|
|||||||
this.recurringDepositAccount.expectedFirstDepositOnDate,
|
this.recurringDepositAccount.expectedFirstDepositOnDate,
|
||||||
dateFormat
|
dateFormat
|
||||||
),
|
),
|
||||||
|
charts: [
|
||||||
|
{
|
||||||
|
chartSlabs:
|
||||||
|
this.recurringDepositsAccountProductTemplate?.accountChart?.chartSlabs ??
|
||||||
|
this.recurringDepositsAccountProductTemplate?.activeChart?.chartSlabs ??
|
||||||
|
this.recurringDepositsAccountProductTemplate?.interestRateCharts?.[0]?.chartSlabs ??
|
||||||
|
[]
|
||||||
|
}
|
||||||
|
],
|
||||||
dateFormat,
|
dateFormat,
|
||||||
monthDayFormat,
|
monthDayFormat,
|
||||||
locale
|
locale
|
||||||
|
|||||||
-16
@@ -27,22 +27,6 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="racc-alert racc-alert--editable">
|
|
||||||
<div class="racc-alert__content">
|
|
||||||
<h4>Balance Swap Option</h4>
|
|
||||||
<p>Enable automated transfer of excess funds to linked deposits or vice-versa to maintain liquidity.</p>
|
|
||||||
</div>
|
|
||||||
<mat-slide-toggle [(ngModel)]="balanceSwapOption" color="primary"></mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="racc-alert racc-alert--editable">
|
|
||||||
<div class="racc-alert__content">
|
|
||||||
<h4>MSME</h4>
|
|
||||||
<p>Micro, Small and Medium Enterprises — mark this account under MSME category.</p>
|
|
||||||
</div>
|
|
||||||
<mat-slide-toggle [(ngModel)]="msme" color="primary"></mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="racc-alert racc-alert--editable">
|
<div class="racc-alert racc-alert--editable">
|
||||||
<div class="racc-alert__content">
|
<div class="racc-alert__content">
|
||||||
<h4>Auto Debit Facility</h4>
|
<h4>Auto Debit Facility</h4>
|
||||||
|
|||||||
-6
@@ -31,15 +31,11 @@ export class RecurringDepositsAccountCurrencyStepComponent implements OnChanges
|
|||||||
nomineeData: any = null;
|
nomineeData: any = null;
|
||||||
|
|
||||||
// Editable feature toggles (pre-filled from product, overridable by user)
|
// Editable feature toggles (pre-filled from product, overridable by user)
|
||||||
balanceSwapOption = false;
|
|
||||||
msme = false;
|
|
||||||
autoDebitFacility = false;
|
autoDebitFacility = false;
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (changes['recurringDepositsAccountProductTemplate']?.currentValue) {
|
if (changes['recurringDepositsAccountProductTemplate']?.currentValue) {
|
||||||
const pt = changes['recurringDepositsAccountProductTemplate'].currentValue;
|
const pt = changes['recurringDepositsAccountProductTemplate'].currentValue;
|
||||||
this.balanceSwapOption = !!pt.balanceSwapOption;
|
|
||||||
this.msme = !!pt.msme;
|
|
||||||
this.autoDebitFacility = !!pt.autoDebitFacility;
|
this.autoDebitFacility = !!pt.autoDebitFacility;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,8 +47,6 @@ export class RecurringDepositsAccountCurrencyStepComponent implements OnChanges
|
|||||||
/** Called by parent to collect currency step values */
|
/** Called by parent to collect currency step values */
|
||||||
get currencyStepData() {
|
get currencyStepData() {
|
||||||
return {
|
return {
|
||||||
balanceSwapOption: this.balanceSwapOption,
|
|
||||||
msme: this.msme,
|
|
||||||
autoDebitFacility: this.autoDebitFacility,
|
autoDebitFacility: this.autoDebitFacility,
|
||||||
...(this.nomineeData ? { nominee: this.nomineeData } : {})
|
...(this.nomineeData ? { nominee: this.nomineeData } : {})
|
||||||
};
|
};
|
||||||
|
|||||||
-4
@@ -338,8 +338,6 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
],
|
],
|
||||||
fieldOfficerId: [''],
|
fieldOfficerId: [''],
|
||||||
externalId: [''],
|
externalId: [''],
|
||||||
balanceSwapOption: [false],
|
|
||||||
msme: [false],
|
|
||||||
autoDebitFacility: [false]
|
autoDebitFacility: [false]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -375,8 +373,6 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
this.loadClientAccounts();
|
this.loadClientAccounts();
|
||||||
}
|
}
|
||||||
this.recurringDepositAccountDetailsForm.patchValue({
|
this.recurringDepositAccountDetailsForm.patchValue({
|
||||||
balanceSwapOption: !!response.balanceSwapOption,
|
|
||||||
msme: !!response.msme,
|
|
||||||
autoDebitFacility: !!response.autoDebitFacility
|
autoDebitFacility: !!response.autoDebitFacility
|
||||||
});
|
});
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
|
|||||||
+349
-37
@@ -6,47 +6,359 @@
|
|||||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<div class="layout-column gap-2px">
|
<div class="fdrc-wrapper">
|
||||||
<!-- ── Tenure Calculator ───────────────────────────────────────── -->
|
<!-- ══ Chart Header Card ════════════════════════════════════════════════ -->
|
||||||
@if (_activeRdVersion) {
|
@if (interestRateChartData.length > 0) {
|
||||||
<div class="tenure-calc">
|
<div class="fdrc-header-card">
|
||||||
<div class="tenure-calc__header">
|
<div class="fdrc-header-card__left">
|
||||||
<i class="fas fa-calculator"></i>
|
<div class="fdrc-header-card__icon">
|
||||||
<span>Tenure & Interest Calculator</span>
|
<i class="fas fa-chart-line"></i>
|
||||||
</div>
|
|
||||||
<div class="tenure-calc__body">
|
|
||||||
<div class="tenure-calc__input-row">
|
|
||||||
<mat-form-field appearance="outline" class="tenure-calc__field">
|
|
||||||
<mat-label>Enter Tenure (Days)</mat-label>
|
|
||||||
<input matInput type="number" [(ngModel)]="tenureDays" min="1" placeholder="e.g. 365" />
|
|
||||||
<mat-hint>Enter the number of days for your RD</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
@if (tenureDays && tenureDays > 0) {
|
<div class="fdrc-header-card__title">{{ chartName || 'RD Interest Rate Chart' }}</div>
|
||||||
@if (applicableSlab) {
|
@if (chartDescription) {
|
||||||
<div class="tenure-calc__result">
|
<div class="fdrc-header-card__desc">{{ chartDescription }}</div>
|
||||||
<div class="tenure-calc__result-row">
|
|
||||||
<span class="tenure-calc__result-label">Tenure</span>
|
|
||||||
<span class="tenure-calc__result-value">{{ tenureDays }} Days</span>
|
|
||||||
</div>
|
|
||||||
<div class="tenure-calc__result-row">
|
|
||||||
<span class="tenure-calc__result-label">Applicable Slab</span>
|
|
||||||
<span class="tenure-calc__result-value">{{ applicableSlab.description }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="tenure-calc__result-row tenure-calc__result-row--highlight">
|
|
||||||
<span class="tenure-calc__result-label">Annual Interest Rate</span>
|
|
||||||
<span class="tenure-calc__result-rate">{{ applicableSlab.annualInterestRate | number: '1.2-2' }}%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<div class="tenure-calc__no-match">
|
|
||||||
<i class="fas fa-exclamation-circle"></i>
|
|
||||||
No matching slab found for {{ tenureDays }} days
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fdrc-header-card__meta">
|
||||||
|
<div class="fdrc-header-card__dates">
|
||||||
|
<span class="fdrc-meta-item">
|
||||||
|
<i class="fas fa-calendar-check"></i>
|
||||||
|
From: <strong>{{ chartFromDate | dateFormat }}</strong>
|
||||||
|
</span>
|
||||||
|
@if (chartEndDate) {
|
||||||
|
<span class="fdrc-meta-item">
|
||||||
|
<i class="fas fa-calendar-times"></i>
|
||||||
|
To: <strong>{{ chartEndDate | dateFormat }}</strong>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (isPrimaryGroupingByAmount) {
|
||||||
|
<span class="fdrc-pill fdrc-pill--amount"> <i class="fas fa-layer-group"></i> Grouped by Amount </span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<!-- ══ Tenure Calculator ════════════════════════════════════════════════ -->
|
||||||
|
<div class="fdrc-calc-card">
|
||||||
|
<div class="fdrc-calc-card__header">
|
||||||
|
<div class="fdrc-calc-card__header-left">
|
||||||
|
<div class="fdrc-calc-icon">
|
||||||
|
<i class="fas fa-calculator"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="fdrc-calc-card__title">RD Rate Calculator</span>
|
||||||
|
<span class="fdrc-calc-card__sub">Enter your RD tenure to find the applicable interest rate</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdrc-calc-card__body">
|
||||||
|
<div class="fdrc-calc-inputs">
|
||||||
|
<!-- Tenure Value -->
|
||||||
|
<div class="fdrc-calc-field">
|
||||||
|
<label class="fdrc-calc-label">Deposit Tenure</label>
|
||||||
|
<div class="fdrc-tenure-row">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="fdrc-tenure-input"
|
||||||
|
[(ngModel)]="tenureValue"
|
||||||
|
[min]="1"
|
||||||
|
placeholder="Enter value"
|
||||||
|
(keyup.enter)="calculateRate()"
|
||||||
|
/>
|
||||||
|
<!-- Period Unit Selector -->
|
||||||
|
<div class="fdrc-period-tabs">
|
||||||
|
@for (unit of allowedPeriodUnits; track unit) {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="fdrc-period-tab"
|
||||||
|
[class.fdrc-period-tab--active]="tenurePeriodUnit === unit"
|
||||||
|
(click)="tenurePeriodUnit = unit; onPeriodUnitChange()"
|
||||||
|
>
|
||||||
|
{{ unit }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Principal Amount (Monthly Installment) -->
|
||||||
|
<div class="fdrc-calc-field">
|
||||||
|
<label class="fdrc-calc-label">
|
||||||
|
Monthly Installment
|
||||||
|
<span class="fdrc-calc-optional">(optional — for maturity estimation)</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. 5000"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdrc-calc-actions">
|
||||||
|
<button type="button" class="fdrc-calc-btn" (click)="calculateRate()">
|
||||||
|
<i class="fas fa-search"></i> Find Rate
|
||||||
|
</button>
|
||||||
|
@if (tenureValue || matchResult) {
|
||||||
|
<button type="button" class="fdrc-calc-reset" (click)="resetCalculator()">
|
||||||
|
<i class="fas fa-times"></i> Reset
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Validation Warning -->
|
||||||
|
@if (validationWarning) {
|
||||||
|
<div class="fdrc-alert fdrc-alert--warn">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<span>{{ validationWarning }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Match Result -->
|
||||||
|
@if (matchResult) {
|
||||||
|
<div class="fdrc-result-card">
|
||||||
|
<div class="fdrc-result-card__check">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
</div>
|
||||||
|
<div class="fdrc-result-card__body">
|
||||||
|
<!-- Selected Tenure Banner -->
|
||||||
|
<div class="fdrc-tenure-banner">
|
||||||
|
<i class="fas fa-calendar-check"></i>
|
||||||
|
<div class="fdrc-tenure-banner__content">
|
||||||
|
<span class="fdrc-tenure-banner__label">Your Selected RD Tenure</span>
|
||||||
|
<span class="fdrc-tenure-banner__value">{{ tenureValue }} {{ tenurePeriodUnit }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="fdrc-tenure-banner__saved"> <i class="fas fa-save"></i> Will be saved </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdrc-result-rate">
|
||||||
|
<span class="fdrc-result-rate__value">{{ matchResult.appliedRate | number: '1.2-2' }}%</span>
|
||||||
|
<span class="fdrc-result-rate__label">Annual Interest Rate</span>
|
||||||
|
</div>
|
||||||
|
<div class="fdrc-result-meta">
|
||||||
|
<div class="fdrc-result-meta__item">
|
||||||
|
<i class="fas fa-layer-group"></i>
|
||||||
|
<span
|
||||||
|
>Applicable Slab:
|
||||||
|
<strong>
|
||||||
|
{{ matchResult.slab.fromPeriod }}–{{ matchResult.slab.toPeriod ?? '∞' }}
|
||||||
|
{{ matchResult.slab.periodType?.value }}
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (matchResult.slab.description) {
|
||||||
|
<div class="fdrc-result-meta__item">
|
||||||
|
<i class="fas fa-tag"></i>
|
||||||
|
<span>{{ matchResult.slab.description }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Maturity Summary -->
|
||||||
|
@if (matchResult.maturityAmount !== null) {
|
||||||
|
<div class="fdrc-maturity-card">
|
||||||
|
<div class="fdrc-maturity-card__header">
|
||||||
|
<i class="fas fa-coins"></i>
|
||||||
|
<span>RD Maturity Summary (Estimate)</span>
|
||||||
|
</div>
|
||||||
|
<div class="fdrc-maturity-card__body">
|
||||||
|
<div class="fdrc-maturity-row">
|
||||||
|
<span class="fdrc-maturity-row__label">Monthly Installment</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">Estimated Interest Earned</span>
|
||||||
|
<span class="fdrc-maturity-row__value">₹ {{ matchResult.interestEarned | number: '1.2-2' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="fdrc-maturity-row fdrc-maturity-row--total">
|
||||||
|
<span class="fdrc-maturity-row__label">Estimated Maturity Amount</span>
|
||||||
|
<span class="fdrc-maturity-row__value fdrc-maturity-row__value--big">
|
||||||
|
₹ {{ matchResult.maturityAmount | number: '1.2-2' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (!matchResult.maturityAmount) {
|
||||||
|
<div class="fdrc-payout-hint">
|
||||||
|
<i class="fas fa-lightbulb"></i>
|
||||||
|
Enter a monthly installment amount above to see the maturity calculation.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ══ Rate Chart Table ══════════════════════════════════════════════════ -->
|
||||||
|
<div class="fdrc-table-card">
|
||||||
|
<div class="fdrc-table-card__header">
|
||||||
|
<div class="fdrc-table-card__title-row">
|
||||||
|
<i class="fas fa-table"></i>
|
||||||
|
<span class="fdrc-table-card__title">Interest Rate Slabs</span>
|
||||||
|
@if (interestRateChartData.length) {
|
||||||
|
<span class="fdrc-slab-count"
|
||||||
|
>{{ interestRateChartData.length }} slab{{ interestRateChartData.length !== 1 ? 's' : '' }}</span
|
||||||
|
>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (matchResult) {
|
||||||
|
<span class="fdrc-highlight-note"> <i class="fas fa-highlighter"></i> Matched slab is highlighted </span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdrc-table-wrap">
|
||||||
|
<table mat-table [dataSource]="interestRateChartData" #chartsTable multiTemplateDataRows class="fdrc-table">
|
||||||
|
<!-- Period Column -->
|
||||||
|
<ng-container matColumnDef="period">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="fdrc-th">Period</th>
|
||||||
|
<td mat-cell *matCellDef="let slab" class="fdrc-td fdrc-td--period">
|
||||||
|
<span class="fdrc-period-range">
|
||||||
|
{{ slab.fromPeriod }}
|
||||||
|
@if (slab.toPeriod !== null) {
|
||||||
|
– {{ slab.toPeriod }}
|
||||||
|
} @else {
|
||||||
|
+
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
<span class="fdrc-period-type">{{ slab.periodType?.value }}</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Amount Range Column -->
|
||||||
|
<ng-container matColumnDef="amountRange">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="fdrc-th">Amount Range</th>
|
||||||
|
<td mat-cell *matCellDef="let slab" class="fdrc-td">
|
||||||
|
@if (slab.amountRangeFrom || slab.amountRangeTo) {
|
||||||
|
<span>{{ slab.amountRangeFrom | number }} – {{ slab.amountRangeTo | number }}</span>
|
||||||
|
} @else {
|
||||||
|
<span class="fdrc-td--na">—</span>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Interest Column -->
|
||||||
|
<ng-container matColumnDef="interest">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="fdrc-th">Interest Rate</th>
|
||||||
|
<td mat-cell *matCellDef="let slab" class="fdrc-td">
|
||||||
|
<span class="fdrc-rate-badge">{{ slab.annualInterestRate | number: '1.2-2' }}%</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Description Column -->
|
||||||
|
<ng-container matColumnDef="description">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="fdrc-th">Description</th>
|
||||||
|
<td mat-cell *matCellDef="let slab" class="fdrc-td">
|
||||||
|
{{ slab.description || '—' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="fdrc-th">Incentives</th>
|
||||||
|
<td mat-cell *matCellDef="let slab; let i = dataIndex" class="fdrc-td">
|
||||||
|
@if (slab.incentives?.length) {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="fdrc-incentive-btn"
|
||||||
|
(click)="expandChartSlabIndex = expandChartSlabIndex === i ? null : i"
|
||||||
|
>
|
||||||
|
<fa-icon [icon]="expandChartSlabIndex !== i ? 'eye' : 'eye-slash'"></fa-icon>
|
||||||
|
{{ expandChartSlabIndex === i ? 'Hide' : 'View' }} ({{ slab.incentives.length }})
|
||||||
|
</button>
|
||||||
|
} @else {
|
||||||
|
<span class="fdrc-td--na">None</span>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Incentives Expanded Row -->
|
||||||
|
<ng-container matColumnDef="incentives">
|
||||||
|
<td
|
||||||
|
mat-cell
|
||||||
|
*matCellDef="let slab; let i = dataIndex"
|
||||||
|
[attr.colspan]="chartSlabsDisplayedColumns.length"
|
||||||
|
class="fdrc-incentives-cell"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="fdrc-incentives-wrap"
|
||||||
|
[@expandChartSlab]="i === expandChartSlabIndex ? 'expanded' : 'collapsed'"
|
||||||
|
>
|
||||||
|
@if (slab.incentives?.length) {
|
||||||
|
<div class="fdrc-incentives-inner">
|
||||||
|
<div class="fdrc-incentives-title"><i class="fas fa-gift"></i> Incentives for this slab</div>
|
||||||
|
<table mat-table [dataSource]="slab.incentives" class="fdrc-incentives-table">
|
||||||
|
<ng-container matColumnDef="entityType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Entity Type</th>
|
||||||
|
<td mat-cell *matCellDef="let inc">{{ inc.entityType?.value }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="attributeName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Attribute</th>
|
||||||
|
<td mat-cell *matCellDef="let inc">{{ inc.attributeName?.value }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="conditionType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Condition</th>
|
||||||
|
<td mat-cell *matCellDef="let inc">{{ inc.conditionType?.value | titlecase }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="attributeValue">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Value</th>
|
||||||
|
<td mat-cell *matCellDef="let inc">{{ inc.attributeValueDesc }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="incentiveType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Type</th>
|
||||||
|
<td mat-cell *matCellDef="let inc">{{ inc.incentiveType?.value }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Amount</th>
|
||||||
|
<td mat-cell *matCellDef="let inc">{{ inc.amount }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="incentivesDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: incentivesDisplayedColumns"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="chartSlabsDisplayedColumns" class="fdrc-header-row"></tr>
|
||||||
|
<tr
|
||||||
|
mat-row
|
||||||
|
*matRowDef="let row; columns: chartSlabsDisplayedColumns; let i = dataIndex"
|
||||||
|
class="fdrc-data-row"
|
||||||
|
[class.fdrc-data-row--matched]="isMatchedSlab(i)"
|
||||||
|
></tr>
|
||||||
|
<tr
|
||||||
|
mat-row
|
||||||
|
*matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns"
|
||||||
|
class="fdrc-incentives-row"
|
||||||
|
></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
@if (!interestRateChartData.length) {
|
||||||
|
<div class="fdrc-empty">
|
||||||
|
<i class="fas fa-chart-bar"></i>
|
||||||
|
<span>No interest rate slabs defined for this product.</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+789
-78
@@ -6,125 +6,836 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// ── Tenure Calculator ─────────────────────────────────────────────────────────
|
$border: #e2e8f0;
|
||||||
.tenure-calc {
|
$gray-50: #f8f9fa;
|
||||||
border: 1px solid #e2e8f0;
|
$gray-100: #f1f5f9;
|
||||||
border-radius: 10px;
|
$gray-200: #e8eaed;
|
||||||
overflow: hidden;
|
$gray-600: #64748b;
|
||||||
|
$gray-700: #334155;
|
||||||
|
$gray-900: #0f172a;
|
||||||
|
$blue: #2563eb;
|
||||||
|
$blue-light: #dbeafe;
|
||||||
|
$green: #16a34a;
|
||||||
|
$green-light: #dcfce7;
|
||||||
|
$amber: #d97706;
|
||||||
|
$amber-light: #fef3c7;
|
||||||
|
$purple: #7c3aed;
|
||||||
|
$purple-light: #ede9fe;
|
||||||
|
$red: #dc2626;
|
||||||
|
$red-light: #fee2e2;
|
||||||
|
|
||||||
|
.fdrc-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Chart Header Card ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
.fdrc-header-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 1px 4px rgb(0 0 0 / 5%);
|
border: 1px solid $border;
|
||||||
margin-bottom: 1.25rem;
|
border-radius: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: $blue-light;
|
||||||
|
color: $blue;
|
||||||
|
font-size: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-600;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__dates {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-meta-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-700;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Pills ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
.fdrc-pill {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&--type {
|
||||||
|
background: #f1f5f9;
|
||||||
|
color: $gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--single {
|
||||||
|
background: $amber-light;
|
||||||
|
color: $amber;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--double {
|
||||||
|
background: $purple-light;
|
||||||
|
color: $purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--special {
|
||||||
|
background: $green-light;
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--amount {
|
||||||
|
background: $blue-light;
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Calculator Card ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
.fdrc-calc-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
justify-content: space-between;
|
||||||
background: #f0f9ff;
|
padding: 14px 20px;
|
||||||
border-bottom: 1px solid #bae6fd;
|
background: $gray-50;
|
||||||
padding: 0.65rem 1rem;
|
border-bottom: 1px solid $gray-200;
|
||||||
font-size: 0.85rem;
|
}
|
||||||
|
|
||||||
|
&__header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $gray-900;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__sub {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-600;
|
||||||
|
display: block;
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: $blue-light;
|
||||||
|
color: $blue;
|
||||||
|
font-size: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-inputs {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
flex: 1 1 220px;
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-700;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-optional {
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: 0;
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-tenure-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-tenure-input {
|
||||||
|
height: 42px;
|
||||||
|
border: 1.5px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
background: #fff;
|
||||||
|
width: 130px;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: $blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--amount {
|
||||||
|
width: 180px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-period-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 14px;
|
||||||
|
height: 42px;
|
||||||
|
background: $gray-100;
|
||||||
|
border: 1.5px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-period-tabs {
|
||||||
|
display: flex;
|
||||||
|
border: 1.5px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-period-tab {
|
||||||
|
height: 42px;
|
||||||
|
padding: 0 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
border: none;
|
||||||
|
background: #fff;
|
||||||
|
color: $gray-600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
background 0.15s,
|
||||||
|
color 0.15s;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
border-left: 1px solid $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
background: $blue;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-amount-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-amount-prefix {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 42px;
|
||||||
|
background: $gray-100;
|
||||||
|
border: 1.5px solid $border;
|
||||||
|
border-right: none;
|
||||||
|
border-radius: 8px 0 0 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-amount-row .fdrc-tenure-input {
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: $amber;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
height: 42px;
|
||||||
|
padding: 0 22px;
|
||||||
|
background: $blue;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #1d4ed8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-calc-reset {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
height: 42px;
|
||||||
|
padding: 0 16px;
|
||||||
|
background: transparent;
|
||||||
|
color: $gray-600;
|
||||||
|
border: 1.5px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
border-color 0.15s,
|
||||||
|
color 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $red;
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Alert ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
.fdrc-alert {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-top: 1px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--warn {
|
||||||
|
background: $amber-light;
|
||||||
|
color: #b45309;
|
||||||
|
border: 1px solid rgba($amber, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Tenure Banner (inside result card) ───────────────────────────────────
|
||||||
|
|
||||||
|
.fdrc-tenure-banner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1.5px solid rgba($green, 0.4);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
> i {
|
||||||
|
font-size: 18px;
|
||||||
|
color: $green;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 10px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #0369a1;
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.06em;
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: $gray-900;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__saved {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
background: $green-light;
|
||||||
|
color: $green;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Result Card ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
.fdrc-result-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 18px 20px;
|
||||||
|
background: $green-light;
|
||||||
|
border: 1.5px solid rgba($green, 0.35);
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
&__check {
|
||||||
|
font-size: 26px;
|
||||||
|
color: $green;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-result-rate {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: $green;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #166534;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-result-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-700;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 13px;
|
color: $green;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Maturity Card ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
.fdrc-maturity-card {
|
||||||
|
margin-top: 16px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1.5px solid rgba($green, 0.2);
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
background: rgba($green, 0.05);
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-bottom: 1px solid rgba($green, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #166534;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
padding: 1rem 1.25rem;
|
padding: 12px 14px;
|
||||||
}
|
|
||||||
|
|
||||||
&__input-row {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-maturity-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-600;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__field {
|
&__value {
|
||||||
width: 260px;
|
font-size: 13px;
|
||||||
}
|
font-weight: 700;
|
||||||
|
color: $gray-900;
|
||||||
&__result {
|
|
||||||
margin-top: 1rem;
|
|
||||||
border: 1px solid #e0f2fe;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__result-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.55rem 1rem;
|
|
||||||
border-bottom: 1px solid #f0f9ff;
|
|
||||||
font-size: 0.88rem;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--highlight {
|
&--highlight {
|
||||||
background: #f0f9ff;
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--big {
|
||||||
|
font-size: 16px;
|
||||||
|
color: $green;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__result-label {
|
&--interest {
|
||||||
color: #64748b;
|
padding-top: 4px;
|
||||||
font-weight: 500;
|
margin-top: 4px;
|
||||||
|
border-top: 1px dashed $border;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__result-value {
|
&--total {
|
||||||
color: #1e293b;
|
padding-top: 8px;
|
||||||
font-weight: 600;
|
margin-top: 4px;
|
||||||
|
border-top: 1.5px solid $gray-200;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__result-rate {
|
.fdrc-payout-hint {
|
||||||
font-size: 1.25rem;
|
display: flex;
|
||||||
font-weight: 800;
|
align-items: center;
|
||||||
color: #0369a1;
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-600;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: $amber;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__no-match {
|
// ── Rate Chart Table Card ─────────────────────────────────────────────────
|
||||||
margin-top: 0.75rem;
|
|
||||||
padding: 0.6rem 1rem;
|
.fdrc-table-card {
|
||||||
border-radius: 8px;
|
background: #fff;
|
||||||
background: #fef2f2;
|
border: 1px solid $border;
|
||||||
color: #dc2626;
|
border-radius: 12px;
|
||||||
font-size: 0.85rem;
|
overflow: hidden;
|
||||||
font-weight: 600;
|
|
||||||
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
justify-content: space-between;
|
||||||
|
padding: 14px 20px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 13px;
|
color: $blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-t {
|
.fdrc-slab-count {
|
||||||
margin-top: 2em;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 8px;
|
||||||
|
background: $blue-light;
|
||||||
|
color: $blue;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
.fdrc-highlight-note {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $green;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-table-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
tr.incentives-row {
|
.fdrc-th {
|
||||||
height: 0;
|
font-size: 11px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: $gray-700 !important;
|
||||||
|
background: $gray-50;
|
||||||
|
padding: 10px 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
> td {
|
.fdrc-td {
|
||||||
padding: 0;
|
padding: 12px 16px !important;
|
||||||
}
|
font-size: 13px;
|
||||||
|
color: $gray-700;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
.incentives {
|
&--period {
|
||||||
overflow: hidden;
|
display: flex;
|
||||||
}
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.mat-card {
|
&--na {
|
||||||
border-radius: 0;
|
color: #94a3b8;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-period-range {
|
||||||
|
font-weight: 700;
|
||||||
|
color: $gray-900;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-period-type {
|
||||||
|
font-size: 11px;
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-rate-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px 10px;
|
||||||
|
background: $blue-light;
|
||||||
|
color: $blue;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-incentive-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
height: 32px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1.5px solid $border;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
border-color 0.15s,
|
||||||
|
color 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $blue;
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-header-row {
|
||||||
|
background: $gray-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-data-row {
|
||||||
|
transition: background 0.15s;
|
||||||
|
|
||||||
|
&:hover td {
|
||||||
|
background: $gray-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--matched td {
|
||||||
|
background: $green-light !important;
|
||||||
|
border-left: 3px solid $green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-incentives-row {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-incentives-cell {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-incentives-wrap {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-incentives-inner {
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-incentives-title {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $gray-700;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: $amber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-incentives-table {
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-size: 10px !important;
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
background: $gray-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdrc-empty {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px;
|
||||||
|
color: $gray-600;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 32px;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+264
-49
@@ -6,8 +6,8 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Angular Imports */
|
import { Component, OnInit, Input, OnChanges, ViewChild, Output, EventEmitter, inject } from '@angular/core';
|
||||||
import { Component, OnInit, Input, OnChanges, ViewChild, Output, EventEmitter } from '@angular/core';
|
import { RecurringDepositsService } from '../../recurring-deposits.service';
|
||||||
import {
|
import {
|
||||||
MatTable,
|
MatTable,
|
||||||
MatColumnDef,
|
MatColumnDef,
|
||||||
@@ -30,6 +30,18 @@ import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
|||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
export type RdPeriodUnit = 'Days' | 'Months' | 'Years';
|
||||||
|
|
||||||
|
export interface SlabMatchResult {
|
||||||
|
slab: any;
|
||||||
|
slabIndex: number;
|
||||||
|
appliedRate: number;
|
||||||
|
maturityAmount?: number;
|
||||||
|
interestEarned?: number;
|
||||||
|
tenureDays?: number;
|
||||||
|
daysInYear?: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recurring Deposits Account Interest Rate Chart Step
|
* Recurring Deposits Account Interest Rate Chart Step
|
||||||
*/
|
*/
|
||||||
@@ -64,45 +76,44 @@ import { FormsModule } from '@angular/forms';
|
|||||||
TitleCasePipe,
|
TitleCasePipe,
|
||||||
DateFormatPipe,
|
DateFormatPipe,
|
||||||
FormatNumberPipe,
|
FormatNumberPipe,
|
||||||
YesnoPipe
|
YesnoPipe,
|
||||||
|
DecimalPipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class RecurringDepositsAccountInterestRateChartStepComponent implements OnInit, OnChanges {
|
export class RecurringDepositsAccountInterestRateChartStepComponent implements OnInit, OnChanges {
|
||||||
@Input() recurringDepositsAccountTemplate: any;
|
@Input() recurringDepositsAccountTemplate: any;
|
||||||
@Input() recurringDepositsAccountProductTemplate: any;
|
@Input() recurringDepositsAccountProductTemplate: any;
|
||||||
|
@Input() submittedOnDate: Date | null = null;
|
||||||
|
|
||||||
/**
|
@Output() tenureSelected = new EventEmitter<{
|
||||||
* CBS base rate chart loaded from the active base-interest-rate service.
|
depositPeriod: number;
|
||||||
* When set, this table is shown instead of the legacy accountChart.chartSlabs.
|
depositPeriodFrequencyId: number;
|
||||||
*/
|
depositAmount?: number;
|
||||||
@Input() set rdInterestRates(rates: any[]) {
|
maturityAmount?: number;
|
||||||
this._rdInterestRates = rates || [];
|
interestEarned?: number;
|
||||||
this._activeRdVersion = this._rdInterestRates.find((v: any) => v.isActive) ?? this._rdInterestRates[0] ?? null;
|
daysInYear?: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
get interestRateChartData(): any[] {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
if (!template) return [];
|
||||||
|
return (
|
||||||
|
template.accountChart?.chartSlabs ??
|
||||||
|
template.activeChart?.chartSlabs ??
|
||||||
|
template.interestRateCharts?.[0]?.chartSlabs ??
|
||||||
|
template.interestRateChart?.chartSlabs ??
|
||||||
|
template.chartSlabs ??
|
||||||
|
[]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_rdInterestRates: any[] = [];
|
chartSlabsDisplayedColumns: string[] = [
|
||||||
_activeRdVersion: any = null;
|
|
||||||
|
|
||||||
readonly rdSlabColumns = [
|
|
||||||
'srNo',
|
|
||||||
'description',
|
|
||||||
'fromPeriod',
|
|
||||||
'toPeriod',
|
|
||||||
'periodTypeLabel',
|
|
||||||
'annualInterestRate'
|
|
||||||
];
|
|
||||||
|
|
||||||
/** Interest Rate Chart Data (legacy – used when no custom rate chart is available) */
|
|
||||||
interestRateChartData: any = [];
|
|
||||||
/** Columns to be displayed in interest rate chart table. */
|
|
||||||
chartSlabsDisplayedColumns: any[] = [
|
|
||||||
'period',
|
'period',
|
||||||
'amountRange',
|
'amountRange',
|
||||||
'interest',
|
'interest',
|
||||||
'description',
|
'description',
|
||||||
'actions'
|
'actions'
|
||||||
];
|
];
|
||||||
/** Columns to be displayed in incentives sub-table. */
|
|
||||||
incentivesDisplayedColumns: string[] = [
|
incentivesDisplayedColumns: string[] = [
|
||||||
'entityType',
|
'entityType',
|
||||||
'attributeName',
|
'attributeName',
|
||||||
@@ -111,38 +122,242 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
|
|||||||
'incentiveType',
|
'incentiveType',
|
||||||
'amount'
|
'amount'
|
||||||
];
|
];
|
||||||
/** Additional Column to display incentives table */
|
|
||||||
chartSlabsIncentivesDisplayedColumns: string[] = ['incentives'];
|
chartSlabsIncentivesDisplayedColumns: string[] = ['incentives'];
|
||||||
/** Expand Chart Slab Index used in the view */
|
|
||||||
expandChartSlabIndex: number;
|
|
||||||
|
|
||||||
/** Tenure entered by user (in days) */
|
expandChartSlabIndex: number | null = null;
|
||||||
tenureDays: number | null = null;
|
|
||||||
|
|
||||||
@ViewChild('chartsTable', { static: true }) chartsTableRef: MatTable<Element>;
|
@ViewChild('chartsTable', { static: true }) chartsTableRef: MatTable<Element>;
|
||||||
|
|
||||||
constructor() {}
|
// ── Tenure Calculator ───────────────────────────────────────────────────
|
||||||
|
tenureValue: number | null = null;
|
||||||
|
tenurePeriodUnit: RdPeriodUnit = 'Months';
|
||||||
|
principalAmount: number | null = null; // Monthly installment for RD
|
||||||
|
|
||||||
/** Find the matching slab for the entered tenure */
|
matchResult: SlabMatchResult | null = null;
|
||||||
get applicableSlab(): any | null {
|
validationWarning: string | null = null;
|
||||||
if (!this.tenureDays || !this._activeRdVersion?.slabs?.length) return null;
|
|
||||||
const days = this.tenureDays;
|
/** Period unit options */
|
||||||
return (
|
readonly allowedPeriodUnits: RdPeriodUnit[] = [
|
||||||
this._activeRdVersion.slabs.find((s: any) => {
|
'Months',
|
||||||
const from = s.fromPeriod ?? 0;
|
'Years'
|
||||||
const to = s.toPeriod ?? Infinity;
|
];
|
||||||
return days >= from && days <= to;
|
|
||||||
}) ?? null
|
private recurringDepositsService = inject(RecurringDepositsService);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
if (this.recurringDepositsAccountProductTemplate) {
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
this.interestRateChartData = this.recurringDepositsAccountProductTemplate.accountChart?.chartSlabs ?? [];
|
if (template) {
|
||||||
|
if (this.chartsTableRef) {
|
||||||
|
this.chartsTableRef.renderRows();
|
||||||
|
}
|
||||||
|
this.resetCalculator();
|
||||||
|
this.tenurePeriodUnit = this.allowedPeriodUnits[1]; // Default to Months
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
// Convert entered tenure to Days for slab matching
|
||||||
this.interestRateChartData = [];
|
private toDays(value: number, unit: RdPeriodUnit): number {
|
||||||
|
if (unit === 'Days') return value;
|
||||||
|
if (unit === 'Months') return value * 30;
|
||||||
|
return value * 365;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert slab period to days
|
||||||
|
private slabPeriodToDays(period: number, periodTypeValue: string): number {
|
||||||
|
const p = (periodTypeValue || '').toLowerCase();
|
||||||
|
if (p.includes('month')) return period * 30;
|
||||||
|
if (p.includes('year')) return period * 365;
|
||||||
|
return period; // days
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateRate() {
|
||||||
|
this.validationWarning = null;
|
||||||
|
this.matchResult = null;
|
||||||
|
|
||||||
|
if (!this.tenureValue || this.tenureValue <= 0) {
|
||||||
|
this.validationWarning = 'Please enter a valid tenure value greater than 0.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enteredDays = this.toDays(this.tenureValue, this.tenurePeriodUnit);
|
||||||
|
const slabs: any[] = this.interestRateChartData || [];
|
||||||
|
|
||||||
|
if (!slabs.length) {
|
||||||
|
this.validationWarning = 'No interest rate slabs are defined for this product.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let matchedSlab: any = null;
|
||||||
|
let matchedIndex = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < slabs.length; i++) {
|
||||||
|
const slab = slabs[i];
|
||||||
|
const periodTypeValue = slab.periodType?.value || 'Days';
|
||||||
|
const fromDays = this.slabPeriodToDays(slab.fromPeriod ?? 0, periodTypeValue);
|
||||||
|
const toDays = slab.toPeriod != null ? this.slabPeriodToDays(slab.toPeriod, periodTypeValue) : Infinity;
|
||||||
|
|
||||||
|
if (enteredDays >= fromDays && enteredDays <= toDays) {
|
||||||
|
matchedSlab = slab;
|
||||||
|
matchedIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchedSlab) {
|
||||||
|
this.validationWarning = 'No applicable interest rate slab found for the entered tenure.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rate = matchedSlab.annualInterestRate ?? 0;
|
||||||
|
const result: SlabMatchResult = {
|
||||||
|
slab: matchedSlab,
|
||||||
|
slabIndex: matchedIndex,
|
||||||
|
appliedRate: rate,
|
||||||
|
tenureDays: enteredDays
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maturity Estimation via Backend API
|
||||||
|
if (this.principalAmount && this.principalAmount > 0) {
|
||||||
|
const compoundingType = this.recurringDepositsAccountProductTemplate?.interestCompoundingPeriodType?.id;
|
||||||
|
let compoundingMonths = 1; // Default Monthly
|
||||||
|
if (compoundingType === 5) compoundingMonths = 3;
|
||||||
|
else if (compoundingType === 6) compoundingMonths = 6;
|
||||||
|
else if (compoundingType === 7) compoundingMonths = 12;
|
||||||
|
else if (compoundingType === 1) compoundingMonths = 0; // Special case for Daily in my API logic
|
||||||
|
|
||||||
|
// Calculate exact months based on unit to avoid rounding errors (e.g. 10 years = 120 months, not 122)
|
||||||
|
let tenureInMonths = 0;
|
||||||
|
if (this.tenurePeriodUnit === 'Years') {
|
||||||
|
tenureInMonths = this.tenureValue * 12;
|
||||||
|
} else if (this.tenurePeriodUnit === 'Months') {
|
||||||
|
tenureInMonths = this.tenureValue;
|
||||||
|
} else {
|
||||||
|
tenureInMonths = Math.floor(enteredDays / 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculationData: any = {
|
||||||
|
depositAmount: this.principalAmount,
|
||||||
|
annualInterestRate: rate,
|
||||||
|
tenureInMonths: tenureInMonths,
|
||||||
|
interestCompoundingPeriodInMonths: compoundingMonths
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.submittedOnDate) {
|
||||||
|
const date = new Date(this.submittedOnDate);
|
||||||
|
calculationData.submittedOnDate = date.toISOString().split('T')[0];
|
||||||
|
calculationData.dateFormat = 'yyyy-MM-dd'; // Standard ISO for API
|
||||||
|
calculationData.locale = 'en';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recurringDepositsService.calculateRecurringDepositInterest(calculationData).subscribe((apiResult: any) => {
|
||||||
|
result.maturityAmount = apiResult.maturityAmount;
|
||||||
|
result.interestEarned = apiResult.totalInterest;
|
||||||
|
result.daysInYear = apiResult.daysInYear;
|
||||||
|
this.matchResult = { ...result };
|
||||||
|
this.emitTenureSelection();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.matchResult = result;
|
||||||
|
this.emitTenureSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private emitTenureSelection() {
|
||||||
|
const frequencyId = this.resolveFrequencyId(this.tenurePeriodUnit);
|
||||||
|
if (frequencyId !== null && this.matchResult) {
|
||||||
|
this.tenureSelected.emit({
|
||||||
|
depositPeriod: this.tenureValue || 0,
|
||||||
|
depositPeriodFrequencyId: frequencyId,
|
||||||
|
depositAmount: this.principalAmount || undefined,
|
||||||
|
maturityAmount: this.matchResult.maturityAmount,
|
||||||
|
interestEarned: this.matchResult.interestEarned,
|
||||||
|
daysInYear: this.matchResult.daysInYear
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveFrequencyId(unit: RdPeriodUnit): number | null {
|
||||||
|
const options: any[] = this.recurringDepositsAccountProductTemplate?.periodFrequencyTypeOptions ?? [];
|
||||||
|
const unitLower = unit.toLowerCase();
|
||||||
|
const match = options.find((o: any) => (o.value || '').toLowerCase().includes(unitLower));
|
||||||
|
if (match) return match.id;
|
||||||
|
const fallback: Record<RdPeriodUnit, number> = { Days: 0, Months: 2, Years: 3 };
|
||||||
|
return fallback[unit] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCalculator() {
|
||||||
|
this.tenureValue = null;
|
||||||
|
this.principalAmount = null;
|
||||||
|
this.matchResult = null;
|
||||||
|
this.validationWarning = null;
|
||||||
|
this.tenurePeriodUnit = 'Months';
|
||||||
|
}
|
||||||
|
|
||||||
|
onPeriodUnitChange() {
|
||||||
|
this.matchResult = null;
|
||||||
|
this.validationWarning = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
isMatchedSlab(index: number): boolean {
|
||||||
|
return this.matchResult?.slabIndex === index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Header Getters ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
get chartName(): string {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return (
|
||||||
|
template?.accountChart?.name ||
|
||||||
|
template?.activeChart?.name ||
|
||||||
|
template?.interestRateCharts?.[0]?.name ||
|
||||||
|
template?.interestRateChart?.name ||
|
||||||
|
template?.name ||
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get chartFromDate(): any {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return (
|
||||||
|
template?.accountChart?.fromDate ||
|
||||||
|
template?.activeChart?.fromDate ||
|
||||||
|
template?.interestRateCharts?.[0]?.fromDate ||
|
||||||
|
template?.interestRateChart?.fromDate ||
|
||||||
|
template?.fromDate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get chartEndDate(): any {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return (
|
||||||
|
template?.accountChart?.endDate ||
|
||||||
|
template?.activeChart?.endDate ||
|
||||||
|
template?.interestRateCharts?.[0]?.endDate ||
|
||||||
|
template?.interestRateChart?.endDate ||
|
||||||
|
template?.endDate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get chartDescription(): string {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return (
|
||||||
|
template?.accountChart?.description ||
|
||||||
|
template?.activeChart?.description ||
|
||||||
|
template?.interestRateCharts?.[0]?.description ||
|
||||||
|
template?.interestRateChart?.description ||
|
||||||
|
template?.description ||
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isPrimaryGroupingByAmount(): boolean {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return !!(
|
||||||
|
template?.accountChart?.isPrimaryGroupingByAmount ||
|
||||||
|
template?.activeChart?.isPrimaryGroupingByAmount ||
|
||||||
|
template?.interestRateCharts?.[0]?.isPrimaryGroupingByAmount ||
|
||||||
|
template?.interestRateChart?.isPrimaryGroupingByAmount ||
|
||||||
|
template?.isPrimaryGroupingByAmount
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+210
-180
@@ -422,18 +422,6 @@
|
|||||||
<span class="preview-value">{{ recurringDepositsAccountProductTemplate.taxGroup.name }}</span>
|
<span class="preview-value">{{ recurringDepositsAccountProductTemplate.taxGroup.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="preview-field">
|
|
||||||
<span class="preview-label">Balance Swap Option</span>
|
|
||||||
<span class="preview-value preview-badge" [class.preview-badge--yes]="rdCurrencyFlags?.balanceSwapOption">
|
|
||||||
{{ rdCurrencyFlags?.balanceSwapOption | yesNo }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="preview-field">
|
|
||||||
<span class="preview-label">MSME</span>
|
|
||||||
<span class="preview-value preview-badge" [class.preview-badge--yes]="rdCurrencyFlags?.msme">
|
|
||||||
{{ rdCurrencyFlags?.msme | yesNo }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="preview-field">
|
<div class="preview-field">
|
||||||
<span class="preview-label">Auto Debit Facility</span>
|
<span class="preview-label">Auto Debit Facility</span>
|
||||||
<span class="preview-value preview-badge" [class.preview-badge--yes]="rdCurrencyFlags?.autoDebitFacility">
|
<span class="preview-value preview-badge" [class.preview-badge--yes]="rdCurrencyFlags?.autoDebitFacility">
|
||||||
@@ -497,7 +485,7 @@
|
|||||||
<div class="preview-dorm-step__body">
|
<div class="preview-dorm-step__body">
|
||||||
<span class="preview-dorm-step__label">Inactive</span>
|
<span class="preview-dorm-step__label">Inactive</span>
|
||||||
<span class="preview-dorm-step__days">
|
<span class="preview-dorm-step__days">
|
||||||
@if (recurringDepositsAccountProductTemplate?.daysToInactiveFromProduct != null) {
|
@if (recurringDepositsAccountProductTemplate?.daysToInactiveFromProduct !== null) {
|
||||||
After {{ recurringDepositsAccountProductTemplate.daysToInactiveFromProduct }} days of no transaction
|
After {{ recurringDepositsAccountProductTemplate.daysToInactiveFromProduct }} days of no transaction
|
||||||
} @else {
|
} @else {
|
||||||
Not configured
|
Not configured
|
||||||
@@ -511,7 +499,7 @@
|
|||||||
<div class="preview-dorm-step__body">
|
<div class="preview-dorm-step__body">
|
||||||
<span class="preview-dorm-step__label">Dormant</span>
|
<span class="preview-dorm-step__label">Dormant</span>
|
||||||
<span class="preview-dorm-step__days">
|
<span class="preview-dorm-step__days">
|
||||||
@if (recurringDepositsAccountProductTemplate?.daysToDormancyFromProduct != null) {
|
@if (recurringDepositsAccountProductTemplate?.daysToDormancyFromProduct !== null) {
|
||||||
{{ recurringDepositsAccountProductTemplate.daysToDormancyFromProduct }} days after inactive
|
{{ recurringDepositsAccountProductTemplate.daysToDormancyFromProduct }} days after inactive
|
||||||
} @else {
|
} @else {
|
||||||
Not configured
|
Not configured
|
||||||
@@ -525,7 +513,7 @@
|
|||||||
<div class="preview-dorm-step__body">
|
<div class="preview-dorm-step__body">
|
||||||
<span class="preview-dorm-step__label">Escheat</span>
|
<span class="preview-dorm-step__label">Escheat</span>
|
||||||
<span class="preview-dorm-step__days">
|
<span class="preview-dorm-step__days">
|
||||||
@if (recurringDepositsAccountProductTemplate?.daysToEscheatFromProduct != null) {
|
@if (recurringDepositsAccountProductTemplate?.daysToEscheatFromProduct !== null) {
|
||||||
{{ recurringDepositsAccountProductTemplate.daysToEscheatFromProduct }} days after dormant
|
{{ recurringDepositsAccountProductTemplate.daysToEscheatFromProduct }} days after dormant
|
||||||
} @else {
|
} @else {
|
||||||
Not configured
|
Not configured
|
||||||
@@ -637,183 +625,225 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ══ 7. Interest Rate (Tenure-based result) ════════════════════ -->
|
<!-- ══ 6. Maturity Summary ═══════════════════════════════════ -->
|
||||||
@if (_activeRdVersion || recurringDepositsAccountProductTemplate?.accountChart) {
|
@if (rdMaturityAmount) {
|
||||||
|
<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">Total Deposit Amount</span>
|
||||||
|
<span class="preview-value"
|
||||||
|
>{{ rdMaturityAmount - rdInterestEarned | formatNumber }} {{ currencyName }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Applicable Interest Rate</span>
|
||||||
|
<span class="preview-value">{{ rdApplicableSlab?.annualInterestRate | formatNumber }}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Tenure</span>
|
||||||
|
<span class="preview-value">{{ selectedTenureLabel }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Estimated Interest Earned</span>
|
||||||
|
<span class="preview-value" style="color: var(--theme-success, #16a34a); font-weight: 500">
|
||||||
|
+ {{ rdInterestEarned | formatNumber }} {{ currencyName }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Days in Year</span>
|
||||||
|
<span class="preview-value">{{ rdDaysInYear ?? 365 }} Days</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field" style="grid-column: span 1">
|
||||||
|
<span class="preview-label">Projected Maturity Amount</span>
|
||||||
|
<span class="preview-value preview-mono" style="font-weight: 600; font-size: 1.25rem">
|
||||||
|
{{ rdMaturityAmount | formatNumber }} {{ currencyName }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- ══ 7. Interest Rate Chart ═══════════════════════════════════ -->
|
||||||
|
@if (interestRateChartData?.length > 0) {
|
||||||
<div class="preview-section">
|
<div class="preview-section">
|
||||||
<div class="preview-section__header">
|
<div class="preview-section__header">
|
||||||
<i class="fas fa-chart-line preview-section__icon"></i>
|
<i class="fas fa-chart-line preview-section__icon"></i>
|
||||||
<span class="preview-section__title">Interest Rate</span>
|
<span class="preview-section__title">{{ 'labels.heading.Interest Rate Chart' | translate }}</span>
|
||||||
@if (_activeRdVersion) {
|
@if (rdTenureDays) {
|
||||||
<span class="preview-section__badge">{{ _activeRdVersion.versionLabel }}</span>
|
<span class="preview-section__badge preview-section__badge--tenure">
|
||||||
|
<i class="fas fa-calendar-check"></i> Selected Tenure: {{ selectedTenureLabel }}
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="preview-body">
|
<div class="preview-body">
|
||||||
@if (_activeRdVersion) {
|
<!-- Selected Tenure + Rate Banner -->
|
||||||
@if (rdTenureDays && rdApplicableSlab) {
|
@if (rdTenureDays && matchedSlabIndex >= 0) {
|
||||||
<div class="preview-tenure-result">
|
<div class="preview-tenure-banner">
|
||||||
<div class="preview-tenure-result__row">
|
<div class="preview-tenure-banner__left">
|
||||||
<span class="preview-label">Tenure</span>
|
<i class="fas fa-calendar-check"></i>
|
||||||
<span class="preview-value">{{ rdTenureDays }} Days</span>
|
<div>
|
||||||
</div>
|
<span class="preview-tenure-banner__label">Your RD Tenure</span>
|
||||||
<div class="preview-tenure-result__row">
|
<span class="preview-tenure-banner__value">{{ selectedTenureLabel }}</span>
|
||||||
<span class="preview-label">Applicable Slab</span>
|
|
||||||
<span class="preview-value">{{ rdApplicableSlab.description }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="preview-tenure-result__row preview-tenure-result__row--highlight">
|
|
||||||
<span class="preview-label">Annual Interest Rate</span>
|
|
||||||
<span class="preview-tenure-result__rate"
|
|
||||||
>{{ rdApplicableSlab.annualInterestRate | formatNumber }}%</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
<div class="preview-tenure-banner__divider"></div>
|
||||||
<span class="preview-muted">No tenure entered — interest rate not calculated</span>
|
<div class="preview-tenure-banner__right">
|
||||||
}
|
<span class="preview-tenure-banner__rate-label">Applicable Interest Rate</span>
|
||||||
} @else {
|
<span class="preview-tenure-banner__rate"
|
||||||
<div class="preview-grid preview-grid--three">
|
>{{ interestRateChartData[matchedSlabIndex]?.annualInterestRate | formatNumber }}%</span
|
||||||
@if (recurringDepositsAccountProductTemplate?.accountChart?.name) {
|
>
|
||||||
<div class="preview-field">
|
</div>
|
||||||
<span class="preview-label">{{ 'labels.inputs.name' | translate }}</span>
|
<div class="preview-tenure-banner__slab">
|
||||||
<span class="preview-value">{{ recurringDepositsAccountProductTemplate.accountChart.name }}</span>
|
<i class="fas fa-layer-group"></i>
|
||||||
</div>
|
Slab: {{ interestRateChartData[matchedSlabIndex]?.fromPeriod }}–{{
|
||||||
}
|
interestRateChartData[matchedSlabIndex]?.toPeriod ?? '∞'
|
||||||
@if (recurringDepositsAccountProductTemplate?.accountChart?.fromDate) {
|
}}
|
||||||
<div class="preview-field">
|
{{ interestRateChartData[matchedSlabIndex]?.periodType?.value }}
|
||||||
<span class="preview-label">{{ 'labels.inputs.Valid from Date' | translate }}</span>
|
|
||||||
<span class="preview-value">{{
|
|
||||||
recurringDepositsAccountProductTemplate.accountChart.fromDate | dateFormat
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if (recurringDepositsAccountProductTemplate?.accountChart?.endDate) {
|
|
||||||
<div class="preview-field">
|
|
||||||
<span class="preview-label">{{ 'labels.inputs.End Date' | translate }}</span>
|
|
||||||
<span class="preview-value">{{
|
|
||||||
recurringDepositsAccountProductTemplate.accountChart.endDate | dateFormat
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if (recurringDepositsAccountProductTemplate?.accountChart?.description) {
|
|
||||||
<div class="preview-field">
|
|
||||||
<span class="preview-label">{{ 'labels.inputs.Description' | translate }}</span>
|
|
||||||
<span class="preview-value">{{
|
|
||||||
recurringDepositsAccountProductTemplate.accountChart.description
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div class="preview-field">
|
|
||||||
<span class="preview-label">{{ 'labels.inputs.Primary Grouping by Amount' | translate }}</span>
|
|
||||||
<span class="preview-value">{{
|
|
||||||
recurringDepositsAccountProductTemplate?.accountChart?.isPrimaryGroupingByAmount | yesNo
|
|
||||||
}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@if (interestRateChartData?.length) {
|
<div class="preview-grid preview-grid--three" style="margin-bottom: 1rem">
|
||||||
<table
|
@if (chartName) {
|
||||||
class="mat-elevation-z1 irc-table preview-table"
|
<div class="preview-field">
|
||||||
mat-table
|
<span class="preview-label">{{ 'labels.inputs.name' | translate }}</span>
|
||||||
[dataSource]="interestRateChartData"
|
<span class="preview-value">{{ chartName }}</span>
|
||||||
multiTemplateDataRows
|
</div>
|
||||||
>
|
}
|
||||||
<ng-container matColumnDef="period">
|
@if (chartFromDate) {
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Period' | translate }}</th>
|
<div class="preview-field">
|
||||||
<td mat-cell *matCellDef="let chartSlab">
|
<span class="preview-label">Valid From</span>
|
||||||
{{ chartSlab.fromPeriod }} - {{ chartSlab.toPeriod }} {{
|
<span class="preview-value">{{ chartFromDate | dateFormat }}</span>
|
||||||
chartSlab.periodType?.value | translateKey: 'catalogs'
|
</div>
|
||||||
}}
|
}
|
||||||
</td>
|
<div class="preview-field">
|
||||||
</ng-container>
|
<span class="preview-label">Primary Grouping by Amount</span>
|
||||||
<ng-container matColumnDef="amountRange">
|
<span class="preview-value preview-badge" [class.preview-badge--yes]="isPrimaryGroupingByAmount">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount Range' | translate }}</th>
|
{{ isPrimaryGroupingByAmount | yesNo }}
|
||||||
<td mat-cell *matCellDef="let chartSlab">
|
</span>
|
||||||
{{ chartSlab.amountRangeFrom | formatNumber }} - {{ chartSlab.amountRangeTo | formatNumber }}
|
</div>
|
||||||
</td>
|
</div>
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="interest">
|
@if (interestRateChartData?.length) {
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Interest' | translate }}</th>
|
<table
|
||||||
<td mat-cell *matCellDef="let chartSlab">{{ chartSlab.annualInterestRate | formatNumber }} %</td>
|
class="mat-elevation-z1 irc-table preview-table"
|
||||||
</ng-container>
|
mat-table
|
||||||
<ng-container matColumnDef="description">
|
[dataSource]="interestRateChartData"
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Description' | translate }}</th>
|
multiTemplateDataRows
|
||||||
<td mat-cell *matCellDef="let chartSlab">{{ chartSlab.description }}</td>
|
>
|
||||||
</ng-container>
|
<ng-container matColumnDef="period">
|
||||||
<ng-container matColumnDef="actions">
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Period' | translate }}</th>
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
<td mat-cell *matCellDef="let chartSlab">
|
||||||
<td mat-cell *matCellDef="let chartSlab; let chartSlabIndex = dataIndex">
|
{{ chartSlab.fromPeriod }} - {{ chartSlab.toPeriod }} {{
|
||||||
<button
|
chartSlab.periodType?.value | translateKey: 'catalogs'
|
||||||
mat-button
|
}}
|
||||||
color="primary"
|
</td>
|
||||||
(click)="expandChartSlabIndex = expandChartSlabIndex === chartSlabIndex ? null : chartSlabIndex"
|
</ng-container>
|
||||||
>
|
<ng-container matColumnDef="amountRange">
|
||||||
<span *ngIf="expandChartSlabIndex !== chartSlabIndex">
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount Range' | translate }}</th>
|
||||||
<fa-icon icon="eye" class="m-r-10"></fa-icon>
|
<td mat-cell *matCellDef="let chartSlab">
|
||||||
{{ 'labels.buttons.View Incentives' | translate }}
|
{{ chartSlab.amountRangeFrom | formatNumber }} - {{ chartSlab.amountRangeTo | formatNumber }}
|
||||||
</span>
|
</td>
|
||||||
<span *ngIf="expandChartSlabIndex === chartSlabIndex">
|
</ng-container>
|
||||||
<fa-icon icon="eye-slash" class="m-r-10"></fa-icon>
|
<ng-container matColumnDef="interest">
|
||||||
{{ 'labels.buttons.Hide Incentives' | translate }}
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Interest' | translate }}</th>
|
||||||
</span>
|
<td mat-cell *matCellDef="let chartSlab">{{ chartSlab.annualInterestRate | formatNumber }} %</td>
|
||||||
</button>
|
</ng-container>
|
||||||
</td>
|
<ng-container matColumnDef="description">
|
||||||
</ng-container>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Description' | translate }}</th>
|
||||||
<ng-container matColumnDef="incentives">
|
<td mat-cell *matCellDef="let chartSlab">{{ chartSlab.description }}</td>
|
||||||
<td
|
</ng-container>
|
||||||
mat-cell
|
<ng-container matColumnDef="actions">
|
||||||
*matCellDef="let chartSlab; let chartSlabIndex = dataIndex"
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
||||||
[attr.colspan]="chartSlabsDisplayedColumns.length"
|
<td mat-cell *matCellDef="let chartSlab; let chartSlabIndex = dataIndex">
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
color="primary"
|
||||||
|
(click)="expandChartSlabIndex = expandChartSlabIndex === chartSlabIndex ? null : chartSlabIndex"
|
||||||
>
|
>
|
||||||
<div
|
<fa-icon
|
||||||
class="layout-row-wrap flex-100 incentives"
|
[icon]="expandChartSlabIndex === chartSlabIndex ? 'eye-slash' : 'eye'"
|
||||||
[@expandChartSlab]="chartSlabIndex === expandChartSlabIndex ? 'expanded' : 'collapsed'"
|
class="m-r-10"
|
||||||
>
|
></fa-icon>
|
||||||
<mat-card class="layout-row-wrap flex-100">
|
{{
|
||||||
<h4 class="m-b-10 flex-13">{{ 'labels.heading.Incentives' | translate }}</h4>
|
(expandChartSlabIndex === chartSlabIndex
|
||||||
<table
|
? 'labels.buttons.Hide Incentives'
|
||||||
class="flex-100 mat-elevation-z1"
|
: 'labels.buttons.View Incentives'
|
||||||
mat-table
|
) | translate
|
||||||
[dataSource]="chartSlab.incentives"
|
}}
|
||||||
*ngIf="chartSlab.incentives?.length"
|
</button>
|
||||||
>
|
</td>
|
||||||
<ng-container matColumnDef="entityType">
|
</ng-container>
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Entity Type' | translate }}</th>
|
<ng-container matColumnDef="incentives">
|
||||||
<td mat-cell *matCellDef="let incentive">{{ incentive.entityType?.value }}</td>
|
<td
|
||||||
</ng-container>
|
mat-cell
|
||||||
<ng-container matColumnDef="attributeName">
|
*matCellDef="let chartSlab; let chartSlabIndex = dataIndex"
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Attribute Name' | translate }}</th>
|
[attr.colspan]="chartSlabsDisplayedColumns?.length"
|
||||||
<td mat-cell *matCellDef="let incentive">{{ incentive.attributeName?.value }}</td>
|
>
|
||||||
</ng-container>
|
<div
|
||||||
<ng-container matColumnDef="conditionType">
|
class="layout-row-wrap flex-100 incentives"
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Condition Type' | translate }}</th>
|
[@expandChartSlab]="chartSlabIndex === expandChartSlabIndex ? 'expanded' : 'collapsed'"
|
||||||
<td mat-cell *matCellDef="let incentive">{{ incentive.conditionType?.value | titlecase }}</td>
|
>
|
||||||
</ng-container>
|
<mat-card class="layout-row-wrap flex-100">
|
||||||
<ng-container matColumnDef="attributeValue">
|
<h4 class="m-b-10 flex-13">{{ 'labels.heading.Incentives' | translate }}</h4>
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Attribute Value' | translate }}</th>
|
<table
|
||||||
<td mat-cell *matCellDef="let incentive">{{ incentive.attributeValueDesc }}</td>
|
class="flex-100 mat-elevation-z1"
|
||||||
</ng-container>
|
mat-table
|
||||||
<ng-container matColumnDef="incentiveType">
|
[dataSource]="chartSlab.incentives"
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Incentive Type' | translate }}</th>
|
*ngIf="chartSlab.incentives?.length"
|
||||||
<td mat-cell *matCellDef="let incentive">{{ incentive.incentiveType?.value }}</td>
|
>
|
||||||
</ng-container>
|
<ng-container matColumnDef="entityType">
|
||||||
<ng-container matColumnDef="amount">
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Entity Type' | translate }}</th>
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Interest' | translate }}</th>
|
<td mat-cell *matCellDef="let incentive">{{ incentive.entityType?.value }}</td>
|
||||||
<td mat-cell *matCellDef="let incentive">{{ incentive.amount }}</td>
|
</ng-container>
|
||||||
</ng-container>
|
<ng-container matColumnDef="attributeName">
|
||||||
<tr mat-header-row *matHeaderRowDef="incentivesDisplayedColumns"></tr>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Attribute Name' | translate }}</th>
|
||||||
<tr mat-row *matRowDef="let row; columns: incentivesDisplayedColumns"></tr>
|
<td mat-cell *matCellDef="let incentive">{{ incentive.attributeName?.value }}</td>
|
||||||
</table>
|
</ng-container>
|
||||||
</mat-card>
|
<ng-container matColumnDef="conditionType">
|
||||||
</div>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Condition Type' | translate }}</th>
|
||||||
</td>
|
<td mat-cell *matCellDef="let incentive">{{ incentive.conditionType?.value | titlecase }}</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<tr mat-header-row *matHeaderRowDef="chartSlabsDisplayedColumns"></tr>
|
<ng-container matColumnDef="attributeValue">
|
||||||
<tr mat-row *matRowDef="let row; columns: chartSlabsDisplayedColumns"></tr>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Attribute Value' | translate }}</th>
|
||||||
<tr
|
<td mat-cell *matCellDef="let incentive">{{ incentive.attributeValueDesc }}</td>
|
||||||
mat-row
|
</ng-container>
|
||||||
*matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns"
|
<ng-container matColumnDef="incentiveType">
|
||||||
class="incentives-row"
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Incentive Type' | translate }}</th>
|
||||||
></tr>
|
<td mat-cell *matCellDef="let incentive">{{ incentive.incentiveType?.value }}</td>
|
||||||
</table>
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Interest' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let incentive">{{ incentive.amount | formatNumber }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="incentivesDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: incentivesDisplayedColumns"></tr>
|
||||||
|
</table>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="chartSlabsDisplayedColumns"></tr>
|
||||||
|
<tr
|
||||||
|
mat-row
|
||||||
|
*matRowDef="let row; columns: chartSlabsDisplayedColumns; let i = dataIndex"
|
||||||
|
[class.preview-slab-row--matched]="i === matchedSlabIndex"
|
||||||
|
></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns" class="incentives-row"></tr>
|
||||||
|
</table>
|
||||||
|
@if (rdTenureDays && matchedSlabIndex >= 0) {
|
||||||
|
<div class="preview-slab-legend">
|
||||||
|
<span class="preview-slab-legend__dot"></span>
|
||||||
|
Highlighted row is the slab applicable for your selected tenure of
|
||||||
|
<strong>{{ selectedTenureLabel }}</strong>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+110
-17
@@ -181,32 +181,125 @@
|
|||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Preview Tenure Result Card ─────────────────────────────────────────────────
|
// ── Section badge variant for tenure ─────────────────────────────────────────
|
||||||
.preview-tenure-result {
|
.preview-section__badge--tenure {
|
||||||
border: 1px solid #e0f2fe;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #dcfce7;
|
||||||
|
color: #16a34a;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Tenure + Rate banner ──────────────────────────────────────────────────────
|
||||||
|
.preview-tenure-banner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 14px 18px;
|
||||||
|
background: #f0fdf4;
|
||||||
|
border: 1.5px solid #86efac;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
margin-bottom: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
&__row {
|
&__left {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.65rem 1rem;
|
gap: 10px;
|
||||||
border-bottom: 1px solid #f0f9ff;
|
|
||||||
|
|
||||||
&:last-child {
|
> i {
|
||||||
border-bottom: none;
|
font-size: 20px;
|
||||||
}
|
color: #16a34a;
|
||||||
|
|
||||||
&--highlight {
|
|
||||||
background: #f0f9ff;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__rate {
|
&__label {
|
||||||
font-size: 1.5rem;
|
display: block;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
display: block;
|
||||||
|
font-size: 22px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: #0369a1;
|
color: #0f172a;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 40px;
|
||||||
|
background: #86efac;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__rate-label {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__rate {
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #16a34a;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__slab {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #374151;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #16a34a;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Matched slab row highlight ────────────────────────────────────────────────
|
||||||
|
.preview-slab-row--matched td {
|
||||||
|
background: #dcfce7 !important;
|
||||||
|
border-left: 3px solid #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Legend below table ────────────────────────────────────────────────────────
|
||||||
|
.preview-slab-legend {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #4b5563;
|
||||||
|
|
||||||
|
&__dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #dcfce7;
|
||||||
|
border: 2px solid #16a34a;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+117
-11
@@ -77,10 +77,10 @@ export class RecurringDepositsAccountPreviewStepComponent implements OnChanges {
|
|||||||
@Input() rdCurrencyFlags: any = null;
|
@Input() rdCurrencyFlags: any = null;
|
||||||
@Input() rdTenureDays: number | null = null;
|
@Input() rdTenureDays: number | null = null;
|
||||||
@Input() rdApplicableSlab: any = null;
|
@Input() rdApplicableSlab: any = null;
|
||||||
@Input() set rdInterestRates(rates: any[]) {
|
@Input() rdMaturityAmount: number | null = null;
|
||||||
this._rdInterestRates = rates || [];
|
@Input() rdInterestEarned: number | null = null;
|
||||||
this._activeRdVersion = this._rdInterestRates.find((v: any) => v.isActive) ?? this._rdInterestRates[0] ?? null;
|
@Input() rdDaysInYear: number | null = null;
|
||||||
}
|
|
||||||
/** Output the submit action */
|
/** Output the submit action */
|
||||||
@Output() submitEvent = new EventEmitter();
|
@Output() submitEvent = new EventEmitter();
|
||||||
|
|
||||||
@@ -96,9 +96,6 @@ export class RecurringDepositsAccountPreviewStepComponent implements OnChanges {
|
|||||||
return this.recurringDepositsAccountProductTemplate?.currency?.inMultiplesOf ?? '—';
|
return this.recurringDepositsAccountProductTemplate?.currency?.inMultiplesOf ?? '—';
|
||||||
}
|
}
|
||||||
|
|
||||||
_rdInterestRates: any[] = [];
|
|
||||||
_activeRdVersion: any = null;
|
|
||||||
|
|
||||||
readonly rdSlabColumns = [
|
readonly rdSlabColumns = [
|
||||||
'srNo',
|
'srNo',
|
||||||
'description',
|
'description',
|
||||||
@@ -117,8 +114,57 @@ export class RecurringDepositsAccountPreviewStepComponent implements OnChanges {
|
|||||||
'date',
|
'date',
|
||||||
'repaymentsEvery'
|
'repaymentsEvery'
|
||||||
];
|
];
|
||||||
/** Interest Rate Chart Data */
|
get interestRateChartData(): any[] {
|
||||||
interestRateChartData: any = [];
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
if (!template) return [];
|
||||||
|
return (
|
||||||
|
template.accountChart?.chartSlabs ??
|
||||||
|
template.activeChart?.chartSlabs ??
|
||||||
|
template.interestRateCharts?.[0]?.chartSlabs ??
|
||||||
|
template.interestRateChart?.chartSlabs ??
|
||||||
|
template.chartSlabs ??
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Matched Slab Index for highlighting */
|
||||||
|
get matchedSlabIndex(): number {
|
||||||
|
if (!this.rdTenureDays || !this.interestRateChartData) return -1;
|
||||||
|
|
||||||
|
// Find index of the applicable slab in the chart data
|
||||||
|
return this.interestRateChartData.findIndex((slab) => {
|
||||||
|
const pTypeValue = slab.periodType?.value || 'Days';
|
||||||
|
const fromDays = this.slabPeriodToDays(slab.fromPeriod ?? 0, pTypeValue);
|
||||||
|
const toDays = slab.toPeriod != null ? this.slabPeriodToDays(slab.toPeriod, pTypeValue) : Infinity;
|
||||||
|
return this.rdTenureDays >= fromDays && this.rdTenureDays <= toDays;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Selected Tenure Label for banner */
|
||||||
|
get selectedTenureLabel(): string {
|
||||||
|
if (!this.rdTenureDays) return '';
|
||||||
|
if (this.rdTenureDays >= 365) {
|
||||||
|
const years = Math.floor(this.rdTenureDays / 365);
|
||||||
|
const rem = this.rdTenureDays % 365;
|
||||||
|
return years + (years === 1 ? ' Year' : ' Years') + (rem > 0 ? ` ${rem} Days` : '');
|
||||||
|
}
|
||||||
|
if (this.rdTenureDays >= 30) {
|
||||||
|
const months = Math.floor(this.rdTenureDays / 30);
|
||||||
|
const rem = this.rdTenureDays % 30;
|
||||||
|
return months + (months === 1 ? ' Month' : ' Months') + (rem > 0 ? ` ${rem} Days` : '');
|
||||||
|
}
|
||||||
|
return this.rdTenureDays + ' Days';
|
||||||
|
}
|
||||||
|
|
||||||
|
private slabPeriodToDays(period: number, periodTypeValue: string): number {
|
||||||
|
const p = (periodTypeValue || '').toLowerCase();
|
||||||
|
if (p.includes('day')) return period;
|
||||||
|
if (p.includes('week')) return period * 7;
|
||||||
|
if (p.includes('month')) return period * 30;
|
||||||
|
if (p.includes('year')) return period * 365;
|
||||||
|
return period;
|
||||||
|
}
|
||||||
|
|
||||||
/** Columns to be displayed in interest rate chart table. */
|
/** Columns to be displayed in interest rate chart table. */
|
||||||
chartSlabsDisplayedColumns: any[] = [
|
chartSlabsDisplayedColumns: any[] = [
|
||||||
'period',
|
'period',
|
||||||
@@ -144,8 +190,68 @@ export class RecurringDepositsAccountPreviewStepComponent implements OnChanges {
|
|||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
if (this.recurringDepositsAccountProductTemplate) {
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
this.interestRateChartData = this.recurringDepositsAccountProductTemplate.accountChart?.chartSlabs ?? [];
|
if (template) {
|
||||||
|
// Logic for metadata getters is now handled by the dynamic template reference
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Header Getters ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
get chartName(): string {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return (
|
||||||
|
template?.accountChart?.name ||
|
||||||
|
template?.activeChart?.name ||
|
||||||
|
template?.interestRateCharts?.[0]?.name ||
|
||||||
|
template?.interestRateChart?.name ||
|
||||||
|
template?.name ||
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get chartFromDate(): any {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return (
|
||||||
|
template?.accountChart?.fromDate ||
|
||||||
|
template?.activeChart?.fromDate ||
|
||||||
|
template?.interestRateCharts?.[0]?.fromDate ||
|
||||||
|
template?.interestRateChart?.fromDate ||
|
||||||
|
template?.fromDate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get chartEndDate(): any {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return (
|
||||||
|
template?.accountChart?.endDate ||
|
||||||
|
template?.activeChart?.endDate ||
|
||||||
|
template?.interestRateCharts?.[0]?.endDate ||
|
||||||
|
template?.interestRateChart?.endDate ||
|
||||||
|
template?.endDate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get chartDescription(): string {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return (
|
||||||
|
template?.accountChart?.description ||
|
||||||
|
template?.activeChart?.description ||
|
||||||
|
template?.interestRateCharts?.[0]?.description ||
|
||||||
|
template?.interestRateChart?.description ||
|
||||||
|
template?.description ||
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isPrimaryGroupingByAmount(): boolean {
|
||||||
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
||||||
|
return !!(
|
||||||
|
template?.accountChart?.isPrimaryGroupingByAmount ||
|
||||||
|
template?.activeChart?.isPrimaryGroupingByAmount ||
|
||||||
|
template?.interestRateCharts?.[0]?.isPrimaryGroupingByAmount ||
|
||||||
|
template?.interestRateChart?.isPrimaryGroupingByAmount ||
|
||||||
|
template?.isPrimaryGroupingByAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@
|
|||||||
<span>Interest Rate & Posting Terms</span>
|
<span>Interest Rate & Posting Terms</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="rdpt-body">
|
<div class="rdpt-body">
|
||||||
@if (recurringDepositsAccountProductTemplate?.nominalAnnualInterestRate != null) {
|
@if (recurringDepositsAccountProductTemplate?.nominalAnnualInterestRate !== null) {
|
||||||
<div class="rdpt-banner">
|
<div class="rdpt-banner">
|
||||||
<span class="rdpt-banner__icon">ℹ️</span>
|
<span class="rdpt-banner__icon">ℹ️</span>
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
+6
-1
@@ -92,7 +92,12 @@ export class InterestRateChartTabComponent {
|
|||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.route.parent.data.subscribe((data: { recurringDepositsAccountData: any }) => {
|
this.route.parent.data.subscribe((data: { recurringDepositsAccountData: any }) => {
|
||||||
this.interestRateChartData = data.recurringDepositsAccountData.accountChart.chartSlabs;
|
const accountData = data.recurringDepositsAccountData;
|
||||||
|
this.interestRateChartData =
|
||||||
|
accountData.accountChart?.chartSlabs ??
|
||||||
|
accountData.activeChart?.chartSlabs ??
|
||||||
|
accountData.interestRateCharts?.[0]?.chartSlabs ??
|
||||||
|
[];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,4 +173,11 @@ export class RecurringDepositsService {
|
|||||||
params: httpParams
|
params: httpParams
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Calculates the interest for a recurring deposit account
|
||||||
|
* @param data Calculation data
|
||||||
|
*/
|
||||||
|
calculateRecurringDepositInterest(data: any): Observable<any> {
|
||||||
|
return this.http.post(`/recurringdepositaccounts/calculate-rd-interest`, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ import { EditTaxComponentComponent } from './manage-tax-components/edit-tax-comp
|
|||||||
import { ViewTaxGroupComponent } from './manage-tax-groups/view-tax-group/view-tax-group.component';
|
import { ViewTaxGroupComponent } from './manage-tax-groups/view-tax-group/view-tax-group.component';
|
||||||
import { ShareProductsDividendsComponent } from './share-products/dividends-share-product/dividends.components';
|
import { ShareProductsDividendsComponent } from './share-products/dividends-share-product/dividends.components';
|
||||||
import { CreateRecurringDepositProductComponent } from './recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component';
|
import { CreateRecurringDepositProductComponent } from './recurring-deposit-products/create-recurring-deposit-product/create-recurring-deposit-product.component';
|
||||||
import { BaseRecurringDepositInterestRateChartComponent } from './recurring-deposit-products/base-recurring-deposit-interest-rate-chart/base-recurring-deposit-interest-rate-chart.component';
|
|
||||||
import { CreateDividendComponent } from './share-products/create-dividend/create-dividend.component';
|
import { CreateDividendComponent } from './share-products/create-dividend/create-dividend.component';
|
||||||
import { ViewFixedDepositProductComponent } from './fixed-deposit-products/view-fixed-deposit-product/view-fixed-deposit-product.component';
|
import { ViewFixedDepositProductComponent } from './fixed-deposit-products/view-fixed-deposit-product/view-fixed-deposit-product.component';
|
||||||
import { ViewDividendComponent } from './share-products/view-dividend/view-dividend.component';
|
import { ViewDividendComponent } from './share-products/view-dividend/view-dividend.component';
|
||||||
@@ -655,14 +654,6 @@ const routes: Routes = [
|
|||||||
path: 'recurring-deposit-products',
|
path: 'recurring-deposit-products',
|
||||||
data: { title: 'Recurring Deposit Products', breadcrumb: 'Recurring Deposit Products' },
|
data: { title: 'Recurring Deposit Products', breadcrumb: 'Recurring Deposit Products' },
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
path: 'base-interest-rates',
|
|
||||||
component: BaseRecurringDepositInterestRateChartComponent,
|
|
||||||
data: { title: 'CBS Base Interest Rates', breadcrumb: 'Base Interest Rates' },
|
|
||||||
resolve: {
|
|
||||||
recurringDepositProductsTemplate: RecurringDepositProductsTemplateResolver
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
component: CreateRecurringDepositProductComponent,
|
component: CreateRecurringDepositProductComponent,
|
||||||
@@ -717,14 +708,6 @@ const routes: Routes = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'interest-rates',
|
|
||||||
component: RdInterestRatesTabComponent,
|
|
||||||
data: { title: 'CBS Base Chart', breadcrumb: 'CBS Base Chart', routeParamBreadcrumb: false },
|
|
||||||
resolve: {
|
|
||||||
rdInterestRates: rdInterestRatesResolver
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
+100
-7
@@ -34,6 +34,7 @@ import {
|
|||||||
import { ProductsService } from 'app/products/products.service';
|
import { ProductsService } from 'app/products/products.service';
|
||||||
import { SettingsService } from 'app/settings/settings.service';
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
import { Accounting } from 'app/core/utils/accounting';
|
import { Accounting } from 'app/core/utils/accounting';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { RecurringDepositProductPreviewStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component';
|
import { RecurringDepositProductPreviewStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component';
|
||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
@@ -72,6 +73,7 @@ export class CreateRecurringDepositProductComponent {
|
|||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private settingsService = inject(SettingsService);
|
private settingsService = inject(SettingsService);
|
||||||
private accounting = inject(Accounting);
|
private accounting = inject(Accounting);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
|
||||||
@ViewChild(RecurringDepositProductDetailsStepComponent, { static: true })
|
@ViewChild(RecurringDepositProductDetailsStepComponent, { static: true })
|
||||||
recurringDepositProductDetailsStep: RecurringDepositProductDetailsStepComponent;
|
recurringDepositProductDetailsStep: RecurringDepositProductDetailsStepComponent;
|
||||||
@@ -112,7 +114,7 @@ export class CreateRecurringDepositProductComponent {
|
|||||||
});
|
});
|
||||||
this.accountingRuleData = this.accounting.getAccountingRulesForSavings();
|
this.accountingRuleData = this.accounting.getAccountingRulesForSavings();
|
||||||
// Load the CBS base chart as the reference for the editable table
|
// Load the CBS base chart as the reference for the editable table
|
||||||
this.productsService.getRdBaseInterestRates().subscribe({
|
this.productsService.getFdBaseInterestRates().subscribe({
|
||||||
next: (rates: any[]) => {
|
next: (rates: any[]) => {
|
||||||
this.rdInterestRates = rates || [];
|
this.rdInterestRates = rates || [];
|
||||||
},
|
},
|
||||||
@@ -151,7 +153,18 @@ export class CreateRecurringDepositProductComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get activeBaseVersion() {
|
get activeBaseVersion() {
|
||||||
return this.rdInterestRates.find((version: any) => version.isActive) ?? this.rdInterestRates[0] ?? null;
|
const chart =
|
||||||
|
this.rdInterestRates.find((version: any) => version.fdType === 'DOUBLE' && version.isActive) ??
|
||||||
|
this.rdInterestRates.find((version: any) => version.fdType === 'DOUBLE') ??
|
||||||
|
this.rdInterestRates[0];
|
||||||
|
if (chart) {
|
||||||
|
return {
|
||||||
|
...chart,
|
||||||
|
versionLabel: chart.versionLabel?.replace('FD', 'RD'),
|
||||||
|
remarks: chart.remarks?.replace('FD', 'RD')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeBaseSlabs() {
|
get activeBaseSlabs() {
|
||||||
@@ -166,17 +179,66 @@ export class CreateRecurringDepositProductComponent {
|
|||||||
...this.recurringDepositProductSettingsStep.recurringDepositProductSettings,
|
...this.recurringDepositProductSettingsStep.recurringDepositProductSettings,
|
||||||
...this.recurringDepositProductChargesStep.recurringDepositProductCharges,
|
...this.recurringDepositProductChargesStep.recurringDepositProductCharges,
|
||||||
...this.recurringDepositProductAccountingStep.recurringDepositProductAccounting,
|
...this.recurringDepositProductAccountingStep.recurringDepositProductAccounting,
|
||||||
|
charts: this.recurringDepositProductCharts,
|
||||||
rdSlabs: this.activeBaseSlabs
|
rdSlabs: this.activeBaseSlabs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get recurringDepositProductCharts() {
|
||||||
|
const activeBaseVersion = this.activeBaseVersion;
|
||||||
|
if (!activeBaseVersion?.slabs?.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: activeBaseVersion.versionLabel || 'CBS Base Chart',
|
||||||
|
description: activeBaseVersion.remarks || activeBaseVersion.versionLabel || 'CBS base chart',
|
||||||
|
fromDate:
|
||||||
|
this.dateUtils.formatDate(activeBaseVersion.effectiveFromDate, dateFormat) ||
|
||||||
|
activeBaseVersion.effectiveFromDate,
|
||||||
|
...(activeBaseVersion.effectiveToDate
|
||||||
|
? {
|
||||||
|
endDate:
|
||||||
|
this.dateUtils.formatDate(activeBaseVersion.effectiveToDate, dateFormat) ||
|
||||||
|
activeBaseVersion.effectiveToDate
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
isPrimaryGroupingByAmount: false,
|
||||||
|
locale: this.settingsService.language.code,
|
||||||
|
dateFormat,
|
||||||
|
chartSlabs: activeBaseVersion.slabs.map((slab: any) => {
|
||||||
|
const chartSlab: any = {
|
||||||
|
periodType: slab.periodTypeEnum,
|
||||||
|
fromPeriod: slab.fromPeriod,
|
||||||
|
toPeriod: slab.toPeriod,
|
||||||
|
annualInterestRate: slab.annualInterestRate,
|
||||||
|
description: slab.description || '',
|
||||||
|
incentives: [] as any[]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (slab.amountRangeFrom !== null && slab.amountRangeFrom !== undefined && slab.amountRangeFrom !== '') {
|
||||||
|
chartSlab.amountRangeFrom = slab.amountRangeFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slab.amountRangeTo !== null && slab.amountRangeTo !== undefined && slab.amountRangeTo !== '') {
|
||||||
|
chartSlab.amountRangeTo = slab.amountRangeTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chartSlab;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
] as any[];
|
||||||
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
// TODO: Update once language and date settings are setup
|
const recurringDepositProduct: any = this.pruneEmptyValues({
|
||||||
const recurringDepositProduct = {
|
|
||||||
...this.recurringDepositProduct,
|
...this.recurringDepositProduct,
|
||||||
charges: this.recurringDepositProduct.charges.map((charge: any) => ({ id: charge.id })),
|
charts: this.recurringDepositProductCharts,
|
||||||
locale: this.settingsService.language.code // locale required for depositAmount
|
charges: (this.recurringDepositProduct.charges ?? []).map((charge: any) => ({ id: charge.id })),
|
||||||
};
|
locale: this.settingsService.language.code
|
||||||
|
});
|
||||||
if (!recurringDepositProduct.description) {
|
if (!recurringDepositProduct.description) {
|
||||||
recurringDepositProduct.description = '';
|
recurringDepositProduct.description = '';
|
||||||
}
|
}
|
||||||
@@ -191,4 +253,35 @@ export class CreateRecurringDepositProductComponent {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private pruneEmptyValues<T>(value: T): T {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map((item) => this.pruneEmptyValues(item)).filter((item) => item !== undefined) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value && typeof value === 'object') {
|
||||||
|
return Object.entries(value as Record<string, any>).reduce(
|
||||||
|
(acc, [
|
||||||
|
key,
|
||||||
|
currentValue
|
||||||
|
]) => {
|
||||||
|
const cleanedValue = this.pruneEmptyValues(currentValue);
|
||||||
|
if (cleanedValue === undefined) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[key]: cleanedValue
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{} as Record<string, any>
|
||||||
|
) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === '' || value === null || value === undefined) {
|
||||||
|
return undefined as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-2
@@ -34,6 +34,7 @@ import {
|
|||||||
import { ProductsService } from 'app/products/products.service';
|
import { ProductsService } from 'app/products/products.service';
|
||||||
import { SettingsService } from 'app/settings/settings.service';
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
import { Accounting } from 'app/core/utils/accounting';
|
import { Accounting } from 'app/core/utils/accounting';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { RecurringDepositProductPreviewStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component';
|
import { RecurringDepositProductPreviewStepComponent } from '../recurring-deposit-product-stepper/recurring-deposit-product-preview-step/recurring-deposit-product-preview-step.component';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
@@ -70,6 +71,7 @@ export class EditRecurringDepositProductComponent {
|
|||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private settingsService = inject(SettingsService);
|
private settingsService = inject(SettingsService);
|
||||||
private accounting = inject(Accounting);
|
private accounting = inject(Accounting);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
|
||||||
@ViewChild(RecurringDepositProductDetailsStepComponent, { static: true })
|
@ViewChild(RecurringDepositProductDetailsStepComponent, { static: true })
|
||||||
recurringDepositProductDetailsStep: RecurringDepositProductDetailsStepComponent;
|
recurringDepositProductDetailsStep: RecurringDepositProductDetailsStepComponent;
|
||||||
@@ -107,7 +109,7 @@ export class EditRecurringDepositProductComponent {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.route.data.subscribe((data: { recurringDepositProductAndTemplate: any }) => {
|
this.route.data.subscribe((data: { recurringDepositProductAndTemplate: any }) => {
|
||||||
this.recurringDepositProductsTemplate = data.recurringDepositProductAndTemplate;
|
this.recurringDepositProductsTemplate = data.recurringDepositProductAndTemplate;
|
||||||
this.productsService.getRdBaseInterestRates().subscribe({
|
this.productsService.getFdBaseInterestRates().subscribe({
|
||||||
next: (rates: any[]) => {
|
next: (rates: any[]) => {
|
||||||
this.rdInterestRates = rates || [];
|
this.rdInterestRates = rates || [];
|
||||||
},
|
},
|
||||||
@@ -148,7 +150,18 @@ export class EditRecurringDepositProductComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get activeBaseVersion() {
|
get activeBaseVersion() {
|
||||||
return this.rdInterestRates.find((version: any) => version.isActive) ?? this.rdInterestRates[0] ?? null;
|
const chart =
|
||||||
|
this.rdInterestRates.find((version: any) => version.fdType === 'DOUBLE' && version.isActive) ??
|
||||||
|
this.rdInterestRates.find((version: any) => version.fdType === 'DOUBLE') ??
|
||||||
|
this.rdInterestRates[0];
|
||||||
|
if (chart) {
|
||||||
|
return {
|
||||||
|
...chart,
|
||||||
|
versionLabel: chart.versionLabel?.replace('FD', 'RD'),
|
||||||
|
remarks: chart.remarks?.replace('FD', 'RD')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeBaseSlabs() {
|
get activeBaseSlabs() {
|
||||||
@@ -163,6 +176,7 @@ export class EditRecurringDepositProductComponent {
|
|||||||
...this.recurringDepositProductSettingsStep.recurringDepositProductSettings,
|
...this.recurringDepositProductSettingsStep.recurringDepositProductSettings,
|
||||||
...this.recurringDepositProductChargesStep.recurringDepositProductCharges,
|
...this.recurringDepositProductChargesStep.recurringDepositProductCharges,
|
||||||
...this.recurringDepositProductAccountingStep.recurringDepositProductAccounting,
|
...this.recurringDepositProductAccountingStep.recurringDepositProductAccounting,
|
||||||
|
charts: this.recurringDepositProductCharts,
|
||||||
rdSlabs: this.activeBaseSlabs
|
rdSlabs: this.activeBaseSlabs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -170,13 +184,63 @@ export class EditRecurringDepositProductComponent {
|
|||||||
get recurringDepositProductPreview() {
|
get recurringDepositProductPreview() {
|
||||||
return {
|
return {
|
||||||
...this.recurringDepositProduct,
|
...this.recurringDepositProduct,
|
||||||
|
charts: this.recurringDepositProductCharts,
|
||||||
rdSlabs: this.activeBaseSlabs
|
rdSlabs: this.activeBaseSlabs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get recurringDepositProductCharts() {
|
||||||
|
const activeBaseVersion = this.activeBaseVersion;
|
||||||
|
if (!activeBaseVersion?.slabs?.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: activeBaseVersion.versionLabel || 'CBS Base Chart',
|
||||||
|
description: activeBaseVersion.remarks || activeBaseVersion.versionLabel || 'CBS base chart',
|
||||||
|
fromDate:
|
||||||
|
this.dateUtils.formatDate(activeBaseVersion.effectiveFromDate, dateFormat) ||
|
||||||
|
activeBaseVersion.effectiveFromDate,
|
||||||
|
...(activeBaseVersion.effectiveToDate
|
||||||
|
? {
|
||||||
|
endDate:
|
||||||
|
this.dateUtils.formatDate(activeBaseVersion.effectiveToDate, dateFormat) ||
|
||||||
|
activeBaseVersion.effectiveToDate
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
isPrimaryGroupingByAmount: false,
|
||||||
|
locale: this.settingsService.language.code,
|
||||||
|
dateFormat,
|
||||||
|
chartSlabs: activeBaseVersion.slabs.map((slab: any) => {
|
||||||
|
const chartSlab: any = {
|
||||||
|
periodType: slab.periodTypeEnum,
|
||||||
|
fromPeriod: slab.fromPeriod,
|
||||||
|
toPeriod: slab.toPeriod,
|
||||||
|
annualInterestRate: slab.annualInterestRate,
|
||||||
|
description: slab.description || '',
|
||||||
|
incentives: [] as any[]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (slab.amountRangeFrom !== null && slab.amountRangeFrom !== undefined && slab.amountRangeFrom !== '') {
|
||||||
|
chartSlab.amountRangeFrom = slab.amountRangeFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slab.amountRangeTo !== null && slab.amountRangeTo !== undefined && slab.amountRangeTo !== '') {
|
||||||
|
chartSlab.amountRangeTo = slab.amountRangeTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chartSlab;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
] as any[];
|
||||||
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
const recurringDepositProduct: any = this.pruneEmptyValues({
|
const recurringDepositProduct: any = this.pruneEmptyValues({
|
||||||
...this.recurringDepositProduct,
|
...this.recurringDepositProduct,
|
||||||
|
charts: this.recurringDepositProductCharts,
|
||||||
charges: (this.recurringDepositProduct.charges ?? []).map((charge: any) => ({ id: charge.id })),
|
charges: (this.recurringDepositProduct.charges ?? []).map((charge: any) => ({ id: charge.id })),
|
||||||
locale: this.settingsService.language.code
|
locale: this.settingsService.language.code
|
||||||
});
|
});
|
||||||
|
|||||||
-22
@@ -63,28 +63,6 @@
|
|||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rdpc-alert">
|
|
||||||
<div class="rdpc-alert__content">
|
|
||||||
<h4>Balance Swap Option</h4>
|
|
||||||
<p>Enable automated transfer of excess funds to linked deposits or vice-versa to maintain liquidity.</p>
|
|
||||||
</div>
|
|
||||||
<mat-radio-group class="rdpc-alert__choices" formControlName="balanceSwapOption">
|
|
||||||
<mat-radio-button [value]="true">yes</mat-radio-button>
|
|
||||||
<mat-radio-button [value]="false">no</mat-radio-button>
|
|
||||||
</mat-radio-group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rdpc-alert">
|
|
||||||
<div class="rdpc-alert__content">
|
|
||||||
<h4>MSME</h4>
|
|
||||||
<p>Micro, Small and Medium Enterprises</p>
|
|
||||||
</div>
|
|
||||||
<mat-radio-group class="rdpc-alert__choices" formControlName="msme">
|
|
||||||
<mat-radio-button [value]="true">yes</mat-radio-button>
|
|
||||||
<mat-radio-button [value]="false">no</mat-radio-button>
|
|
||||||
</mat-radio-group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rdpc-alert">
|
<div class="rdpc-alert">
|
||||||
<div class="rdpc-alert__content">
|
<div class="rdpc-alert__content">
|
||||||
<h4>Nomination Facility</h4>
|
<h4>Nomination Facility</h4>
|
||||||
|
|||||||
-6
@@ -55,8 +55,6 @@ export class RecurringDepositProductCurrencyStepComponent implements OnInit {
|
|||||||
daysToInactive: this.recurringDepositProductsTemplate.daysToInactive ?? 30,
|
daysToInactive: this.recurringDepositProductsTemplate.daysToInactive ?? 30,
|
||||||
daysToDormancy: this.recurringDepositProductsTemplate.daysToDormancy ?? 60,
|
daysToDormancy: this.recurringDepositProductsTemplate.daysToDormancy ?? 60,
|
||||||
daysToEscheat: this.recurringDepositProductsTemplate.daysToEscheat ?? 90,
|
daysToEscheat: this.recurringDepositProductsTemplate.daysToEscheat ?? 90,
|
||||||
balanceSwapOption: this.recurringDepositProductsTemplate.balanceSwapOption ?? false,
|
|
||||||
msme: this.recurringDepositProductsTemplate.msme ?? false,
|
|
||||||
nominationFacility: this.recurringDepositProductsTemplate.nominationFacility ?? false,
|
nominationFacility: this.recurringDepositProductsTemplate.nominationFacility ?? false,
|
||||||
autoDebitFacility: this.recurringDepositProductsTemplate.autoDebitFacility ?? false
|
autoDebitFacility: this.recurringDepositProductsTemplate.autoDebitFacility ?? false
|
||||||
});
|
});
|
||||||
@@ -80,8 +78,6 @@ export class RecurringDepositProductCurrencyStepComponent implements OnInit {
|
|||||||
daysToInactive: [30],
|
daysToInactive: [30],
|
||||||
daysToDormancy: [60],
|
daysToDormancy: [60],
|
||||||
daysToEscheat: [90],
|
daysToEscheat: [90],
|
||||||
balanceSwapOption: [false],
|
|
||||||
msme: [false],
|
|
||||||
nominationFacility: [false],
|
nominationFacility: [false],
|
||||||
autoDebitFacility: [false]
|
autoDebitFacility: [false]
|
||||||
});
|
});
|
||||||
@@ -93,8 +89,6 @@ export class RecurringDepositProductCurrencyStepComponent implements OnInit {
|
|||||||
currencyCode: formValue.currencyCode,
|
currencyCode: formValue.currencyCode,
|
||||||
digitsAfterDecimal: formValue.digitsAfterDecimal,
|
digitsAfterDecimal: formValue.digitsAfterDecimal,
|
||||||
dormancyAlert: formValue.dormancyAlert,
|
dormancyAlert: formValue.dormancyAlert,
|
||||||
balanceSwapOption: formValue.balanceSwapOption,
|
|
||||||
msme: formValue.msme,
|
|
||||||
nominationFacility: formValue.nominationFacility,
|
nominationFacility: formValue.nominationFacility,
|
||||||
autoDebitFacility: formValue.autoDebitFacility
|
autoDebitFacility: formValue.autoDebitFacility
|
||||||
};
|
};
|
||||||
|
|||||||
-9
@@ -78,15 +78,6 @@
|
|||||||
<mat-error>Account Type <strong>is required</strong></mat-error>
|
<mat-error>Account Type <strong>is required</strong></mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="rdpd-field">
|
|
||||||
<mat-label>Customer Segment</mat-label>
|
|
||||||
<mat-select formControlName="customerSegment">
|
|
||||||
@for (customerSegment of customerSegmentData; track customerSegment) {
|
|
||||||
<mat-option [value]="customerSegment">{{ customerSegment }}</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="rdpd-field">
|
<mat-form-field appearance="outline" class="rdpd-field">
|
||||||
<mat-label>GL Account (Liability)</mat-label>
|
<mat-label>GL Account (Liability)</mat-label>
|
||||||
<mat-select formControlName="glAccountId" [compareWith]="compareById">
|
<mat-select formControlName="glAccountId" [compareWith]="compareById">
|
||||||
|
|||||||
-3
@@ -72,7 +72,6 @@ export class RecurringDepositProductDetailsStepComponent implements OnInit, OnCh
|
|||||||
null,
|
null,
|
||||||
Validators.required
|
Validators.required
|
||||||
],
|
],
|
||||||
customerSegment: [null],
|
|
||||||
glAccountId: [null],
|
glAccountId: [null],
|
||||||
effectiveTo: [''],
|
effectiveTo: [''],
|
||||||
endDate: [''],
|
endDate: [''],
|
||||||
@@ -107,7 +106,6 @@ export class RecurringDepositProductDetailsStepComponent implements OnInit, OnCh
|
|||||||
|
|
||||||
private applyTemplate(template: any) {
|
private applyTemplate(template: any) {
|
||||||
this.accountTypeData = this.getOptionNames(template.accountTypeOptions);
|
this.accountTypeData = this.getOptionNames(template.accountTypeOptions);
|
||||||
this.customerSegmentData = this.getOptionNames(template.customerSegmentOptions);
|
|
||||||
this.liabilityAccountOptions = template.accountingMappingOptions?.liabilityAccountOptions ?? [];
|
this.liabilityAccountOptions = template.accountingMappingOptions?.liabilityAccountOptions ?? [];
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
|
|
||||||
@@ -117,7 +115,6 @@ export class RecurringDepositProductDetailsStepComponent implements OnInit, OnCh
|
|||||||
shortName: isEditing ? template.shortName : template.shortName || '',
|
shortName: isEditing ? template.shortName : template.shortName || '',
|
||||||
description: template.description || '',
|
description: template.description || '',
|
||||||
accountType: template.accountType ?? this.accountTypeData[0] ?? null,
|
accountType: template.accountType ?? this.accountTypeData[0] ?? null,
|
||||||
customerSegment: template.customerSegment ?? this.customerSegmentData[0] ?? null,
|
|
||||||
glAccountId: template.glAccountId ?? null,
|
glAccountId: template.glAccountId ?? null,
|
||||||
effectiveTo: this.parseDate(template.effectiveTo),
|
effectiveTo: this.parseDate(template.effectiveTo),
|
||||||
endDate: this.parseDate(template.endDate),
|
endDate: this.parseDate(template.endDate),
|
||||||
|
|||||||
+25
-23
@@ -1,4 +1,4 @@
|
|||||||
<!--
|
<!--
|
||||||
Copyright since 2025 Mifos Initiative
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
@@ -46,13 +46,6 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (recurringDepositProduct.customerSegment) {
|
|
||||||
<div class="flex-100 layout-row">
|
|
||||||
<span class="flex-40">Customer Segment:</span>
|
|
||||||
<span class="flex-60">{{ recurringDepositProduct.customerSegment }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (recurringDepositProduct.glAccountId) {
|
@if (recurringDepositProduct.glAccountId) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">GL Account (Liability):</span>
|
<span class="flex-40">GL Account (Liability):</span>
|
||||||
@@ -116,20 +109,6 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (recurringDepositProduct.balanceSwapOption !== undefined) {
|
|
||||||
<div class="flex-100 layout-row">
|
|
||||||
<span class="flex-40">Balance Swap Option:</span>
|
|
||||||
<span class="flex-60">{{ recurringDepositProduct.balanceSwapOption | yesNo }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (recurringDepositProduct.msme !== undefined) {
|
|
||||||
<div class="flex-100 layout-row">
|
|
||||||
<span class="flex-40">MSME:</span>
|
|
||||||
<span class="flex-60">{{ recurringDepositProduct.msme | yesNo }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (recurringDepositProduct.nominationFacility !== undefined) {
|
@if (recurringDepositProduct.nominationFacility !== undefined) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Nomination Facility:</span>
|
<span class="flex-40">Nomination Facility:</span>
|
||||||
@@ -144,6 +123,28 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (
|
||||||
|
recurringDepositProduct.minDepositAmount !== undefined || recurringDepositProduct.maxDepositAmount !== undefined
|
||||||
|
) {
|
||||||
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
|
<h3 class="mat-h3 flex-100">Deposit Amount</h3>
|
||||||
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
|
|
||||||
|
@if (recurringDepositProduct.minDepositAmount !== undefined) {
|
||||||
|
<div class="flex-100 layout-row">
|
||||||
|
<span class="flex-40">Minimum:</span>
|
||||||
|
<span class="flex-60">{{ recurringDepositProduct.minDepositAmount }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (recurringDepositProduct.maxDepositAmount !== undefined) {
|
||||||
|
<div class="flex-100 layout-row">
|
||||||
|
<span class="flex-40">Maximum:</span>
|
||||||
|
<span class="flex-60">{{ recurringDepositProduct.maxDepositAmount }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<!-- ── Terms ── -->
|
<!-- ── Terms ── -->
|
||||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Terms' | translate }}</h3>
|
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Terms' | translate }}</h3>
|
||||||
<mat-divider class="flex-100"></mat-divider>
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
@@ -324,8 +325,9 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ── Interest Rate Slabs ── -->
|
<!-- ── Interest Rate Slabs ── -->
|
||||||
@if (recurringDepositProduct.rdSlabs?.length) {
|
@if (recurringDepositProduct.rdSlabs?.length) {
|
||||||
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Interest Rate Charts' | translate }}</h3>
|
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Interest Rate Charts' | translate }}</h3>
|
||||||
<mat-divider class="flex-100"></mat-divider>
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
|
|
||||||
|
|||||||
+39
@@ -7,6 +7,25 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="recurringDepositProductTermsForm">
|
<form [formGroup]="recurringDepositProductTermsForm">
|
||||||
|
<div class="rdpt-section">
|
||||||
|
<div class="rdpt-section__header">
|
||||||
|
<span class="rdpt-section__badge"><i class="fas fa-coins"></i></span>
|
||||||
|
<span>Deposit Amount</span>
|
||||||
|
</div>
|
||||||
|
<div class="rdpt-body">
|
||||||
|
<div class="rdpt-grid rdpt-grid--three">
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>Minimum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minDepositAmount" min="0" step="0.01" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>Maximum</mat-label>
|
||||||
|
<input type="number" matInput formControlName="maxDepositAmount" min="0" step="0.01" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="rdpt-section">
|
<div class="rdpt-section">
|
||||||
<div class="rdpt-section__header">
|
<div class="rdpt-section__header">
|
||||||
<span class="rdpt-section__badge"><i class="fas fa-chart-line"></i></span>
|
<span class="rdpt-section__badge"><i class="fas fa-chart-line"></i></span>
|
||||||
@@ -78,6 +97,26 @@
|
|||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>Interest Compounding Period</mat-label>
|
||||||
|
<mat-select formControlName="interestCompoundingPeriodType" required>
|
||||||
|
@for (opt of interestCompoundingPeriodTypeData; track opt.id) {
|
||||||
|
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>Interest Compounding Period <strong>is required</strong></mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>Days in Year</mat-label>
|
||||||
|
<mat-select formControlName="interestCalculationDaysInYearType" required>
|
||||||
|
@for (opt of interestCalculationDaysInYearTypeData; track opt.id) {
|
||||||
|
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>Days in Year <strong>is required</strong></mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
@if (recurringDepositProductTermsForm.value.interestCalculationType === 2) {
|
@if (recurringDepositProductTermsForm.value.interestCalculationType === 2) {
|
||||||
<mat-form-field appearance="outline" class="rdpt-field">
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
<mat-label>Average Period</mat-label>
|
<mat-label>Average Period</mat-label>
|
||||||
|
|||||||
+8
-10
@@ -85,7 +85,9 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
|||||||
frequencyFixDay: isEditing ? (this.recurringDepositProductsTemplate.frequencyFixDay ?? 30) : 30,
|
frequencyFixDay: isEditing ? (this.recurringDepositProductsTemplate.frequencyFixDay ?? 30) : 30,
|
||||||
lastInterestDate: this.recurringDepositProductsTemplate.lastInterestDate
|
lastInterestDate: this.recurringDepositProductsTemplate.lastInterestDate
|
||||||
? this.parseDate(this.recurringDepositProductsTemplate.lastInterestDate)
|
? this.parseDate(this.recurringDepositProductsTemplate.lastInterestDate)
|
||||||
: ''
|
: '',
|
||||||
|
minDepositAmount: this.recurringDepositProductsTemplate.minDepositAmount ?? '',
|
||||||
|
maxDepositAmount: this.recurringDepositProductsTemplate.maxDepositAmount ?? ''
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +147,9 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
|||||||
30,
|
30,
|
||||||
[Validators.min(0)]
|
[Validators.min(0)]
|
||||||
],
|
],
|
||||||
lastInterestDate: ['']
|
lastInterestDate: [''],
|
||||||
|
minDepositAmount: [''],
|
||||||
|
maxDepositAmount: ['']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,14 +160,8 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
|||||||
if (formValue.effectiveDate instanceof Date) {
|
if (formValue.effectiveDate instanceof Date) {
|
||||||
formValue.effectiveDate = this.dateUtils.formatDate(formValue.effectiveDate, dateFormat) || '';
|
formValue.effectiveDate = this.dateUtils.formatDate(formValue.effectiveDate, dateFormat) || '';
|
||||||
}
|
}
|
||||||
if (formValue.lastInterestDate instanceof Date) {
|
formValue.minDepositAmount = formValue.minDepositAmount;
|
||||||
formValue.lastInterestDate = this.dateUtils.formatDate(formValue.lastInterestDate, dateFormat) || '';
|
formValue.maxDepositAmount = formValue.maxDepositAmount;
|
||||||
}
|
|
||||||
for (const key in formValue) {
|
|
||||||
if (formValue[key] === '') {
|
|
||||||
delete formValue[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return formValue;
|
return formValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,6 @@
|
|||||||
{{ 'labels.buttons.Create Recurring Deposit Product' | translate }}
|
{{ 'labels.buttons.Create Recurring Deposit Product' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="in-block">
|
|
||||||
<button
|
|
||||||
mat-stroked-button
|
|
||||||
color="primary"
|
|
||||||
[routerLink]="['/products/recurring-deposit-products/base-interest-rates']"
|
|
||||||
matTooltip="Open the CBS base interest chart"
|
|
||||||
matTooltipPosition="above"
|
|
||||||
>
|
|
||||||
<fa-icon icon="chart-line" class="m-r-10"></fa-icon>
|
|
||||||
CBS Base Chart
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|||||||
-14
@@ -48,12 +48,6 @@
|
|||||||
<span class="flex-60">{{ recurringDepositProduct.accountType }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.accountType }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (recurringDepositProduct.customerSegment) {
|
|
||||||
<div class="flex-100 layout-row">
|
|
||||||
<span class="flex-40">Customer Segment:</span>
|
|
||||||
<span class="flex-60">{{ recurringDepositProduct.customerSegment }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if (recurringDepositProduct.glAccountId) {
|
@if (recurringDepositProduct.glAccountId) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">GL Account (Liability):</span>
|
<span class="flex-40">GL Account (Liability):</span>
|
||||||
@@ -99,14 +93,6 @@
|
|||||||
<span class="flex-40">Dormancy Alert:</span>
|
<span class="flex-40">Dormancy Alert:</span>
|
||||||
<span class="flex-60">{{ recurringDepositProduct.dormancyAlert | yesNo }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.dormancyAlert | yesNo }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-100 layout-row">
|
|
||||||
<span class="flex-40">Balance Swap Option:</span>
|
|
||||||
<span class="flex-60">{{ recurringDepositProduct.balanceSwapOption | yesNo }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex-100 layout-row">
|
|
||||||
<span class="flex-40">MSME:</span>
|
|
||||||
<span class="flex-60">{{ recurringDepositProduct.msme | yesNo }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Nomination Facility:</span>
|
<span class="flex-40">Nomination Facility:</span>
|
||||||
<span class="flex-60">{{ recurringDepositProduct.nominationFacility | yesNo }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.nominationFacility | yesNo }}</span>
|
||||||
|
|||||||
-9
@@ -18,15 +18,6 @@
|
|||||||
>
|
>
|
||||||
{{ 'labels.heading.General' | translate }}
|
{{ 'labels.heading.General' | translate }}
|
||||||
</a>
|
</a>
|
||||||
<a
|
|
||||||
mat-tab-link
|
|
||||||
[routerLink]="['./interest-rates']"
|
|
||||||
routerLinkActive
|
|
||||||
#interestRates="routerLinkActive"
|
|
||||||
[active]="interestRates.isActive"
|
|
||||||
>
|
|
||||||
CBS Base Chart
|
|
||||||
</a>
|
|
||||||
@for (recurringDepositDatatable of recurringDepositDatatables; track recurringDepositDatatable) {
|
@for (recurringDepositDatatable of recurringDepositDatatables; track recurringDepositDatatable) {
|
||||||
<a
|
<a
|
||||||
mat-tab-link
|
mat-tab-link
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import { VersionService } from 'app/system/version.service';
|
|||||||
/** Environment Configuration */
|
/** Environment Configuration */
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { NgClass, DatePipe } from '@angular/common';
|
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,9 +29,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
templateUrl: './footer.component.html',
|
templateUrl: './footer.component.html',
|
||||||
styleUrls: ['./footer.component.scss'],
|
styleUrls: ['./footer.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
...STANDALONE_SHARED_IMPORTS,
|
...STANDALONE_SHARED_IMPORTS
|
||||||
NgClass,
|
|
||||||
DatePipe
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class FooterComponent implements OnInit, OnDestroy {
|
export class FooterComponent implements OnInit, OnDestroy {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||||
import { MatButton, MatIconButton } from '@angular/material/button';
|
import { MatButton, MatIconButton } from '@angular/material/button';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { MatCard, MatCardTitle, MatCardContent } from '@angular/material/card';
|
import { MatCard, MatCardContent } from '@angular/material/card';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
@@ -41,7 +41,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
imports: [
|
imports: [
|
||||||
...STANDALONE_SHARED_IMPORTS,
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
FaIconComponent,
|
FaIconComponent,
|
||||||
MatCardTitle,
|
|
||||||
MatCheckbox,
|
MatCheckbox,
|
||||||
MatIconButton,
|
MatIconButton,
|
||||||
MatTooltip
|
MatTooltip
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { MatTabNav, MatTabLink, MatTabNavPanel } from '@angular/material/tabs';
|
import { MatTabNav, MatTabLink, MatTabNavPanel } from '@angular/material/tabs';
|
||||||
import { MatDivider } from '@angular/material/divider';
|
|
||||||
import { RouterLinkActive, RouterLink, RouterOutlet } from '@angular/router';
|
import { RouterLinkActive, RouterLink, RouterOutlet } from '@angular/router';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
@@ -26,8 +25,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
MatTabLink,
|
MatTabLink,
|
||||||
RouterLinkActive,
|
RouterLinkActive,
|
||||||
MatTabNavPanel,
|
MatTabNavPanel,
|
||||||
RouterOutlet,
|
RouterOutlet
|
||||||
MatDivider
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CheckerInboxAndTasksComponent {
|
export class CheckerInboxAndTasksComponent {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
|||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatIconButton } from '@angular/material/button';
|
import { MatIconButton } from '@angular/material/button';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
|
||||||
|
|
||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { UsersService } from '../users.service';
|
import { UsersService } from '../users.service';
|
||||||
@@ -35,8 +34,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
...STANDALONE_SHARED_IMPORTS,
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
FaIconComponent,
|
FaIconComponent,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
MatIconButton,
|
MatIconButton
|
||||||
MatTooltip
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ViewUserComponent {
|
export class ViewUserComponent {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
export default {
|
export default {
|
||||||
'mifos_x': {
|
'mifos_x': {
|
||||||
'version': '260507',
|
'version': '260508',
|
||||||
'hash': 'a12ba20d'
|
'hash': '19c0e98e'
|
||||||
},
|
},
|
||||||
'allow_switching_backend_instance': true
|
'allow_switching_backend_instance': true
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user