saving , current and RD account code update
This commit is contained in:
+45
-4
@@ -34,6 +34,7 @@
|
|||||||
<fa-icon icon="eye" size="sm"></fa-icon>
|
<fa-icon icon="eye" size="sm"></fa-icon>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<!-- Step 1: Details -->
|
||||||
<mat-step [stepControl]="currentAccountDetailsForm">
|
<mat-step [stepControl]="currentAccountDetailsForm">
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
||||||
|
|
||||||
@@ -44,14 +45,43 @@
|
|||||||
</mifosx-current-account-details-step>
|
</mifosx-current-account-details-step>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
<mat-step [stepControl]="currentAccountTermsForm">
|
<!-- Step 2: Currency -->
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>CURRENCY</ng-template>
|
||||||
|
|
||||||
<mifosx-current-account-terms-step
|
<mifosx-current-account-currency-step
|
||||||
[currentAccountProductTemplate]="currentAccountProductTemplate"
|
[currentAccountProductTemplate]="currentAccountProductTemplate"
|
||||||
></mifosx-current-account-terms-step>
|
></mifosx-current-account-currency-step>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 3: Term (Interest Rate) -->
|
||||||
|
<mat-step [stepControl]="currentAccountTermForm">
|
||||||
|
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
||||||
|
|
||||||
|
<mifosx-current-account-term-step
|
||||||
|
[currentAccountProductTemplate]="currentAccountProductTemplate"
|
||||||
|
></mifosx-current-account-term-step>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 4: Settings -->
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>SETTINGS</ng-template>
|
||||||
|
|
||||||
|
<mifosx-current-account-settings-step
|
||||||
|
[currentAccountProductTemplate]="currentAccountProductTemplate"
|
||||||
|
></mifosx-current-account-settings-step>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 5: Digital -->
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>DIGITAL</ng-template>
|
||||||
|
|
||||||
|
<mifosx-current-account-digital-step
|
||||||
|
[currentAccountProductTemplate]="currentAccountProductTemplate"
|
||||||
|
></mifosx-current-account-digital-step>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 6: Charges -->
|
||||||
<mat-step>
|
<mat-step>
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
||||||
|
|
||||||
@@ -61,14 +91,25 @@
|
|||||||
[currencyCode]="currentAccountTermsForm.get('currencyCode')"
|
[currencyCode]="currentAccountTermsForm.get('currencyCode')"
|
||||||
>
|
>
|
||||||
</mifosx-current-account-charges-step>
|
</mifosx-current-account-charges-step>
|
||||||
|
|
||||||
|
<!-- Hidden terms step to keep currencyCode form populated -->
|
||||||
|
<div style="display: none">
|
||||||
|
<mifosx-current-account-terms-step
|
||||||
|
[currentAccountProductTemplate]="currentAccountProductTemplate"
|
||||||
|
[currentAccountTemplate]="currentAccountTemplate"
|
||||||
|
></mifosx-current-account-terms-step>
|
||||||
|
</div>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 7: Preview -->
|
||||||
@if (currentAccountFormValid) {
|
@if (currentAccountFormValid) {
|
||||||
<mat-step completed>
|
<mat-step completed>
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
||||||
<mifosx-current-account-preview-step
|
<mifosx-current-account-preview-step
|
||||||
[currentAccountProductTemplate]="currentAccountProductTemplate"
|
[currentAccountProductTemplate]="currentAccountProductTemplate"
|
||||||
[currentAccountTemplate]="currentAccountTemplate"
|
[currentAccountTemplate]="currentAccountTemplate"
|
||||||
|
[currentAccountTermForm]="currentAccountTermForm"
|
||||||
|
[currentAccountSettingsForm]="currentAccountSettingsForm"
|
||||||
[currentAccountTermsForm]="currentAccountTermsForm"
|
[currentAccountTermsForm]="currentAccountTermsForm"
|
||||||
[currentAccount]="currentAccount"
|
[currentAccount]="currentAccount"
|
||||||
(submitEvent)="submit()"
|
(submitEvent)="submit()"
|
||||||
|
|||||||
+34
-15
@@ -13,7 +13,11 @@ import { Router, ActivatedRoute } from '@angular/router';
|
|||||||
/** Custom Components */
|
/** Custom Components */
|
||||||
import { CurrentAccountDetailsStepComponent } from '../current-account-stepper/current-account-details-step/current-account-details-step.component';
|
import { CurrentAccountDetailsStepComponent } from '../current-account-stepper/current-account-details-step/current-account-details-step.component';
|
||||||
import { CurrentAccountTermsStepComponent } from '../current-account-stepper/current-account-terms-step/current-account-terms-step.component';
|
import { CurrentAccountTermsStepComponent } from '../current-account-stepper/current-account-terms-step/current-account-terms-step.component';
|
||||||
|
import { CurrentAccountTermStepComponent } from '../current-account-stepper/current-account-term-step/current-account-term-step.component';
|
||||||
|
import { CurrentAccountSettingsStepComponent } from '../current-account-stepper/current-account-settings-step/current-account-settings-step.component';
|
||||||
import { CurrentAccountChargesStepComponent } from '../current-account-stepper/current-account-charges-step/current-account-charges-step.component';
|
import { CurrentAccountChargesStepComponent } from '../current-account-stepper/current-account-charges-step/current-account-charges-step.component';
|
||||||
|
import { CurrentAccountCurrencyStepComponent } from '../current-account-stepper/current-account-currency-step/current-account-currency-step.component';
|
||||||
|
import { CurrentAccountDigitalStepComponent } from '../current-account-stepper/current-account-digital-step/current-account-digital-step.component';
|
||||||
|
|
||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { CurrentAccountsService } from '../current-accounts.service';
|
import { CurrentAccountsService } from '../current-accounts.service';
|
||||||
@@ -39,6 +43,10 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
MatStep,
|
MatStep,
|
||||||
MatStepLabel,
|
MatStepLabel,
|
||||||
CurrentAccountDetailsStepComponent,
|
CurrentAccountDetailsStepComponent,
|
||||||
|
CurrentAccountCurrencyStepComponent,
|
||||||
|
CurrentAccountTermStepComponent,
|
||||||
|
CurrentAccountSettingsStepComponent,
|
||||||
|
CurrentAccountDigitalStepComponent,
|
||||||
CurrentAccountTermsStepComponent,
|
CurrentAccountTermsStepComponent,
|
||||||
CurrentAccountChargesStepComponent,
|
CurrentAccountChargesStepComponent,
|
||||||
CurrentAccountPreviewStepComponent
|
CurrentAccountPreviewStepComponent
|
||||||
@@ -59,21 +67,19 @@ export class CreateCurrentAccountComponent {
|
|||||||
/** Current Account Details Step */
|
/** Current Account Details Step */
|
||||||
@ViewChild(CurrentAccountDetailsStepComponent, { static: true })
|
@ViewChild(CurrentAccountDetailsStepComponent, { static: true })
|
||||||
currentAccountDetailsStep: CurrentAccountDetailsStepComponent;
|
currentAccountDetailsStep: CurrentAccountDetailsStepComponent;
|
||||||
/** Current Account Terms Step */
|
/** Current Account Term Step (new) */
|
||||||
|
@ViewChild(CurrentAccountTermStepComponent, { static: true })
|
||||||
|
currentAccountTermStep: CurrentAccountTermStepComponent;
|
||||||
|
/** Current Account Settings Step */
|
||||||
|
@ViewChild(CurrentAccountSettingsStepComponent, { static: true })
|
||||||
|
currentAccountSettingsStep: CurrentAccountSettingsStepComponent;
|
||||||
|
/** Current Account Terms Step (old — kept for currencyCode access) */
|
||||||
@ViewChild(CurrentAccountTermsStepComponent, { static: true })
|
@ViewChild(CurrentAccountTermsStepComponent, { static: true })
|
||||||
currentAccountTermsStep: CurrentAccountTermsStepComponent;
|
currentAccountTermsStep: CurrentAccountTermsStepComponent;
|
||||||
/** Current Account Charges Step */
|
/** Current Account Charges Step */
|
||||||
@ViewChild(CurrentAccountChargesStepComponent, { static: true })
|
@ViewChild(CurrentAccountChargesStepComponent, { static: true })
|
||||||
currentAccountChargesStep: CurrentAccountChargesStepComponent;
|
currentAccountChargesStep: CurrentAccountChargesStepComponent;
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches current account template from `resolve`
|
|
||||||
* @param {ActivatedRoute} route Activated Route
|
|
||||||
* @param {Router} router Router
|
|
||||||
* @param {Dates} dateUtils Date Utils
|
|
||||||
* @param {CurrentAccountsService} currentAccountsService Current Accounts Service
|
|
||||||
* @param {SettingsService} settingsService Settings Service
|
|
||||||
*/
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.route.data.subscribe((data: { currentAccountTemplate: any }) => {
|
this.route.data.subscribe((data: { currentAccountTemplate: any }) => {
|
||||||
this.currentAccountTemplate = data.currentAccountTemplate;
|
this.currentAccountTemplate = data.currentAccountTemplate;
|
||||||
@@ -82,7 +88,6 @@ export class CreateCurrentAccountComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets current account product template.
|
* Sets current account product template.
|
||||||
* @param {any} $event API response
|
|
||||||
*/
|
*/
|
||||||
setTemplate($event: any) {
|
setTemplate($event: any) {
|
||||||
this.currentAccountProductTemplate = $event;
|
this.currentAccountProductTemplate = $event;
|
||||||
@@ -96,7 +101,21 @@ export class CreateCurrentAccountComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves current account terms form.
|
* Retrieves new term form (interest rate).
|
||||||
|
*/
|
||||||
|
get currentAccountTermForm() {
|
||||||
|
return this.currentAccountTermStep.currentAccountTermForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves settings form.
|
||||||
|
*/
|
||||||
|
get currentAccountSettingsForm() {
|
||||||
|
return this.currentAccountSettingsStep.currentAccountSettingsForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves old terms form — still used for currencyCode by charges step.
|
||||||
*/
|
*/
|
||||||
get currentAccountTermsForm() {
|
get currentAccountTermsForm() {
|
||||||
return this.currentAccountTermsStep.currentAccountTermsForm;
|
return this.currentAccountTermsStep.currentAccountTermsForm;
|
||||||
@@ -106,7 +125,7 @@ export class CreateCurrentAccountComponent {
|
|||||||
* Checks validity of overall current account form.
|
* Checks validity of overall current account form.
|
||||||
*/
|
*/
|
||||||
get currentAccountFormValid() {
|
get currentAccountFormValid() {
|
||||||
return this.currentAccountDetailsForm.valid && this.currentAccountTermsForm.valid;
|
return this.currentAccountDetailsForm.valid && this.currentAccountTermForm.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,16 +134,16 @@ export class CreateCurrentAccountComponent {
|
|||||||
get currentAccount() {
|
get currentAccount() {
|
||||||
return {
|
return {
|
||||||
...this.currentAccountDetailsStep.currentAccountDetails,
|
...this.currentAccountDetailsStep.currentAccountDetails,
|
||||||
...this.currentAccountTermsStep.currentAccountTerms,
|
...this.currentAccountTermStep.currentAccountTerms,
|
||||||
|
...this.currentAccountSettingsStep.currentAccountSettings,
|
||||||
...this.currentAccountChargesStep.currentAccountCharges
|
...this.currentAccountChargesStep.currentAccountCharges
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new share account.
|
* Creates a new current account.
|
||||||
*/
|
*/
|
||||||
submit() {
|
submit() {
|
||||||
// TODO: Update once language and date settings are setup
|
|
||||||
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';
|
||||||
|
|||||||
+20
-20
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component, OnInit, OnChanges, Input, inject } from '@angular/core';
|
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { UntypedFormControl } from '@angular/forms';
|
import { UntypedFormControl } from '@angular/forms';
|
||||||
|
|
||||||
@@ -69,13 +69,27 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
FormatNumberPipe
|
FormatNumberPipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CurrentAccountChargesStepComponent implements OnInit, OnChanges {
|
export class CurrentAccountChargesStepComponent implements OnInit {
|
||||||
private dialog = inject(MatDialog);
|
private dialog = inject(MatDialog);
|
||||||
private dateUtils = inject(Dates);
|
private dateUtils = inject(Dates);
|
||||||
private translateService = inject(TranslateService);
|
private translateService = inject(TranslateService);
|
||||||
|
|
||||||
/** Current Account Product Template */
|
/** Current Account Product Template */
|
||||||
@Input() currentAccountProductTemplate: any;
|
private _currentAccountProductTemplate: any;
|
||||||
|
@Input()
|
||||||
|
set currentAccountProductTemplate(value: any) {
|
||||||
|
this._currentAccountProductTemplate = value;
|
||||||
|
if (value) {
|
||||||
|
this.chargeData = value.chargeOptions || [];
|
||||||
|
// Only pre-populate from product template for new accounts
|
||||||
|
if (!this.currentAccountTemplate?.id) {
|
||||||
|
this.chargesDataSource = (value.charges || []).map((charge: any) => ({ ...charge, id: charge.chargeId }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get currentAccountProductTemplate(): any {
|
||||||
|
return this._currentAccountProductTemplate;
|
||||||
|
}
|
||||||
/** Current Account Template */
|
/** Current Account Template */
|
||||||
@Input() currentAccountTemplate: any;
|
@Input() currentAccountTemplate: any;
|
||||||
/** Currency Code */
|
/** Currency Code */
|
||||||
@@ -88,8 +102,6 @@ export class CurrentAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
chargesDataSource: {}[] = [];
|
chargesDataSource: {}[] = [];
|
||||||
/** Component is pristine if there has been no changes by user interaction */
|
/** Component is pristine if there has been no changes by user interaction */
|
||||||
pristine = true;
|
pristine = true;
|
||||||
/** For Edit Current Account Form */
|
|
||||||
isChargesPatched = false;
|
|
||||||
/** Display columns for charges table */
|
/** Display columns for charges table */
|
||||||
displayedColumns: string[] = [
|
displayedColumns: string[] = [
|
||||||
'name',
|
'name',
|
||||||
@@ -114,22 +126,10 @@ export class CurrentAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
];
|
];
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.currentAccountTemplate) {
|
if (this.currentAccountTemplate?.id && this.currentAccountTemplate?.charges) {
|
||||||
if (!this.isChargesPatched && this.currentAccountTemplate.charges) {
|
// Edit mode: load saved charges from existing account
|
||||||
this.chargesDataSource =
|
|
||||||
this.currentAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
|
||||||
this.isChargesPatched = true;
|
|
||||||
} else {
|
|
||||||
this.chargesDataSource = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges() {
|
|
||||||
if (this.currentAccountProductTemplate) {
|
|
||||||
this.chargeData = this.currentAccountProductTemplate.chargeOptions;
|
|
||||||
this.chargesDataSource =
|
this.chargesDataSource =
|
||||||
this.currentAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
this.currentAccountTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
<!--
|
||||||
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="sac-section">
|
||||||
|
<div class="sac-section__header">
|
||||||
|
<i class="fas fa-coins"></i>
|
||||||
|
<span>Currency & Account Status Configuration</span>
|
||||||
|
</div>
|
||||||
|
<div class="sac-body">
|
||||||
|
<div class="sac-grid">
|
||||||
|
<mat-form-field appearance="outline" class="sac-field">
|
||||||
|
<mat-label>Currency</mat-label>
|
||||||
|
<input matInput [value]="currentAccountProductTemplate?.currency?.name || ''" readonly />
|
||||||
|
<mat-hint>Inherited from selected product</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sac-field">
|
||||||
|
<mat-label>Decimal Places</mat-label>
|
||||||
|
<input matInput [value]="currentAccountProductTemplate?.currency?.decimalPlaces || ''" readonly />
|
||||||
|
<mat-hint>Inherited from selected product</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sac-alert">
|
||||||
|
<div class="sac-alert__content">
|
||||||
|
<h4>Dormancy Alert</h4>
|
||||||
|
<p>Enable automated notifications when an account becomes inactive based on product rules.</p>
|
||||||
|
</div>
|
||||||
|
<span class="sac-badge" [class.sac-badge--yes]="currentAccountProductTemplate?.dormancyAlert">
|
||||||
|
{{ currentAccountProductTemplate?.dormancyAlert ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sac-alert">
|
||||||
|
<div class="sac-alert__content">
|
||||||
|
<h4>Balance Swap Option</h4>
|
||||||
|
<p>Allows balance to be swapped or transferred between linked accounts as per product configuration.</p>
|
||||||
|
</div>
|
||||||
|
<span class="sac-badge" [class.sac-badge--yes]="currentAccountProductTemplate?.balanceSwapOption">
|
||||||
|
{{ currentAccountProductTemplate?.balanceSwapOption ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sac-alert">
|
||||||
|
<div class="sac-alert__content">
|
||||||
|
<h4>MSME</h4>
|
||||||
|
<p>Account is categorized under the Micro, Small and Medium Enterprises segment.</p>
|
||||||
|
</div>
|
||||||
|
<span class="sac-badge" [class.sac-badge--yes]="currentAccountProductTemplate?.msme">
|
||||||
|
{{ currentAccountProductTemplate?.msme ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sac-alert">
|
||||||
|
<div class="sac-alert__content">
|
||||||
|
<h4>Nomination Facility</h4>
|
||||||
|
<p>Allow account holders to designate individuals to receive funds in the event of account holder's death.</p>
|
||||||
|
</div>
|
||||||
|
<span class="sac-badge" [class.sac-badge--yes]="currentAccountProductTemplate?.nominationFacility">
|
||||||
|
{{ currentAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sac-alert">
|
||||||
|
<div class="sac-alert__content">
|
||||||
|
<h4>Auto Debit Facility</h4>
|
||||||
|
<p>Enable automatic debit instructions for recurring payments linked to this account.</p>
|
||||||
|
</div>
|
||||||
|
<span class="sac-badge" [class.sac-badge--yes]="currentAccountProductTemplate?.autoDebitFacility">
|
||||||
|
{{ currentAccountProductTemplate?.autoDebitFacility ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.sac-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 4px 16px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-alert {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
padding: 18px 24px;
|
||||||
|
border-top: 1px solid $border;
|
||||||
|
background: #f8fafc;
|
||||||
|
|
||||||
|
@media (width <= 760px) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
h4 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #64748b;
|
||||||
|
max-width: 540px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 48px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #fee2e2;
|
||||||
|
color: #b91c1c;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
|
||||||
|
&--yes {
|
||||||
|
background: #dcfce7;
|
||||||
|
color: #15803d;
|
||||||
|
border-color: #bbf7d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-current-account-currency-step',
|
||||||
|
templateUrl: './current-account-currency-step.component.html',
|
||||||
|
styleUrls: ['./current-account-currency-step.component.scss'],
|
||||||
|
imports: [...STANDALONE_SHARED_IMPORTS]
|
||||||
|
})
|
||||||
|
export class CurrentAccountCurrencyStepComponent {
|
||||||
|
@Input() currentAccountProductTemplate: any;
|
||||||
|
}
|
||||||
+112
-49
@@ -7,63 +7,126 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="currentAccountDetailsForm">
|
<form [formGroup]="currentAccountDetailsForm">
|
||||||
<div class="layout-row-wrap gap-2px responsive-column">
|
<!-- Account Details -->
|
||||||
<mat-form-field class="flex-48">
|
<div class="sad-section">
|
||||||
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
<div class="sad-section__header">
|
||||||
<mat-select formControlName="productId" required>
|
<i class="fas fa-info-circle"></i>
|
||||||
@for (product of productData; track product) {
|
<h3>Account Details</h3>
|
||||||
<mat-option [value]="product.id">
|
</div>
|
||||||
{{ product.name }}
|
<div class="sad-grid">
|
||||||
</mat-option>
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
}
|
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
||||||
</mat-select>
|
<mat-select formControlName="productId" required>
|
||||||
<mat-error>
|
@for (product of productData; track product) {
|
||||||
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
<mat-option [value]="product.id">{{ product.name }}</mat-option>
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
}
|
||||||
</mat-error>
|
</mat-select>
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
@if (currentAccountProductSelected) {
|
|
||||||
<mat-form-field class="flex-48" (click)="submittedOnDatePicker.open()">
|
|
||||||
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
[min]="minDate"
|
|
||||||
[max]="maxDate"
|
|
||||||
[matDatepicker]="submittedOnDatePicker"
|
|
||||||
formControlName="submittedOnDate"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
|
||||||
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
}
|
|
||||||
|
|
||||||
@if (currentAccountProductSelected) {
|
@if (currentAccountProductSelected) {
|
||||||
<mat-form-field class="flex-48">
|
<mat-form-field appearance="outline" class="sad-field" (click)="submittedOnDatePicker.open()">
|
||||||
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
||||||
<mat-select formControlName="fieldOfficerId">
|
<input
|
||||||
@for (officer of fieldOfficerData; track officer) {
|
matInput
|
||||||
<mat-option [value]="officer.id">
|
[min]="minDate"
|
||||||
{{ officer.displayName }}
|
[max]="maxDate"
|
||||||
</mat-option>
|
[matDatepicker]="submittedOnDatePicker"
|
||||||
}
|
formControlName="submittedOnDate"
|
||||||
</mat-select>
|
required
|
||||||
</mat-form-field>
|
/>
|
||||||
}
|
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
@if (currentAccountProductSelected) {
|
@if (currentAccountProductSelected) {
|
||||||
<mat-form-field class="flex-48">
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
<mat-label>{{ 'labels.inputs.External ID' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
||||||
<input matInput formControlName="externalId" />
|
<mat-select formControlName="fieldOfficerId">
|
||||||
</mat-form-field>
|
@for (officer of fieldOfficerData; track officer) {
|
||||||
}
|
<mat-option [value]="officer.id">{{ officer.displayName }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (currentAccountProductSelected) {
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.External ID' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="externalId" />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Product Classification (read-only, shown after product is selected) -->
|
||||||
|
@if (currentAccountProductSelected && productTemplate) {
|
||||||
|
<div class="sad-section sad-section--mt">
|
||||||
|
<div class="sad-section__header">
|
||||||
|
<i class="fas fa-tags"></i>
|
||||||
|
<h3>Product Classification</h3>
|
||||||
|
<span class="sad-badge">From Product</span>
|
||||||
|
</div>
|
||||||
|
<div class="sad-grid sad-grid--classification">
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Account Type</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.accountType || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Customer Segment</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.customerSegment || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Product Code</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.productCode || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
@if (productTemplate.effectiveDate) {
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Effective Date</mat-label>
|
||||||
|
<input matInput [value]="formatTemplateDate(productTemplate.effectiveDate)" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (productTemplate.effectiveTo) {
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Effective To</mat-label>
|
||||||
|
<input matInput [value]="formatTemplateDate(productTemplate.effectiveTo)" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (productTemplate.endDate) {
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>End Date</mat-label>
|
||||||
|
<input matInput [value]="formatTemplateDate(productTemplate.endDate)" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Introducer Required</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.introducerRequired ? 'Yes' : 'No'" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="sad-field sad-info-row">
|
||||||
|
<span class="sad-info-label">Share Linking Enabled</span>
|
||||||
|
<span class="sad-badge" [class.sad-badge--yes]="productTemplate.shareLinkingEnabled">
|
||||||
|
{{ productTemplate.shareLinkingEnabled ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
<button mat-raised-button matStepperPrevious disabled>
|
<button mat-raised-button matStepperPrevious disabled>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
|
|||||||
+98
@@ -5,3 +5,101 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.sad-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
&--mt {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 4px 16px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-grid--classification {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-info-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3px 12px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1d4ed8;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
|
||||||
|
&--yes {
|
||||||
|
background: #dcfce7;
|
||||||
|
color: #15803d;
|
||||||
|
border-color: #bbf7d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+43
-8
@@ -7,8 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component, OnInit, Input, Output, EventEmitter, inject } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter, inject, ChangeDetectorRef } from '@angular/core';
|
||||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
|
||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { CurrentAccountsService } from 'app/current-accounts/current-accounts.service';
|
import { CurrentAccountsService } from 'app/current-accounts/current-accounts.service';
|
||||||
@@ -35,6 +36,8 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
|
|||||||
private formBuilder = inject(UntypedFormBuilder);
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
private currentAccountsService = inject(CurrentAccountsService);
|
private currentAccountsService = inject(CurrentAccountsService);
|
||||||
private settingsService = inject(SettingsService);
|
private settingsService = inject(SettingsService);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
private cdr = inject(ChangeDetectorRef);
|
||||||
|
|
||||||
/** Current Account Template */
|
/** Current Account Template */
|
||||||
@Input() currentAccountTemplate: any;
|
@Input() currentAccountTemplate: any;
|
||||||
@@ -57,12 +60,9 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
|
|||||||
/** Current Account Template with product data */
|
/** Current Account Template with product data */
|
||||||
@Output() currentAccountProductTemplate = new EventEmitter();
|
@Output() currentAccountProductTemplate = new EventEmitter();
|
||||||
|
|
||||||
/**
|
/** Local copy of product template for displaying classification info */
|
||||||
* Sets share account details form.
|
productTemplate: any = null;
|
||||||
* @param {FormBuilder} formBuilder Form Builder.
|
|
||||||
* @param {CurrentAccountsService} currentAccountsService Current Accounts Service.
|
|
||||||
* @param {SettingsService} settingsService Setting service
|
|
||||||
*/
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createCurrentAccountDetailsForm();
|
this.createCurrentAccountDetailsForm();
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.currentAccountDetailsForm.patchValue({
|
this.currentAccountDetailsForm.patchValue({
|
||||||
submittedOnDate: new Date()
|
submittedOnDate: this.settingsService.businessDate
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,9 +115,23 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
|
|||||||
this.currentAccountsService
|
this.currentAccountsService
|
||||||
.getCurrentAccountTemplate(entityId, productId, this.currentAccountTemplate.groupId ? true : false)
|
.getCurrentAccountTemplate(entityId, productId, this.currentAccountTemplate.groupId ? true : false)
|
||||||
.subscribe((response: any) => {
|
.subscribe((response: any) => {
|
||||||
|
// Ensure currency is included in the product template
|
||||||
|
if (!response.currency) {
|
||||||
|
// Try to get currency from the selected product
|
||||||
|
const selectedProduct = this.currentAccountTemplate.productOptions?.find(
|
||||||
|
(product: any) => product.id == productId
|
||||||
|
);
|
||||||
|
if (selectedProduct?.currency) {
|
||||||
|
response.currency = selectedProduct.currency;
|
||||||
|
} else if (this.currentAccountTemplate.currency) {
|
||||||
|
response.currency = this.currentAccountTemplate.currency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.productTemplate = response;
|
||||||
this.currentAccountProductTemplate.emit(response);
|
this.currentAccountProductTemplate.emit(response);
|
||||||
this.fieldOfficerData = response.fieldOfficerOptions;
|
this.fieldOfficerData = response.fieldOfficerOptions;
|
||||||
this.currentAccountProductSelected = true;
|
this.currentAccountProductSelected = true;
|
||||||
|
this.cdr.detectChanges();
|
||||||
if (!this.isFieldOfficerPatched && this.currentAccountTemplate.fieldOfficerId) {
|
if (!this.isFieldOfficerPatched && this.currentAccountTemplate.fieldOfficerId) {
|
||||||
this.currentAccountDetailsForm.get('fieldOfficerId').patchValue(this.currentAccountTemplate.fieldOfficerId);
|
this.currentAccountDetailsForm.get('fieldOfficerId').patchValue(this.currentAccountTemplate.fieldOfficerId);
|
||||||
this.isFieldOfficerPatched = true;
|
this.isFieldOfficerPatched = true;
|
||||||
@@ -128,6 +142,27 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats template dates into the current CBS date format.
|
||||||
|
*/
|
||||||
|
formatTemplateDate(value: any): string {
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
let dateValue: Date | null = null;
|
||||||
|
if (value instanceof Date) {
|
||||||
|
dateValue = value;
|
||||||
|
} else if (Array.isArray(value) && value.length >= 3) {
|
||||||
|
dateValue = new Date(value[0], value[1] - 1, value[2]);
|
||||||
|
} else {
|
||||||
|
dateValue = new Date(value);
|
||||||
|
}
|
||||||
|
if (!dateValue || Number.isNaN(dateValue.getTime())) {
|
||||||
|
return Array.isArray(value) ? value.join('/') : String(value);
|
||||||
|
}
|
||||||
|
return this.dateUtils.formatDate(dateValue, 'M/d/yyyy') || '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns current account form value.
|
* Returns current account form value.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+234
@@ -0,0 +1,234 @@
|
|||||||
|
<!--
|
||||||
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="spl-section">
|
||||||
|
<div class="spl-section__header">
|
||||||
|
<span class="spl-section__icon">
|
||||||
|
<i class="fas fa-chart-bar"></i>
|
||||||
|
</span>
|
||||||
|
<div class="spl-section__title">Transaction & Product Limits</div>
|
||||||
|
<div class="spl-section__badge">FROM PRODUCT</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="spl-section__body">
|
||||||
|
<div class="spl-features">
|
||||||
|
<div class="spl-features__header">Account Features (as per RBI guidelines)</div>
|
||||||
|
|
||||||
|
<div class="spl-features__grid">
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-credit-card"></i> ATM Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.atmFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-wallet"></i> ATM Withdrawal</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.atmWithdrawalFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-mobile-alt"></i> UPI Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.upiFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-exchange-alt"></i> POS Transactions</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.posTransactionsFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-book"></i> Cheque Book</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.chequeBook" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-bolt"></i> IMPS Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.impsFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-sms"></i> SMS Alerts</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.smsAlerts" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-bell"></i> BBPS Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.bbpsFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-shield-alt"></i> Insurance Cover</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.insuranceCover" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-bolt"></i> BEPG Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.bepgFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-money-check-alt"></i> NEFT Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.neftFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-money-check"></i> RTGS Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.rtgsFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-balance-scale"></i> Inter-Bank Total Cap</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.interBankTotalCapFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-arrow-right-arrow-left"></i> Withdrawal Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.withdrawalFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-id-card"></i> Deposit Facility (KYC)</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.depositFacilityKyc" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-credit-card"></i> Virtual Debit Card</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.virtualDebitCard" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (accountFacilityLimits.length > 0) {
|
||||||
|
<div class="spl-table-wrap">
|
||||||
|
<table class="spl-table">
|
||||||
|
<colgroup>
|
||||||
|
<col class="spl-table__col--label" />
|
||||||
|
<col class="spl-table__col--value" />
|
||||||
|
<col class="spl-table__col--value" />
|
||||||
|
<col class="spl-table__col--value" />
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Limit Type</th>
|
||||||
|
<th>General Limit (Daily)</th>
|
||||||
|
<th>Minor Account Limit</th>
|
||||||
|
<th>Small Saving Limit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (row of accountFacilityLimits; track row.limitType) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.limitType }}</td>
|
||||||
|
<td><input type="text" class="spl-input" [value]="row.generalLimit" disabled /></td>
|
||||||
|
<td><input type="text" class="spl-input" [value]="row.minorAccountLimit" disabled /></td>
|
||||||
|
<td><input type="text" class="spl-input" [value]="row.smallSavingLimit" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (depositLimitKyc?.general) {
|
||||||
|
<div class="spl-table-wrap spl-table-wrap--kyc">
|
||||||
|
<h4 class="mat-h4">Deposit Limit (KYC)</h4>
|
||||||
|
<table class="spl-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Account Type</th>
|
||||||
|
<th>KYC Type</th>
|
||||||
|
<th>Limit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>General</td>
|
||||||
|
<td>
|
||||||
|
<select class="spl-select" [ngModel]="depositLimitKyc.general.kycType" disabled>
|
||||||
|
<option>{{ depositLimitKyc.general.kycType }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" class="spl-input" [value]="depositLimitKyc.general.limit" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Minor</td>
|
||||||
|
<td>
|
||||||
|
<select class="spl-select" [ngModel]="depositLimitKyc.minor.kycType" disabled>
|
||||||
|
<option>{{ depositLimitKyc.minor.kycType }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><input type="number" class="spl-input" [value]="depositLimitKyc.minor.limit" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Small Saving</td>
|
||||||
|
<td>
|
||||||
|
<select class="spl-select" [ngModel]="depositLimitKyc.smallSaving.kycType" disabled>
|
||||||
|
<option>{{ depositLimitKyc.smallSaving.kycType }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><input type="number" class="spl-input" [value]="depositLimitKyc.smallSaving.limit" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="spl-note">
|
||||||
|
<strong>Note:</strong>
|
||||||
|
These facilities are defined at product level and applied to this account. Small Saving Account limits are
|
||||||
|
strictly capped at Rs.1,00,000 maximum balance per RBI Master Circular.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+199
@@ -0,0 +1,199 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$border: #d9e2ec;
|
||||||
|
$border-strong: #c7d2de;
|
||||||
|
$text: #24364b;
|
||||||
|
$muted: #64748b;
|
||||||
|
$header-bg: #f8fafc;
|
||||||
|
|
||||||
|
.spl-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: $header-bg;
|
||||||
|
border-bottom: 1px solid $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #eff6ff;
|
||||||
|
color: #2563eb;
|
||||||
|
font-size: 14px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__title {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__badge {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1d4ed8;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__body {
|
||||||
|
padding: 18px 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-table-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-table {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid $border;
|
||||||
|
|
||||||
|
col.spl-table__col--label {
|
||||||
|
width: 22%;
|
||||||
|
}
|
||||||
|
|
||||||
|
col.spl-table__col--value {
|
||||||
|
width: 26%;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid $border;
|
||||||
|
padding: 6px 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #f8fafc;
|
||||||
|
color: #64748b;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
background: #f8fafc;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
font-size: 13px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-input,
|
||||||
|
.spl-select {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-input {
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid $border-strong;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0 8px;
|
||||||
|
background: #f8fafc;
|
||||||
|
color: $muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-select {
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid $border-strong;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0 6px;
|
||||||
|
background: #f8fafc;
|
||||||
|
color: $muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-features {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-features__header {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $muted;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-features__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 12px 16px;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 640px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-feature {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-feature__label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #334155;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #2563eb;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-note {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f8fafc;
|
||||||
|
color: $muted;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
const DEFAULT_FACILITY_CONFIG = {
|
||||||
|
atmFacility: false,
|
||||||
|
atmWithdrawalFacility: false,
|
||||||
|
upiFacility: false,
|
||||||
|
posTransactionsFacility: false,
|
||||||
|
chequeBook: false,
|
||||||
|
impsFacility: false,
|
||||||
|
smsAlerts: false,
|
||||||
|
bbpsFacility: false,
|
||||||
|
insuranceCover: false,
|
||||||
|
bepgFacility: false,
|
||||||
|
neftFacility: false,
|
||||||
|
rtgsFacility: false,
|
||||||
|
interBankTotalCapFacility: false,
|
||||||
|
withdrawalFacility: false,
|
||||||
|
depositFacilityKyc: false,
|
||||||
|
virtualDebitCard: false
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-current-account-digital-step',
|
||||||
|
templateUrl: './current-account-digital-step.component.html',
|
||||||
|
styleUrls: ['./current-account-digital-step.component.scss'],
|
||||||
|
imports: [
|
||||||
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
FormsModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CurrentAccountDigitalStepComponent {
|
||||||
|
private _currentAccountProductTemplate: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set currentAccountProductTemplate(value: any) {
|
||||||
|
this._currentAccountProductTemplate = value;
|
||||||
|
}
|
||||||
|
get currentAccountProductTemplate(): any {
|
||||||
|
return this._currentAccountProductTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
get accountFacilityConfig() {
|
||||||
|
return this._currentAccountProductTemplate?.accountFacilityConfig || DEFAULT_FACILITY_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
get accountFacilityLimits(): any[] {
|
||||||
|
return this._currentAccountProductTemplate?.accountFacilityLimits || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get depositLimitKyc() {
|
||||||
|
return (
|
||||||
|
this._currentAccountProductTemplate?.depositLimitKyc || {
|
||||||
|
general: { kycType: '', limit: '' },
|
||||||
|
minor: { kycType: '', limit: '' },
|
||||||
|
smallSaving: { kycType: '', limit: '' }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+499
-239
@@ -6,251 +6,511 @@
|
|||||||
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="responsive-column">
|
<div class="preview-wrapper">
|
||||||
<h3 class="mat-h3 flex-fill">{{ 'labels.heading.Details' | translate }}</h3>
|
<!-- Details -->
|
||||||
|
<div class="preview-section">
|
||||||
<mat-divider class="flex-fill"></mat-divider>
|
<div class="preview-section__header">
|
||||||
<div class="flex-fill layout-row">
|
<span class="preview-section__title">{{ 'labels.heading.Details' | translate }}</span>
|
||||||
<span class="flex-40">{{ 'labels.inputs.Product' | translate }}</span>
|
|
||||||
<span class="flex-60">{{
|
|
||||||
currentAccount.productId | find: currentAccountTemplate.productOptions : 'id' : 'name'
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Submitted On' | translate }}</span>
|
|
||||||
<span class="flex-60">{{ currentAccount.submittedOnDate | dateFormat }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.External Id' | translate }}</span>
|
|
||||||
@if (currentAccount.externalId) {
|
|
||||||
<span class="flex-60">
|
|
||||||
<mifosx-external-identifier externalId="{{ currentAccount.externalId }}"></mifosx-external-identifier>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
@if (!currentAccount.externalId) {
|
|
||||||
<span class="flex-60">
|
|
||||||
{{ 'labels.inputs.Unassigned' | translate }}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Field Officer' | translate }}</span>
|
|
||||||
<span class="flex-60">{{
|
|
||||||
currentAccount.fieldOfficerId | find: currentAccountProductTemplate.fieldOfficerOptions : 'id' : 'displayName'
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (activeClientMembers) {
|
|
||||||
<h3 class="mat-h3 margin-t flex-fill">{{ 'labels.heading.Client Members' | translate }}</h3>
|
|
||||||
<mat-divider class="flex-fill"></mat-divider>
|
|
||||||
<div class="flex-60">
|
|
||||||
<table mat-table #membersTable [dataSource]="dataSource">
|
|
||||||
<!-- ID Column -->
|
|
||||||
<ng-container matColumnDef="id">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client ID' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
|
|
||||||
</ng-container>
|
|
||||||
<!-- Name Column -->
|
|
||||||
<ng-container matColumnDef="name">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client Name' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let element">{{ element.displayName }}</td>
|
|
||||||
</ng-container>
|
|
||||||
<tr mat-header-row *matHeaderRowDef="membersDisplayedColumns"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: membersDisplayedColumns"></tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
<h3 class="mat-h3 margin-t flex-fill">{{ 'labels.heading.Terms' | translate }}</h3>
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Product' | translate }}</span>
|
||||||
<mat-divider class="flex-fill"></mat-divider>
|
<span class="preview-value">{{
|
||||||
<div class="flex-fill layout-row">
|
currentAccount?.productId | find: currentAccountTemplate?.productOptions : 'id' : 'name'
|
||||||
<span class="flex-40">{{ 'labels.inputs.Currency' | translate }}</span>
|
}}</span>
|
||||||
<span class="flex-60">{{ currentAccountTermsForm.get('currencyCode').value }}</span>
|
</div>
|
||||||
</div>
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Submitted On' | translate }}</span>
|
||||||
<div class="flex-fill layout-row">
|
<span class="preview-value">{{ currentAccount?.submittedOnDate | dateFormat }}</span>
|
||||||
<span class="flex-40">{{ 'labels.inputs.Decimal Places' | translate }}</span>
|
</div>
|
||||||
<span class="flex-60">{{ currentAccountTermsForm.get('decimal').value }}</span>
|
<div class="preview-field">
|
||||||
</div>
|
<span class="preview-label">{{ 'labels.inputs.Field Officer' | translate }}</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
<div class="flex-fill layout-row">
|
currentAccount?.fieldOfficerId
|
||||||
<span class="flex-40">{{ 'labels.inputs.Nominal Annual Interest' | translate }}:</span>
|
| find: currentAccountProductTemplate?.fieldOfficerOptions : 'id' : 'displayName'
|
||||||
<span class="flex-60">{{ currentAccount.nominalAnnualInterestRate | formatNumber }} %</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
<div class="flex-fill layout-row">
|
<span class="preview-label">{{ 'labels.inputs.External Id' | translate }}</span>
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Compounding Period' | translate }}:</span>
|
@if (currentAccount?.externalId) {
|
||||||
<span class="flex-60">{{
|
<span class="preview-value">
|
||||||
getCatalogTranslation(
|
<mifosx-external-identifier externalId="{{ currentAccount.externalId }}"></mifosx-external-identifier>
|
||||||
currentAccount.interestCompoundingPeriodType
|
</span>
|
||||||
| find: currentAccountProductTemplate.interestCompoundingPeriodTypeOptions : 'id' : 'value'
|
} @else {
|
||||||
)
|
<span class="preview-value">{{ 'labels.inputs.Unassigned' | translate }}</span>
|
||||||
}}</span>
|
}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Posting Period' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{
|
|
||||||
getCatalogTranslation(
|
|
||||||
currentAccount.interestPostingPeriodType
|
|
||||||
| find: currentAccountProductTemplate.interestPostingPeriodTypeOptions : 'id' : 'value'
|
|
||||||
)
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{
|
|
||||||
getCatalogTranslation(
|
|
||||||
currentAccount.interestCalculationType
|
|
||||||
| find: currentAccountProductTemplate.interestCalculationTypeOptions : 'id' : 'value'
|
|
||||||
)
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Days in Year' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{
|
|
||||||
getCatalogTranslation(
|
|
||||||
currentAccount.interestCalculationDaysInYearType
|
|
||||||
| find: currentAccountProductTemplate.interestCalculationDaysInYearTypeOptions : 'id' : 'value'
|
|
||||||
)
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (currentAccount.minRequiredOpeningBalance) {
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Minimum Opening Balance' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ currentAccount.minRequiredOpeningBalance | formatNumber }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ currentAccount.withdrawalFeeForTransfers | yesNo }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (currentAccount.lockinPeriodFrequency) {
|
<!-- Product Classification -->
|
||||||
<div class="flex-fill layout-row">
|
@if (
|
||||||
<span class="flex-40">{{ 'labels.inputs.Lock-in Period' | translate }}:</span>
|
currentAccountProductTemplate?.accountType ||
|
||||||
<span class="flex-60">{{
|
currentAccountProductTemplate?.customerSegment ||
|
||||||
getCatalogTranslation(
|
currentAccountProductTemplate?.productCode ||
|
||||||
currentAccount.lockinPeriodFrequency +
|
currentAccountProductTemplate?.effectiveDate ||
|
||||||
' ' +
|
currentAccountProductTemplate?.effectiveTo ||
|
||||||
(currentAccount.lockinPeriodFrequencyType
|
currentAccountProductTemplate?.endDate ||
|
||||||
| find: currentAccountProductTemplate.lockinPeriodFrequencyTypeOptions : 'id' : 'value')
|
currentAccountProductTemplate?.introducerRequired !== undefined ||
|
||||||
)
|
currentAccountProductTemplate?.shareLinkingEnabled !== undefined
|
||||||
}}</span>
|
) {
|
||||||
</div>
|
<div class="preview-section">
|
||||||
}
|
<div class="preview-section__header">
|
||||||
|
<span class="preview-section__title">Product Classification</span>
|
||||||
<div class="flex-fill layout-row">
|
</div>
|
||||||
<span class="flex-40">{{ 'labels.inputs.Is Overdraft Allowed' | translate }}:</span>
|
<div class="preview-body">
|
||||||
<span class="flex-60">{{ currentAccount.allowOverdraft | yesNo }}</span>
|
<div class="preview-grid preview-grid--three">
|
||||||
</div>
|
@if (currentAccountProductTemplate?.accountType) {
|
||||||
|
<div class="preview-field">
|
||||||
@if (currentAccount.minOverdraftForInterestCalculation) {
|
<span class="preview-label">Account Type</span>
|
||||||
<div class="flex-fill layout-row">
|
<span class="preview-value">{{ currentAccountProductTemplate?.accountType }}</span>
|
||||||
<span class="flex-40"
|
</div>
|
||||||
>{{ 'labels.inputs.Minimum Overdraft Required for Interest Calculation' | translate }}:</span
|
}
|
||||||
>
|
@if (currentAccountProductTemplate?.customerSegment) {
|
||||||
<span class="flex-60">{{ currentAccount.minOverdraftForInterestCalculation | formatNumber }}</span>
|
<div class="preview-field">
|
||||||
</div>
|
<span class="preview-label">Customer Segment</span>
|
||||||
}
|
<span class="preview-value">{{ currentAccountProductTemplate?.customerSegment }}</span>
|
||||||
|
</div>
|
||||||
@if (currentAccount.nominalAnnualInterestRateOverdraft) {
|
}
|
||||||
<div class="flex-fill layout-row">
|
@if (currentAccountProductTemplate?.productCode) {
|
||||||
<span class="flex-40">{{ 'labels.inputs.Nominal Annual Interest for Overdraft' | translate }}:</span>
|
<div class="preview-field">
|
||||||
<span class="flex-60">{{ currentAccount.nominalAnnualInterestRateOverdraft | formatNumber }} %</span>
|
<span class="preview-label">Product Code</span>
|
||||||
</div>
|
<span class="preview-value">{{ currentAccountProductTemplate?.productCode }}</span>
|
||||||
}
|
</div>
|
||||||
|
}
|
||||||
@if (currentAccount.overdraftLimit) {
|
@if (currentAccountProductTemplate?.effectiveDate) {
|
||||||
<div class="flex-fill layout-row">
|
<div class="preview-field">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Maximum Overdraft Amount Limit' | translate }}:</span>
|
<span class="preview-label">Effective Date</span>
|
||||||
<span class="flex-60">{{ currentAccount.overdraftLimit | formatNumber }}</span>
|
<span class="preview-value">{{ currentAccountProductTemplate?.effectiveDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@if (currentAccountProductTemplate?.effectiveTo) {
|
||||||
<div class="flex-fill layout-row">
|
<div class="preview-field">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Enforce Minimum Balance' | translate }}:</span>
|
<span class="preview-label">Effective To</span>
|
||||||
<span class="flex-60">{{ currentAccount.enforceMinRequiredBalance | yesNo }}</span>
|
<span class="preview-value">{{ currentAccountProductTemplate?.effectiveTo | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
@if (currentAccount.minRequiredBalance) {
|
@if (currentAccountProductTemplate?.endDate) {
|
||||||
<div class="flex-fill layout-row">
|
<div class="preview-field">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Minimum Balance' | translate }}:</span>
|
<span class="preview-label">End Date</span>
|
||||||
<span class="flex-60">{{ currentAccount.minRequiredBalance }}</span>
|
<span class="preview-value">{{ currentAccountProductTemplate?.endDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@if (
|
||||||
@if (currentAccount.charges.length) {
|
currentAccountProductTemplate?.introducerRequired !== undefined &&
|
||||||
<div class="layout-row-wrap responsive-column flex-fill">
|
currentAccountProductTemplate?.introducerRequired !== null
|
||||||
<h3 class="mat-h3 flex-fill">{{ 'labels.inputs.Charges' | translate }}</h3>
|
) {
|
||||||
<mat-divider class="flex-fill"></mat-divider>
|
<div class="preview-field">
|
||||||
<table class="flex-fill mat-elevation-z1" mat-table [dataSource]="currentAccount.charges">
|
<span class="preview-label">Introducer Required</span>
|
||||||
<ng-container matColumnDef="name">
|
<span
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
|
class="preview-value preview-badge"
|
||||||
<td mat-cell *matCellDef="let charge">
|
[class.preview-badge--yes]="currentAccountProductTemplate?.introducerRequired"
|
||||||
{{ charge.name + ', ' + charge.currency.displaySymbol }}
|
>
|
||||||
</td>
|
{{ currentAccountProductTemplate?.introducerRequired | yesNo }}
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="chargeCalculationType">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
{{ charge.chargeCalculationType.value | translateKey: 'catalogs' }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="amount">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
{{ charge.amount | formatNumber }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="date">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
@if (charge.chargeTimeType.value === 'Specified due date' || charge.chargeTimeType.value === 'Weekly Fee') {
|
|
||||||
<span>
|
|
||||||
{{ (charge.dueDate | dateFormat) || 'Unassigned' }}
|
|
||||||
</span>
|
</span>
|
||||||
}
|
</div>
|
||||||
@if (charge.chargeTimeType.value === 'Monthly Fee' || charge.chargeTimeType.value === 'Annual Fee') {
|
}
|
||||||
<span>
|
@if (
|
||||||
{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}
|
currentAccountProductTemplate?.shareLinkingEnabled !== undefined &&
|
||||||
|
currentAccountProductTemplate?.shareLinkingEnabled !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Share Linking Enabled</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="currentAccountProductTemplate?.shareLinkingEnabled"
|
||||||
|
>
|
||||||
|
{{ currentAccountProductTemplate?.shareLinkingEnabled | yesNo }}
|
||||||
</span>
|
</span>
|
||||||
}
|
</div>
|
||||||
@if (
|
}
|
||||||
!(
|
</div>
|
||||||
charge.chargeTimeType.value === 'Monthly Fee' ||
|
</div>
|
||||||
charge.chargeTimeType.value === 'Annual Fee' ||
|
</div>
|
||||||
charge.chargeTimeType.value === 'Specified due date' ||
|
}
|
||||||
charge.chargeTimeType.value === 'Weekly Fee'
|
|
||||||
)
|
<!-- Currency -->
|
||||||
) {
|
<div class="preview-section">
|
||||||
<span> N/A </span>
|
<div class="preview-section__header">
|
||||||
}
|
<span class="preview-section__title">{{ 'labels.heading.Currency' | translate }}</span>
|
||||||
</td>
|
</div>
|
||||||
</ng-container>
|
<div class="preview-body">
|
||||||
<ng-container matColumnDef="repaymentsEvery">
|
<div class="preview-grid preview-grid--three">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Repayments Every' | translate }}</th>
|
<div class="preview-field">
|
||||||
<td mat-cell *matCellDef="let charge">
|
<span class="preview-label">{{ 'labels.inputs.Currency' | translate }}</span>
|
||||||
{{ charge.feeInterval || 'Not Provided' }}
|
<span class="preview-value">{{ currentAccountTermsForm?.get('currencyCode')?.value }}</span>
|
||||||
</td>
|
</div>
|
||||||
</ng-container>
|
<div class="preview-field">
|
||||||
<ng-container matColumnDef="chargeTimeType">
|
<span class="preview-label">{{ 'labels.inputs.Decimal Places' | translate }}</span>
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
<span class="preview-value">{{ currentAccountProductTemplate?.currency?.decimalPlaces }}</span>
|
||||||
<td mat-cell *matCellDef="let charge">
|
</div>
|
||||||
{{ charge.chargeTimeType.value }}
|
<div class="preview-field">
|
||||||
</td>
|
<span class="preview-label">{{ 'labels.inputs.Currency In Multiples Of' | translate }}</span>
|
||||||
</ng-container>
|
<span class="preview-value">{{ currentAccountProductTemplate?.currency?.inMultiplesOf }}</span>
|
||||||
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
</div>
|
||||||
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns"></tr>
|
@if (
|
||||||
</table>
|
currentAccountProductTemplate?.dormancyAlert !== undefined &&
|
||||||
|
currentAccountProductTemplate?.dormancyAlert !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Dormancy Alert</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="currentAccountProductTemplate?.dormancyAlert"
|
||||||
|
>
|
||||||
|
{{ currentAccountProductTemplate?.dormancyAlert | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (
|
||||||
|
currentAccountProductTemplate?.balanceSwapOption !== undefined &&
|
||||||
|
currentAccountProductTemplate?.balanceSwapOption !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Balance Swap Option</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="currentAccountProductTemplate?.balanceSwapOption"
|
||||||
|
>
|
||||||
|
{{ currentAccountProductTemplate?.balanceSwapOption | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (currentAccountProductTemplate?.msme !== undefined && currentAccountProductTemplate?.msme !== null) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">MSME</span>
|
||||||
|
<span class="preview-value preview-badge" [class.preview-badge--yes]="currentAccountProductTemplate?.msme">
|
||||||
|
{{ currentAccountProductTemplate?.msme | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (
|
||||||
|
currentAccountProductTemplate?.nominationFacility !== undefined &&
|
||||||
|
currentAccountProductTemplate?.nominationFacility !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Nomination Facility</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="currentAccountProductTemplate?.nominationFacility"
|
||||||
|
>
|
||||||
|
{{ currentAccountProductTemplate?.nominationFacility | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (
|
||||||
|
currentAccountProductTemplate?.autoDebitFacility !== undefined &&
|
||||||
|
currentAccountProductTemplate?.autoDebitFacility !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Auto Debit Facility</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="currentAccountProductTemplate?.autoDebitFacility"
|
||||||
|
>
|
||||||
|
{{ currentAccountProductTemplate?.autoDebitFacility | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Terms -->
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<span class="preview-section__title">{{ 'labels.heading.Terms' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Interest Posting Frequency</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
termPreview?.interestPostingPeriodType
|
||||||
|
| find: currentAccountProductTemplate?.interestPostingPeriodTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Auto Interest Post</span>
|
||||||
|
<span class="preview-value preview-badge" [class.preview-badge--yes]="termPreview?.autoPost">
|
||||||
|
{{ termPreview?.autoPost | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (termPreview?.frequencyFixDay !== undefined && termPreview?.frequencyFixDay !== null) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Frequency Fix Day</span>
|
||||||
|
<span class="preview-value">{{ termPreview?.frequencyFixDay }} Days</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (termPreview?.lastInterestDate) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Last Interest Date</span>
|
||||||
|
<span class="preview-value">{{ termPreview?.lastInterestDate | dateFormat }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Settings -->
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<span class="preview-section__title">{{ 'labels.heading.Settings' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
|
@if (
|
||||||
|
settingsPreview?.minRequiredOpeningBalance !== undefined &&
|
||||||
|
settingsPreview?.minRequiredOpeningBalance !== null &&
|
||||||
|
settingsPreview?.minRequiredOpeningBalance !== ''
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Minimum Opening Balance' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ settingsPreview?.minRequiredOpeningBalance | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Enforce Minimum Balance' | translate }}</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="settingsPreview?.enforceMinRequiredBalance"
|
||||||
|
>
|
||||||
|
{{ settingsPreview?.enforceMinRequiredBalance | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (
|
||||||
|
settingsPreview?.minRequiredBalance !== undefined &&
|
||||||
|
settingsPreview?.minRequiredBalance !== null &&
|
||||||
|
settingsPreview?.minRequiredBalance !== ''
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Minimum Balance' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ settingsPreview?.minRequiredBalance | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Balance Frequency</span>
|
||||||
|
<span class="preview-value">{{ settingsPreview?.balanceFrequency || 'Unassigned' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="settingsPreview?.withdrawalFeeForTransfers"
|
||||||
|
>
|
||||||
|
{{ settingsPreview?.withdrawalFeeForTransfers | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Enable Lock-in Period</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="settingsPreview?.enableLockinPeriod || !!settingsPreview?.lockinPeriodFrequency"
|
||||||
|
>
|
||||||
|
{{ settingsPreview?.enableLockinPeriod || !!settingsPreview?.lockinPeriodFrequency | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (settingsPreview?.lockinPeriodFrequency) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Lock-in Period' | translate }}</span>
|
||||||
|
<span class="preview-value">
|
||||||
|
{{ settingsPreview?.lockinPeriodFrequency }}
|
||||||
|
{{
|
||||||
|
settingsPreview?.lockinPeriodFrequencyType
|
||||||
|
| find: currentAccountProductTemplate?.lockinPeriodFrequencyTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Account Closed Zero Balance In Day Closing</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="settingsPreview?.accountClosedZeroBalanceInDayClosing"
|
||||||
|
>
|
||||||
|
{{ settingsPreview?.accountClosedZeroBalanceInDayClosing | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Is Overdraft Allowed' | translate }}</span>
|
||||||
|
<span class="preview-value preview-badge" [class.preview-badge--yes]="settingsPreview?.allowOverdraft">
|
||||||
|
{{ settingsPreview?.allowOverdraft | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (settingsPreview?.allowOverdraft) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Avg Balance Calc Period</span>
|
||||||
|
<span class="preview-value">{{ settingsPreview?.avgBalanceCalcPeriod || 'Unassigned' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Valid For Period</span>
|
||||||
|
<span class="preview-value">{{ settingsPreview?.validForPeriod || 'Unassigned' }}</span>
|
||||||
|
</div>
|
||||||
|
@if (
|
||||||
|
settingsPreview?.overdraftLimit !== undefined &&
|
||||||
|
settingsPreview?.overdraftLimit !== null &&
|
||||||
|
settingsPreview?.overdraftLimit !== ''
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">OD Limit %</span>
|
||||||
|
<span class="preview-value">{{ settingsPreview?.overdraftLimit | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (
|
||||||
|
settingsPreview?.nominalAnnualInterestRateOverdraft !== undefined &&
|
||||||
|
settingsPreview?.nominalAnnualInterestRateOverdraft !== null &&
|
||||||
|
settingsPreview?.nominalAnnualInterestRateOverdraft !== ''
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Interest on OD %</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
settingsPreview?.nominalAnnualInterestRateOverdraft | formatNumber
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">OD Account Closed Zero Balance In Day Closing</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="settingsPreview?.odAccountClosedZeroBalanceInDayClosing"
|
||||||
|
>
|
||||||
|
{{ settingsPreview?.odAccountClosedZeroBalanceInDayClosing | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (hasDigitalSection) {
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<span class="preview-section__title">Digital</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
|
@for (facility of digitalFacilityLabels; track facility.key) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ facility.label }}</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="digitalPreview?.accountFacilityConfig?.[facility.key]"
|
||||||
|
>
|
||||||
|
{{ digitalPreview?.accountFacilityConfig?.[facility.key] | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (digitalPreview?.accountFacilityLimits?.length) {
|
||||||
|
<table class="mat-elevation-z1 preview-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Limit Type</th>
|
||||||
|
<th>General Limit</th>
|
||||||
|
<th>Minor Account Limit</th>
|
||||||
|
<th>Small Saving Limit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (row of digitalPreview.accountFacilityLimits; track row.limitType) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.limitType }}</td>
|
||||||
|
<td>{{ row.generalLimit || 'Unassigned' }}</td>
|
||||||
|
<td>{{ row.minorAccountLimit || 'Unassigned' }}</td>
|
||||||
|
<td>{{ row.smallSavingLimit || 'Unassigned' }}</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (digitalPreview?.depositLimitKyc?.general) {
|
||||||
|
<table class="mat-elevation-z1 preview-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Account Type</th>
|
||||||
|
<th>KYC Type</th>
|
||||||
|
<th>Limit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>General</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.general.kycType || 'Unassigned' }}</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.general.limit || 'Unassigned' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Minor</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.minor?.kycType || 'Unassigned' }}</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.minor?.limit || 'Unassigned' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Small Saving</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.smallSaving?.kycType || 'Unassigned' }}</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.smallSaving?.limit || 'Unassigned' }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Charges -->
|
||||||
|
@if (currentAccount?.charges?.length) {
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<span class="preview-section__title">{{ 'labels.inputs.Charges' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<table class="mat-elevation-z1 preview-table" mat-table [dataSource]="currentAccount.charges">
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let charge">{{ charge.name + ', ' + charge.currency?.displaySymbol }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="chargeCalculationType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
{{ charge.chargeCalculationType?.value | translateKey: 'catalogs' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let charge">{{ charge.amount | formatNumber }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="chargeTimeType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let charge">{{ charge.chargeTimeType?.value }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="date">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
@if (
|
||||||
|
charge.chargeTimeType?.value === 'Specified due date' || charge.chargeTimeType?.value === 'Weekly Fee'
|
||||||
|
) {
|
||||||
|
<span>{{ (charge.dueDate | dateFormat) || 'Unassigned' }}</span>
|
||||||
|
} @else if (
|
||||||
|
charge.chargeTimeType?.value === 'Monthly Fee' || charge.chargeTimeType?.value === 'Annual Fee'
|
||||||
|
) {
|
||||||
|
<span>{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}</span>
|
||||||
|
} @else {
|
||||||
|
<span>N/A</span>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="repaymentsEvery">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Repayments Every' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let charge">{{ charge.feeInterval || 'Not Provided' }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+80
-12
@@ -6,27 +6,95 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
table {
|
.preview-wrapper {
|
||||||
width: 100%;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.25rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-elevation-z1 {
|
.preview-section {
|
||||||
margin: 1em 0 1.5em;
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 1px 3px rgb(0 0 0 / 6%);
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h2,
|
.preview-body {
|
||||||
h3,
|
padding: 1rem 1.25rem;
|
||||||
h4 {
|
}
|
||||||
margin: 0;
|
|
||||||
|
.preview-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem 1.5rem;
|
||||||
|
|
||||||
|
&--three {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 480px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-label {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: #888;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-value {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
color: #222;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
.preview-badge {
|
||||||
margin: 0.5em 0;
|
display: inline-block;
|
||||||
|
padding: 0.1rem 0.55rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #ffebee;
|
||||||
|
color: #c62828;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
&--yes {
|
||||||
|
background: #e8f5e9;
|
||||||
|
color: #2e7d32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-divider {
|
.preview-table {
|
||||||
margin: 0 0 0.5em;
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-t {
|
.margin-t {
|
||||||
|
|||||||
+44
-1
@@ -36,7 +36,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
* Current account preview step
|
* Current account preview step
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'mifosx-current-account-account-preview-step',
|
selector: 'mifosx-current-account-preview-step',
|
||||||
templateUrl: './current-account-preview-step.component.html',
|
templateUrl: './current-account-preview-step.component.html',
|
||||||
styleUrls: ['./current-account-preview-step.component.scss'],
|
styleUrls: ['./current-account-preview-step.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
@@ -62,6 +62,25 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CurrentAccountPreviewStepComponent implements OnChanges {
|
export class CurrentAccountPreviewStepComponent implements OnChanges {
|
||||||
|
readonly digitalFacilityLabels = [
|
||||||
|
{ key: 'atmFacility', label: 'ATM Facility' },
|
||||||
|
{ key: 'atmWithdrawalFacility', label: 'ATM Withdrawal' },
|
||||||
|
{ key: 'upiFacility', label: 'UPI Facility' },
|
||||||
|
{ key: 'posTransactionsFacility', label: 'POS Transactions' },
|
||||||
|
{ key: 'chequeBook', label: 'Cheque Book' },
|
||||||
|
{ key: 'impsFacility', label: 'IMPS Facility' },
|
||||||
|
{ key: 'smsAlerts', label: 'SMS Alerts' },
|
||||||
|
{ key: 'bbpsFacility', label: 'BBPS Facility' },
|
||||||
|
{ key: 'insuranceCover', label: 'Insurance Cover' },
|
||||||
|
{ key: 'bepgFacility', label: 'BEPG Facility' },
|
||||||
|
{ key: 'neftFacility', label: 'NEFT Facility' },
|
||||||
|
{ key: 'rtgsFacility', label: 'RTGS Facility' },
|
||||||
|
{ key: 'interBankTotalCapFacility', label: 'Inter-Bank Total Cap' },
|
||||||
|
{ key: 'withdrawalFacility', label: 'Withdrawal Facility' },
|
||||||
|
{ key: 'depositFacilityKyc', label: 'Deposit Facility (KYC)' },
|
||||||
|
{ key: 'virtualDebitCard', label: 'Virtual Debit Card' }
|
||||||
|
];
|
||||||
|
|
||||||
private translateService = inject(TranslateService);
|
private translateService = inject(TranslateService);
|
||||||
|
|
||||||
/** Current Account Product Template */
|
/** Current Account Product Template */
|
||||||
@@ -70,6 +89,10 @@ export class CurrentAccountPreviewStepComponent implements OnChanges {
|
|||||||
@Input() currentAccountTemplate: any;
|
@Input() currentAccountTemplate: any;
|
||||||
/** Current Account Terms Form */
|
/** Current Account Terms Form */
|
||||||
@Input() currentAccountTermsForm: any;
|
@Input() currentAccountTermsForm: any;
|
||||||
|
/** Current Account Term Form */
|
||||||
|
@Input() currentAccountTermForm: any;
|
||||||
|
/** Current Account Settings Form */
|
||||||
|
@Input() currentAccountSettingsForm: any;
|
||||||
/** Current Account */
|
/** Current Account */
|
||||||
@Input() currentAccount: any;
|
@Input() currentAccount: any;
|
||||||
@Input() activeClientMembers?: any;
|
@Input() activeClientMembers?: any;
|
||||||
@@ -104,4 +127,24 @@ export class CurrentAccountPreviewStepComponent implements OnChanges {
|
|||||||
getCatalogTranslation(text: string): string {
|
getCatalogTranslation(text: string): string {
|
||||||
return this.translateService.instant('labels.catalogs.' + text);
|
return this.translateService.instant('labels.catalogs.' + text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get termPreview(): any {
|
||||||
|
return this.currentAccountTermForm?.getRawValue?.() || this.currentAccount || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get settingsPreview(): any {
|
||||||
|
return this.currentAccountSettingsForm?.getRawValue?.() || this.currentAccount || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get digitalPreview(): any {
|
||||||
|
return this.currentAccountProductTemplate || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasDigitalSection(): boolean {
|
||||||
|
return !!(
|
||||||
|
this.digitalPreview?.accountFacilityConfig ||
|
||||||
|
this.digitalPreview?.accountFacilityLimits?.length ||
|
||||||
|
this.digitalPreview?.depositLimitKyc
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+174
@@ -0,0 +1,174 @@
|
|||||||
|
<!--
|
||||||
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<form [formGroup]="currentAccountSettingsForm">
|
||||||
|
<!-- Section 1: Balance & Minimum Balance -->
|
||||||
|
<div class="sps-section">
|
||||||
|
<div class="sps-section__header">
|
||||||
|
<span class="sps-section__badge"><i class="fas fa-balance-scale"></i></span>
|
||||||
|
<span>Balance & Minimum Balance</span>
|
||||||
|
</div>
|
||||||
|
<div class="sps-body">
|
||||||
|
<div class="sps-layout sps-layout--balance">
|
||||||
|
<div class="sps-row sps-row--three">
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>{{ 'labels.inputs.Minimum Opening Balance' | translate }}</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minRequiredOpeningBalance" min="0" step="0.01" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="sps-toggle sps-toggle--inline">
|
||||||
|
<mat-checkbox labelPosition="before" formControlName="enforceMinRequiredBalance">
|
||||||
|
<span>Enforce Minimum Balance</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>Minimum Balance (₹) *</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minRequiredBalance" min="0" step="0.01" />
|
||||||
|
<mat-hint>PENAL CHARGE APPLIES ON BREACH</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sps-row sps-row--two">
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>Balance Frequency</mat-label>
|
||||||
|
<mat-select formControlName="balanceFrequency">
|
||||||
|
@for (freq of periodFrequencyData; track freq.value) {
|
||||||
|
<mat-option [value]="freq.value">{{ freq.value }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="sps-toggle sps-toggle--inline sps-toggle--right">
|
||||||
|
<mat-checkbox labelPosition="before" formControlName="withdrawalFeeForTransfers">
|
||||||
|
<span>{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 2: Lock-in Period -->
|
||||||
|
<div class="sps-section">
|
||||||
|
<div class="sps-section__header">
|
||||||
|
<span class="sps-section__badge"><i class="fas fa-lock"></i></span>
|
||||||
|
<span>Lock-in Period</span>
|
||||||
|
</div>
|
||||||
|
<div class="sps-body">
|
||||||
|
<div class="sps-lockin-row">
|
||||||
|
<span>Enable Lock-in Period</span>
|
||||||
|
<mat-checkbox formControlName="enableLockinPeriod"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (currentAccountSettingsForm.value.enableLockinPeriod) {
|
||||||
|
<div class="sps-lockin-fields">
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>{{ 'labels.inputs.Frequency' | translate }} *</mat-label>
|
||||||
|
<input type="number" matInput formControlName="lockinPeriodFrequency" min="1" step="1" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>{{ 'labels.inputs.Type' | translate }} *</mat-label>
|
||||||
|
<mat-select formControlName="lockinPeriodFrequencyType">
|
||||||
|
@for (type of lockinPeriodFrequencyTypeData; track type) {
|
||||||
|
<mat-option [value]="type.id">{{ type.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 3: Additional Rules -->
|
||||||
|
<div class="sps-section">
|
||||||
|
<div class="sps-section__header">
|
||||||
|
<span class="sps-section__badge"><i class="fas fa-cog"></i></span>
|
||||||
|
<span>Additional Rules</span>
|
||||||
|
</div>
|
||||||
|
<div class="sps-body">
|
||||||
|
<div class="sps-toggle sps-toggle--inline">
|
||||||
|
<mat-checkbox formControlName="accountClosedZeroBalanceInDayClosing">
|
||||||
|
<span>Account Closed with Zero Balance in Day Closing</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 4: Overdraft (OD) Facility -->
|
||||||
|
<div class="sps-section">
|
||||||
|
<div class="sps-section__header">
|
||||||
|
<span class="sps-section__badge"><i class="fas fa-hand-holding-usd"></i></span>
|
||||||
|
<span>Overdraft (OD) Facility</span>
|
||||||
|
</div>
|
||||||
|
<div class="sps-body">
|
||||||
|
<div class="sps-od-layout">
|
||||||
|
<!-- Left: checkboxes -->
|
||||||
|
<div class="sps-od-checks">
|
||||||
|
<div class="sps-od-check-row">
|
||||||
|
<mat-checkbox formControlName="allowOverdraft">
|
||||||
|
<span>Is Overdraft Allowed</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
@if (currentAccountSettingsForm.value.allowOverdraft) {
|
||||||
|
<div class="sps-od-check-row">
|
||||||
|
<mat-checkbox formControlName="odAccountClosedZeroBalanceInDayClosing">
|
||||||
|
<span>OD Account Closed with Zero Balance in Day Closing</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
<p class="sps-od-check-hint">
|
||||||
|
Automatically closes OD accounts when balance reaches zero during day-end closing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right: OD details panel — only visible when OD is allowed -->
|
||||||
|
@if (currentAccountSettingsForm.value.allowOverdraft) {
|
||||||
|
<div class="sps-od-panel">
|
||||||
|
<div class="sps-od-radio-group">
|
||||||
|
<span class="sps-od-radio-label">Average Balance Calculation in Period</span>
|
||||||
|
<mat-radio-group formControlName="avgBalanceCalcPeriod" class="sps-od-radio-choices">
|
||||||
|
<mat-radio-button value="6M">6M</mat-radio-button>
|
||||||
|
<mat-radio-button value="1Y">1Y</mat-radio-button>
|
||||||
|
<mat-radio-button value="3Y">3Y</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sps-od-radio-group">
|
||||||
|
<span class="sps-od-radio-label">Valid for Period</span>
|
||||||
|
<mat-radio-group formControlName="validForPeriod" class="sps-od-radio-choices">
|
||||||
|
<mat-radio-button value="3M">3M</mat-radio-button>
|
||||||
|
<mat-radio-button value="1Y">1Y</mat-radio-button>
|
||||||
|
<mat-radio-button value="3Y">3Y</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sps-od-inputs">
|
||||||
|
<mat-form-field appearance="outline" class="sps-field">
|
||||||
|
<mat-label>OD Limit %</mat-label>
|
||||||
|
<input type="number" matInput formControlName="overdraftLimit" min="0" step="0.01" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sps-field">
|
||||||
|
<mat-label>Interest on OD %</mat-label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
matInput
|
||||||
|
formControlName="nominalAnnualInterestRateOverdraft"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
+255
@@ -0,0 +1,255 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.sps-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-section__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-body {
|
||||||
|
background: #fff;
|
||||||
|
padding: 18px 24px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-field {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&--full {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--compact {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-layout--balance {
|
||||||
|
gap: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-layout--additional {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
@media (width <= 1100px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-row {
|
||||||
|
display: grid;
|
||||||
|
gap: 18px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-row--three {
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.sps-field--compact:first-child {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-toggle--inline {
|
||||||
|
align-self: center;
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-row--two {
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.sps-field--compact {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 56px;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--full {
|
||||||
|
min-height: auto;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--inline {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--right {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-lockin-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-lockin-row mat-checkbox {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-lockin-fields {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
@media (width <= 640px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Overdraft (OD) Facility ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
.sps-od-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-od-checks {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-od-check-row {
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: $gray-50;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-od-check-hint {
|
||||||
|
margin: 6px 0 0 28px;
|
||||||
|
font-size: 11.5px;
|
||||||
|
color: #64748b;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-od-panel {
|
||||||
|
background: #f0f7ff;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-od-radio-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid #dbeafe;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-od-radio-label {
|
||||||
|
font-size: 10.5px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.07em;
|
||||||
|
color: #3b82f6;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-od-radio-choices {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-od-inputs {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
@media (width <= 640px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, Input, OnInit, inject } from '@angular/core';
|
||||||
|
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
|
import { MatRadioGroup, MatRadioButton } from '@angular/material/radio';
|
||||||
|
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-current-account-settings-step',
|
||||||
|
templateUrl: './current-account-settings-step.component.html',
|
||||||
|
styleUrls: ['./current-account-settings-step.component.scss'],
|
||||||
|
imports: [
|
||||||
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
MatCheckbox,
|
||||||
|
MatRadioGroup,
|
||||||
|
MatRadioButton,
|
||||||
|
FormatNumberPipe
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CurrentAccountSettingsStepComponent implements OnInit {
|
||||||
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
|
|
||||||
|
private _currentAccountProductTemplate: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set currentAccountProductTemplate(value: any) {
|
||||||
|
this._currentAccountProductTemplate = value;
|
||||||
|
if (value && this.currentAccountSettingsForm) {
|
||||||
|
this.applyTemplate(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get currentAccountProductTemplate(): any {
|
||||||
|
return this._currentAccountProductTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAccountSettingsForm: UntypedFormGroup;
|
||||||
|
|
||||||
|
lockinPeriodFrequencyTypeData: any[] = [];
|
||||||
|
|
||||||
|
periodFrequencyData: any[] = [
|
||||||
|
{ value: 'Daily' },
|
||||||
|
{ value: 'Weekly' },
|
||||||
|
{ value: 'Monthly' },
|
||||||
|
{ value: 'Quarterly' },
|
||||||
|
{ value: 'Half Yearly' },
|
||||||
|
{ value: 'Yearly' }];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.createForm();
|
||||||
|
this.buildDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
createForm() {
|
||||||
|
this.currentAccountSettingsForm = this.formBuilder.group({
|
||||||
|
minRequiredOpeningBalance: [
|
||||||
|
'',
|
||||||
|
[Validators.min(0)]
|
||||||
|
],
|
||||||
|
withdrawalFeeForTransfers: [false],
|
||||||
|
enforceMinRequiredBalance: [false],
|
||||||
|
minRequiredBalance: [
|
||||||
|
'',
|
||||||
|
[Validators.min(0)]
|
||||||
|
],
|
||||||
|
balanceFrequency: ['Monthly'],
|
||||||
|
enableLockinPeriod: [false],
|
||||||
|
lockinPeriodFrequency: [''],
|
||||||
|
lockinPeriodFrequencyType: [''],
|
||||||
|
accountClosedZeroBalanceInDayClosing: [false],
|
||||||
|
allowOverdraft: [false],
|
||||||
|
odAccountClosedZeroBalanceInDayClosing: [false],
|
||||||
|
avgBalanceCalcPeriod: ['6M'],
|
||||||
|
validForPeriod: ['3M'],
|
||||||
|
overdraftLimit: [
|
||||||
|
'',
|
||||||
|
[Validators.min(0)]
|
||||||
|
],
|
||||||
|
nominalAnnualInterestRateOverdraft: [
|
||||||
|
'',
|
||||||
|
[Validators.min(0)]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (this._currentAccountProductTemplate) {
|
||||||
|
this.applyTemplate(this._currentAccountProductTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyTemplate(t: any) {
|
||||||
|
this.lockinPeriodFrequencyTypeData = t.lockinPeriodFrequencyTypeOptions || [];
|
||||||
|
const hasLockin = !!(t.lockinPeriodFrequency && t.lockinPeriodFrequency > 0);
|
||||||
|
|
||||||
|
this.currentAccountSettingsForm.patchValue({
|
||||||
|
minRequiredOpeningBalance: t.minRequiredOpeningBalance,
|
||||||
|
withdrawalFeeForTransfers: t.withdrawalFeeForTransfers || false,
|
||||||
|
enforceMinRequiredBalance: t.enforceMinRequiredBalance || false,
|
||||||
|
minRequiredBalance: t.minRequiredBalance,
|
||||||
|
balanceFrequency: t.balanceFrequency || 'Monthly',
|
||||||
|
enableLockinPeriod: hasLockin,
|
||||||
|
lockinPeriodFrequency: hasLockin ? t.lockinPeriodFrequency : '',
|
||||||
|
lockinPeriodFrequencyType: hasLockin ? t.lockinPeriodFrequencyType?.id || '' : '',
|
||||||
|
accountClosedZeroBalanceInDayClosing: t.accountClosedZeroBalanceInDayClosing || false,
|
||||||
|
allowOverdraft: t.allowOverdraft || false,
|
||||||
|
odAccountClosedZeroBalanceInDayClosing: t.odAccountClosedZeroBalanceInDayClosing || false,
|
||||||
|
avgBalanceCalcPeriod: t.avgBalanceCalcPeriod || '6M',
|
||||||
|
validForPeriod: t.validForPeriod || '3M',
|
||||||
|
overdraftLimit: t.overdraftLimit || '',
|
||||||
|
nominalAnnualInterestRateOverdraft: t.nominalAnnualInterestRateOverdraft || ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDependencies() {
|
||||||
|
this.currentAccountSettingsForm.get('enableLockinPeriod').valueChanges.subscribe((enabled: boolean) => {
|
||||||
|
const freq = this.currentAccountSettingsForm.get('lockinPeriodFrequency');
|
||||||
|
const type = this.currentAccountSettingsForm.get('lockinPeriodFrequencyType');
|
||||||
|
if (enabled) {
|
||||||
|
freq.setValidators([
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(1)
|
||||||
|
]);
|
||||||
|
type.setValidators([Validators.required]);
|
||||||
|
} else {
|
||||||
|
freq.setValue('');
|
||||||
|
type.setValue('');
|
||||||
|
freq.clearValidators();
|
||||||
|
type.clearValidators();
|
||||||
|
}
|
||||||
|
freq.updateValueAndValidity();
|
||||||
|
type.updateValueAndValidity();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentAccountSettings() {
|
||||||
|
const value = { ...this.currentAccountSettingsForm.getRawValue() };
|
||||||
|
|
||||||
|
if (!value.enableLockinPeriod) {
|
||||||
|
value.lockinPeriodFrequency = null;
|
||||||
|
value.lockinPeriodFrequencyType = null;
|
||||||
|
}
|
||||||
|
delete value.enableLockinPeriod;
|
||||||
|
delete value.balanceFrequency;
|
||||||
|
|
||||||
|
if (!value.allowOverdraft) {
|
||||||
|
delete value.overdraftLimit;
|
||||||
|
delete value.nominalAnnualInterestRateOverdraft;
|
||||||
|
delete value.avgBalanceCalcPeriod;
|
||||||
|
delete value.validForPeriod;
|
||||||
|
delete value.odAccountClosedZeroBalanceInDayClosing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
<!--
|
||||||
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<form [formGroup]="currentAccountTermForm">
|
||||||
|
<div class="sat-section">
|
||||||
|
<div class="sat-section__header">
|
||||||
|
<span class="sat-section__badge"><i class="fas fa-chart-line"></i></span>
|
||||||
|
<span>Interest On OD</span>
|
||||||
|
</div>
|
||||||
|
<div class="sat-body">
|
||||||
|
<!-- Interest Posting Frequency -->
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>Interest Posting Frequency</mat-label>
|
||||||
|
<mat-select formControlName="interestPostingPeriodType" required>
|
||||||
|
@for (opt of interestPostingPeriodTypeData; track opt) {
|
||||||
|
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>Interest Posting Frequency is <strong>required</strong></mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Footer: Auto Post + Frequency Fix Day + Last Interest Date -->
|
||||||
|
<div class="sat-footer">
|
||||||
|
<div class="sat-footer__grid">
|
||||||
|
<div class="sat-toggle-card">
|
||||||
|
<div>
|
||||||
|
<h4>Auto Interest Post</h4>
|
||||||
|
</div>
|
||||||
|
<div class="sat-toggle-card__choices">
|
||||||
|
@for (option of yesNoOptions; track option.value) {
|
||||||
|
<label class="sat-radio-label">
|
||||||
|
<input type="radio" formControlName="autoPost" [value]="option.value" />
|
||||||
|
<span>{{ option.label }}</span>
|
||||||
|
</label>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>Frequency (Fix Day)</mat-label>
|
||||||
|
<input type="number" matInput formControlName="frequencyFixDay" min="0" />
|
||||||
|
<mat-hint>Days</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sat-field" (click)="lastInterestDatePicker.open()">
|
||||||
|
<mat-label>Last Interest Date</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="lastInterestDatePicker"
|
||||||
|
formControlName="lastInterestDate"
|
||||||
|
[min]="minDate"
|
||||||
|
[max]="maxDate"
|
||||||
|
/>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="lastInterestDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #lastInterestDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sat-note">
|
||||||
|
<span>⚠️</span>
|
||||||
|
<span>Daily product-based interest accrual enabled — interest calculated EOD on daily closing balance.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.sat-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-section__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-body {
|
||||||
|
background: #fff;
|
||||||
|
padding: 18px 24px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-banner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #eff6ff;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
font-size: 13px;
|
||||||
|
color: $gray-900;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 4px 16px;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid $border;
|
||||||
|
|
||||||
|
&__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr 1fr;
|
||||||
|
gap: 4px 16px;
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-toggle-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: $gray-50;
|
||||||
|
min-height: 56px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #64748b;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__choices {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-radio-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
[type='radio'] {
|
||||||
|
accent-color: $accent;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-note {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fffbeb;
|
||||||
|
border: 1px solid #fde68a;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #92400e;
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, Input, OnInit, inject } from '@angular/core';
|
||||||
|
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-current-account-term-step',
|
||||||
|
templateUrl: './current-account-term-step.component.html',
|
||||||
|
styleUrls: ['./current-account-term-step.component.scss'],
|
||||||
|
imports: [...STANDALONE_SHARED_IMPORTS]
|
||||||
|
})
|
||||||
|
export class CurrentAccountTermStepComponent implements OnInit {
|
||||||
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
|
||||||
|
private _currentAccountProductTemplate: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set currentAccountProductTemplate(value: any) {
|
||||||
|
this._currentAccountProductTemplate = value;
|
||||||
|
if (value && this.currentAccountTermForm) {
|
||||||
|
this.applyTemplate(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get currentAccountProductTemplate(): any {
|
||||||
|
return this._currentAccountProductTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAccountTermForm: UntypedFormGroup;
|
||||||
|
|
||||||
|
interestPostingPeriodTypeData: any[] = [];
|
||||||
|
|
||||||
|
yesNoOptions = [
|
||||||
|
{ value: true, label: 'Yes' },
|
||||||
|
{ value: false, label: 'No' }
|
||||||
|
];
|
||||||
|
|
||||||
|
minDate = new Date(2000, 0, 1);
|
||||||
|
maxDate = new Date();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.createForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
createForm() {
|
||||||
|
this.currentAccountTermForm = this.formBuilder.group({
|
||||||
|
currencyCode: [{ value: '', disabled: true }],
|
||||||
|
interestPostingPeriodType: [
|
||||||
|
'',
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
autoPost: [true],
|
||||||
|
frequencyFixDay: [
|
||||||
|
30,
|
||||||
|
[Validators.min(0)]
|
||||||
|
],
|
||||||
|
lastInterestDate: ['']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.maxDate = this.settingsService.businessDate;
|
||||||
|
if (this._currentAccountProductTemplate) {
|
||||||
|
this.applyTemplate(this._currentAccountProductTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyTemplate(t: any) {
|
||||||
|
this.maxDate = this.settingsService.businessDate;
|
||||||
|
this.interestPostingPeriodTypeData = t.interestPostingPeriodTypeOptions || [];
|
||||||
|
|
||||||
|
this.currentAccountTermForm.patchValue({
|
||||||
|
currencyCode: t.currency?.code,
|
||||||
|
interestPostingPeriodType: t.interestPostingPeriodType?.id,
|
||||||
|
autoPost: t.autoPost ?? true,
|
||||||
|
frequencyFixDay: t.frequencyFixDay ?? 30,
|
||||||
|
lastInterestDate: t.lastInterestDate ? new Date(t.lastInterestDate) : ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentAccountTerms() {
|
||||||
|
const value = { ...this.currentAccountTermForm.getRawValue() };
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
|
||||||
|
if (value.lastInterestDate instanceof Date) {
|
||||||
|
value.lastInterestDate = this.dateUtils.formatDate(value.lastInterestDate, dateFormat) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
delete value.currencyCode;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -93,6 +93,7 @@
|
|||||||
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
|
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
|
||||||
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
||||||
[recurringDepositAccountData]="recurringDepositAccountData"
|
[recurringDepositAccountData]="recurringDepositAccountData"
|
||||||
|
[rdInterestRates]="rdInterestRates"
|
||||||
(submitEvent)="submit()"
|
(submitEvent)="submit()"
|
||||||
>
|
>
|
||||||
</mifosx-recurring-deposits-account-preview-step>
|
</mifosx-recurring-deposits-account-preview-step>
|
||||||
|
|||||||
+1
@@ -94,6 +94,7 @@
|
|||||||
[recurringDepositsAccountTemplate]="recurringDepositsAccountAndTemplate"
|
[recurringDepositsAccountTemplate]="recurringDepositsAccountAndTemplate"
|
||||||
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
|
||||||
[recurringDepositAccountData]="recurringDepositAccountData"
|
[recurringDepositAccountData]="recurringDepositAccountData"
|
||||||
|
[rdInterestRates]="rdInterestRates"
|
||||||
(submitEvent)="submit()"
|
(submitEvent)="submit()"
|
||||||
>
|
>
|
||||||
</mifosx-recurring-deposits-account-preview-step>
|
</mifosx-recurring-deposits-account-preview-step>
|
||||||
|
|||||||
+13
-2
@@ -79,7 +79,7 @@ export class RecurringDepositsAccountChargesStepComponent implements OnInit, OnC
|
|||||||
@Input() recurringDepositAccountFormValid: boolean;
|
@Input() recurringDepositAccountFormValid: boolean;
|
||||||
|
|
||||||
/** Charges Data */
|
/** Charges Data */
|
||||||
chargeData: any;
|
chargeData: any[] = [];
|
||||||
/** Charges Data Source */
|
/** Charges Data Source */
|
||||||
chargesDataSource: {}[] = [];
|
chargesDataSource: {}[] = [];
|
||||||
/** Charges table columns */
|
/** Charges table columns */
|
||||||
@@ -102,8 +102,10 @@ export class RecurringDepositsAccountChargesStepComponent implements OnInit, OnC
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.chargesDataSource = [];
|
this.chargesDataSource = [];
|
||||||
if (this.recurringDepositsAccountTemplate.id && this.recurringDepositsAccountTemplate.charges) {
|
if (this.recurringDepositsAccountTemplate.id && this.recurringDepositsAccountTemplate.charges) {
|
||||||
|
// Edit mode: load saved charges from existing account
|
||||||
this.chargesDataSource =
|
this.chargesDataSource =
|
||||||
this.recurringDepositsAccountTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
this.recurringDepositsAccountTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
||||||
|
this.isChargesPatched = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,8 +122,17 @@ export class RecurringDepositsAccountChargesStepComponent implements OnInit, OnC
|
|||||||
}
|
}
|
||||||
if (this.recurringDepositsAccountProductTemplate) {
|
if (this.recurringDepositsAccountProductTemplate) {
|
||||||
this.chargeData = this.recurringDepositsAccountProductTemplate.chargeOptions.filter(
|
this.chargeData = this.recurringDepositsAccountProductTemplate.chargeOptions.filter(
|
||||||
(c: Charge) => c.currency.code === this.currency.code
|
(c: Charge) => c.currency.code === this.currency?.code
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// New account: pre-populate charges from product template
|
||||||
|
if (!this.isChargesPatched && !this.recurringDepositsAccountTemplate?.id) {
|
||||||
|
const productCharges = this.recurringDepositsAccountProductTemplate.charges;
|
||||||
|
if (Array.isArray(productCharges) && productCharges.length > 0) {
|
||||||
|
this.chargesDataSource = productCharges.map((charge: any) => ({ ...charge, id: charge.chargeId }));
|
||||||
|
}
|
||||||
|
this.isChargesPatched = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+188
-61
@@ -7,80 +7,207 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="recurringDepositAccountDetailsForm">
|
<form [formGroup]="recurringDepositAccountDetailsForm">
|
||||||
<div class="layout-row-wrap gap-2px responsive-column">
|
<!-- ===================== Account Details ===================== -->
|
||||||
<mat-form-field class="flex-48">
|
<div class="rad-section">
|
||||||
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
<div class="rad-section__header">
|
||||||
<mat-select formControlName="productId" required>
|
<i class="fas fa-info-circle"></i>
|
||||||
@for (product of productData; track product) {
|
<h3>Account Details</h3>
|
||||||
<mat-option [value]="product.id">
|
</div>
|
||||||
{{ product.name }}
|
<div class="rad-grid">
|
||||||
</mat-option>
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
}
|
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
||||||
</mat-select>
|
<mat-select formControlName="productId" required>
|
||||||
<mat-error>
|
@for (product of productData; track product) {
|
||||||
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
<mat-option [value]="product.id">{{ product.name }}</mat-option>
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
}
|
||||||
</mat-error>
|
</mat-select>
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
@if (isProductSelected) {
|
|
||||||
<mat-form-field class="flex-48" (click)="submittedOnDatePicker.open()">
|
|
||||||
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
[min]="minDate"
|
|
||||||
[max]="maxDate"
|
|
||||||
[matDatepicker]="submittedOnDatePicker"
|
|
||||||
formControlName="submittedOnDate"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
|
||||||
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{ 'labels.inputs.Submission Date' | translate }} {{ 'labels.commons.is' | translate }}
|
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
}
|
|
||||||
|
|
||||||
@if (isProductSelected) {
|
@if (isProductSelected) {
|
||||||
<mat-form-field class="flex-48">
|
<mat-form-field appearance="outline" class="rad-field" (click)="submittedOnDatePicker.open()">
|
||||||
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
||||||
<mat-select formControlName="fieldOfficerId">
|
<input
|
||||||
@for (officer of fieldOfficerData; track officer) {
|
matInput
|
||||||
<mat-option [value]="officer.id">
|
[min]="minDate"
|
||||||
{{ officer.displayName }}
|
[max]="maxDate"
|
||||||
</mat-option>
|
[matDatepicker]="submittedOnDatePicker"
|
||||||
}
|
formControlName="submittedOnDate"
|
||||||
</mat-select>
|
required
|
||||||
</mat-form-field>
|
/>
|
||||||
}
|
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
@if (isProductSelected) {
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
<mat-form-field class="flex-48">
|
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
||||||
<mat-label>{{ 'labels.inputs.External id' | translate }}</mat-label>
|
<mat-select formControlName="fieldOfficerId">
|
||||||
<input
|
@for (officer of fieldOfficerData; track officer) {
|
||||||
matInput
|
<mat-option [value]="officer.id">{{ officer.displayName }}</mat-option>
|
||||||
formControlName="externalId"
|
}
|
||||||
matTooltip="{{ 'tooltips.Provides an external id' | translate }}"
|
</mat-select>
|
||||||
/>
|
</mat-form-field>
|
||||||
</mat-form-field>
|
|
||||||
}
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.External id' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="externalId" />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (isProductSelected && productTemplate) {
|
||||||
|
<!-- ===================== Product Classification ===================== -->
|
||||||
|
<div class="rad-section rad-section--mt">
|
||||||
|
<div class="rad-section__header">
|
||||||
|
<i class="fas fa-tags"></i>
|
||||||
|
<h3>Product Classification</h3>
|
||||||
|
<span class="rad-badge">From Product</span>
|
||||||
|
</div>
|
||||||
|
<div class="rad-grid rad-grid--classification">
|
||||||
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>Account Type</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.accountType || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>Customer Segment</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.customerSegment || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>Product Code</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.productCode || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
@if (productTemplate.effectiveDate) {
|
||||||
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>Effective Date</mat-label>
|
||||||
|
<input matInput [value]="formatTemplateDate(productTemplate.effectiveDate)" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (productTemplate.endDate) {
|
||||||
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>End Date</mat-label>
|
||||||
|
<input matInput [value]="formatTemplateDate(productTemplate.endDate)" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>Introducer Required</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.introducerRequired ? 'Yes' : 'No'" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="rad-field rad-info-row">
|
||||||
|
<span class="rad-info-label">Share Linking Enabled</span>
|
||||||
|
<span class="rad-badge" [class.rad-badge--yes]="productTemplate.shareLinkingEnabled">
|
||||||
|
{{ productTemplate.shareLinkingEnabled ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ===================== Currency & Account Status Configuration ===================== -->
|
||||||
|
<div class="rad-section rad-section--mt">
|
||||||
|
<div class="rad-section__header">
|
||||||
|
<i class="fas fa-coins"></i>
|
||||||
|
<h3>Currency & Account Status Configuration</h3>
|
||||||
|
<span class="rad-badge">From Product</span>
|
||||||
|
</div>
|
||||||
|
<div class="rad-body">
|
||||||
|
@if (productTemplate.currency) {
|
||||||
|
<div class="rad-grid">
|
||||||
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>Currency</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.currency.name" readonly />
|
||||||
|
<mat-hint>Default: Indian Rupee - auto-set for domestic products</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
@if (
|
||||||
|
productTemplate.currency.decimalPlaces !== undefined && productTemplate.currency.decimalPlaces !== null
|
||||||
|
) {
|
||||||
|
<mat-form-field appearance="outline" class="rad-field">
|
||||||
|
<mat-label>Decimal Places</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.currency.decimalPlaces" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="rad-alert">
|
||||||
|
<div class="rad-alert__content">
|
||||||
|
<h4>Dormancy Alert</h4>
|
||||||
|
<p>Enable automated notifications when an account becomes inactive based on product rules.</p>
|
||||||
|
</div>
|
||||||
|
<div class="rad-alert__choices">
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.dormancyAlert === true">yes</span>
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.dormancyAlert === false">no</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rad-alert">
|
||||||
|
<div class="rad-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>
|
||||||
|
<div class="rad-alert__choices">
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.balanceSwapOption === true">yes</span>
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.balanceSwapOption === false">no</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rad-alert">
|
||||||
|
<div class="rad-alert__content">
|
||||||
|
<h4>MSME</h4>
|
||||||
|
<p>Micro, Small and Medium Enterprises</p>
|
||||||
|
</div>
|
||||||
|
<div class="rad-alert__choices">
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.msme === true">yes</span>
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.msme === false">no</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rad-alert">
|
||||||
|
<div class="rad-alert__content">
|
||||||
|
<h4>Nomination Facility</h4>
|
||||||
|
<p>
|
||||||
|
Allow account holders to designate individuals to receive funds in the event of account holder's death.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="rad-alert__choices">
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.nominationFacility === true">yes</span>
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.nominationFacility === false">no</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rad-alert">
|
||||||
|
<div class="rad-alert__content">
|
||||||
|
<h4>Auto Debit Facility</h4>
|
||||||
|
<p>Enable automatic deduction of funds for recurring payments, utility bills, or EMI obligations.</p>
|
||||||
|
</div>
|
||||||
|
<div class="rad-alert__choices">
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.autoDebitFacility === true">yes</span>
|
||||||
|
<span class="rad-yn" [class.rad-yn--active]="productTemplate.autoDebitFacility === false">no</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
<button mat-raised-button matStepperPrevious disabled>
|
<button mat-raised-button matStepperPrevious disabled>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
{{ 'labels.buttons.Previous' | translate }}
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button mat-raised-button matStepperNext [disabled]="recurringDepositAccountDetailsForm.invalid">
|
||||||
mat-raised-button
|
|
||||||
matStepperNext
|
|
||||||
[disabled]="
|
|
||||||
!recurringDepositAccountDetailsForm.get('productId')?.value ||
|
|
||||||
!recurringDepositAccountDetailsForm.get('submittedOnDate')?.value
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ 'labels.buttons.Next' | translate }}
|
{{ 'labels.buttons.Next' | translate }}
|
||||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
+176
-2
@@ -6,6 +6,180 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.margin-t {
|
$accent: #0ea5e9;
|
||||||
margin-top: 1em;
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
// ── Section card ───────────────────────────────────────────────
|
||||||
|
.rad-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
&--mt {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 2-col grid (Account Details inputs) ───────────────────────
|
||||||
|
.rad-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 4px 16px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 3-col grid (Product Classification) ───────────────────────
|
||||||
|
.rad-grid--classification {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rad-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Share Linking badge row ────────────────────────────────────
|
||||||
|
.rad-info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rad-info-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rad-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3px 12px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1d4ed8;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
|
||||||
|
&--yes {
|
||||||
|
background: #dcfce7;
|
||||||
|
color: #15803d;
|
||||||
|
border-color: #bbf7d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Currency section body ──────────────────────────────────────
|
||||||
|
.rad-body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Yes / No alert rows (same as rdpc-alert) ──────────────────
|
||||||
|
.rad-alert {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
padding: 18px 24px 24px;
|
||||||
|
border-top: 1px solid $border;
|
||||||
|
background: #f8fafc;
|
||||||
|
|
||||||
|
@media (width <= 760px) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
h4 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #64748b;
|
||||||
|
max-width: 540px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__choices {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 18px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── yes / no indicator pills ───────────────────────────────────
|
||||||
|
.rad-yn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #94a3b8;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid #cbd5e1;
|
||||||
|
background: #fff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
color: $gray-900;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
border-color: #22c55e;
|
||||||
|
background: radial-gradient(circle, #22c55e 45%, #fff 46%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-12
@@ -7,9 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component, OnInit, Input, Output, EventEmitter, inject } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter, inject, ChangeDetectorRef } from '@angular/core';
|
||||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
import { SettingsService } from 'app/settings/settings.service';
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
|
||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { RecurringDepositsService } from '../../recurring-deposits.service';
|
import { RecurringDepositsService } from '../../recurring-deposits.service';
|
||||||
@@ -37,6 +38,8 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
private formBuilder = inject(UntypedFormBuilder);
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
private recurringDepositsService = inject(RecurringDepositsService);
|
private recurringDepositsService = inject(RecurringDepositsService);
|
||||||
private settingsService = inject(SettingsService);
|
private settingsService = inject(SettingsService);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
private cdr = inject(ChangeDetectorRef);
|
||||||
|
|
||||||
/** Recurring Deposits Account Template */
|
/** Recurring Deposits Account Template */
|
||||||
@Input() recurringDepositsAccountTemplate: any;
|
@Input() recurringDepositsAccountTemplate: any;
|
||||||
@@ -52,19 +55,16 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
/** For edit recurring deposits form */
|
/** For edit recurring deposits form */
|
||||||
isFieldOfficerPatched = false;
|
isFieldOfficerPatched = false;
|
||||||
/** Recurring Deposits Account Details Form */
|
/** Recurring Deposits Account Details Form */
|
||||||
recurringDepositAccountDetailsForm: UntypedFormGroup;
|
recurringDepositAccountDetailsForm!: UntypedFormGroup;
|
||||||
|
|
||||||
isProductSelected = false;
|
isProductSelected = false;
|
||||||
|
|
||||||
/** Recurring Deposits Account Template with product data */
|
/** Recurring Deposits Account Template with product data */
|
||||||
@Output() recurringDepositsAccountProductTemplate = new EventEmitter();
|
@Output() recurringDepositsAccountProductTemplate = new EventEmitter();
|
||||||
|
|
||||||
/**
|
/** Local copy of product template for displaying classification info */
|
||||||
* Sets recurring deposits account details form.
|
productTemplate: any = null;
|
||||||
* @param {FormBuilder} formBuilder Form Builder.
|
|
||||||
* @param {RecurringDepositsService} recurringDepositsService Recurring Deposits Service.
|
|
||||||
* @param {SettingsService} settingsService Settings Service
|
|
||||||
*/
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createRecurringDepositsAccountDetailsForm();
|
this.createRecurringDepositsAccountDetailsForm();
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,7 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
if (this.recurringDepositsAccountTemplate) {
|
if (this.recurringDepositsAccountTemplate) {
|
||||||
this.productData = this.recurringDepositsAccountTemplate.productOptions;
|
this.productData = this.recurringDepositsAccountTemplate.productOptions;
|
||||||
if (this.recurringDepositsAccountTemplate.depositProductId) {
|
if (this.recurringDepositsAccountTemplate.depositProductId) {
|
||||||
|
// Edit mode: patch from saved data
|
||||||
this.recurringDepositAccountDetailsForm.patchValue({
|
this.recurringDepositAccountDetailsForm.patchValue({
|
||||||
productId: this.recurringDepositsAccountTemplate.depositProductId,
|
productId: this.recurringDepositsAccountTemplate.depositProductId,
|
||||||
submittedOnDate:
|
submittedOnDate:
|
||||||
@@ -82,6 +83,11 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
new Date(this.recurringDepositsAccountTemplate.timeline.submittedOnDate),
|
new Date(this.recurringDepositsAccountTemplate.timeline.submittedOnDate),
|
||||||
externalId: this.recurringDepositsAccountTemplate.externalId
|
externalId: this.recurringDepositsAccountTemplate.externalId
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// New account: default submittedOnDate to today's business date
|
||||||
|
this.recurringDepositAccountDetailsForm.patchValue({
|
||||||
|
submittedOnDate: this.settingsService.businessDate
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,25 +115,72 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
buildDependencies() {
|
buildDependencies() {
|
||||||
const clientId = this.recurringDepositsAccountTemplate.clientId;
|
const clientId = this.recurringDepositsAccountTemplate.clientId;
|
||||||
this.recurringDepositAccountDetailsForm.get('productId').valueChanges.subscribe((productId: string) => {
|
this.recurringDepositAccountDetailsForm.get('productId')!.valueChanges.subscribe((productId: string) => {
|
||||||
this.recurringDepositsService
|
this.recurringDepositsService
|
||||||
.getRecurringDepositsAccountTemplate(clientId, productId)
|
.getRecurringDepositsAccountTemplate(clientId, productId)
|
||||||
.subscribe((response: any) => {
|
.subscribe((response: any) => {
|
||||||
|
// Ensure currency is included in the product template
|
||||||
|
if (!response.currency) {
|
||||||
|
// Try to get currency from the selected product
|
||||||
|
const selectedProduct = this.recurringDepositsAccountTemplate.productOptions?.find(
|
||||||
|
(product: any) => product.id == productId
|
||||||
|
);
|
||||||
|
if (selectedProduct?.currency) {
|
||||||
|
response.currency = selectedProduct.currency;
|
||||||
|
} else if (this.recurringDepositsAccountTemplate.currency) {
|
||||||
|
response.currency = this.recurringDepositsAccountTemplate.currency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.productTemplate = response;
|
||||||
this.recurringDepositsAccountProductTemplate.emit(response);
|
this.recurringDepositsAccountProductTemplate.emit(response);
|
||||||
this.fieldOfficerData = response.fieldOfficerOptions;
|
this.fieldOfficerData = response.fieldOfficerOptions;
|
||||||
this.isProductSelected = true;
|
this.isProductSelected = true;
|
||||||
|
this.cdr.detectChanges();
|
||||||
if (!this.isFieldOfficerPatched && this.recurringDepositsAccountTemplate.fieldOfficerId) {
|
if (!this.isFieldOfficerPatched && this.recurringDepositsAccountTemplate.fieldOfficerId) {
|
||||||
this.recurringDepositAccountDetailsForm
|
this.recurringDepositAccountDetailsForm
|
||||||
.get('fieldOfficerId')
|
.get('fieldOfficerId')!
|
||||||
.patchValue(this.recurringDepositsAccountTemplate.fieldOfficerId);
|
.patchValue(this.recurringDepositsAccountTemplate.fieldOfficerId);
|
||||||
this.isFieldOfficerPatched = true;
|
this.isFieldOfficerPatched = true;
|
||||||
} else {
|
} else {
|
||||||
this.recurringDepositAccountDetailsForm.get('fieldOfficerId').patchValue('');
|
this.recurringDepositAccountDetailsForm.get('fieldOfficerId')!.patchValue('');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a value is not null, undefined, or empty string.
|
||||||
|
*/
|
||||||
|
hasValue(val: any): boolean {
|
||||||
|
return val !== null && val !== undefined && val !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats template dates into the current CBS date format.
|
||||||
|
* Handles backend date arrays like [2026, 4, 20] as well as strings and Date objects.
|
||||||
|
*/
|
||||||
|
formatTemplateDate(value: any): string {
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let dateValue: Date | null = null;
|
||||||
|
|
||||||
|
if (value instanceof Date) {
|
||||||
|
dateValue = value;
|
||||||
|
} else if (Array.isArray(value) && value.length >= 3) {
|
||||||
|
dateValue = new Date(value[0], value[1] - 1, value[2]);
|
||||||
|
} else {
|
||||||
|
dateValue = new Date(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dateValue || Number.isNaN(dateValue.getTime())) {
|
||||||
|
return Array.isArray(value) ? value.join('/') : String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dateUtils.formatDate(dateValue, 'M/d/yyyy') || '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns recurring deposits account details form value.
|
* Returns recurring deposits account details form value.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+8
-8
@@ -49,30 +49,30 @@
|
|||||||
|
|
||||||
<!-- ── Legacy chart (shown only when no custom rate chart available) ── -->
|
<!-- ── Legacy chart (shown only when no custom rate chart available) ── -->
|
||||||
@if (!_activeRdVersion) {
|
@if (!_activeRdVersion) {
|
||||||
<div class="flex-fill" *ngIf="recurringDepositsAccountProductTemplate?.accountChart.name">
|
<div class="flex-fill" *ngIf="recurringDepositsAccountProductTemplate?.accountChart?.name">
|
||||||
<span class="flex-40">{{ 'labels.inputs.name' | translate }}:</span>
|
<span class="flex-40">{{ 'labels.inputs.name' | translate }}:</span>
|
||||||
<span class="flex-60">{{ recurringDepositsAccountProductTemplate.accountChart.name }}</span>
|
<span class="flex-60">{{ recurringDepositsAccountProductTemplate?.accountChart?.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-fill">
|
<div class="flex-fill">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Valid from Date' | translate }}:</span>
|
<span class="flex-40">{{ 'labels.inputs.Valid from Date' | translate }}:</span>
|
||||||
<span class="flex-60">{{ recurringDepositsAccountProductTemplate?.accountChart.fromDate | dateFormat }}</span>
|
<span class="flex-60">{{ recurringDepositsAccountProductTemplate?.accountChart?.fromDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-fill" *ngIf="recurringDepositsAccountProductTemplate?.accountChart.endDate">
|
<div class="flex-fill" *ngIf="recurringDepositsAccountProductTemplate?.accountChart?.endDate">
|
||||||
<span class="flex-40">{{ 'labels.inputs.End Date' | translate }}:</span>
|
<span class="flex-40">{{ 'labels.inputs.End Date' | translate }}:</span>
|
||||||
<span class="flex-60">{{ recurringDepositsAccountProductTemplate?.accountChart.endDate | dateFormat }}</span>
|
<span class="flex-60">{{ recurringDepositsAccountProductTemplate?.accountChart?.endDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-fill" *ngIf="recurringDepositsAccountProductTemplate?.accountChart.description">
|
<div class="flex-fill" *ngIf="recurringDepositsAccountProductTemplate?.accountChart?.description">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Description' | translate }}:</span>
|
<span class="flex-40">{{ 'labels.inputs.Description' | translate }}:</span>
|
||||||
<span class="flex-60">{{ recurringDepositsAccountProductTemplate?.accountChart.description }}</span>
|
<span class="flex-60">{{ recurringDepositsAccountProductTemplate?.accountChart?.description }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-fill">
|
<div class="flex-fill">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Primary Grouping by Amount' | translate }}:</span>
|
<span class="flex-40">{{ 'labels.inputs.Primary Grouping by Amount' | translate }}:</span>
|
||||||
<span class="flex-60">{{
|
<span class="flex-60">{{
|
||||||
recurringDepositsAccountProductTemplate?.accountChart.isPrimaryGroupingByAmount | yesNo
|
recurringDepositsAccountProductTemplate?.accountChart?.isPrimaryGroupingByAmount | yesNo
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -120,7 +120,7 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
|
|||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
if (this.recurringDepositsAccountProductTemplate) {
|
if (this.recurringDepositsAccountProductTemplate) {
|
||||||
this.interestRateChartData = this.recurringDepositsAccountProductTemplate.accountChart.chartSlabs;
|
this.interestRateChartData = this.recurringDepositsAccountProductTemplate.accountChart?.chartSlabs ?? [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+834
-404
File diff suppressed because it is too large
Load Diff
+180
-20
@@ -6,35 +6,120 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
table {
|
// ── Wrapper ──────────────────────────────────────────────────────────────────
|
||||||
width: 100%;
|
.preview-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.25rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-elevation-z1 {
|
// ── Section card ─────────────────────────────────────────────────────────────
|
||||||
margin: 1em 0 1.5em;
|
.preview-section {
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 1px 3px rgb(0 0 0 / 6%);
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h2,
|
// ── Body ─────────────────────────────────────────────────────────────────────
|
||||||
h3,
|
.preview-body {
|
||||||
h4 {
|
padding: 1rem 1.25rem;
|
||||||
margin: 0;
|
}
|
||||||
|
|
||||||
|
// ── Subsection (inside Settings) ─────────────────────────────────────────────
|
||||||
|
.preview-subsection {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-divider {
|
||||||
|
margin: 0.75rem 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Grid ─────────────────────────────────────────────────────────────────────
|
||||||
|
.preview-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem 1.5rem;
|
||||||
|
|
||||||
|
&--three {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 480px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Field ─────────────────────────────────────────────────────────────────────
|
||||||
|
.preview-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-label {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: #888;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-value {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
color: #222;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
// ── Yes/No badge ─────────────────────────────────────────────────────────────
|
||||||
margin: 0.5em 0;
|
.preview-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.1rem 0.55rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #ffebee;
|
||||||
|
color: #c62828;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
&--yes {
|
||||||
|
background: #e8f5e9;
|
||||||
|
color: #2e7d32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-divider {
|
// ── Tables ───────────────────────────────────────────────────────────────────
|
||||||
margin: 0 0 0.5em;
|
.preview-table {
|
||||||
}
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
.margin-t {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-b {
|
|
||||||
margin-bottom: 0.25em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.irc-table {
|
.irc-table {
|
||||||
@@ -56,3 +141,78 @@ mat-divider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.margin-t {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Product feature flag rows (same style as details step) ────────────────────
|
||||||
|
.preview-feature-list {
|
||||||
|
margin-top: 1rem;
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-feature-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-feature-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: #26a69a;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-feature-choices {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rad-yn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
padding: 0.2rem 0.75rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 2px solid #ccc;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: #999;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid #ccc;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
border-color: #4caf50;
|
||||||
|
color: #2e7d32;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
border-color: #4caf50;
|
||||||
|
background: #4caf50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+17
-1
@@ -73,9 +73,25 @@ export class RecurringDepositsAccountPreviewStepComponent implements OnChanges {
|
|||||||
@Input() recurringDepositsAccountTemplate: any;
|
@Input() recurringDepositsAccountTemplate: any;
|
||||||
@Input() recurringDepositsAccountProductTemplate: any;
|
@Input() recurringDepositsAccountProductTemplate: any;
|
||||||
@Input() recurringDepositAccountData: any;
|
@Input() recurringDepositAccountData: any;
|
||||||
|
@Input() set rdInterestRates(rates: any[]) {
|
||||||
|
this._rdInterestRates = rates || [];
|
||||||
|
this._activeRdVersion = this._rdInterestRates.find((v: any) => v.isActive) ?? this._rdInterestRates[0] ?? null;
|
||||||
|
}
|
||||||
/** Output the submit action */
|
/** Output the submit action */
|
||||||
@Output() submitEvent = new EventEmitter();
|
@Output() submitEvent = new EventEmitter();
|
||||||
|
|
||||||
|
_rdInterestRates: any[] = [];
|
||||||
|
_activeRdVersion: any = null;
|
||||||
|
|
||||||
|
readonly rdSlabColumns = [
|
||||||
|
'srNo',
|
||||||
|
'description',
|
||||||
|
'fromPeriod',
|
||||||
|
'toPeriod',
|
||||||
|
'periodTypeLabel',
|
||||||
|
'annualInterestRate'
|
||||||
|
];
|
||||||
|
|
||||||
/** Charges Displayed Columns */
|
/** Charges Displayed Columns */
|
||||||
chargesDisplayedColumns: string[] = [
|
chargesDisplayedColumns: string[] = [
|
||||||
'name',
|
'name',
|
||||||
@@ -113,7 +129,7 @@ export class RecurringDepositsAccountPreviewStepComponent implements OnChanges {
|
|||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
if (this.recurringDepositsAccountProductTemplate) {
|
if (this.recurringDepositsAccountProductTemplate) {
|
||||||
this.interestRateChartData = this.recurringDepositsAccountProductTemplate.accountChart.chartSlabs;
|
this.interestRateChartData = this.recurringDepositsAccountProductTemplate.accountChart?.chartSlabs ?? [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+137
-211
@@ -7,219 +7,145 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="recurringDepositAccountSettingsForm">
|
<form [formGroup]="recurringDepositAccountSettingsForm">
|
||||||
<div class="layout-row-wrap gap-2px responsive-column">
|
<!-- ===================== Premature Option ===================== -->
|
||||||
<mat-checkbox labelPosition="before" formControlName="isMandatoryDeposit" class="margin-v flex-48">
|
<div class="rdps-section">
|
||||||
{{ 'labels.inputs.Is Mandatory Deposit' | translate }}?
|
<div class="rdps-section__header">
|
||||||
</mat-checkbox>
|
<span class="rdps-section__badge"><i class="fas fa-balance-scale"></i></span>
|
||||||
|
<span>Premature option</span>
|
||||||
<mat-checkbox class="flex-48 margin-v" labelPosition="before" formControlName="adjustAdvanceTowardsFuturePayments">
|
</div>
|
||||||
{{ 'labels.inputs.Adjust advance payments toward future installments' | translate }}?
|
<div class="rdps-body">
|
||||||
</mat-checkbox>
|
<div class="rdps-premature-row">
|
||||||
|
<mat-checkbox formControlName="loanFacility">Loan Facility</mat-checkbox>
|
||||||
<mat-checkbox labelPosition="before" formControlName="allowWithdrawal" class="margin-v flex-48">
|
<mat-checkbox formControlName="prematureWithdrawalOption">Premature Withdrawal option</mat-checkbox>
|
||||||
{{ 'labels.inputs.Allow Withdrawals' | translate }}?
|
<mat-checkbox formControlName="tenureFlexibility">Tenure Flexibility</mat-checkbox>
|
||||||
</mat-checkbox>
|
<mat-checkbox formControlName="lienMarked">Lien Marked</mat-checkbox>
|
||||||
|
<mat-checkbox formControlName="withdrawalFeeForTransfers">Apply Withdrawal Fee for Transfers</mat-checkbox>
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Lock-in Period' | translate }}</h4>
|
</div>
|
||||||
|
</div>
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
|
||||||
<input type="number" matInput formControlName="lockinPeriodFrequency" />
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="lockinPeriodFrequencyType">
|
|
||||||
@for (lockinPeriodFrequencyType of lockinPeriodFrequencyTypeData; track lockinPeriodFrequencyType) {
|
|
||||||
<mat-option [value]="lockinPeriodFrequencyType.id">
|
|
||||||
{{ lockinPeriodFrequencyType.value | translateKey: 'catalogs' }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Recurring Deposit Details' | translate }}</h4>
|
|
||||||
@if (currency) {
|
|
||||||
<mifosx-input-amount
|
|
||||||
[currency]="currency"
|
|
||||||
[isRequired]="true"
|
|
||||||
class="flex-48"
|
|
||||||
[inputFormControl]="recurringDepositAccountSettingsForm.controls.mandatoryRecommendedDepositAmount"
|
|
||||||
[inputLabel]="'Recurring Deposit Amount'"
|
|
||||||
>
|
|
||||||
</mifosx-input-amount>
|
|
||||||
}
|
|
||||||
|
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Deposit Period' | translate }}</h4>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Deposit Period' | translate }}</mat-label>
|
|
||||||
<input type="number" matInput formControlName="depositPeriod" required />
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="depositPeriodFrequencyId" required>
|
|
||||||
@for (periodFrequencyType of periodFrequencyTypeData; track periodFrequencyType) {
|
|
||||||
<mat-option [value]="periodFrequencyType.id">
|
|
||||||
{{ periodFrequencyType.value | translateKey: 'catalogs' }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-checkbox labelPosition="before" formControlName="isCalendarInherited" class="margin-v flex-48">
|
|
||||||
{{ 'labels.inputs.Deposit Frequency Same as Group/Center meeting' | translate }}
|
|
||||||
</mat-checkbox>
|
|
||||||
|
|
||||||
@if (!recurringDepositAccountSettingsForm.value.isCalendarInherited) {
|
|
||||||
<mat-form-field class="flex-48" (click)="expectedFirstDepositOnDatePicker.open()">
|
|
||||||
<mat-label>{{ 'labels.inputs.Deposit Start Date' | translate }}</mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
[min]="minDate"
|
|
||||||
[max]="maxDate"
|
|
||||||
[matDatepicker]="expectedFirstDepositOnDatePicker"
|
|
||||||
formControlName="expectedFirstDepositOnDate"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<mat-datepicker-toggle matSuffix [for]="expectedFirstDepositOnDatePicker"></mat-datepicker-toggle>
|
|
||||||
<mat-datepicker #expectedFirstDepositOnDatePicker></mat-datepicker>
|
|
||||||
</mat-form-field>
|
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Deposit Frequency' | translate }}</h4>
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Deposit Frequency' | translate }}</mat-label>
|
|
||||||
<input type="number" matInput formControlName="recurringFrequency" required />
|
|
||||||
<mat-error>
|
|
||||||
{{ 'labels.inputs.Deposit Frequency' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="recurringFrequencyType" required>
|
|
||||||
@for (periodFrequencyType of periodFrequencyTypeData; track periodFrequencyType) {
|
|
||||||
<mat-option [value]="periodFrequencyType.id">
|
|
||||||
{{ periodFrequencyType.value | translateKey: 'catalogs' }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
<mat-error>
|
|
||||||
{{ 'labels.inputs.Deposit Frequency Type' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
}
|
|
||||||
|
|
||||||
<mat-divider class="flex-98"></mat-divider>
|
|
||||||
|
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Minimum Deposit Term' | translate }}</h4>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
|
||||||
<input type="number" matInput formControlName="minDepositTerm" required />
|
|
||||||
<mat-error>
|
|
||||||
{{ 'labels.inputs.Minimum Deposit Term Frequency' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Frequency Type' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="minDepositTermTypeId" required>
|
|
||||||
@for (periodFrequencyType of periodFrequencyTypeData; track periodFrequencyType) {
|
|
||||||
<mat-option [value]="periodFrequencyType.id">
|
|
||||||
{{ periodFrequencyType.value | translateKey: 'catalogs' }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
<mat-error>
|
|
||||||
{{ 'labels.inputs.Minimum Deposit Term Frequency Type' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.And thereafter, in Multiples of' | translate }}</h4>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
|
||||||
<input type="number" matInput formControlName="inMultiplesOfDepositTerm" />
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="inMultiplesOfDepositTermTypeId">
|
|
||||||
@for (periodFrequencyType of periodFrequencyTypeData; track periodFrequencyType) {
|
|
||||||
<mat-option [value]="periodFrequencyType.id">
|
|
||||||
{{ periodFrequencyType.value | translateKey: 'catalogs' }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Maximum Deposit Term' | translate }}</h4>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
|
||||||
<input type="number" matInput formControlName="maxDepositTerm" />
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="maxDepositTermTypeId">
|
|
||||||
@for (periodFrequencyType of periodFrequencyTypeData; track periodFrequencyType) {
|
|
||||||
<mat-option [value]="periodFrequencyType.id">
|
|
||||||
{{ periodFrequencyType.value | translateKey: 'catalogs' }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-divider class="flex-98"></mat-divider>
|
|
||||||
|
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.For Pre-mature closure' | translate }}</h4>
|
|
||||||
|
|
||||||
<mat-checkbox class="flex-73 margin-v" labelPosition="before" formControlName="preClosurePenalApplicable">
|
|
||||||
{{ 'labels.inputs.Apply Penal Interest (less)' | translate }}
|
|
||||||
</mat-checkbox>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Penal Interest' | translate }} (%)</mat-label>
|
|
||||||
<input type="number" matInput formControlName="preClosurePenalInterest" />
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Period' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="preClosurePenalInterestOnTypeId">
|
|
||||||
@for (preClosurePenalInterestOnType of preClosurePenalInterestOnTypeData; track preClosurePenalInterestOnType) {
|
|
||||||
<mat-option [value]="preClosurePenalInterestOnType.id">
|
|
||||||
{{ preClosurePenalInterestOnType.value | translateKey: 'catalogs' }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<!-- <mat-divider class="flex-98"></mat-divider> -->
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Minimum Balance For Interest Calculation' | translate }}</mat-label>
|
|
||||||
<input type="number" matInput formControlName="minBalanceForInterestCalculation" />
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
@if (taxGroup) {
|
|
||||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Tax Group' | translate }}</h4>
|
|
||||||
<mat-checkbox labelPosition="before" formControlName="withHoldTax" class="margin-v flex-48">
|
|
||||||
{{ 'labels.inputs.Is Withhold Tax Applicable' | translate }}?
|
|
||||||
</mat-checkbox>
|
|
||||||
@if (recurringDepositAccountSettingsForm.controls.taxGroupId) {
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Tax Group' | translate }}</mat-label>
|
|
||||||
<input type="text" matInput formControlName="taxGroupId" />
|
|
||||||
</mat-form-field>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ===================== Lock-in Period ===================== -->
|
||||||
|
<div class="rdps-section">
|
||||||
|
<div class="rdps-section__header">
|
||||||
|
<span class="rdps-section__badge"><i class="fas fa-lock"></i></span>
|
||||||
|
<span>{{ 'labels.inputs.Lock-in Period' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-body">
|
||||||
|
<div class="rdps-lockin-row">
|
||||||
|
<span>Enable Lock-in Period</span>
|
||||||
|
<mat-checkbox formControlName="enableLockinPeriod"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (recurringDepositAccountSettingsForm.value.enableLockinPeriod) {
|
||||||
|
<div class="rdps-lockin-fields">
|
||||||
|
<mat-form-field appearance="outline" class="rdps-field">
|
||||||
|
<mat-label>Frequency*</mat-label>
|
||||||
|
<input type="number" matInput formControlName="lockinPeriodFrequency" min="1" step="1" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdps-field">
|
||||||
|
<mat-label>Type*</mat-label>
|
||||||
|
<mat-select formControlName="lockinPeriodFrequencyType">
|
||||||
|
@for (lockinPeriodFrequencyType of lockinPeriodFrequencyTypeData; track lockinPeriodFrequencyType) {
|
||||||
|
<mat-option [value]="lockinPeriodFrequencyType.id">
|
||||||
|
{{ lockinPeriodFrequencyType.value | translateKey: 'catalogs' }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ===================== Maturity Instructions ===================== -->
|
||||||
|
<div class="rdps-section">
|
||||||
|
<div class="rdps-section__header">
|
||||||
|
<span class="rdps-section__badge"><i class="fas fa-lock"></i></span>
|
||||||
|
<span>Maturity Instructions</span>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-body rdps-body--list">
|
||||||
|
<div class="rdps-list-row">
|
||||||
|
<span>Maturity Instructions</span>
|
||||||
|
<mat-checkbox formControlName="maturityInstructions"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-list-row">
|
||||||
|
<span
|
||||||
|
>Option to credit maturity proceeds directly to the linked account or reinvest in another product (like
|
||||||
|
FD).</span
|
||||||
|
>
|
||||||
|
<mat-checkbox formControlName="creditMaturityProceeds"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-list-row">
|
||||||
|
<span>Penalty / Grace Period for Missed Installments</span>
|
||||||
|
<mat-checkbox formControlName="penaltyGracePeriodMissedInstallments"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-list-row">
|
||||||
|
<span>Panel Request Applicable</span>
|
||||||
|
<mat-checkbox formControlName="panelRequestApplicable"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-list-row">
|
||||||
|
<span>No penalty for nominee / legal heir closure</span>
|
||||||
|
<mat-checkbox formControlName="noPenaltyNomineeHeirClosure"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-list-row">
|
||||||
|
<span>Deceased Settlement</span>
|
||||||
|
<mat-checkbox formControlName="deceasedSettlement"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-list-row">
|
||||||
|
<span>Allow Debit Txn after due date</span>
|
||||||
|
<mat-checkbox formControlName="allowDebitTxnAfterDueDate"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ===================== Additional Rules ===================== -->
|
||||||
|
<div class="rdps-section">
|
||||||
|
<div class="rdps-section__header">
|
||||||
|
<span class="rdps-section__badge"><i class="fas fa-cog"></i></span>
|
||||||
|
<span>Additional Rules</span>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-body">
|
||||||
|
<div class="rdps-layout rdps-layout--additional">
|
||||||
|
<div class="rdps-toggle rdps-toggle--inline">
|
||||||
|
<mat-checkbox formControlName="accountClosedZeroBalanceInDayClosing">
|
||||||
|
<span>Account Closed with Zero balance in Day Closing</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdps-field">
|
||||||
|
<mat-label>Balance Required for Interest Calculation</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minBalanceForInterestCalculation" min="0" step="0.01" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ===================== Withhold Tax (if applicable) ===================== -->
|
||||||
|
@if (recurringDepositAccountSettingsForm.contains('withHoldTax')) {
|
||||||
|
<div class="rdps-section">
|
||||||
|
<div class="rdps-section__header">
|
||||||
|
<span class="rdps-section__badge"><i class="fas fa-percent"></i></span>
|
||||||
|
<span>{{ 'labels.inputs.Is Withhold Tax Applicable' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="rdps-body">
|
||||||
|
<div class="rdps-lockin-row">
|
||||||
|
<span>{{ 'labels.inputs.Is Withhold Tax Applicable' | translate }}</span>
|
||||||
|
<mat-checkbox formControlName="withHoldTax"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
@if (
|
||||||
|
recurringDepositAccountSettingsForm.value.withHoldTax &&
|
||||||
|
recurringDepositAccountSettingsForm.contains('taxGroupId')
|
||||||
|
) {
|
||||||
|
<mat-form-field appearance="outline" class="rdps-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Tax Group' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="taxGroupId" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
<button mat-raised-button matStepperPrevious>
|
<button mat-raised-button matStepperPrevious>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
|
|||||||
+164
-13
@@ -6,27 +6,178 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
h4 {
|
$accent: #0ea5e9;
|
||||||
font-weight: 500;
|
$border: #e2e8f0;
|
||||||
margin: 1em 0;
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.rdps-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
span:not(.rdps-section__badge) {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
.rdps-section__badge {
|
||||||
font-weight: 500;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-divider {
|
.rdps-body {
|
||||||
margin: 1em 0 2em;
|
background: #fff;
|
||||||
|
padding: 18px 24px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-v {
|
.rdps-body--list {
|
||||||
margin: 1em 0;
|
padding: 0 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-b {
|
.rdps-field {
|
||||||
margin: 0 0 1em;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-t {
|
.rdps-premature-row {
|
||||||
margin-top: 1em;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdps-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px 16px;
|
||||||
|
|
||||||
|
&--two {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
|
||||||
|
@media (width <= 640px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--mt {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdps-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 40px;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--inline {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdps-layout--additional {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
|
||||||
|
@media (width <= 1100px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdps-list-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 14px 0;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
max-width: 85%;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdps-lockin-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdps-lockin-fields {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
@media (width <= 640px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdps-sub-header {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
margin: 16px 0 8px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdps-checkbox-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 56px;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-55
@@ -24,8 +24,6 @@ import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
|||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
/** Custom Services */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recurring Deposits Account Settings Step
|
* Recurring Deposits Account Settings Step
|
||||||
*/
|
*/
|
||||||
@@ -58,19 +56,15 @@ export class RecurringDepositsAccountSettingsStepComponent implements OnInit, On
|
|||||||
/** Maximum date allowed. */
|
/** Maximum date allowed. */
|
||||||
maxDate = new Date();
|
maxDate = new Date();
|
||||||
/** Lockin Period Frequency Type Data */
|
/** Lockin Period Frequency Type Data */
|
||||||
lockinPeriodFrequencyTypeData: any;
|
lockinPeriodFrequencyTypeData: any[] = [];
|
||||||
/** Period Frequency Type Data */
|
/** Period Frequency Type Data */
|
||||||
periodFrequencyTypeData: any;
|
periodFrequencyTypeData: any[] = [];
|
||||||
/** Preclosure Penal Interest Type on Data */
|
/** Preclosure Penal Interest Type on Data */
|
||||||
preClosurePenalInterestOnTypeData: any;
|
preClosurePenalInterestOnTypeData: any[] = [];
|
||||||
/** Tax Group */
|
/** Tax Group */
|
||||||
taxGroup: any;
|
taxGroup: any;
|
||||||
currency: Currency | null = null;
|
currency: Currency | null = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {FormBuilder} formBuilder Form Builder
|
|
||||||
* @param {SettingsService} settingsService Settings Service
|
|
||||||
*/
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createRecurringDepositAccountSettingsForm();
|
this.createRecurringDepositAccountSettingsForm();
|
||||||
this.buildDependencies();
|
this.buildDependencies();
|
||||||
@@ -82,14 +76,33 @@ export class RecurringDepositsAccountSettingsStepComponent implements OnInit, On
|
|||||||
: this.recurringDepositsAccountTemplate;
|
: this.recurringDepositsAccountTemplate;
|
||||||
if (recurringDepositsAccount) {
|
if (recurringDepositsAccount) {
|
||||||
this.currency = recurringDepositsAccount.currency;
|
this.currency = recurringDepositsAccount.currency;
|
||||||
|
|
||||||
|
// Patch CBS settings fields from product template
|
||||||
this.recurringDepositAccountSettingsForm.patchValue({
|
this.recurringDepositAccountSettingsForm.patchValue({
|
||||||
isMandatoryDeposit: recurringDepositsAccount.isMandatoryDeposit,
|
loanFacility: recurringDepositsAccount.loanFacility ?? false,
|
||||||
adjustAdvanceTowardsFuturePayments: recurringDepositsAccount.adjustAdvanceTowardsFuturePayments,
|
prematureWithdrawalOption: recurringDepositsAccount.prematureWithdrawalOption ?? false,
|
||||||
allowWithdrawal: recurringDepositsAccount.allowWithdrawal,
|
tenureFlexibility: recurringDepositsAccount.tenureFlexibility ?? false,
|
||||||
lockinPeriodFrequency: recurringDepositsAccount.lockinPeriodFrequency,
|
lienMarked: recurringDepositsAccount.lienMarked ?? false,
|
||||||
|
withdrawalFeeForTransfers: recurringDepositsAccount.withdrawalFeeForTransfers ?? false,
|
||||||
|
enableLockinPeriod: recurringDepositsAccount.enableLockinPeriod ?? false,
|
||||||
|
lockinPeriodFrequency: recurringDepositsAccount.lockinPeriodFrequency ?? '',
|
||||||
lockinPeriodFrequencyType: recurringDepositsAccount.lockinPeriodFrequencyType
|
lockinPeriodFrequencyType: recurringDepositsAccount.lockinPeriodFrequencyType
|
||||||
? recurringDepositsAccount.lockinPeriodFrequencyType.id
|
? recurringDepositsAccount.lockinPeriodFrequencyType.id
|
||||||
: '',
|
: '',
|
||||||
|
maturityInstructions: recurringDepositsAccount.maturityInstructions ?? false,
|
||||||
|
creditMaturityProceeds: recurringDepositsAccount.creditMaturityProceeds ?? false,
|
||||||
|
penaltyGracePeriodMissedInstallments: recurringDepositsAccount.penaltyGracePeriodMissedInstallments ?? false,
|
||||||
|
panelRequestApplicable: recurringDepositsAccount.panelRequestApplicable ?? false,
|
||||||
|
noPenaltyNomineeHeirClosure: recurringDepositsAccount.noPenaltyNomineeHeirClosure ?? false,
|
||||||
|
deceasedSettlement: recurringDepositsAccount.deceasedSettlement ?? false,
|
||||||
|
allowDebitTxnAfterDueDate: recurringDepositsAccount.allowDebitTxnAfterDueDate ?? false,
|
||||||
|
accountClosedZeroBalanceInDayClosing: recurringDepositsAccount.accountClosedZeroBalanceInDayClosing ?? false,
|
||||||
|
minBalanceForInterestCalculation: recurringDepositsAccount.minBalanceForInterestCalculation ?? '',
|
||||||
|
|
||||||
|
// Account-specific fields — auto-filled from product template (hidden from UI)
|
||||||
|
isMandatoryDeposit: recurringDepositsAccount.isMandatoryDeposit,
|
||||||
|
adjustAdvanceTowardsFuturePayments: recurringDepositsAccount.adjustAdvanceTowardsFuturePayments,
|
||||||
|
allowWithdrawal: recurringDepositsAccount.allowWithdrawal,
|
||||||
minDepositTerm: recurringDepositsAccount.minDepositTerm,
|
minDepositTerm: recurringDepositsAccount.minDepositTerm,
|
||||||
minDepositTermTypeId: recurringDepositsAccount.minDepositTermType
|
minDepositTermTypeId: recurringDepositsAccount.minDepositTermType
|
||||||
? recurringDepositsAccount.minDepositTermType.id
|
? recurringDepositsAccount.minDepositTermType.id
|
||||||
@@ -107,17 +120,16 @@ export class RecurringDepositsAccountSettingsStepComponent implements OnInit, On
|
|||||||
preClosurePenalInterestOnTypeId: recurringDepositsAccount.preClosurePenalInterestOnType
|
preClosurePenalInterestOnTypeId: recurringDepositsAccount.preClosurePenalInterestOnType
|
||||||
? recurringDepositsAccount.preClosurePenalInterestOnType.id
|
? recurringDepositsAccount.preClosurePenalInterestOnType.id
|
||||||
: '',
|
: '',
|
||||||
minBalanceForInterestCalculation: recurringDepositsAccount.minBalanceForInterestCalculation,
|
|
||||||
depositPeriod: recurringDepositsAccount.minDepositTerm,
|
depositPeriod: recurringDepositsAccount.minDepositTerm,
|
||||||
depositPeriodFrequencyId: recurringDepositsAccount.minDepositTermType.id,
|
depositPeriodFrequencyId: recurringDepositsAccount.minDepositTermType
|
||||||
expectedFirstDepositOnDate:
|
? recurringDepositsAccount.minDepositTermType.id
|
||||||
recurringDepositsAccount.expectedFirstDepositOnDate &&
|
: '',
|
||||||
new Date(recurringDepositsAccount.expectedFirstDepositOnDate),
|
|
||||||
recurringFrequency: recurringDepositsAccount.recurringFrequency,
|
recurringFrequency: recurringDepositsAccount.recurringFrequency,
|
||||||
recurringFrequencyType: recurringDepositsAccount.recurringFrequencyType
|
recurringFrequencyType: recurringDepositsAccount.recurringFrequencyType
|
||||||
? recurringDepositsAccount.recurringFrequencyType.id
|
? recurringDepositsAccount.recurringFrequencyType.id
|
||||||
: ''
|
: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
if (recurringDepositsAccount.withHoldTax) {
|
if (recurringDepositsAccount.withHoldTax) {
|
||||||
this.recurringDepositAccountSettingsForm.addControl('withHoldTax', new UntypedFormControl(false));
|
this.recurringDepositAccountSettingsForm.addControl('withHoldTax', new UntypedFormControl(false));
|
||||||
this.recurringDepositAccountSettingsForm.get('withHoldTax').valueChanges.subscribe((value: boolean) => {
|
this.recurringDepositAccountSettingsForm.get('withHoldTax').valueChanges.subscribe((value: boolean) => {
|
||||||
@@ -135,7 +147,7 @@ export class RecurringDepositsAccountSettingsStepComponent implements OnInit, On
|
|||||||
});
|
});
|
||||||
this.recurringDepositAccountSettingsForm
|
this.recurringDepositAccountSettingsForm
|
||||||
.get('withHoldTax')
|
.get('withHoldTax')
|
||||||
.patchValue(this.recurringDepositsAccountTemplate.withHoldTax);
|
.patchValue(this.recurringDepositsAccountTemplate?.withHoldTax);
|
||||||
} else {
|
} else {
|
||||||
this.recurringDepositAccountSettingsForm.removeControl('withHoldTax');
|
this.recurringDepositAccountSettingsForm.removeControl('withHoldTax');
|
||||||
}
|
}
|
||||||
@@ -157,38 +169,38 @@ export class RecurringDepositsAccountSettingsStepComponent implements OnInit, On
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates recurring deposits account terms form.
|
|
||||||
*/
|
|
||||||
createRecurringDepositAccountSettingsForm() {
|
createRecurringDepositAccountSettingsForm() {
|
||||||
this.recurringDepositAccountSettingsForm = this.formBuilder.group({
|
this.recurringDepositAccountSettingsForm = this.formBuilder.group({
|
||||||
|
// CBS Settings (visible — same as product settings page)
|
||||||
|
loanFacility: [false],
|
||||||
|
prematureWithdrawalOption: [false],
|
||||||
|
tenureFlexibility: [false],
|
||||||
|
lienMarked: [false],
|
||||||
|
withdrawalFeeForTransfers: [false],
|
||||||
|
enableLockinPeriod: [false],
|
||||||
|
lockinPeriodFrequency: [''],
|
||||||
|
lockinPeriodFrequencyType: [''],
|
||||||
|
maturityInstructions: [false],
|
||||||
|
creditMaturityProceeds: [false],
|
||||||
|
penaltyGracePeriodMissedInstallments: [false],
|
||||||
|
panelRequestApplicable: [false],
|
||||||
|
noPenaltyNomineeHeirClosure: [false],
|
||||||
|
deceasedSettlement: [false],
|
||||||
|
allowDebitTxnAfterDueDate: [false],
|
||||||
|
accountClosedZeroBalanceInDayClosing: [false],
|
||||||
|
minBalanceForInterestCalculation: [''],
|
||||||
|
|
||||||
|
// Account-specific fields (hidden from UI — auto-filled from product template)
|
||||||
isMandatoryDeposit: [''],
|
isMandatoryDeposit: [''],
|
||||||
adjustAdvanceTowardsFuturePayments: [''],
|
adjustAdvanceTowardsFuturePayments: [''],
|
||||||
allowWithdrawal: [''],
|
allowWithdrawal: [''],
|
||||||
lockinPeriodFrequency: [''],
|
mandatoryRecommendedDepositAmount: [''],
|
||||||
lockinPeriodFrequencyType: [''],
|
depositPeriod: [''],
|
||||||
mandatoryRecommendedDepositAmount: [
|
depositPeriodFrequencyId: [''],
|
||||||
'',
|
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
depositPeriod: [
|
|
||||||
'',
|
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
depositPeriodFrequencyId: [
|
|
||||||
'',
|
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
isCalendarInherited: [''],
|
isCalendarInherited: [''],
|
||||||
expectedFirstDepositOnDate: [''],
|
expectedFirstDepositOnDate: [''],
|
||||||
recurringFrequency: [
|
recurringFrequency: [''],
|
||||||
'',
|
recurringFrequencyType: [''],
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
recurringFrequencyType: [
|
|
||||||
'',
|
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
minDepositTerm: [{ value: '', disabled: true }],
|
minDepositTerm: [{ value: '', disabled: true }],
|
||||||
minDepositTermTypeId: [{ value: '', disabled: true }],
|
minDepositTermTypeId: [{ value: '', disabled: true }],
|
||||||
inMultiplesOfDepositTerm: [{ value: '', disabled: true }],
|
inMultiplesOfDepositTerm: [{ value: '', disabled: true }],
|
||||||
@@ -197,23 +209,16 @@ export class RecurringDepositsAccountSettingsStepComponent implements OnInit, On
|
|||||||
maxDepositTermTypeId: [{ value: '', disabled: true }],
|
maxDepositTermTypeId: [{ value: '', disabled: true }],
|
||||||
preClosurePenalApplicable: [{ value: '', disabled: true }],
|
preClosurePenalApplicable: [{ value: '', disabled: true }],
|
||||||
preClosurePenalInterest: [{ value: '', disabled: true }],
|
preClosurePenalInterest: [{ value: '', disabled: true }],
|
||||||
preClosurePenalInterestOnTypeId: [{ value: '', disabled: true }],
|
preClosurePenalInterestOnTypeId: [{ value: '', disabled: true }]
|
||||||
minBalanceForInterestCalculation: [{ value: '', disabled: true }]
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets all select dropdown options.
|
|
||||||
*/
|
|
||||||
setOptions(recurringDepositsAccount: any) {
|
setOptions(recurringDepositsAccount: any) {
|
||||||
this.lockinPeriodFrequencyTypeData = recurringDepositsAccount.lockinPeriodFrequencyTypeOptions;
|
this.lockinPeriodFrequencyTypeData = recurringDepositsAccount.lockinPeriodFrequencyTypeOptions;
|
||||||
this.periodFrequencyTypeData = recurringDepositsAccount.periodFrequencyTypeOptions;
|
this.periodFrequencyTypeData = recurringDepositsAccount.periodFrequencyTypeOptions;
|
||||||
this.preClosurePenalInterestOnTypeData = recurringDepositsAccount.preClosurePenalInterestOnTypeOptions;
|
this.preClosurePenalInterestOnTypeData = recurringDepositsAccount.preClosurePenalInterestOnTypeOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribes to value changes and sets new form controls accordingly.
|
|
||||||
*/
|
|
||||||
buildDependencies() {
|
buildDependencies() {
|
||||||
this.recurringDepositAccountSettingsForm
|
this.recurringDepositAccountSettingsForm
|
||||||
.get('isCalendarInherited')
|
.get('isCalendarInherited')
|
||||||
@@ -230,9 +235,6 @@ export class RecurringDepositsAccountSettingsStepComponent implements OnInit, On
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns recurring deposits account settings form value.
|
|
||||||
*/
|
|
||||||
get recurringDepositAccountSettings() {
|
get recurringDepositAccountSettings() {
|
||||||
return this.recurringDepositAccountSettingsForm.value;
|
return this.recurringDepositAccountSettingsForm.value;
|
||||||
}
|
}
|
||||||
|
|||||||
+161
-59
@@ -7,69 +7,171 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="recurringDepositAccountTermsForm">
|
<form [formGroup]="recurringDepositAccountTermsForm">
|
||||||
<div class="layout-row-wrap gap-2px responsive-column">
|
<!-- ===================== Interest Rate & Posting Terms ===================== -->
|
||||||
<mat-form-field class="flex-48">
|
<div class="rdpt-section">
|
||||||
<mat-label>{{ 'labels.inputs.Interest Compounding Period' | translate }}</mat-label>
|
<div class="rdpt-section__header">
|
||||||
<mat-select formControlName="interestCompoundingPeriodType" required>
|
<span class="rdpt-section__badge"><i class="fas fa-chart-line"></i></span>
|
||||||
@for (interestCompoundingPeriodType of interestCompoundingPeriodTypeData; track interestCompoundingPeriodType) {
|
<span>Interest Rate & Posting Terms</span>
|
||||||
<mat-option [value]="interestCompoundingPeriodType.id">
|
</div>
|
||||||
{{ interestCompoundingPeriodType.value | translateKey: 'catalogs' }}
|
<div class="rdpt-body">
|
||||||
</mat-option>
|
@if (recurringDepositsAccountProductTemplate?.nominalAnnualInterestRate != null) {
|
||||||
}
|
<div class="rdpt-banner">
|
||||||
</mat-select>
|
<span class="rdpt-banner__icon">ℹ️</span>
|
||||||
<mat-error>
|
<span>
|
||||||
{{ 'labels.inputs.Interest Compounding Period' | translate }} {{ 'labels.commons.is' | translate }}
|
Recurring Deposit base rate:
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
<b>{{ recurringDepositsAccountProductTemplate?.nominalAnnualInterestRate }}%</b>
|
||||||
</mat-error>
|
</span>
|
||||||
</mat-form-field>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
<!-- Row 1: Nominal Annual Interest | Effective Date | Interest Calculation Type -->
|
||||||
<mat-label>{{ 'labels.inputs.Interest Posting Period' | translate }}</mat-label>
|
<div class="rdpt-grid rdpt-grid--three">
|
||||||
<mat-select formControlName="interestPostingPeriodType" required>
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
@for (interestPostingPeriodType of interestPostingPeriodTypeData; track interestPostingPeriodType) {
|
<mat-label>Nominal Annual Interest</mat-label>
|
||||||
<mat-option [value]="interestPostingPeriodType.id">
|
<input matInput [value]="recurringDepositsAccountProductTemplate?.nominalAnnualInterestRate ?? ''" readonly />
|
||||||
{{ interestPostingPeriodType.value | translateKey: 'catalogs' }}
|
</mat-form-field>
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
<mat-error>
|
|
||||||
{{ 'labels.inputs.Interest Posting Period' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
<mat-label>{{ 'labels.inputs.Interest Calculated using' | translate }}</mat-label>
|
<mat-label>Effective Date</mat-label>
|
||||||
<mat-select formControlName="interestCalculationType" required>
|
<input matInput [value]="formatDate(recurringDepositsAccountProductTemplate?.effectiveDate)" readonly />
|
||||||
@for (interestCalculationType of interestCalculationTypeData; track interestCalculationType) {
|
</mat-form-field>
|
||||||
<mat-option [value]="interestCalculationType.id">
|
|
||||||
{{ interestCalculationType.value | translateKey: 'catalogs' }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
<mat-error>
|
|
||||||
{{ 'labels.inputs.Interest Calculated using' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-48">
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
<mat-label>{{ 'labels.inputs.Days in Year' | translate }}</mat-label>
|
<mat-label>Interest Calculation Type</mat-label>
|
||||||
<mat-select formControlName="interestCalculationDaysInYearType" required>
|
<input
|
||||||
@for (
|
matInput
|
||||||
interestCalculationDaysInYearType of interestCalculationDaysInYearTypeData;
|
[value]="
|
||||||
track interestCalculationDaysInYearType
|
recurringDepositsAccountProductTemplate?.interestCalculationTypeName ??
|
||||||
) {
|
(recurringDepositsAccountProductTemplate?.interestCalculationType?.id
|
||||||
<mat-option [value]="interestCalculationDaysInYearType.id">
|
| find: recurringDepositsAccountProductTemplate?.interestCalculationTypeOptions : 'id' : 'value') ??
|
||||||
{{ interestCalculationDaysInYearType.value | translateKey: 'catalogs' }}
|
''
|
||||||
</mat-option>
|
"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Row 2: Interest Calculate | Average Period | Interest Posting Period -->
|
||||||
|
<div class="rdpt-grid rdpt-grid--three">
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Interest Calculated using' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="interestCalculationType" required>
|
||||||
|
@for (interestCalculationType of interestCalculationTypeData; track interestCalculationType) {
|
||||||
|
<mat-option [value]="interestCalculationType.id">
|
||||||
|
{{ interestCalculationType.value | translateKey: 'catalogs' }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Interest Calculated using' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>Average Period</mat-label>
|
||||||
|
<input matInput [value]="recurringDepositsAccountProductTemplate?.averagePeriod ?? ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Interest Posting Period' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="interestPostingPeriodType" required>
|
||||||
|
@for (interestPostingPeriodType of interestPostingPeriodTypeData; track interestPostingPeriodType) {
|
||||||
|
<mat-option [value]="interestPostingPeriodType.id">
|
||||||
|
{{ interestPostingPeriodType.value | translateKey: 'catalogs' }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Interest Posting Period' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Row 3: Interest Compounding Period -->
|
||||||
|
<div class="rdpt-grid rdpt-grid--three">
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Interest Compounding Period' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="interestCompoundingPeriodType" required>
|
||||||
|
@for (
|
||||||
|
interestCompoundingPeriodType of interestCompoundingPeriodTypeData;
|
||||||
|
track interestCompoundingPeriodType
|
||||||
|
) {
|
||||||
|
<mat-option [value]="interestCompoundingPeriodType.id">
|
||||||
|
{{ interestCompoundingPeriodType.value | translateKey: 'catalogs' }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Interest Compounding Period' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer: Auto Interest Post | Frequency Fix Day | Last Interest Date -->
|
||||||
|
<div class="rdpt-footer">
|
||||||
|
<div class="rdpt-footer__grid">
|
||||||
|
<div class="rdpt-toggle-card">
|
||||||
|
<div>
|
||||||
|
<h4>Auto Interest Post</h4>
|
||||||
|
</div>
|
||||||
|
<div class="rdpt-toggle-card__choices">
|
||||||
|
<label class="rdpt-radio">
|
||||||
|
<input type="radio" [checked]="recurringDepositsAccountProductTemplate?.autoPost === true" disabled />
|
||||||
|
<span>Yes</span>
|
||||||
|
</label>
|
||||||
|
<label class="rdpt-radio">
|
||||||
|
<input type="radio" [checked]="recurringDepositsAccountProductTemplate?.autoPost === false" disabled />
|
||||||
|
<span>No</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>Frequency (Fix Day)</mat-label>
|
||||||
|
<input matInput [value]="recurringDepositsAccountProductTemplate?.frequencyFixDay ?? ''" readonly />
|
||||||
|
<mat-hint>Days</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>Last Interest Date</mat-label>
|
||||||
|
<input matInput [value]="formatDate(recurringDepositsAccountProductTemplate?.lastInterestDate)" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (recurringDepositAccountTermsForm.value.interestCalculationType === 1) {
|
||||||
|
<div class="rdpt-note">
|
||||||
|
<span>⚠️</span>
|
||||||
|
<span
|
||||||
|
>Daily product-based interest accrual enabled - interest calculated EOD on daily closing balance.</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</mat-select>
|
|
||||||
<mat-error>
|
<!-- Days in Year (account-level field) -->
|
||||||
{{ 'labels.inputs.Days in Year' | translate }} {{ 'labels.commons.is' | translate }}
|
<div class="rdpt-footer__grid rdpt-footer__grid--single">
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
</mat-error>
|
<mat-label>{{ 'labels.inputs.Days in Year' | translate }}</mat-label>
|
||||||
</mat-form-field>
|
<mat-select formControlName="interestCalculationDaysInYearType" required>
|
||||||
|
@for (
|
||||||
|
interestCalculationDaysInYearType of interestCalculationDaysInYearTypeData;
|
||||||
|
track interestCalculationDaysInYearType
|
||||||
|
) {
|
||||||
|
<mat-option [value]="interestCalculationDaysInYearType.id">
|
||||||
|
{{ interestCalculationDaysInYearType.value | translateKey: 'catalogs' }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Days in Year' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
|
|||||||
+164
@@ -5,3 +5,167 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.rdpt-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3,
|
||||||
|
span:not(.rdpt-section__badge) {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-section__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 4px 16px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
|
||||||
|
&--three {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
|
||||||
|
&--three {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-field {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&--full {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-banner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 16px 24px 0;
|
||||||
|
padding: 12px 14px;
|
||||||
|
background: #eff6ff;
|
||||||
|
border: 1px solid #dbeafe;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #1d4ed8;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-footer {
|
||||||
|
padding: 0 24px 24px;
|
||||||
|
border-top: 1px solid #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-footer__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
padding-top: 18px;
|
||||||
|
|
||||||
|
&--single {
|
||||||
|
grid-template-columns: repeat(1, 1fr);
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-toggle-card {
|
||||||
|
padding: 12px 0;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__choices {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-radio {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
[type='radio'] {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
accent-color: $accent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdpt-note {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 18px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid #fde68a;
|
||||||
|
background: #fffbeb;
|
||||||
|
color: #b45309;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|||||||
+71
-15
@@ -13,6 +13,7 @@ import { SettingsService } from 'app/settings/settings.service';
|
|||||||
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
import { FindPipe } from '../../../../pipes/find.pipe';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recurring Deposits Terms Step
|
* Recurring Deposits Terms Step
|
||||||
@@ -25,7 +26,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
...STANDALONE_SHARED_IMPORTS,
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
MatStepperPrevious,
|
MatStepperPrevious,
|
||||||
FaIconComponent,
|
FaIconComponent,
|
||||||
MatStepperNext
|
MatStepperNext,
|
||||||
|
FindPipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class RecurringDepositsAccountTermsStepComponent implements OnInit, OnChanges {
|
export class RecurringDepositsAccountTermsStepComponent implements OnInit, OnChanges {
|
||||||
@@ -42,13 +44,13 @@ export class RecurringDepositsAccountTermsStepComponent implements OnInit, OnCha
|
|||||||
/** Recurring Deposits Account Terms Form */
|
/** Recurring Deposits Account Terms Form */
|
||||||
recurringDepositAccountTermsForm: UntypedFormGroup;
|
recurringDepositAccountTermsForm: UntypedFormGroup;
|
||||||
/** Interest Compounding Period Type Data */
|
/** Interest Compounding Period Type Data */
|
||||||
interestCompoundingPeriodTypeData: any;
|
interestCompoundingPeriodTypeData: any[] = [];
|
||||||
/** Interest Posting Period Type Data */
|
/** Interest Posting Period Type Data */
|
||||||
interestPostingPeriodTypeData: any;
|
interestPostingPeriodTypeData: any[] = [];
|
||||||
/** Interest Calculation Type Data */
|
/** Interest Calculation Type Data */
|
||||||
interestCalculationTypeData: any;
|
interestCalculationTypeData: any[] = [];
|
||||||
/** Interest Calculation Days in Year Data */
|
/** Interest Calculation Days in Year Data */
|
||||||
interestCalculationDaysInYearTypeData: any;
|
interestCalculationDaysInYearTypeData: any[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {FormBuilder} formBuilder Form Builder
|
* @param {FormBuilder} formBuilder Form Builder
|
||||||
@@ -61,17 +63,18 @@ export class RecurringDepositsAccountTermsStepComponent implements OnInit, OnCha
|
|||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
if (this.recurringDepositsAccountProductTemplate) {
|
if (this.recurringDepositsAccountProductTemplate) {
|
||||||
this.setOptions();
|
this.setOptions();
|
||||||
|
this.initializeTermsValues();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.maxDate = this.settingsService.businessDate;
|
this.maxDate = this.settingsService.businessDate;
|
||||||
if (this.recurringDepositsAccountTemplate.id) {
|
if (this.isEditMode()) {
|
||||||
this.recurringDepositAccountTermsForm.patchValue({
|
this.recurringDepositAccountTermsForm.patchValue({
|
||||||
interestCompoundingPeriodType: this.recurringDepositsAccountTemplate.interestCompoundingPeriodType.id,
|
interestCompoundingPeriodType: this.recurringDepositsAccountTemplate?.interestCompoundingPeriodType?.id,
|
||||||
interestPostingPeriodType: this.recurringDepositsAccountTemplate.interestPostingPeriodType.id,
|
interestPostingPeriodType: this.recurringDepositsAccountTemplate?.interestPostingPeriodType?.id,
|
||||||
interestCalculationType: this.recurringDepositsAccountTemplate.interestCalculationType.id,
|
interestCalculationType: this.recurringDepositsAccountTemplate?.interestCalculationType?.id,
|
||||||
interestCalculationDaysInYearType: this.recurringDepositsAccountTemplate.interestCalculationDaysInYearType.id
|
interestCalculationDaysInYearType: this.recurringDepositsAccountTemplate?.interestCalculationDaysInYearType?.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,17 +113,70 @@ export class RecurringDepositsAccountTermsStepComponent implements OnInit, OnCha
|
|||||||
this.interestCalculationTypeData = this.recurringDepositsAccountProductTemplate.interestCalculationTypeOptions;
|
this.interestCalculationTypeData = this.recurringDepositsAccountProductTemplate.interestCalculationTypeOptions;
|
||||||
this.interestCalculationDaysInYearTypeData =
|
this.interestCalculationDaysInYearTypeData =
|
||||||
this.recurringDepositsAccountProductTemplate.interestCalculationDaysInYearTypeOptions;
|
this.recurringDepositsAccountProductTemplate.interestCalculationDaysInYearTypeOptions;
|
||||||
if (!this.recurringDepositsAccountTemplate.id) {
|
if (!this.isEditMode()) {
|
||||||
this.recurringDepositAccountTermsForm.patchValue({
|
this.recurringDepositAccountTermsForm.patchValue({
|
||||||
interestCompoundingPeriodType: this.recurringDepositsAccountProductTemplate.interestCompoundingPeriodType.id,
|
interestCompoundingPeriodType: this.recurringDepositsAccountProductTemplate?.interestCompoundingPeriodType?.id,
|
||||||
interestPostingPeriodType: this.recurringDepositsAccountProductTemplate.interestPostingPeriodType.id,
|
interestPostingPeriodType: this.recurringDepositsAccountProductTemplate?.interestPostingPeriodType?.id,
|
||||||
interestCalculationType: this.recurringDepositsAccountProductTemplate.interestCalculationType.id,
|
interestCalculationType: this.recurringDepositsAccountProductTemplate?.interestCalculationType?.id,
|
||||||
interestCalculationDaysInYearType:
|
interestCalculationDaysInYearType:
|
||||||
this.recurringDepositsAccountProductTemplate.interestCalculationDaysInYearType.id
|
this.recurringDepositsAccountProductTemplate?.interestCalculationDaysInYearType?.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** True when component is used for account edit flow. */
|
||||||
|
private isEditMode(): boolean {
|
||||||
|
return !!this.recurringDepositsAccountTemplate?.depositProductId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures create flow always gets product defaults while edit flow keeps saved values.
|
||||||
|
*/
|
||||||
|
private initializeTermsValues(): void {
|
||||||
|
const isEdit = this.isEditMode();
|
||||||
|
const fallbackFromProduct = {
|
||||||
|
interestCompoundingPeriodType: this.recurringDepositsAccountProductTemplate?.interestCompoundingPeriodType?.id,
|
||||||
|
interestPostingPeriodType: this.recurringDepositsAccountProductTemplate?.interestPostingPeriodType?.id,
|
||||||
|
interestCalculationType: this.recurringDepositsAccountProductTemplate?.interestCalculationType?.id,
|
||||||
|
interestCalculationDaysInYearType:
|
||||||
|
this.recurringDepositsAccountProductTemplate?.interestCalculationDaysInYearType?.id
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isEdit) {
|
||||||
|
this.recurringDepositAccountTermsForm.patchValue(fallbackFromProduct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const current = this.recurringDepositAccountTermsForm.value;
|
||||||
|
this.recurringDepositAccountTermsForm.patchValue({
|
||||||
|
interestCompoundingPeriodType:
|
||||||
|
current.interestCompoundingPeriodType ?? fallbackFromProduct.interestCompoundingPeriodType,
|
||||||
|
interestPostingPeriodType: current.interestPostingPeriodType ?? fallbackFromProduct.interestPostingPeriodType,
|
||||||
|
interestCalculationType: current.interestCalculationType ?? fallbackFromProduct.interestCalculationType,
|
||||||
|
interestCalculationDaysInYearType:
|
||||||
|
current.interestCalculationDaysInYearType ?? fallbackFromProduct.interestCalculationDaysInYearType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a date value (Date, [y,m,d] array, or string) to a display string.
|
||||||
|
*/
|
||||||
|
formatDate(value: any): string {
|
||||||
|
if (!value) return '';
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
const [
|
||||||
|
y,
|
||||||
|
m,
|
||||||
|
d
|
||||||
|
] = value;
|
||||||
|
return `${String(d).padStart(2, '0')}/${String(m).padStart(2, '0')}/${y}`;
|
||||||
|
}
|
||||||
|
if (value instanceof Date) {
|
||||||
|
return value.toLocaleDateString();
|
||||||
|
}
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns recurring deposits account terms form value.
|
* Returns recurring deposits account terms form value.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+4
-4
@@ -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
|
||||||
@@ -61,14 +61,14 @@
|
|||||||
@if (CurrentProduct.effectiveTo) {
|
@if (CurrentProduct.effectiveTo) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective To:</span>
|
<span class="flex-40">Effective To:</span>
|
||||||
<span class="flex-60">{{ CurrentProduct.effectiveTo }}</span>
|
<span class="flex-60">{{ CurrentProduct.effectiveTo | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (CurrentProduct.endDate) {
|
@if (CurrentProduct.endDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">End Date:</span>
|
<span class="flex-40">End Date:</span>
|
||||||
<span class="flex-60">{{ CurrentProduct.endDate }}</span>
|
<span class="flex-60">{{ CurrentProduct.endDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
@if (CurrentProduct.lastInterestDate) {
|
@if (CurrentProduct.lastInterestDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Last Interest Date:</span>
|
<span class="flex-40">Last Interest Date:</span>
|
||||||
<span class="flex-60">{{ CurrentProduct.lastInterestDate }}</span>
|
<span class="flex-60">{{ CurrentProduct.lastInterestDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -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
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
} from '@angular/material/table';
|
} from '@angular/material/table';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { FindPipe } from '../../../../pipes/find.pipe';
|
import { FindPipe } from '../../../../pipes/find.pipe';
|
||||||
|
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||||
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
@@ -47,6 +48,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
MatRow,
|
MatRow,
|
||||||
FaIconComponent,
|
FaIconComponent,
|
||||||
FindPipe,
|
FindPipe,
|
||||||
|
DateFormatPipe,
|
||||||
FormatNumberPipe,
|
FormatNumberPipe,
|
||||||
YesnoPipe
|
YesnoPipe
|
||||||
]
|
]
|
||||||
|
|||||||
-5
@@ -100,11 +100,6 @@
|
|||||||
<span>Account Closed with Zero Balance in Day Closing</span>
|
<span>Account Closed with Zero Balance in Day Closing</span>
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="sps-toggle sps-toggle--inline">
|
|
||||||
<mat-checkbox formControlName="withHoldTax">
|
|
||||||
<span>Withhold Tax is Applicable</span>
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -54,7 +54,6 @@ export class CurrentProductSettingsStepComponent implements OnInit {
|
|||||||
withdrawalFeeForTransfers: this.CurrentProductsTemplate.withdrawalFeeForTransfers,
|
withdrawalFeeForTransfers: this.CurrentProductsTemplate.withdrawalFeeForTransfers,
|
||||||
enforceMinRequiredBalance: this.CurrentProductsTemplate.enforceMinRequiredBalance,
|
enforceMinRequiredBalance: this.CurrentProductsTemplate.enforceMinRequiredBalance,
|
||||||
minRequiredBalance: this.CurrentProductsTemplate.minRequiredBalance,
|
minRequiredBalance: this.CurrentProductsTemplate.minRequiredBalance,
|
||||||
withHoldTax: this.CurrentProductsTemplate.withHoldTax ?? false,
|
|
||||||
balanceFrequency: isEditing ? this.CurrentProductsTemplate.balanceFrequency || defaultPeriod : defaultPeriod,
|
balanceFrequency: isEditing ? this.CurrentProductsTemplate.balanceFrequency || defaultPeriod : defaultPeriod,
|
||||||
enableLockinPeriod: !!(
|
enableLockinPeriod: !!(
|
||||||
this.CurrentProductsTemplate.lockinPeriodFrequency && this.CurrentProductsTemplate.lockinPeriodFrequency > 0
|
this.CurrentProductsTemplate.lockinPeriodFrequency && this.CurrentProductsTemplate.lockinPeriodFrequency > 0
|
||||||
@@ -107,7 +106,6 @@ export class CurrentProductSettingsStepComponent implements OnInit {
|
|||||||
'',
|
'',
|
||||||
[Validators.min(0)]
|
[Validators.min(0)]
|
||||||
],
|
],
|
||||||
withHoldTax: [false],
|
|
||||||
balanceFrequency: ['Monthly'],
|
balanceFrequency: ['Monthly'],
|
||||||
enableLockinPeriod: [false],
|
enableLockinPeriod: [false],
|
||||||
lockinPeriodFrequency: [''],
|
lockinPeriodFrequency: [''],
|
||||||
@@ -159,6 +157,8 @@ export class CurrentProductSettingsStepComponent implements OnInit {
|
|||||||
get CurrentProductSettings() {
|
get CurrentProductSettings() {
|
||||||
const formValue = { ...this.CurrentProductSettingsForm.value };
|
const formValue = { ...this.CurrentProductSettingsForm.value };
|
||||||
|
|
||||||
|
delete formValue.withHoldTax;
|
||||||
|
|
||||||
if (!formValue.allowOverdraft) {
|
if (!formValue.allowOverdraft) {
|
||||||
delete formValue.overdraftLimit;
|
delete formValue.overdraftLimit;
|
||||||
delete formValue.nominalAnnualInterestRateOverdraft;
|
delete formValue.nominalAnnualInterestRateOverdraft;
|
||||||
|
|||||||
+4
-4
@@ -75,21 +75,21 @@
|
|||||||
@if (currentProduct.effectiveDate) {
|
@if (currentProduct.effectiveDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective Date:</span>
|
<span class="flex-40">Effective Date:</span>
|
||||||
<span class="flex-60">{{ currentProduct.effectiveDate }}</span>
|
<span class="flex-60">{{ currentProduct.effectiveDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (currentProduct.effectiveTo) {
|
@if (currentProduct.effectiveTo) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective To:</span>
|
<span class="flex-40">Effective To:</span>
|
||||||
<span class="flex-60">{{ currentProduct.effectiveTo }}</span>
|
<span class="flex-60">{{ currentProduct.effectiveTo | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (currentProduct.endDate) {
|
@if (currentProduct.endDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">End Date:</span>
|
<span class="flex-40">End Date:</span>
|
||||||
<span class="flex-60">{{ currentProduct.endDate }}</span>
|
<span class="flex-60">{{ currentProduct.endDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@
|
|||||||
@if (currentProduct.lastInterestDate) {
|
@if (currentProduct.lastInterestDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Last Interest Date:</span>
|
<span class="flex-40">Last Interest Date:</span>
|
||||||
<span class="flex-60">{{ currentProduct.lastInterestDate }}</span>
|
<span class="flex-60">{{ currentProduct.lastInterestDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -23,6 +23,7 @@ import {
|
|||||||
MatRow
|
MatRow
|
||||||
} from '@angular/material/table';
|
} from '@angular/material/table';
|
||||||
import { ViewSavingsAccountingDetailsComponent } from '../../../../shared/accounting/view-savings-accounting-details/view-savings-accounting-details.component';
|
import { ViewSavingsAccountingDetailsComponent } from '../../../../shared/accounting/view-savings-accounting-details/view-savings-accounting-details.component';
|
||||||
|
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||||
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
@@ -46,6 +47,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
MatRowDef,
|
MatRowDef,
|
||||||
MatRow,
|
MatRow,
|
||||||
ViewSavingsAccountingDetailsComponent,
|
ViewSavingsAccountingDetailsComponent,
|
||||||
|
DateFormatPipe,
|
||||||
FormatNumberPipe,
|
FormatNumberPipe,
|
||||||
YesnoPipe
|
YesnoPipe
|
||||||
]
|
]
|
||||||
|
|||||||
+5
-5
@@ -150,11 +150,11 @@ export class EditRecurringDepositProductComponent {
|
|||||||
this.recurringDepositProductTermsForm.valid &&
|
this.recurringDepositProductTermsForm.valid &&
|
||||||
this.recurringDepositProductSettingsForm.valid &&
|
this.recurringDepositProductSettingsForm.valid &&
|
||||||
this.recurringDepositProductAccountingForm.valid &&
|
this.recurringDepositProductAccountingForm.valid &&
|
||||||
(this.recurringDepositProductDetailsForm.pristine ||
|
(!this.recurringDepositProductDetailsForm.pristine ||
|
||||||
this.recurringDepositProductCurrencyForm.pristine ||
|
!this.recurringDepositProductCurrencyForm.pristine ||
|
||||||
this.recurringDepositProductTermsForm.pristine ||
|
!this.recurringDepositProductTermsForm.pristine ||
|
||||||
this.recurringDepositProductSettingsForm.pristine ||
|
!this.recurringDepositProductSettingsForm.pristine ||
|
||||||
this.recurringDepositProductAccountingForm.pristine)
|
!this.recurringDepositProductAccountingForm.pristine)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-15
@@ -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
|
||||||
@@ -7,10 +7,10 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<div class="responsive-column">
|
<div class="responsive-column">
|
||||||
<!-- ── Product Name ── -->
|
<!-- ── Product Name ── -->
|
||||||
<h2 class="mat-h2 flex-100">{{ recurringDepositProduct.name }}</h2>
|
<h2 class="mat-h2 flex-100">{{ recurringDepositProduct.name }}</h2>
|
||||||
|
|
||||||
<!-- ── Details ── -->
|
<!-- ── Details ── -->
|
||||||
<mat-divider class="flex-100"></mat-divider>
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Details' | translate }}</h3>
|
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Details' | translate }}</h3>
|
||||||
<mat-divider class="flex-100"></mat-divider>
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ── Product Classification ── -->
|
<!-- ── Product Classification ── -->
|
||||||
@if (
|
@if (
|
||||||
recurringDepositProduct.accountType ||
|
recurringDepositProduct.accountType ||
|
||||||
recurringDepositProduct.customerSegment ||
|
recurringDepositProduct.customerSegment ||
|
||||||
@@ -63,14 +63,14 @@
|
|||||||
@if (recurringDepositProduct.effectiveTo) {
|
@if (recurringDepositProduct.effectiveTo) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective To:</span>
|
<span class="flex-40">Effective To:</span>
|
||||||
<span class="flex-60">{{ recurringDepositProduct.effectiveTo }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.effectiveTo | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (recurringDepositProduct.endDate) {
|
@if (recurringDepositProduct.endDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">End Date:</span>
|
<span class="flex-40">End Date:</span>
|
||||||
<span class="flex-60">{{ recurringDepositProduct.endDate }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.endDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ── Currency ── -->
|
<!-- ── Currency ── -->
|
||||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Currency' | translate }}</h3>
|
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Currency' | translate }}</h3>
|
||||||
<mat-divider class="flex-100"></mat-divider>
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
</div>
|
</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>
|
||||||
|
|
||||||
@@ -169,11 +169,22 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (recurringDepositProduct.interestCalculateSelector) {
|
||||||
|
<div class="flex-100 layout-row">
|
||||||
|
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||||
|
<span class="flex-60">{{
|
||||||
|
recurringDepositProduct.interestCalculateSelector
|
||||||
|
| find: recurringDepositProductsTemplate.interestCalculationTypeOption : 'id' : 'value'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
||||||
<span class="flex-60">{{
|
<span class="flex-60">{{
|
||||||
recurringDepositProduct.interestCalculationType
|
recurringDepositProduct.interestCalculationType
|
||||||
| find: recurringDepositProductsTemplate.interestCalculationTypeOptions : 'id' : 'value'
|
| find: recurringDepositProductsTemplate.interestCalculationTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -188,7 +199,7 @@
|
|||||||
@if (recurringDepositProduct.effectiveDate) {
|
@if (recurringDepositProduct.effectiveDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective Date:</span>
|
<span class="flex-40">Effective Date:</span>
|
||||||
<span class="flex-60">{{ recurringDepositProduct.effectiveDate }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.effectiveDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,11 +229,11 @@
|
|||||||
@if (recurringDepositProduct.lastInterestDate) {
|
@if (recurringDepositProduct.lastInterestDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Last Interest Date:</span>
|
<span class="flex-40">Last Interest Date:</span>
|
||||||
<span class="flex-60">{{ recurringDepositProduct.lastInterestDate }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.lastInterestDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ── Settings ── -->
|
<!-- ── Settings ── -->
|
||||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Settings' | translate }}</h3>
|
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Settings' | translate }}</h3>
|
||||||
<mat-divider class="flex-100"></mat-divider>
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
|
|
||||||
@@ -313,7 +324,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ── Interest Rate Slabs ── -->
|
<!-- ── Interest Rate Slabs ── -->
|
||||||
@if (recurringDepositProduct.rdSlabs?.length) {
|
@if (recurringDepositProduct.rdSlabs?.length) {
|
||||||
<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>
|
||||||
@@ -334,7 +345,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="toPeriod">
|
<ng-container matColumnDef="toPeriod">
|
||||||
<th mat-header-cell *matHeaderCellDef>To</th>
|
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||||
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? '—' }}</td>
|
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? '—' }}</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="periodTypeLabel">
|
<ng-container matColumnDef="periodTypeLabel">
|
||||||
<th mat-header-cell *matHeaderCellDef>Period</th>
|
<th mat-header-cell *matHeaderCellDef>Period</th>
|
||||||
@@ -352,7 +363,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ── Charges ── -->
|
<!-- ── Charges ── -->
|
||||||
@if (recurringDepositProduct.charges?.length) {
|
@if (recurringDepositProduct.charges?.length) {
|
||||||
<div class="layout-row-wrap responsive-column flex-100">
|
<div class="layout-row-wrap responsive-column flex-100">
|
||||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Charges' | translate }}</h3>
|
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Charges' | translate }}</h3>
|
||||||
@@ -380,7 +391,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ── Accounting ── -->
|
<!-- ── Accounting ── -->
|
||||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Accounting' | translate }}</h3>
|
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Accounting' | translate }}</h3>
|
||||||
<mat-divider class="flex-100"></mat-divider>
|
<mat-divider class="flex-100"></mat-divider>
|
||||||
|
|
||||||
|
|||||||
+16
-19
@@ -54,14 +54,22 @@
|
|||||||
<mat-form-field appearance="outline" class="rdpt-field">
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
||||||
<mat-select
|
<mat-select
|
||||||
formControlName="interestCalculationType"
|
formControlName="interestCalculateSelector"
|
||||||
matTooltip="{{ 'tooltips.The method used to calculate interest' | translate }}"
|
matTooltip="{{ 'tooltips.The method used to calculate interest' | translate }}"
|
||||||
required
|
|
||||||
>
|
>
|
||||||
@for (interestCalculationType of interestCalculationTypeOptionData; track interestCalculationType.id) {
|
@for (opt of interestCalculationTypeOptionData; track opt.id) {
|
||||||
<mat-option [value]="interestCalculationType.id">
|
<mat-option [value]="opt.id">{{ opt.value }}</mat-option>
|
||||||
{{ interestCalculationType.value | translateKey: 'catalogs' }}
|
}
|
||||||
</mat-option>
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rdpt-grid rdpt-grid--three">
|
||||||
|
<mat-form-field appearance="outline" class="rdpt-field">
|
||||||
|
<mat-label>Interest Calculate</mat-label>
|
||||||
|
<mat-select formControlName="interestCalculationType" required>
|
||||||
|
@for (mode of interestCalculationTypeData; track mode.id) {
|
||||||
|
<mat-option [value]="mode.id">{{ mode.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
}
|
}
|
||||||
</mat-select>
|
</mat-select>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
@@ -69,19 +77,8 @@
|
|||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rdpt-grid rdpt-grid--three">
|
@if (recurringDepositProductTermsForm.value.interestCalculationType === 2) {
|
||||||
<mat-form-field appearance="outline" class="rdpt-field">
|
|
||||||
<mat-label>Interest Calculate</mat-label>
|
|
||||||
<mat-select formControlName="interestCalculateSelector" required>
|
|
||||||
@for (mode of interestCalculationTypeData; track mode.id) {
|
|
||||||
<mat-option [value]="mode.id">{{ mode.value }}</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
@if (recurringDepositProductTermsForm.value.interestCalculateSelector === 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>
|
||||||
<mat-select formControlName="averagePeriod">
|
<mat-select formControlName="averagePeriod">
|
||||||
@@ -142,7 +139,7 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (recurringDepositProductTermsForm.value.interestCalculateSelector === 1) {
|
@if (recurringDepositProductTermsForm.value.interestCalculationType === 1) {
|
||||||
<div class="rdpt-note">
|
<div class="rdpt-note">
|
||||||
<span>⚠️</span>
|
<span>⚠️</span>
|
||||||
<span
|
<span
|
||||||
|
|||||||
+15
-23
@@ -49,10 +49,10 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.interestCompoundingPeriodTypeData = this.recurringDepositProductsTemplate.interestCompoundingPeriodTypeOptions;
|
this.interestCompoundingPeriodTypeData = this.recurringDepositProductsTemplate.interestCompoundingPeriodTypeOptions;
|
||||||
this.interestPostingPeriodTypeData = this.recurringDepositProductsTemplate.interestPostingPeriodTypeOptions;
|
this.interestPostingPeriodTypeData = this.recurringDepositProductsTemplate.interestPostingPeriodTypeOptions;
|
||||||
|
// Standard enum (1=Daily, 2=Average) → "Interest Calculate" dropdown → interestCalculationType form control
|
||||||
this.interestCalculationTypeData = this.recurringDepositProductsTemplate.interestCalculationTypeOptions;
|
this.interestCalculationTypeData = this.recurringDepositProductsTemplate.interestCalculationTypeOptions;
|
||||||
this.interestCalculationTypeOptionData =
|
// CBS code values → "Interest Calculation Type" dropdown → interestCalculateSelector form control
|
||||||
this.recurringDepositProductsTemplate.interestCalculationTypeOption ||
|
this.interestCalculationTypeOptionData = this.recurringDepositProductsTemplate.interestCalculationTypeOption || [];
|
||||||
this.recurringDepositProductsTemplate.interestCalculationTypeOptions;
|
|
||||||
this.interestCalculationDaysInYearTypeData =
|
this.interestCalculationDaysInYearTypeData =
|
||||||
this.recurringDepositProductsTemplate.interestCalculationDaysInYearTypeOptions;
|
this.recurringDepositProductsTemplate.interestCalculationDaysInYearTypeOptions;
|
||||||
this.periodFrequencyData = this.recurringDepositProductsTemplate.periodFrequencyOptions || [];
|
this.periodFrequencyData = this.recurringDepositProductsTemplate.periodFrequencyOptions || [];
|
||||||
@@ -60,37 +60,33 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
|||||||
|
|
||||||
const isEditing = !!this.recurringDepositProductsTemplate.id;
|
const isEditing = !!this.recurringDepositProductsTemplate.id;
|
||||||
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
||||||
|
const savedEnumTypeId =
|
||||||
|
this.recurringDepositProductsTemplate.interestCalculationType?.id ||
|
||||||
|
this.interestCalculationTypeData?.[0]?.id ||
|
||||||
|
1;
|
||||||
|
const savedSelectorId = isEditing
|
||||||
|
? (this.recurringDepositProductsTemplate.interestCalculateSelector ??
|
||||||
|
this.interestCalculationTypeOptionData?.[0]?.id ??
|
||||||
|
null)
|
||||||
|
: (this.interestCalculationTypeOptionData?.[0]?.id ?? null);
|
||||||
|
|
||||||
this.recurringDepositProductTermsForm.patchValue({
|
this.recurringDepositProductTermsForm.patchValue({
|
||||||
nominalAnnualInterestRate: this.recurringDepositProductsTemplate.nominalAnnualInterestRate ?? 0,
|
nominalAnnualInterestRate: this.recurringDepositProductsTemplate.nominalAnnualInterestRate ?? 0,
|
||||||
interestCompoundingPeriodType: this.recurringDepositProductsTemplate.interestCompoundingPeriodType?.id,
|
interestCompoundingPeriodType: this.recurringDepositProductsTemplate.interestCompoundingPeriodType?.id,
|
||||||
interestPostingPeriodType: this.recurringDepositProductsTemplate.interestPostingPeriodType?.id,
|
interestPostingPeriodType: this.recurringDepositProductsTemplate.interestPostingPeriodType?.id,
|
||||||
interestCalculationType: isEditing
|
interestCalculationType: savedEnumTypeId,
|
||||||
? this.recurringDepositProductsTemplate.interestCalculationTypeOption?.id ||
|
|
||||||
this.interestCalculationTypeOptionData?.[0]?.id ||
|
|
||||||
1
|
|
||||||
: this.interestCalculationTypeOptionData?.[0]?.id || 1,
|
|
||||||
interestCalculationDaysInYearType: this.recurringDepositProductsTemplate.interestCalculationDaysInYearType?.id,
|
interestCalculationDaysInYearType: this.recurringDepositProductsTemplate.interestCalculationDaysInYearType?.id,
|
||||||
effectiveDate: this.recurringDepositProductsTemplate.effectiveDate
|
effectiveDate: this.recurringDepositProductsTemplate.effectiveDate
|
||||||
? this.parseDate(this.recurringDepositProductsTemplate.effectiveDate)
|
? this.parseDate(this.recurringDepositProductsTemplate.effectiveDate)
|
||||||
: '',
|
: '',
|
||||||
interestCalculateSelector: isEditing
|
interestCalculateSelector: savedSelectorId,
|
||||||
? this.recurringDepositProductsTemplate.interestCalculateSelector || 1
|
|
||||||
: this.recurringDepositProductsTemplate.interestCalculationType?.id ||
|
|
||||||
this.interestCalculationTypeData?.[0]?.id ||
|
|
||||||
1,
|
|
||||||
averagePeriod: isEditing ? this.recurringDepositProductsTemplate.averagePeriod || defaultPeriod : defaultPeriod,
|
averagePeriod: isEditing ? this.recurringDepositProductsTemplate.averagePeriod || defaultPeriod : defaultPeriod,
|
||||||
autoPost: isEditing ? (this.recurringDepositProductsTemplate.autoPost ?? true) : true,
|
autoPost: isEditing ? (this.recurringDepositProductsTemplate.autoPost ?? true) : true,
|
||||||
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)
|
||||||
: ''
|
: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentInterestCalcType = this.recurringDepositProductTermsForm.get('interestCalculationType').value;
|
|
||||||
if (currentInterestCalcType && currentInterestCalcType > 2) {
|
|
||||||
this.recurringDepositProductTermsForm.patchValue({ interestCalculationType: 1 });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseDate(dateValue: any): Date | null {
|
private parseDate(dateValue: any): Date | null {
|
||||||
@@ -163,10 +159,6 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
|||||||
if (formValue.lastInterestDate instanceof Date) {
|
if (formValue.lastInterestDate instanceof Date) {
|
||||||
formValue.lastInterestDate = this.dateUtils.formatDate(formValue.lastInterestDate, dateFormat) || '';
|
formValue.lastInterestDate = this.dateUtils.formatDate(formValue.lastInterestDate, dateFormat) || '';
|
||||||
}
|
}
|
||||||
if (formValue.interestCalculationType && formValue.interestCalculationType > 2) {
|
|
||||||
formValue.interestCalculationType = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key in formValue) {
|
for (const key in formValue) {
|
||||||
if (formValue[key] === '') {
|
if (formValue[key] === '') {
|
||||||
delete formValue[key];
|
delete formValue[key];
|
||||||
|
|||||||
+7
-7
@@ -141,16 +141,16 @@
|
|||||||
<span class="flex-60">{{ recurringDepositProduct.effectiveDate | dateFormat }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.effectiveDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="flex-100 layout-row">
|
@if (recurringDepositProduct.interestCalculationTypeName) {
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculationType.value }}</span>
|
|
||||||
</div>
|
|
||||||
@if (recurringDepositProduct.interestCalculateSelector !== undefined) {
|
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Interest Calculate:</span>
|
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculateSelector }}</span>
|
<span class="flex-60">{{ recurringDepositProduct.interestCalculationTypeName }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
<div class="flex-100 layout-row">
|
||||||
|
<span class="flex-40">Interest Calculate:</span>
|
||||||
|
<span class="flex-60">{{ recurringDepositProduct.interestCalculationType?.value }}</span>
|
||||||
|
</div>
|
||||||
@if (recurringDepositProduct.averagePeriod) {
|
@if (recurringDepositProduct.averagePeriod) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Average Period:</span>
|
<span class="flex-40">Average Period:</span>
|
||||||
|
|||||||
+8
-9
@@ -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
|
||||||
@@ -61,14 +61,14 @@
|
|||||||
@if (savingProduct.effectiveTo) {
|
@if (savingProduct.effectiveTo) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective To:</span>
|
<span class="flex-40">Effective To:</span>
|
||||||
<span class="flex-60">{{ savingProduct.effectiveTo }}</span>
|
<span class="flex-60">{{ savingProduct.effectiveTo | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (savingProduct.endDate) {
|
@if (savingProduct.endDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">End Date:</span>
|
<span class="flex-40">End Date:</span>
|
||||||
<span class="flex-60">{{ savingProduct.endDate }}</span>
|
<span class="flex-60">{{ savingProduct.endDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,9 +158,7 @@
|
|||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||||
<span class="flex-60">{{
|
<span class="flex-60">{{
|
||||||
savingProduct.interestCalculationType
|
savingProduct.interestCalculateSelector | find: interestCalculationTypeOptionData : 'id' : 'value'
|
||||||
| find: interestCalculationTypeOptionData : 'id' : 'value'
|
|
||||||
| translateKey: 'catalogs'
|
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -176,15 +174,16 @@
|
|||||||
@if (savingProduct.effectiveDate) {
|
@if (savingProduct.effectiveDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective Date:</span>
|
<span class="flex-40">Effective Date:</span>
|
||||||
<span class="flex-60">{{ savingProduct.effectiveDate }}</span>
|
<span class="flex-60">{{ savingProduct.effectiveDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Interest Calculate:</span>
|
<span class="flex-40">Interest Calculate:</span>
|
||||||
<span class="flex-60">{{
|
<span class="flex-60">{{
|
||||||
savingProduct.interestCalculateSelector
|
savingProduct.interestCalculationType
|
||||||
| find: savingProductsTemplate?.interestCalculationTypeOptions : 'id' : 'value'
|
| find: savingProductsTemplate?.interestCalculationTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -214,7 +213,7 @@
|
|||||||
@if (savingProduct.lastInterestDate) {
|
@if (savingProduct.lastInterestDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Last Interest Date:</span>
|
<span class="flex-40">Last Interest Date:</span>
|
||||||
<span class="flex-60">{{ savingProduct.lastInterestDate }}</span>
|
<span class="flex-60">{{ savingProduct.lastInterestDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -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
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
} from '@angular/material/table';
|
} from '@angular/material/table';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { FindPipe } from '../../../../pipes/find.pipe';
|
import { FindPipe } from '../../../../pipes/find.pipe';
|
||||||
|
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||||
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
@@ -47,6 +48,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
MatRow,
|
MatRow,
|
||||||
FaIconComponent,
|
FaIconComponent,
|
||||||
FindPipe,
|
FindPipe,
|
||||||
|
DateFormatPipe,
|
||||||
FormatNumberPipe,
|
FormatNumberPipe,
|
||||||
YesnoPipe
|
YesnoPipe
|
||||||
]
|
]
|
||||||
|
|||||||
+7
-3
@@ -82,11 +82,15 @@ export class SavingProductSettingsStepComponent implements OnInit {
|
|||||||
this.savingProductsTemplate.lockinPeriodFrequency &&
|
this.savingProductsTemplate.lockinPeriodFrequency &&
|
||||||
this.savingProductsTemplate.lockinPeriodFrequency > 0
|
this.savingProductsTemplate.lockinPeriodFrequency > 0
|
||||||
) {
|
) {
|
||||||
|
// Backend returns lockinPeriodFrequencyType.id as integer (0=DAYS,1=WEEKS,2=MONTHS,3=YEARS)
|
||||||
|
// but the form dropdown uses string IDs, so reverse-map the number back to string
|
||||||
|
const reverseFrequencyTypeMap: { [key: number]: string } = { 0: 'DAYS', 1: 'WEEKS', 2: 'MONTHS', 3: 'YEARS' };
|
||||||
|
const freqTypeId = this.savingProductsTemplate.lockinPeriodFrequencyType?.id;
|
||||||
|
const freqTypeString = reverseFrequencyTypeMap[freqTypeId] ?? '';
|
||||||
|
|
||||||
this.savingProductSettingsForm.patchValue({
|
this.savingProductSettingsForm.patchValue({
|
||||||
lockinPeriodFrequency: this.savingProductsTemplate.lockinPeriodFrequency,
|
lockinPeriodFrequency: this.savingProductsTemplate.lockinPeriodFrequency,
|
||||||
lockinPeriodFrequencyType:
|
lockinPeriodFrequencyType: freqTypeString
|
||||||
this.savingProductsTemplate.lockinPeriodFrequencyType &&
|
|
||||||
this.savingProductsTemplate.lockinPeriodFrequencyType.id
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-19
@@ -54,14 +54,22 @@
|
|||||||
<mat-form-field appearance="outline" class="spt-field">
|
<mat-form-field appearance="outline" class="spt-field">
|
||||||
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
||||||
<mat-select
|
<mat-select
|
||||||
formControlName="interestCalculationType"
|
formControlName="interestCalculateSelector"
|
||||||
matTooltip="{{ 'tooltips.The method used to calculate interest' | translate }}"
|
matTooltip="{{ 'tooltips.The method used to calculate interest' | translate }}"
|
||||||
required
|
|
||||||
>
|
>
|
||||||
@for (interestCalculationType of interestCalculationTypeOptionData; track interestCalculationType.id) {
|
@for (opt of interestCalculationTypeOptionData; track opt.id) {
|
||||||
<mat-option [value]="interestCalculationType.id">
|
<mat-option [value]="opt.id">{{ opt.value }}</mat-option>
|
||||||
{{ interestCalculationType.value | translateKey: 'catalogs' }}
|
}
|
||||||
</mat-option>
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="spt-grid spt-grid--three">
|
||||||
|
<mat-form-field appearance="outline" class="spt-field">
|
||||||
|
<mat-label>Interest Calculate</mat-label>
|
||||||
|
<mat-select formControlName="interestCalculationType" required>
|
||||||
|
@for (mode of interestCalculationTypeData; track mode.id) {
|
||||||
|
<mat-option [value]="mode.id">{{ mode.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
}
|
}
|
||||||
</mat-select>
|
</mat-select>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
@@ -69,19 +77,8 @@
|
|||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="spt-grid spt-grid--three">
|
@if (savingProductTermsForm.value.interestCalculationType === 2) {
|
||||||
<mat-form-field appearance="outline" class="spt-field">
|
|
||||||
<mat-label>Interest Calculate</mat-label>
|
|
||||||
<mat-select formControlName="interestCalculateSelector" required>
|
|
||||||
@for (mode of interestCalculationTypeData; track mode.id) {
|
|
||||||
<mat-option [value]="mode.id">{{ mode.value }}</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
@if (savingProductTermsForm.value.interestCalculateSelector === 2) {
|
|
||||||
<mat-form-field appearance="outline" class="spt-field">
|
<mat-form-field appearance="outline" class="spt-field">
|
||||||
<mat-label>Average Period</mat-label>
|
<mat-label>Average Period</mat-label>
|
||||||
<mat-select formControlName="averagePeriod">
|
<mat-select formControlName="averagePeriod">
|
||||||
@@ -142,7 +139,7 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (savingProductTermsForm.value.interestCalculateSelector === 1) {
|
@if (savingProductTermsForm.value.interestCalculationType === 1) {
|
||||||
<div class="spt-note">
|
<div class="spt-note">
|
||||||
<span>⚠️</span>
|
<span>⚠️</span>
|
||||||
<span
|
<span
|
||||||
|
|||||||
+19
-41
@@ -59,59 +59,43 @@ export class SavingProductTermsStepComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.interestCompoundingPeriodTypeData = this.savingProductsTemplate.interestCompoundingPeriodTypeOptions;
|
this.interestCompoundingPeriodTypeData = this.savingProductsTemplate.interestCompoundingPeriodTypeOptions;
|
||||||
this.interestPostingPeriodTypeData = this.savingProductsTemplate.interestPostingPeriodTypeOptions;
|
this.interestPostingPeriodTypeData = this.savingProductsTemplate.interestPostingPeriodTypeOptions;
|
||||||
|
// interestCalculationTypeData = standard enum (1=Daily, 2=Average) → "Interest Calculate" dropdown → interestCalculationType form control
|
||||||
this.interestCalculationTypeData = this.savingProductsTemplate.interestCalculationTypeOptions;
|
this.interestCalculationTypeData = this.savingProductsTemplate.interestCalculationTypeOptions;
|
||||||
this.interestCalculationTypeOptionData =
|
// interestCalculationTypeOptionData = CBS code values from DB → "Interest Calculation Type" dropdown → interestCalculateSelector form control
|
||||||
this.savingProductsTemplate.interestCalculationTypeOption ||
|
this.interestCalculationTypeOptionData = this.savingProductsTemplate.interestCalculationTypeOption || [];
|
||||||
this.savingProductsTemplate.interestCalculationTypeOptions;
|
|
||||||
this.interestCalculationDaysInYearTypeData = this.savingProductsTemplate.interestCalculationDaysInYearTypeOptions;
|
this.interestCalculationDaysInYearTypeData = this.savingProductsTemplate.interestCalculationDaysInYearTypeOptions;
|
||||||
this.periodFrequencyData = this.savingProductsTemplate.periodFrequencyOptions || [];
|
this.periodFrequencyData = this.savingProductsTemplate.periodFrequencyOptions || [];
|
||||||
this.setYesNoOptions(this.savingProductsTemplate?.yesNoOptions);
|
this.setYesNoOptions(this.savingProductsTemplate?.yesNoOptions);
|
||||||
|
|
||||||
const isEditing = !!this.savingProductsTemplate.id;
|
const isEditing = !!this.savingProductsTemplate.id;
|
||||||
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
||||||
|
// interestCalculationType = standard enum id (1=Daily Balance, 2=Average Daily Balance)
|
||||||
|
const savedEnumTypeId =
|
||||||
|
this.savingProductsTemplate.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1;
|
||||||
|
// interestCalculateSelector = CBS code value id from interestCalculationTypeOption
|
||||||
|
const savedSelectorId = isEditing
|
||||||
|
? (this.savingProductsTemplate.interestCalculateSelector ??
|
||||||
|
this.interestCalculationTypeOptionData?.[0]?.id ??
|
||||||
|
null)
|
||||||
|
: (this.interestCalculationTypeOptionData?.[0]?.id ?? null);
|
||||||
|
|
||||||
this.savingProductTermsForm.patchValue({
|
this.savingProductTermsForm.patchValue({
|
||||||
nominalAnnualInterestRate: isEditing
|
nominalAnnualInterestRate: this.savingProductsTemplate.nominalAnnualInterestRate,
|
||||||
? this.savingProductsTemplate.nominalAnnualInterestRate
|
interestCompoundingPeriodType: this.savingProductsTemplate.interestCompoundingPeriodType?.id,
|
||||||
: this.savingProductsTemplate.nominalAnnualInterestRate,
|
interestPostingPeriodType: this.savingProductsTemplate.interestPostingPeriodType?.id,
|
||||||
interestCompoundingPeriodType: isEditing
|
interestCalculationType: savedEnumTypeId,
|
||||||
? this.savingProductsTemplate.interestCompoundingPeriodType?.id
|
interestCalculationDaysInYearType: this.savingProductsTemplate.interestCalculationDaysInYearType?.id,
|
||||||
: this.savingProductsTemplate.interestCompoundingPeriodType?.id,
|
|
||||||
interestPostingPeriodType: isEditing
|
|
||||||
? this.savingProductsTemplate.interestPostingPeriodType?.id
|
|
||||||
: this.savingProductsTemplate.interestPostingPeriodType?.id,
|
|
||||||
interestCalculationType: isEditing
|
|
||||||
? this.savingProductsTemplate.interestCalculationTypeOption?.id ||
|
|
||||||
this.interestCalculationTypeOptionData?.[0]?.id ||
|
|
||||||
1
|
|
||||||
: this.interestCalculationTypeOptionData?.[0]?.id || 1,
|
|
||||||
interestCalculationDaysInYearType: isEditing
|
|
||||||
? this.savingProductsTemplate.interestCalculationDaysInYearType?.id
|
|
||||||
: this.savingProductsTemplate.interestCalculationDaysInYearType?.id,
|
|
||||||
effectiveDate: this.savingProductsTemplate.effectiveDate
|
effectiveDate: this.savingProductsTemplate.effectiveDate
|
||||||
? new Date(this.savingProductsTemplate.effectiveDate)
|
? new Date(this.savingProductsTemplate.effectiveDate)
|
||||||
: '',
|
: '',
|
||||||
interestCalculateSelector: isEditing
|
interestCalculateSelector: savedSelectorId,
|
||||||
? this.savingProductsTemplate.interestCalculateSelector || 1
|
|
||||||
: this.savingProductsTemplate.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1,
|
|
||||||
averagePeriod: isEditing ? this.savingProductsTemplate.averagePeriod || defaultPeriod : defaultPeriod,
|
averagePeriod: isEditing ? this.savingProductsTemplate.averagePeriod || defaultPeriod : defaultPeriod,
|
||||||
autoPost: isEditing ? (this.savingProductsTemplate.autoPost ?? true) : true,
|
autoPost: isEditing ? (this.savingProductsTemplate.autoPost ?? true) : true,
|
||||||
frequencyFixDay: isEditing ? this.savingProductsTemplate.frequencyFixDay || 30 : 30,
|
frequencyFixDay: isEditing ? (this.savingProductsTemplate.frequencyFixDay ?? 30) : 30,
|
||||||
lastInterestDate: this.savingProductsTemplate.lastInterestDate
|
lastInterestDate: this.savingProductsTemplate.lastInterestDate
|
||||||
? new Date(this.savingProductsTemplate.lastInterestDate)
|
? new Date(this.savingProductsTemplate.lastInterestDate)
|
||||||
: ''
|
: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
// Map code value IDs to backend enum values for interestCalculationType
|
|
||||||
// Backend expects: 1=Daily Balance, 2=Average Daily Balance
|
|
||||||
const currentInterestCalcType = this.savingProductTermsForm.get('interestCalculationType').value;
|
|
||||||
if (currentInterestCalcType && currentInterestCalcType > 2) {
|
|
||||||
// If it's a code value ID (like 340, 341), map it to backend enum
|
|
||||||
// Default to 1 (Daily Balance) for custom code values
|
|
||||||
this.savingProductTermsForm.patchValue({
|
|
||||||
interestCalculationType: 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setYesNoOptions(options: any[]) {
|
private setYesNoOptions(options: any[]) {
|
||||||
@@ -198,12 +182,6 @@ export class SavingProductTermsStepComponent implements OnInit {
|
|||||||
formValue.lastInterestDate = this.dateUtils.formatDate(prevLastInterestDate, dateFormat) || '';
|
formValue.lastInterestDate = this.dateUtils.formatDate(prevLastInterestDate, dateFormat) || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert interestCalculationType from code value ID to backend enum value
|
|
||||||
// Backend expects: 1=Daily Balance, 2=Average Daily Balance
|
|
||||||
if (formValue.interestCalculationType && formValue.interestCalculationType > 2) {
|
|
||||||
formValue.interestCalculationType = 1; // Default to Daily Balance
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...formValue
|
...formValue
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-7
@@ -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
|
||||||
@@ -75,21 +75,21 @@
|
|||||||
@if (savingProduct.effectiveDate) {
|
@if (savingProduct.effectiveDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective Date:</span>
|
<span class="flex-40">Effective Date:</span>
|
||||||
<span class="flex-60">{{ savingProduct.effectiveDate }}</span>
|
<span class="flex-60">{{ savingProduct.effectiveDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (savingProduct.effectiveTo) {
|
@if (savingProduct.effectiveTo) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective To:</span>
|
<span class="flex-40">Effective To:</span>
|
||||||
<span class="flex-60">{{ savingProduct.effectiveTo }}</span>
|
<span class="flex-60">{{ savingProduct.effectiveTo | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (savingProduct.endDate) {
|
@if (savingProduct.endDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">End Date:</span>
|
<span class="flex-40">End Date:</span>
|
||||||
<span class="flex-60">{{ savingProduct.endDate }}</span>
|
<span class="flex-60">{{ savingProduct.endDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +164,13 @@
|
|||||||
<span class="flex-60">{{ savingProduct.interestPostingPeriodType.value | translateKey: 'catalogs' }}</span>
|
<span class="flex-60">{{ savingProduct.interestPostingPeriodType.value | translateKey: 'catalogs' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (savingProduct.interestCalculationTypeName) {
|
||||||
|
<div class="flex-100 layout-row">
|
||||||
|
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||||
|
<span class="flex-60">{{ savingProduct.interestCalculationTypeName }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
||||||
<span class="flex-60">{{ savingProduct.interestCalculationType.value | translateKey: 'catalogs' }}</span>
|
<span class="flex-60">{{ savingProduct.interestCalculationType.value | translateKey: 'catalogs' }}</span>
|
||||||
@@ -177,13 +184,13 @@
|
|||||||
@if (savingProduct.effectiveDate) {
|
@if (savingProduct.effectiveDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Effective Date:</span>
|
<span class="flex-40">Effective Date:</span>
|
||||||
<span class="flex-60">{{ savingProduct.effectiveDate }}</span>
|
<span class="flex-60">{{ savingProduct.effectiveDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Interest Calculate:</span>
|
<span class="flex-40">Interest Calculate:</span>
|
||||||
<span class="flex-60">{{ savingProduct.interestCalculateSelector }}</span>
|
<span class="flex-60">{{ savingProduct.interestCalculationType?.value | translateKey: 'catalogs' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (savingProduct.averagePeriod) {
|
@if (savingProduct.averagePeriod) {
|
||||||
@@ -208,7 +215,7 @@
|
|||||||
@if (savingProduct.lastInterestDate) {
|
@if (savingProduct.lastInterestDate) {
|
||||||
<div class="flex-100 layout-row">
|
<div class="flex-100 layout-row">
|
||||||
<span class="flex-40">Last Interest Date:</span>
|
<span class="flex-40">Last Interest Date:</span>
|
||||||
<span class="flex-60">{{ savingProduct.lastInterestDate }}</span>
|
<span class="flex-60">{{ savingProduct.lastInterestDate | dateFormat }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -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
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
MatRow
|
MatRow
|
||||||
} from '@angular/material/table';
|
} from '@angular/material/table';
|
||||||
import { ViewSavingsAccountingDetailsComponent } from '../../../../shared/accounting/view-savings-accounting-details/view-savings-accounting-details.component';
|
import { ViewSavingsAccountingDetailsComponent } from '../../../../shared/accounting/view-savings-accounting-details/view-savings-accounting-details.component';
|
||||||
|
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||||
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
@@ -48,6 +49,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
MatRow,
|
MatRow,
|
||||||
ViewSavingsAccountingDetailsComponent,
|
ViewSavingsAccountingDetailsComponent,
|
||||||
FormatNumberPipe,
|
FormatNumberPipe,
|
||||||
|
DateFormatPipe,
|
||||||
YesnoPipe
|
YesnoPipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -34,8 +34,9 @@
|
|||||||
<fa-icon icon="eye" size="sm"></fa-icon>
|
<fa-icon icon="eye" size="sm"></fa-icon>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<!-- Step 1: Product Classification + Details -->
|
||||||
<mat-step [stepControl]="savingsAccountDetailsForm">
|
<mat-step [stepControl]="savingsAccountDetailsForm">
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
<ng-template matStepLabel>Product Classification</ng-template>
|
||||||
|
|
||||||
<mifosx-savings-account-details-step
|
<mifosx-savings-account-details-step
|
||||||
[savingsAccountTemplate]="savingsAccountTemplate"
|
[savingsAccountTemplate]="savingsAccountTemplate"
|
||||||
@@ -44,14 +45,83 @@
|
|||||||
</mifosx-savings-account-details-step>
|
</mifosx-savings-account-details-step>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
<mat-step [stepControl]="savingsAccountTermsForm">
|
<!-- Step 2: Currency -->
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>Currency</ng-template>
|
||||||
|
|
||||||
<mifosx-savings-account-terms-step
|
<mifosx-savings-account-currency-step [savingsAccountProductTemplate]="savingsAccountProductTemplate">
|
||||||
[savingsAccountProductTemplate]="savingsAccountProductTemplate"
|
</mifosx-savings-account-currency-step>
|
||||||
></mifosx-savings-account-terms-step>
|
|
||||||
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
{{ 'labels.buttons.Next' | translate }}
|
||||||
|
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 3: Term -->
|
||||||
|
<mat-step [stepControl]="savingsAccountTermsForm">
|
||||||
|
<ng-template matStepLabel>Term</ng-template>
|
||||||
|
|
||||||
|
<mifosx-savings-account-term-step [savingsAccountProductTemplate]="savingsAccountProductTemplate">
|
||||||
|
</mifosx-savings-account-term-step>
|
||||||
|
|
||||||
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext [disabled]="savingsAccountTermsForm.invalid">
|
||||||
|
{{ 'labels.buttons.Next' | translate }}
|
||||||
|
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 4: Settings -->
|
||||||
|
<mat-step [stepControl]="savingsAccountSettingsForm">
|
||||||
|
<ng-template matStepLabel>{{ 'labels.inputs.SETTINGS' | translate }}</ng-template>
|
||||||
|
|
||||||
|
<mifosx-savings-account-settings-step [savingsAccountProductTemplate]="savingsAccountProductTemplate">
|
||||||
|
</mifosx-savings-account-settings-step>
|
||||||
|
|
||||||
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
{{ 'labels.buttons.Next' | translate }}
|
||||||
|
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 5: Digital -->
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>Digital</ng-template>
|
||||||
|
|
||||||
|
<mifosx-savings-account-digital-step [savingsAccountProductTemplate]="savingsAccountProductTemplate">
|
||||||
|
</mifosx-savings-account-digital-step>
|
||||||
|
|
||||||
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
|
<button mat-raised-button matStepperPrevious>
|
||||||
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button matStepperNext>
|
||||||
|
{{ 'labels.buttons.Next' | translate }}
|
||||||
|
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 6: Charges -->
|
||||||
<mat-step>
|
<mat-step>
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
||||||
|
|
||||||
@@ -63,6 +133,7 @@
|
|||||||
</mifosx-savings-account-charges-step>
|
</mifosx-savings-account-charges-step>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
|
<!-- Step 7: Preview -->
|
||||||
@if (savingsAccountFormValid) {
|
@if (savingsAccountFormValid) {
|
||||||
<mat-step completed>
|
<mat-step completed>
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ import { Router, ActivatedRoute } from '@angular/router';
|
|||||||
|
|
||||||
/** Custom Components */
|
/** Custom Components */
|
||||||
import { SavingsAccountDetailsStepComponent } from '../savings-account-stepper/savings-account-details-step/savings-account-details-step.component';
|
import { SavingsAccountDetailsStepComponent } from '../savings-account-stepper/savings-account-details-step/savings-account-details-step.component';
|
||||||
import { SavingsAccountTermsStepComponent } from '../savings-account-stepper/savings-account-terms-step/savings-account-terms-step.component';
|
import { SavingsAccountTermStepComponent } from '../savings-account-stepper/savings-account-term-step/savings-account-term-step.component';
|
||||||
|
import { SavingsAccountSettingsStepComponent } from '../savings-account-stepper/savings-account-settings-step/savings-account-settings-step.component';
|
||||||
import { SavingsAccountChargesStepComponent } from '../savings-account-stepper/savings-account-charges-step/savings-account-charges-step.component';
|
import { SavingsAccountChargesStepComponent } from '../savings-account-stepper/savings-account-charges-step/savings-account-charges-step.component';
|
||||||
|
import { SavingsAccountCurrencyStepComponent } from '../savings-account-stepper/savings-account-currency-step/savings-account-currency-step.component';
|
||||||
|
import { SavingsAccountDigitalStepComponent } from '../savings-account-stepper/savings-account-digital-step/savings-account-digital-step.component';
|
||||||
|
|
||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { SavingsService } from '../savings.service';
|
import { SavingsService } from '../savings.service';
|
||||||
@@ -39,8 +42,11 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
MatStep,
|
MatStep,
|
||||||
MatStepLabel,
|
MatStepLabel,
|
||||||
SavingsAccountDetailsStepComponent,
|
SavingsAccountDetailsStepComponent,
|
||||||
SavingsAccountTermsStepComponent,
|
SavingsAccountCurrencyStepComponent,
|
||||||
|
SavingsAccountTermStepComponent,
|
||||||
|
SavingsAccountSettingsStepComponent,
|
||||||
SavingsAccountChargesStepComponent,
|
SavingsAccountChargesStepComponent,
|
||||||
|
SavingsAccountDigitalStepComponent,
|
||||||
SavingsAccountPreviewStepComponent
|
SavingsAccountPreviewStepComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -56,75 +62,58 @@ export class CreateSavingsAccountComponent {
|
|||||||
/** Savings Account Product Template */
|
/** Savings Account Product Template */
|
||||||
savingsAccountProductTemplate: any;
|
savingsAccountProductTemplate: any;
|
||||||
|
|
||||||
/** Savings Account Details Step */
|
|
||||||
@ViewChild(SavingsAccountDetailsStepComponent, { static: true })
|
@ViewChild(SavingsAccountDetailsStepComponent, { static: true })
|
||||||
savingsAccountDetailsStep: SavingsAccountDetailsStepComponent;
|
savingsAccountDetailsStep: SavingsAccountDetailsStepComponent;
|
||||||
/** Savings Account Terms Step */
|
|
||||||
@ViewChild(SavingsAccountTermsStepComponent, { static: true })
|
@ViewChild(SavingsAccountTermStepComponent, { static: true })
|
||||||
savingsAccountTermsStep: SavingsAccountTermsStepComponent;
|
savingsAccountTermStep: SavingsAccountTermStepComponent;
|
||||||
/** Savings Account Charges Step */
|
|
||||||
|
@ViewChild(SavingsAccountSettingsStepComponent, { static: true })
|
||||||
|
savingsAccountSettingsStep: SavingsAccountSettingsStepComponent;
|
||||||
|
|
||||||
@ViewChild(SavingsAccountChargesStepComponent, { static: true })
|
@ViewChild(SavingsAccountChargesStepComponent, { static: true })
|
||||||
savingsAccountChargesStep: SavingsAccountChargesStepComponent;
|
savingsAccountChargesStep: SavingsAccountChargesStepComponent;
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches savings account template from `resolve`
|
|
||||||
* @param {ActivatedRoute} route Activated Route
|
|
||||||
* @param {Router} router Router
|
|
||||||
* @param {Dates} dateUtils Date Utils
|
|
||||||
* @param {SavingsService} savingsService Savings Service
|
|
||||||
* @param {SettingsService} settingsService Settings Service
|
|
||||||
*/
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.route.data.subscribe((data: { savingsAccountTemplate: any }) => {
|
this.route.data.subscribe((data: { savingsAccountTemplate: any }) => {
|
||||||
this.savingsAccountTemplate = data.savingsAccountTemplate;
|
this.savingsAccountTemplate = data.savingsAccountTemplate;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets savings account product template.
|
|
||||||
* @param {any} $event API response
|
|
||||||
*/
|
|
||||||
setTemplate($event: any) {
|
setTemplate($event: any) {
|
||||||
this.savingsAccountProductTemplate = $event;
|
this.savingsAccountProductTemplate = $event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves savings account details form.
|
|
||||||
*/
|
|
||||||
get savingsAccountDetailsForm() {
|
get savingsAccountDetailsForm() {
|
||||||
return this.savingsAccountDetailsStep.savingsAccountDetailsForm;
|
return this.savingsAccountDetailsStep.savingsAccountDetailsForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves savings account terms form.
|
|
||||||
*/
|
|
||||||
get savingsAccountTermsForm() {
|
get savingsAccountTermsForm() {
|
||||||
return this.savingsAccountTermsStep.savingsAccountTermsForm;
|
return this.savingsAccountTermStep.savingsAccountTermForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
get savingsAccountSettingsForm() {
|
||||||
|
return this.savingsAccountSettingsStep.savingsAccountSettingsForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks validity of overall savings account form.
|
|
||||||
*/
|
|
||||||
get savingsAccountFormValid() {
|
get savingsAccountFormValid() {
|
||||||
return this.savingsAccountDetailsForm.valid && this.savingsAccountTermsForm.valid;
|
return (
|
||||||
|
this.savingsAccountDetailsForm.valid &&
|
||||||
|
this.savingsAccountTermsForm.valid &&
|
||||||
|
this.savingsAccountSettingsForm.valid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves savings account object.
|
|
||||||
*/
|
|
||||||
get savingsAccount() {
|
get savingsAccount() {
|
||||||
return {
|
return {
|
||||||
...this.savingsAccountDetailsStep.savingsAccountDetails,
|
...this.savingsAccountDetailsStep.savingsAccountDetails,
|
||||||
...this.savingsAccountTermsStep.savingsAccountTerms,
|
...this.savingsAccountTermStep.savingsAccountTerms,
|
||||||
|
...this.savingsAccountSettingsStep.savingsAccountSettings,
|
||||||
...this.savingsAccountChargesStep.savingsAccountCharges
|
...this.savingsAccountChargesStep.savingsAccountCharges
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new share account.
|
|
||||||
*/
|
|
||||||
submit() {
|
submit() {
|
||||||
// TODO: Update once language and date settings are setup
|
|
||||||
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';
|
||||||
|
|||||||
+15
-11
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component, OnInit, OnChanges, Input, inject } from '@angular/core';
|
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { UntypedFormControl } from '@angular/forms';
|
import { UntypedFormControl } from '@angular/forms';
|
||||||
|
|
||||||
@@ -69,13 +69,25 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
FormatNumberPipe
|
FormatNumberPipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SavingsAccountChargesStepComponent implements OnInit, OnChanges {
|
export class SavingsAccountChargesStepComponent implements OnInit {
|
||||||
private dialog = inject(MatDialog);
|
private dialog = inject(MatDialog);
|
||||||
private dateUtils = inject(Dates);
|
private dateUtils = inject(Dates);
|
||||||
private translateService = inject(TranslateService);
|
private translateService = inject(TranslateService);
|
||||||
|
|
||||||
|
private _savingsAccountProductTemplate: any;
|
||||||
|
|
||||||
/** Savings Account Product Template */
|
/** Savings Account Product Template */
|
||||||
@Input() savingsAccountProductTemplate: any;
|
@Input()
|
||||||
|
set savingsAccountProductTemplate(value: any) {
|
||||||
|
this._savingsAccountProductTemplate = value;
|
||||||
|
if (value) {
|
||||||
|
this.chargeData = value.chargeOptions || [];
|
||||||
|
this.chargesDataSource = (value.charges || []).map((charge: any) => ({ ...charge, id: charge.chargeId }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get savingsAccountProductTemplate(): any {
|
||||||
|
return this._savingsAccountProductTemplate;
|
||||||
|
}
|
||||||
/** Savings Account Template */
|
/** Savings Account Template */
|
||||||
@Input() savingsAccountTemplate: any;
|
@Input() savingsAccountTemplate: any;
|
||||||
/** Currency Code */
|
/** Currency Code */
|
||||||
@@ -126,14 +138,6 @@ export class SavingsAccountChargesStepComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
|
||||||
if (this.savingsAccountProductTemplate) {
|
|
||||||
this.chargeData = this.savingsAccountProductTemplate.chargeOptions;
|
|
||||||
this.chargesDataSource =
|
|
||||||
this.savingsAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the charge to charges table
|
* Adds the charge to charges table
|
||||||
* @param {any} charge Charge
|
* @param {any} charge Charge
|
||||||
|
|||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
<!--
|
||||||
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="sac-section">
|
||||||
|
<div class="sac-section__header">
|
||||||
|
<i class="fas fa-coins"></i>
|
||||||
|
<span>Currency & Account Status Configuration</span>
|
||||||
|
</div>
|
||||||
|
<div class="sac-body">
|
||||||
|
<div class="sac-grid">
|
||||||
|
<mat-form-field appearance="outline" class="sac-field">
|
||||||
|
<mat-label>Currency</mat-label>
|
||||||
|
<input matInput [value]="savingsAccountProductTemplate?.currency?.name || ''" readonly />
|
||||||
|
<mat-hint>Inherited from selected product</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sac-field">
|
||||||
|
<mat-label>Decimal Places</mat-label>
|
||||||
|
<input matInput [value]="savingsAccountProductTemplate?.currency?.decimalPlaces || ''" readonly />
|
||||||
|
<mat-hint>Inherited from selected product</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sac-alert">
|
||||||
|
<div class="sac-alert__content">
|
||||||
|
<h4>Dormancy Alert</h4>
|
||||||
|
<p>Enable automated notifications when an account becomes inactive based on product rules.</p>
|
||||||
|
</div>
|
||||||
|
<span class="sac-badge" [class.sac-badge--yes]="savingsAccountProductTemplate?.dormancyAlert">
|
||||||
|
{{ savingsAccountProductTemplate?.dormancyAlert ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sac-alert">
|
||||||
|
<div class="sac-alert__content">
|
||||||
|
<h4>Nomination Facility</h4>
|
||||||
|
<p>Allow account holders to designate individuals to receive funds in the event of account holder's death.</p>
|
||||||
|
</div>
|
||||||
|
<span class="sac-badge" [class.sac-badge--yes]="savingsAccountProductTemplate?.nominationFacility">
|
||||||
|
{{ savingsAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.sac-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 4px 16px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-alert {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
padding: 18px 24px;
|
||||||
|
border-top: 1px solid $border;
|
||||||
|
background: #f8fafc;
|
||||||
|
|
||||||
|
@media (width <= 760px) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
h4 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #64748b;
|
||||||
|
max-width: 540px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sac-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 48px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #fee2e2;
|
||||||
|
color: #b91c1c;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
|
||||||
|
&--yes {
|
||||||
|
background: #dcfce7;
|
||||||
|
color: #15803d;
|
||||||
|
border-color: #bbf7d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-savings-account-currency-step',
|
||||||
|
templateUrl: './savings-account-currency-step.component.html',
|
||||||
|
styleUrls: ['./savings-account-currency-step.component.scss'],
|
||||||
|
imports: [...STANDALONE_SHARED_IMPORTS]
|
||||||
|
})
|
||||||
|
export class SavingsAccountCurrencyStepComponent {
|
||||||
|
@Input() savingsAccountProductTemplate: any;
|
||||||
|
}
|
||||||
+106
-50
@@ -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
|
||||||
@@ -7,63 +7,119 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="savingsAccountDetailsForm">
|
<form [formGroup]="savingsAccountDetailsForm">
|
||||||
<div class="layout-row-wrap gap-2px responsive-column">
|
<!-- Account Details -->
|
||||||
<mat-form-field class="flex-48">
|
<div class="sad-section">
|
||||||
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
<div class="sad-section__header">
|
||||||
<mat-select formControlName="productId" required>
|
<i class="fas fa-info-circle"></i>
|
||||||
@for (product of productData; track product) {
|
<h3>Account Details</h3>
|
||||||
<mat-option [value]="product.id">
|
</div>
|
||||||
{{ product.name }}
|
<div class="sad-grid">
|
||||||
</mat-option>
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
}
|
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
||||||
</mat-select>
|
<mat-select formControlName="productId" required>
|
||||||
<mat-error>
|
@for (product of productData; track product) {
|
||||||
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
<mat-option [value]="product.id">{{ product.name }}</mat-option>
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
}
|
||||||
</mat-error>
|
</mat-select>
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
@if (savingsProductSelected) {
|
|
||||||
<mat-form-field class="flex-48" (click)="submittedOnDatePicker.open()">
|
|
||||||
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
[min]="minDate"
|
|
||||||
[max]="maxDate"
|
|
||||||
[matDatepicker]="submittedOnDatePicker"
|
|
||||||
formControlName="submittedOnDate"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
|
||||||
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
}
|
|
||||||
|
|
||||||
@if (savingsProductSelected) {
|
@if (savingsProductSelected) {
|
||||||
<mat-form-field class="flex-48">
|
<mat-form-field appearance="outline" class="sad-field" (click)="submittedOnDatePicker.open()">
|
||||||
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
||||||
<mat-select formControlName="fieldOfficerId">
|
<input
|
||||||
@for (officer of fieldOfficerData; track officer) {
|
matInput
|
||||||
<mat-option [value]="officer.id">
|
[min]="minDate"
|
||||||
{{ officer.displayName }}
|
[max]="maxDate"
|
||||||
</mat-option>
|
[matDatepicker]="submittedOnDatePicker"
|
||||||
}
|
formControlName="submittedOnDate"
|
||||||
</mat-select>
|
required
|
||||||
</mat-form-field>
|
/>
|
||||||
}
|
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
@if (savingsProductSelected) {
|
@if (savingsProductSelected) {
|
||||||
<mat-form-field class="flex-48">
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
<mat-label>{{ 'labels.inputs.External ID' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
||||||
<input matInput formControlName="externalId" />
|
<mat-select formControlName="fieldOfficerId">
|
||||||
</mat-form-field>
|
@for (officer of fieldOfficerData; track officer) {
|
||||||
}
|
<mat-option [value]="officer.id">{{ officer.displayName }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (savingsProductSelected) {
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.External ID' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="externalId" />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Product Classification (read-only, shown after product is selected) -->
|
||||||
|
@if (savingsProductSelected) {
|
||||||
|
<div class="sad-section sad-section--mt">
|
||||||
|
<div class="sad-section__header">
|
||||||
|
<i class="fas fa-tags"></i>
|
||||||
|
<h3>Product Classification</h3>
|
||||||
|
<span class="sad-badge">From Product</span>
|
||||||
|
</div>
|
||||||
|
<div class="sad-grid sad-grid--classification">
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Account Type</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.accountType || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Customer Segment</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.customerSegment || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Product Code</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.productCode || ''" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
@if (productTemplate.effectiveDate) {
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Effective Date</mat-label>
|
||||||
|
<input matInput [value]="formatTemplateDate(productTemplate.effectiveDate)" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (productTemplate.endDate) {
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>End Date</mat-label>
|
||||||
|
<input matInput [value]="formatTemplateDate(productTemplate.endDate)" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sad-field">
|
||||||
|
<mat-label>Introducer Required</mat-label>
|
||||||
|
<input matInput [value]="productTemplate.introducerRequired ? 'Yes' : 'No'" readonly />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="sad-field sad-info-row">
|
||||||
|
<span class="sad-info-label">Share Linking Enabled</span>
|
||||||
|
<span class="sad-badge" [class.sad-badge--yes]="productTemplate.shareLinkingEnabled">
|
||||||
|
{{ productTemplate.shareLinkingEnabled ? 'Yes' : 'No' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
<button mat-raised-button matStepperPrevious disabled>
|
<button mat-raised-button matStepperPrevious disabled>
|
||||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
|
|||||||
+98
@@ -5,3 +5,101 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.sad-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
&--mt {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 4px 16px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-grid--classification {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-info-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sad-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3px 12px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1d4ed8;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
|
||||||
|
&--yes {
|
||||||
|
background: #dcfce7;
|
||||||
|
color: #15803d;
|
||||||
|
border-color: #bbf7d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+37
-3
@@ -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
|
||||||
@@ -7,12 +7,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component, OnInit, Input, Output, EventEmitter, inject } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter, inject, ChangeDetectorRef } from '@angular/core';
|
||||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
|
||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { SavingsService } from 'app/savings/savings.service';
|
import { SavingsService } from 'app/savings/savings.service';
|
||||||
import { SettingsService } from 'app/settings/settings.service';
|
|
||||||
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
@@ -35,6 +36,8 @@ export class SavingsAccountDetailsStepComponent implements OnInit {
|
|||||||
private formBuilder = inject(UntypedFormBuilder);
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
private savingsService = inject(SavingsService);
|
private savingsService = inject(SavingsService);
|
||||||
private settingsService = inject(SettingsService);
|
private settingsService = inject(SettingsService);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
private cdr = inject(ChangeDetectorRef);
|
||||||
|
|
||||||
/** Savings Account Template */
|
/** Savings Account Template */
|
||||||
@Input() savingsAccountTemplate: any;
|
@Input() savingsAccountTemplate: any;
|
||||||
@@ -57,6 +60,9 @@ export class SavingsAccountDetailsStepComponent implements OnInit {
|
|||||||
/** Savings Account Template with product data */
|
/** Savings Account Template with product data */
|
||||||
@Output() savingsAccountProductTemplate = new EventEmitter();
|
@Output() savingsAccountProductTemplate = new EventEmitter();
|
||||||
|
|
||||||
|
/** Local copy of product template for displaying classification info */
|
||||||
|
productTemplate: any = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets share account details form.
|
* Sets share account details form.
|
||||||
* @param {FormBuilder} formBuilder Form Builder.
|
* @param {FormBuilder} formBuilder Form Builder.
|
||||||
@@ -115,9 +121,11 @@ export class SavingsAccountDetailsStepComponent implements OnInit {
|
|||||||
this.savingsService
|
this.savingsService
|
||||||
.getSavingsAccountTemplate(entityId, productId, this.savingsAccountTemplate.groupId ? true : false)
|
.getSavingsAccountTemplate(entityId, productId, this.savingsAccountTemplate.groupId ? true : false)
|
||||||
.subscribe((response: any) => {
|
.subscribe((response: any) => {
|
||||||
|
this.productTemplate = response;
|
||||||
this.savingsAccountProductTemplate.emit(response);
|
this.savingsAccountProductTemplate.emit(response);
|
||||||
this.fieldOfficerData = response.fieldOfficerOptions;
|
this.fieldOfficerData = response.fieldOfficerOptions;
|
||||||
this.savingsProductSelected = true;
|
this.savingsProductSelected = true;
|
||||||
|
this.cdr.detectChanges();
|
||||||
if (!this.isFieldOfficerPatched && this.savingsAccountTemplate.fieldOfficerId) {
|
if (!this.isFieldOfficerPatched && this.savingsAccountTemplate.fieldOfficerId) {
|
||||||
this.savingsAccountDetailsForm.get('fieldOfficerId').patchValue(this.savingsAccountTemplate.fieldOfficerId);
|
this.savingsAccountDetailsForm.get('fieldOfficerId').patchValue(this.savingsAccountTemplate.fieldOfficerId);
|
||||||
this.isFieldOfficerPatched = true;
|
this.isFieldOfficerPatched = true;
|
||||||
@@ -128,6 +136,32 @@ export class SavingsAccountDetailsStepComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats template dates into the current CBS date format.
|
||||||
|
* Handles backend date arrays like [2026, 4, 20] as well as strings and Date objects.
|
||||||
|
*/
|
||||||
|
formatTemplateDate(value: any): string {
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let dateValue: Date | null = null;
|
||||||
|
|
||||||
|
if (value instanceof Date) {
|
||||||
|
dateValue = value;
|
||||||
|
} else if (Array.isArray(value) && value.length >= 3) {
|
||||||
|
dateValue = new Date(value[0], value[1] - 1, value[2]);
|
||||||
|
} else {
|
||||||
|
dateValue = new Date(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dateValue || Number.isNaN(dateValue.getTime())) {
|
||||||
|
return Array.isArray(value) ? value.join('/') : String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dateUtils.formatDate(dateValue, 'M/d/yyyy') || '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns savings account form value.
|
* Returns savings account form value.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+234
@@ -0,0 +1,234 @@
|
|||||||
|
<!--
|
||||||
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="spl-section">
|
||||||
|
<div class="spl-section__header">
|
||||||
|
<span class="spl-section__icon">
|
||||||
|
<i class="fas fa-chart-bar"></i>
|
||||||
|
</span>
|
||||||
|
<div class="spl-section__title">Transaction & Product Limits</div>
|
||||||
|
<div class="spl-section__badge">FROM PRODUCT</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="spl-section__body">
|
||||||
|
<div class="spl-features">
|
||||||
|
<div class="spl-features__header">Account Features (as per RBI guidelines)</div>
|
||||||
|
|
||||||
|
<div class="spl-features__grid">
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-automobile"></i> ATM Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.atmFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-wallet"></i> ATM Withdrawal</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.atmWithdrawalFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-mobile-alt"></i> UPI Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.upiFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-exchange-alt"></i> POS Transactions</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.posTransactionsFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-book"></i> Cheque Book</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.chequeBook" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-bolt"></i> IMPS Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.impsFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-sms"></i> SMS Alerts</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.smsAlerts" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-bell"></i> BBPS Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.bbpsFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-shield-alt"></i> Insurance Cover</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.insuranceCover" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-bolt"></i> BEPG Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.bepgFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-money-check-alt"></i> NEFT Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.neftFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-money-check"></i> RTGS Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.rtgsFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-balance-scale"></i> Inter-Bank Total Cap</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.interBankTotalCapFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-arrow-right-arrow-left"></i> Withdrawal Facility</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.withdrawalFacility" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-id-card"></i> Deposit Facility (KYC)</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.depositFacilityKyc" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="spl-feature">
|
||||||
|
<span class="spl-feature__label"><i class="fas fa-credit-card"></i> Virtual Debit Card</span>
|
||||||
|
<select class="spl-select" [ngModel]="accountFacilityConfig.virtualDebitCard" disabled>
|
||||||
|
<option [ngValue]="false">No</option>
|
||||||
|
<option [ngValue]="true">Yes</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (accountFacilityLimits.length > 0) {
|
||||||
|
<div class="spl-table-wrap">
|
||||||
|
<table class="spl-table">
|
||||||
|
<colgroup>
|
||||||
|
<col class="spl-table__col--label" />
|
||||||
|
<col class="spl-table__col--value" />
|
||||||
|
<col class="spl-table__col--value" />
|
||||||
|
<col class="spl-table__col--value" />
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Limit Type</th>
|
||||||
|
<th>General Limit (Daily)</th>
|
||||||
|
<th>Minor Account Limit</th>
|
||||||
|
<th>Small Saving Limit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (row of accountFacilityLimits; track row.limitType) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.limitType }}</td>
|
||||||
|
<td><input type="text" class="spl-input" [value]="row.generalLimit" disabled /></td>
|
||||||
|
<td><input type="text" class="spl-input" [value]="row.minorAccountLimit" disabled /></td>
|
||||||
|
<td><input type="text" class="spl-input" [value]="row.smallSavingLimit" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (depositLimitKyc?.general) {
|
||||||
|
<div class="spl-table-wrap spl-table-wrap--kyc">
|
||||||
|
<h4 class="mat-h4">Deposit Limit (KYC)</h4>
|
||||||
|
<table class="spl-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Account Type</th>
|
||||||
|
<th>KYC Type</th>
|
||||||
|
<th>Limit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>General</td>
|
||||||
|
<td>
|
||||||
|
<select class="spl-select" [ngModel]="depositLimitKyc.general.kycType" disabled>
|
||||||
|
<option>{{ depositLimitKyc.general.kycType }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" class="spl-input" [value]="depositLimitKyc.general.limit" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Minor</td>
|
||||||
|
<td>
|
||||||
|
<select class="spl-select" [ngModel]="depositLimitKyc.minor.kycType" disabled>
|
||||||
|
<option>{{ depositLimitKyc.minor.kycType }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><input type="number" class="spl-input" [value]="depositLimitKyc.minor.limit" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Small Saving</td>
|
||||||
|
<td>
|
||||||
|
<select class="spl-select" [ngModel]="depositLimitKyc.smallSaving.kycType" disabled>
|
||||||
|
<option>{{ depositLimitKyc.smallSaving.kycType }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><input type="number" class="spl-input" [value]="depositLimitKyc.smallSaving.limit" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="spl-note">
|
||||||
|
<strong>Note:</strong>
|
||||||
|
These facilities are defined at product level and applied to this account. Small Saving Account limits are
|
||||||
|
strictly capped at Rs.1,00,000 maximum balance per RBI Master Circular.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+199
@@ -0,0 +1,199 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$border: #d9e2ec;
|
||||||
|
$border-strong: #c7d2de;
|
||||||
|
$text: #24364b;
|
||||||
|
$muted: #64748b;
|
||||||
|
$header-bg: #f8fafc;
|
||||||
|
|
||||||
|
.spl-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: $header-bg;
|
||||||
|
border-bottom: 1px solid $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #eff6ff;
|
||||||
|
color: #2563eb;
|
||||||
|
font-size: 14px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__title {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__badge {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1d4ed8;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-section__body {
|
||||||
|
padding: 18px 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-table-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-table {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid $border;
|
||||||
|
|
||||||
|
col.spl-table__col--label {
|
||||||
|
width: 22%;
|
||||||
|
}
|
||||||
|
|
||||||
|
col.spl-table__col--value {
|
||||||
|
width: 26%;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid $border;
|
||||||
|
padding: 6px 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #f8fafc;
|
||||||
|
color: #64748b;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
background: #f8fafc;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
font-size: 13px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-input,
|
||||||
|
.spl-select {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-input {
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid $border-strong;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0 8px;
|
||||||
|
background: #f8fafc;
|
||||||
|
color: $muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-select {
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid $border-strong;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0 6px;
|
||||||
|
background: #f8fafc;
|
||||||
|
color: $muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-features {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-features__header {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $muted;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-features__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 12px 16px;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 640px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-feature {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-feature__label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #334155;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #2563eb;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spl-note {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f8fafc;
|
||||||
|
color: $muted;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
const DEFAULT_FACILITY_CONFIG = {
|
||||||
|
atmFacility: false,
|
||||||
|
atmWithdrawalFacility: false,
|
||||||
|
upiFacility: false,
|
||||||
|
posTransactionsFacility: false,
|
||||||
|
chequeBook: false,
|
||||||
|
impsFacility: false,
|
||||||
|
smsAlerts: false,
|
||||||
|
bbpsFacility: false,
|
||||||
|
insuranceCover: false,
|
||||||
|
bepgFacility: false,
|
||||||
|
neftFacility: false,
|
||||||
|
rtgsFacility: false,
|
||||||
|
interBankTotalCapFacility: false,
|
||||||
|
withdrawalFacility: false,
|
||||||
|
depositFacilityKyc: false,
|
||||||
|
virtualDebitCard: false
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-savings-account-digital-step',
|
||||||
|
templateUrl: './savings-account-digital-step.component.html',
|
||||||
|
styleUrls: ['./savings-account-digital-step.component.scss'],
|
||||||
|
imports: [
|
||||||
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
FormsModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SavingsAccountDigitalStepComponent {
|
||||||
|
private _savingsAccountProductTemplate: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set savingsAccountProductTemplate(value: any) {
|
||||||
|
this._savingsAccountProductTemplate = value;
|
||||||
|
}
|
||||||
|
get savingsAccountProductTemplate(): any {
|
||||||
|
return this._savingsAccountProductTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
get accountFacilityConfig() {
|
||||||
|
return this._savingsAccountProductTemplate?.accountFacilityConfig || DEFAULT_FACILITY_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
get accountFacilityLimits(): any[] {
|
||||||
|
return this._savingsAccountProductTemplate?.accountFacilityLimits || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get depositLimitKyc() {
|
||||||
|
return (
|
||||||
|
this._savingsAccountProductTemplate?.depositLimitKyc || {
|
||||||
|
general: { kycType: '', limit: '' },
|
||||||
|
minor: { kycType: '', limit: '' },
|
||||||
|
smallSaving: { kycType: '', limit: '' }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+540
-233
@@ -6,251 +6,558 @@
|
|||||||
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="responsive-column">
|
<div class="preview-wrapper">
|
||||||
<h3 class="mat-h3 flex-fill">{{ 'labels.heading.Details' | translate }}</h3>
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
<mat-divider class="flex-fill"></mat-divider>
|
<span class="preview-section__title">{{ 'labels.heading.Details' | translate }}</span>
|
||||||
<div class="flex-fill layout-row">
|
</div>
|
||||||
<span class="flex-40">{{ 'labels.inputs.Product' | translate }}</span>
|
<div class="preview-body">
|
||||||
<span class="flex-60">{{
|
<div class="preview-grid preview-grid--three">
|
||||||
savingsAccount.productId | find: savingsAccountTemplate.productOptions : 'id' : 'name'
|
<div class="preview-field">
|
||||||
}}</span>
|
<span class="preview-label">{{ 'labels.inputs.Product' | translate }}</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
savingsAccount?.productId | find: savingsAccountTemplate?.productOptions : 'id' : 'name'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Submitted On' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccount?.submittedOnDate | dateFormat }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Field Officer' | translate }}</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
savingsAccount?.fieldOfficerId
|
||||||
|
| find: savingsAccountProductTemplate?.fieldOfficerOptions : 'id' : 'displayName'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.External Id' | translate }}</span>
|
||||||
|
@if (savingsAccount?.externalId) {
|
||||||
|
<span class="preview-value">
|
||||||
|
<mifosx-external-identifier externalId="{{ savingsAccount.externalId }}"></mifosx-external-identifier>
|
||||||
|
</span>
|
||||||
|
} @else {
|
||||||
|
<span class="preview-value">{{ 'labels.inputs.Unassigned' | translate }}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
@if (
|
||||||
<span class="flex-40">{{ 'labels.inputs.Submitted On' | translate }}</span>
|
savingsAccountProductTemplate?.accountType ||
|
||||||
<span class="flex-60">{{ savingsAccount.submittedOnDate | dateFormat }}</span>
|
savingsAccountProductTemplate?.customerSegment ||
|
||||||
|
savingsAccountProductTemplate?.productCode ||
|
||||||
|
savingsAccountProductTemplate?.effectiveDate ||
|
||||||
|
savingsAccountProductTemplate?.endDate ||
|
||||||
|
savingsAccountProductTemplate?.introducerRequired !== undefined ||
|
||||||
|
savingsAccountProductTemplate?.shareLinkingEnabled !== undefined
|
||||||
|
) {
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<span class="preview-section__title">Product Classification</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
|
@if (savingsAccountProductTemplate?.accountType) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Account Type</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.accountType }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccountProductTemplate?.customerSegment) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Customer Segment</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.customerSegment }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccountProductTemplate?.productCode) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Product Code</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.productCode }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccountProductTemplate?.effectiveDate) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Effective Date</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.effectiveDate | dateFormat }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccountProductTemplate?.endDate) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">End Date</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.endDate | dateFormat }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (
|
||||||
|
savingsAccountProductTemplate?.introducerRequired !== undefined &&
|
||||||
|
savingsAccountProductTemplate?.introducerRequired !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Introducer Required</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccountProductTemplate?.introducerRequired"
|
||||||
|
>
|
||||||
|
{{ savingsAccountProductTemplate?.introducerRequired | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (
|
||||||
|
savingsAccountProductTemplate?.shareLinkingEnabled !== undefined &&
|
||||||
|
savingsAccountProductTemplate?.shareLinkingEnabled !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Share Linking Enabled</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccountProductTemplate?.shareLinkingEnabled"
|
||||||
|
>
|
||||||
|
{{ savingsAccountProductTemplate?.shareLinkingEnabled | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<span class="preview-section__title">{{ 'labels.heading.Currency' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Currency' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountTermsForm?.get('currencyCode')?.value }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Decimal Places' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.currency?.decimalPlaces }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Currency In Multiples Of' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.currency?.inMultiplesOf }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (
|
||||||
|
savingsAccountProductTemplate?.dormancyAlert !== undefined &&
|
||||||
|
savingsAccountProductTemplate?.dormancyAlert !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Dormancy Alert</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccountProductTemplate?.dormancyAlert"
|
||||||
|
>
|
||||||
|
{{ savingsAccountProductTemplate?.dormancyAlert | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (
|
||||||
|
savingsAccountProductTemplate?.balanceSwapOption !== undefined &&
|
||||||
|
savingsAccountProductTemplate?.balanceSwapOption !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Balance Swap Option</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccountProductTemplate?.balanceSwapOption"
|
||||||
|
>
|
||||||
|
{{ savingsAccountProductTemplate?.balanceSwapOption | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (savingsAccountProductTemplate?.msme !== undefined && savingsAccountProductTemplate?.msme !== null) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">MSME</span>
|
||||||
|
<span class="preview-value preview-badge" [class.preview-badge--yes]="savingsAccountProductTemplate?.msme">
|
||||||
|
{{ savingsAccountProductTemplate?.msme | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (
|
||||||
|
savingsAccountProductTemplate?.nominationFacility !== undefined &&
|
||||||
|
savingsAccountProductTemplate?.nominationFacility !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Nomination Facility</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccountProductTemplate?.nominationFacility"
|
||||||
|
>
|
||||||
|
{{ savingsAccountProductTemplate?.nominationFacility | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (
|
||||||
|
savingsAccountProductTemplate?.autoDebitFacility !== undefined &&
|
||||||
|
savingsAccountProductTemplate?.autoDebitFacility !== null
|
||||||
|
) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Auto Debit Facility</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccountProductTemplate?.autoDebitFacility"
|
||||||
|
>
|
||||||
|
{{ savingsAccountProductTemplate?.autoDebitFacility | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
<div class="preview-section">
|
||||||
<span class="flex-40">{{ 'labels.inputs.External Id' | translate }}</span>
|
<div class="preview-section__header">
|
||||||
@if (savingsAccount.externalId) {
|
<span class="preview-section__title">{{ 'labels.heading.Terms' | translate }}</span>
|
||||||
<span class="flex-60">
|
</div>
|
||||||
<mifosx-external-identifier externalId="{{ savingsAccount.externalId }}"></mifosx-external-identifier>
|
<div class="preview-body">
|
||||||
</span>
|
<div class="preview-grid preview-grid--three">
|
||||||
}
|
<div class="preview-field">
|
||||||
@if (!savingsAccount.externalId) {
|
<span class="preview-label">{{ 'labels.inputs.Nominal Annual Interest' | translate }}</span>
|
||||||
<span class="flex-60">
|
<span class="preview-value">{{ savingsAccount?.nominalAnnualInterestRate | formatNumber }} %</span>
|
||||||
{{ 'labels.inputs.Unassigned' | translate }}
|
</div>
|
||||||
</span>
|
@if (savingsAccountProductTemplate?.interestCalculationTypeName) {
|
||||||
}
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Interest Calculation Mode</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.interestCalculationTypeName }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Interest Compounding Period' | translate }}</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
savingsAccount?.interestCompoundingPeriodType
|
||||||
|
| find: savingsAccountProductTemplate?.interestCompoundingPeriodTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Interest Posting Period' | translate }}</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
savingsAccount?.interestPostingPeriodType
|
||||||
|
| find: savingsAccountProductTemplate?.interestPostingPeriodTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Interest Calculated using' | translate }}</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
savingsAccount?.interestCalculationType
|
||||||
|
| find: savingsAccountProductTemplate?.interestCalculationTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Days in Year' | translate }}</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
savingsAccount?.interestCalculationDaysInYearType
|
||||||
|
| find: savingsAccountProductTemplate?.interestCalculationDaysInYearTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Interest Calculation Selector</span>
|
||||||
|
<span class="preview-value">{{
|
||||||
|
savingsAccount?.interestCalculateSelector
|
||||||
|
| find: savingsAccountProductTemplate?.interestCalculationTypeOption : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
@if (savingsAccount?.averagePeriod) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Average Period</span>
|
||||||
|
<span class="preview-value">{{ savingsAccount?.averagePeriod }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Auto Post</span>
|
||||||
|
<span class="preview-value preview-badge" [class.preview-badge--yes]="savingsAccount?.autoPost">
|
||||||
|
{{ savingsAccount?.autoPost | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (savingsAccountProductTemplate?.lastInterestDate) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Last Interest Date</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.lastInterestDate | dateFormat }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccount?.frequencyFixDay !== undefined && savingsAccount?.frequencyFixDay !== null) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Frequency Fix Day</span>
|
||||||
|
<span class="preview-value">{{ savingsAccount?.frequencyFixDay }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
<div class="preview-section">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Field Officer' | translate }}</span>
|
<div class="preview-section__header">
|
||||||
<span class="flex-60">{{
|
<span class="preview-section__title">{{ 'labels.heading.Settings' | translate }}</span>
|
||||||
savingsAccount.fieldOfficerId | find: savingsAccountProductTemplate.fieldOfficerOptions : 'id' : 'displayName'
|
</div>
|
||||||
}}</span>
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
|
@if (savingsAccount?.minRequiredOpeningBalance) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Minimum Opening Balance' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccount?.minRequiredOpeningBalance | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccount?.withdrawalFeeForTransfers"
|
||||||
|
>
|
||||||
|
{{ savingsAccount?.withdrawalFeeForTransfers | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (savingsAccount?.lockinPeriodFrequency) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Lock-in Period' | translate }}</span>
|
||||||
|
<span class="preview-value">
|
||||||
|
{{ savingsAccount?.lockinPeriodFrequency }}
|
||||||
|
{{
|
||||||
|
savingsAccount?.lockinPeriodFrequencyType
|
||||||
|
| find: savingsAccountProductTemplate?.lockinPeriodFrequencyTypeOptions : 'id' : 'value'
|
||||||
|
| translateKey: 'catalogs'
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Is Overdraft Allowed' | translate }}</span>
|
||||||
|
<span class="preview-value preview-badge" [class.preview-badge--yes]="savingsAccount?.allowOverdraft">
|
||||||
|
{{ savingsAccount?.allowOverdraft | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (savingsAccount?.minOverdraftForInterestCalculation) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{
|
||||||
|
'labels.inputs.Minimum Overdraft Required for Interest Calculation' | translate
|
||||||
|
}}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccount?.minOverdraftForInterestCalculation | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccount?.nominalAnnualInterestRateOverdraft) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Nominal Annual Interest for Overdraft' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccount?.nominalAnnualInterestRateOverdraft | formatNumber }} %</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccount?.overdraftLimit) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Maximum Overdraft Amount Limit' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccount?.overdraftLimit | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Enforce Minimum Balance' | translate }}</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccount?.enforceMinRequiredBalance"
|
||||||
|
>
|
||||||
|
{{ savingsAccount?.enforceMinRequiredBalance | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (savingsAccountProductTemplate?.enableLockinPeriod !== undefined) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Enable Lock-in Period</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccountProductTemplate?.enableLockinPeriod"
|
||||||
|
>
|
||||||
|
{{ savingsAccountProductTemplate?.enableLockinPeriod | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccountProductTemplate?.balanceFrequency) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Balance Frequency</span>
|
||||||
|
<span class="preview-value">{{ savingsAccountProductTemplate?.balanceFrequency }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccountProductTemplate?.accountClosedZeroBalanceInDayClosing !== undefined) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Account Closed Zero Balance In Day Closing</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="savingsAccountProductTemplate?.accountClosedZeroBalanceInDayClosing"
|
||||||
|
>
|
||||||
|
{{ savingsAccountProductTemplate?.accountClosedZeroBalanceInDayClosing | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (savingsAccount?.minRequiredBalance) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ 'labels.inputs.Minimum Balance' | translate }}</span>
|
||||||
|
<span class="preview-value">{{ savingsAccount?.minRequiredBalance }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (hasDigitalSection) {
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<span class="preview-section__title">Digital</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
|
@for (facility of digitalFacilityLabels; track facility.key) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">{{ facility.label }}</span>
|
||||||
|
<span
|
||||||
|
class="preview-value preview-badge"
|
||||||
|
[class.preview-badge--yes]="digitalPreview?.accountFacilityConfig?.[facility.key]"
|
||||||
|
>
|
||||||
|
{{ digitalPreview?.accountFacilityConfig?.[facility.key] | yesNo }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (digitalPreview?.accountFacilityLimits?.length) {
|
||||||
|
<table class="mat-elevation-z1 preview-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Limit Type</th>
|
||||||
|
<th>General Limit</th>
|
||||||
|
<th>Minor Account Limit</th>
|
||||||
|
<th>Small Saving Limit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (row of digitalPreview.accountFacilityLimits; track row.limitType) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.limitType }}</td>
|
||||||
|
<td>{{ row.generalLimit || 'Unassigned' }}</td>
|
||||||
|
<td>{{ row.minorAccountLimit || 'Unassigned' }}</td>
|
||||||
|
<td>{{ row.smallSavingLimit || 'Unassigned' }}</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (digitalPreview?.depositLimitKyc?.general) {
|
||||||
|
<table class="mat-elevation-z1 preview-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Account Type</th>
|
||||||
|
<th>KYC Type</th>
|
||||||
|
<th>Limit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>General</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.general.kycType || 'Unassigned' }}</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.general.limit || 'Unassigned' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Minor</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.minor?.kycType || 'Unassigned' }}</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.minor?.limit || 'Unassigned' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Small Saving</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.smallSaving?.kycType || 'Unassigned' }}</td>
|
||||||
|
<td>{{ digitalPreview.depositLimitKyc.smallSaving?.limit || 'Unassigned' }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@if (activeClientMembers) {
|
@if (activeClientMembers) {
|
||||||
<h3 class="mat-h3 margin-t flex-fill">{{ 'labels.heading.Client Members' | translate }}</h3>
|
<div class="preview-section">
|
||||||
<mat-divider class="flex-fill"></mat-divider>
|
<div class="preview-section__header">
|
||||||
<div class="flex-60">
|
<span class="preview-section__title">{{ 'labels.heading.Client Members' | translate }}</span>
|
||||||
<table mat-table #membersTable [dataSource]="dataSource">
|
</div>
|
||||||
<!-- ID Column -->
|
<div class="preview-body">
|
||||||
<ng-container matColumnDef="id">
|
<table class="mat-elevation-z1 preview-table" mat-table #membersTable [dataSource]="dataSource">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client ID' | translate }}</th>
|
<ng-container matColumnDef="id">
|
||||||
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client ID' | translate }}</th>
|
||||||
</ng-container>
|
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
|
||||||
<!-- Name Column -->
|
</ng-container>
|
||||||
<ng-container matColumnDef="name">
|
<ng-container matColumnDef="name">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client Name' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client Name' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let element">{{ element.displayName }}</td>
|
<td mat-cell *matCellDef="let element">{{ element.displayName }}</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<tr mat-header-row *matHeaderRowDef="membersDisplayedColumns"></tr>
|
<tr mat-header-row *matHeaderRowDef="membersDisplayedColumns"></tr>
|
||||||
<tr mat-row *matRowDef="let row; columns: membersDisplayedColumns"></tr>
|
<tr mat-row *matRowDef="let row; columns: membersDisplayedColumns"></tr>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<h3 class="mat-h3 margin-t flex-fill">{{ 'labels.heading.Terms' | translate }}</h3>
|
@if (savingsAccount?.charges?.length) {
|
||||||
|
<div class="preview-section">
|
||||||
<mat-divider class="flex-fill"></mat-divider>
|
<div class="preview-section__header">
|
||||||
<div class="flex-fill layout-row">
|
<span class="preview-section__title">{{ 'labels.inputs.Charges' | translate }}</span>
|
||||||
<span class="flex-40">{{ 'labels.inputs.Currency' | translate }}</span>
|
</div>
|
||||||
<span class="flex-60">{{ savingsAccountTermsForm.get('currencyCode').value }}</span>
|
<div class="preview-body">
|
||||||
</div>
|
<table class="mat-elevation-z1 preview-table" mat-table [dataSource]="savingsAccount.charges">
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
<div class="flex-fill layout-row">
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
|
||||||
<span class="flex-40">{{ 'labels.inputs.Decimal Places' | translate }}</span>
|
<td mat-cell *matCellDef="let charge">{{ charge.name + ', ' + charge.currency.displaySymbol }}</td>
|
||||||
<span class="flex-60">{{ savingsAccountTermsForm.get('decimal').value }}</span>
|
</ng-container>
|
||||||
</div>
|
<ng-container matColumnDef="chargeCalculationType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||||
<div class="flex-fill layout-row">
|
<td mat-cell *matCellDef="let charge">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Nominal Annual Interest' | translate }}:</span>
|
{{ charge.chargeCalculationType.value | translateKey: 'catalogs' }}
|
||||||
<span class="flex-60">{{ savingsAccount.nominalAnnualInterestRate | formatNumber }} %</span>
|
</td>
|
||||||
</div>
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
<div class="flex-fill layout-row">
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Compounding Period' | translate }}:</span>
|
<td mat-cell *matCellDef="let charge">{{ charge.amount | formatNumber }}</td>
|
||||||
<span class="flex-60">{{
|
</ng-container>
|
||||||
getCatalogTranslation(
|
<ng-container matColumnDef="date">
|
||||||
savingsAccount.interestCompoundingPeriodType
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
|
||||||
| find: savingsAccountProductTemplate.interestCompoundingPeriodTypeOptions : 'id' : 'value'
|
<td mat-cell *matCellDef="let charge">
|
||||||
)
|
@if (
|
||||||
}}</span>
|
charge.chargeTimeType.value === 'Specified due date' || charge.chargeTimeType.value === 'Weekly Fee'
|
||||||
</div>
|
) {
|
||||||
|
<span>{{ (charge.dueDate | dateFormat) || 'Unassigned' }}</span>
|
||||||
<div class="flex-fill layout-row">
|
}
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Posting Period' | translate }}:</span>
|
@if (charge.chargeTimeType.value === 'Monthly Fee' || charge.chargeTimeType.value === 'Annual Fee') {
|
||||||
<span class="flex-60">{{
|
<span>{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}</span>
|
||||||
getCatalogTranslation(
|
}
|
||||||
savingsAccount.interestPostingPeriodType
|
@if (
|
||||||
| find: savingsAccountProductTemplate.interestPostingPeriodTypeOptions : 'id' : 'value'
|
!(
|
||||||
)
|
charge.chargeTimeType.value === 'Monthly Fee' ||
|
||||||
}}</span>
|
charge.chargeTimeType.value === 'Annual Fee' ||
|
||||||
</div>
|
charge.chargeTimeType.value === 'Specified due date' ||
|
||||||
|
charge.chargeTimeType.value === 'Weekly Fee'
|
||||||
<div class="flex-fill layout-row">
|
)
|
||||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
) {
|
||||||
<span class="flex-60">{{
|
<span>N/A</span>
|
||||||
getCatalogTranslation(
|
}
|
||||||
savingsAccount.interestCalculationType
|
</td>
|
||||||
| find: savingsAccountProductTemplate.interestCalculationTypeOptions : 'id' : 'value'
|
</ng-container>
|
||||||
)
|
<ng-container matColumnDef="repaymentsEvery">
|
||||||
}}</span>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Repayments Every' | translate }}</th>
|
||||||
</div>
|
<td mat-cell *matCellDef="let charge">{{ charge.feeInterval || 'Not Provided' }}</td>
|
||||||
|
</ng-container>
|
||||||
<div class="flex-fill layout-row">
|
<ng-container matColumnDef="chargeTimeType">
|
||||||
<span class="flex-40">{{ 'labels.inputs.Days in Year' | translate }}:</span>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||||
<span class="flex-60">{{
|
<td mat-cell *matCellDef="let charge">{{ charge.chargeTimeType.value }}</td>
|
||||||
getCatalogTranslation(
|
</ng-container>
|
||||||
savingsAccount.interestCalculationDaysInYearType
|
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
||||||
| find: savingsAccountProductTemplate.interestCalculationDaysInYearTypeOptions : 'id' : 'value'
|
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns"></tr>
|
||||||
)
|
</table>
|
||||||
}}</span>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (savingsAccount.minRequiredOpeningBalance) {
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Minimum Opening Balance' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ savingsAccount.minRequiredOpeningBalance | formatNumber }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ savingsAccount.withdrawalFeeForTransfers | yesNo }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (savingsAccount.lockinPeriodFrequency) {
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Lock-in Period' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{
|
|
||||||
getCatalogTranslation(
|
|
||||||
savingsAccount.lockinPeriodFrequency +
|
|
||||||
' ' +
|
|
||||||
(savingsAccount.lockinPeriodFrequencyType
|
|
||||||
| find: savingsAccountProductTemplate.lockinPeriodFrequencyTypeOptions : 'id' : 'value')
|
|
||||||
)
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Is Overdraft Allowed' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ savingsAccount.allowOverdraft | yesNo }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (savingsAccount.minOverdraftForInterestCalculation) {
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40"
|
|
||||||
>{{ 'labels.inputs.Minimum Overdraft Required for Interest Calculation' | translate }}:</span
|
|
||||||
>
|
|
||||||
<span class="flex-60">{{ savingsAccount.minOverdraftForInterestCalculation | formatNumber }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (savingsAccount.nominalAnnualInterestRateOverdraft) {
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Nominal Annual Interest for Overdraft' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ savingsAccount.nominalAnnualInterestRateOverdraft | formatNumber }} %</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (savingsAccount.overdraftLimit) {
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Maximum Overdraft Amount Limit' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ savingsAccount.overdraftLimit | formatNumber }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Enforce Minimum Balance' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ savingsAccount.enforceMinRequiredBalance | yesNo }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (savingsAccount.minRequiredBalance) {
|
|
||||||
<div class="flex-fill layout-row">
|
|
||||||
<span class="flex-40">{{ 'labels.inputs.Minimum Balance' | translate }}:</span>
|
|
||||||
<span class="flex-60">{{ savingsAccount.minRequiredBalance }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (savingsAccount.charges.length) {
|
|
||||||
<div class="layout-row-wrap responsive-column flex-fill">
|
|
||||||
<h3 class="mat-h3 flex-fill">{{ 'labels.inputs.Charges' | translate }}</h3>
|
|
||||||
<mat-divider class="flex-fill"></mat-divider>
|
|
||||||
<table class="flex-fill mat-elevation-z1" mat-table [dataSource]="savingsAccount.charges">
|
|
||||||
<ng-container matColumnDef="name">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
{{ charge.name + ', ' + charge.currency.displaySymbol }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="chargeCalculationType">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
{{ charge.chargeCalculationType.value | translateKey: 'catalogs' }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="amount">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
{{ charge.amount | formatNumber }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="date">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
@if (charge.chargeTimeType.value === 'Specified due date' || charge.chargeTimeType.value === 'Weekly Fee') {
|
|
||||||
<span>
|
|
||||||
{{ (charge.dueDate | dateFormat) || 'Unassigned' }}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
@if (charge.chargeTimeType.value === 'Monthly Fee' || charge.chargeTimeType.value === 'Annual Fee') {
|
|
||||||
<span>
|
|
||||||
{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
@if (
|
|
||||||
!(
|
|
||||||
charge.chargeTimeType.value === 'Monthly Fee' ||
|
|
||||||
charge.chargeTimeType.value === 'Annual Fee' ||
|
|
||||||
charge.chargeTimeType.value === 'Specified due date' ||
|
|
||||||
charge.chargeTimeType.value === 'Weekly Fee'
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
<span> N/A </span>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="repaymentsEvery">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Repayments Every' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
{{ charge.feeInterval || 'Not Provided' }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="chargeTimeType">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
{{ charge.chargeTimeType.value }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns"></tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+80
-12
@@ -6,27 +6,95 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
table {
|
.preview-wrapper {
|
||||||
width: 100%;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.25rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-elevation-z1 {
|
.preview-section {
|
||||||
margin: 1em 0 1.5em;
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 1px 3px rgb(0 0 0 / 6%);
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h2,
|
.preview-body {
|
||||||
h3,
|
padding: 1rem 1.25rem;
|
||||||
h4 {
|
}
|
||||||
margin: 0;
|
|
||||||
|
.preview-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem 1.5rem;
|
||||||
|
|
||||||
|
&--three {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 480px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-label {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: #888;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-value {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
color: #222;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
.preview-badge {
|
||||||
margin: 0.5em 0;
|
display: inline-block;
|
||||||
|
padding: 0.1rem 0.55rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #ffebee;
|
||||||
|
color: #c62828;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
&--yes {
|
||||||
|
background: #e8f5e9;
|
||||||
|
color: #2e7d32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-divider {
|
.preview-table {
|
||||||
margin: 0 0 0.5em;
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-t {
|
.margin-t {
|
||||||
|
|||||||
+29
-5
@@ -7,8 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component, Input, Output, EventEmitter, OnChanges, inject } from '@angular/core';
|
import { Component, Input, Output, EventEmitter, OnChanges } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import {
|
import {
|
||||||
MatTableDataSource,
|
MatTableDataSource,
|
||||||
MatTable,
|
MatTable,
|
||||||
@@ -62,7 +61,24 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SavingsAccountPreviewStepComponent implements OnChanges {
|
export class SavingsAccountPreviewStepComponent implements OnChanges {
|
||||||
private translateService = inject(TranslateService);
|
readonly digitalFacilityLabels = [
|
||||||
|
{ key: 'atmFacility', label: 'ATM Facility' },
|
||||||
|
{ key: 'atmWithdrawalFacility', label: 'ATM Withdrawal' },
|
||||||
|
{ key: 'upiFacility', label: 'UPI Facility' },
|
||||||
|
{ key: 'posTransactionsFacility', label: 'POS Transactions' },
|
||||||
|
{ key: 'chequeBook', label: 'Cheque Book' },
|
||||||
|
{ key: 'impsFacility', label: 'IMPS Facility' },
|
||||||
|
{ key: 'smsAlerts', label: 'SMS Alerts' },
|
||||||
|
{ key: 'bbpsFacility', label: 'BBPS Facility' },
|
||||||
|
{ key: 'insuranceCover', label: 'Insurance Cover' },
|
||||||
|
{ key: 'bepgFacility', label: 'BEPG Facility' },
|
||||||
|
{ key: 'neftFacility', label: 'NEFT Facility' },
|
||||||
|
{ key: 'rtgsFacility', label: 'RTGS Facility' },
|
||||||
|
{ key: 'interBankTotalCapFacility', label: 'Inter-Bank Total Cap' },
|
||||||
|
{ key: 'withdrawalFacility', label: 'Withdrawal Facility' },
|
||||||
|
{ key: 'depositFacilityKyc', label: 'Deposit Facility (KYC)' },
|
||||||
|
{ key: 'virtualDebitCard', label: 'Virtual Debit Card' }
|
||||||
|
];
|
||||||
|
|
||||||
/** Savings Account Product Template */
|
/** Savings Account Product Template */
|
||||||
@Input() savingsAccountProductTemplate: any;
|
@Input() savingsAccountProductTemplate: any;
|
||||||
@@ -102,7 +118,15 @@ export class SavingsAccountPreviewStepComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCatalogTranslation(text: string): string {
|
get digitalPreview(): any {
|
||||||
return this.translateService.instant('labels.catalogs.' + text);
|
return this.savingsAccountProductTemplate || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasDigitalSection(): boolean {
|
||||||
|
return !!(
|
||||||
|
this.digitalPreview?.accountFacilityConfig ||
|
||||||
|
this.digitalPreview?.accountFacilityLimits?.length ||
|
||||||
|
this.digitalPreview?.depositLimitKyc
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
<!--
|
||||||
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<form [formGroup]="savingsAccountSettingsForm">
|
||||||
|
<div class="sps-section">
|
||||||
|
<div class="sps-section__header">
|
||||||
|
<span class="sps-section__badge"><i class="fas fa-balance-scale"></i></span>
|
||||||
|
<span>Balance & Minimum Balance</span>
|
||||||
|
</div>
|
||||||
|
<div class="sps-body">
|
||||||
|
<div class="sps-layout sps-layout--balance">
|
||||||
|
<div class="sps-row sps-row--three">
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>{{ 'labels.inputs.Minimum Opening Balance' | translate }}</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minRequiredOpeningBalance" min="0" step="0.01" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="sps-toggle sps-toggle--inline">
|
||||||
|
<mat-checkbox labelPosition="before" formControlName="enforceMinRequiredBalance">
|
||||||
|
<span>Enforce Minimum Balance</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>Minimum Balance (₹)</mat-label>
|
||||||
|
<input type="number" matInput formControlName="minRequiredBalance" min="0" step="0.01" />
|
||||||
|
<mat-hint>Penal charge applies on breach</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sps-row sps-row--two">
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>Balance Frequency</mat-label>
|
||||||
|
<mat-select formControlName="balanceFrequency">
|
||||||
|
@for (freq of periodFrequencyData; track freq.value) {
|
||||||
|
<mat-option [value]="freq.value">{{ freq.value }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="sps-toggle sps-toggle--inline sps-toggle--right">
|
||||||
|
<mat-checkbox labelPosition="before" formControlName="withdrawalFeeForTransfers">
|
||||||
|
<span>{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sps-section">
|
||||||
|
<div class="sps-section__header">
|
||||||
|
<span class="sps-section__badge"><i class="fas fa-lock"></i></span>
|
||||||
|
<span>Lock-in Period</span>
|
||||||
|
</div>
|
||||||
|
<div class="sps-body">
|
||||||
|
<div class="sps-lockin-row">
|
||||||
|
<span>Enable Lock-in Period</span>
|
||||||
|
<mat-checkbox formControlName="enableLockinPeriod"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (savingsAccountSettingsForm.value.enableLockinPeriod) {
|
||||||
|
<div class="sps-lockin-fields">
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>{{ 'labels.inputs.Frequency' | translate }}*</mat-label>
|
||||||
|
<input type="number" matInput formControlName="lockinPeriodFrequency" min="1" step="1" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>{{ 'labels.inputs.Type' | translate }}*</mat-label>
|
||||||
|
<mat-select formControlName="lockinPeriodFrequencyType">
|
||||||
|
@for (type of lockinPeriodFrequencyTypeData; track type) {
|
||||||
|
<mat-option [value]="type.id">{{ type.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sps-section">
|
||||||
|
<div class="sps-section__header">
|
||||||
|
<span class="sps-section__badge"><i class="fas fa-cog"></i></span>
|
||||||
|
<span>Additional Rules</span>
|
||||||
|
</div>
|
||||||
|
<div class="sps-body">
|
||||||
|
<div class="sps-layout sps-layout--additional">
|
||||||
|
<div class="sps-toggle sps-toggle--full">
|
||||||
|
<mat-checkbox formControlName="accountClosedZeroBalanceInDayClosing">
|
||||||
|
<span>Account Closed with Zero balance in Day Closing</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (savingsAccountProductTemplate?.minBalanceForInterestCalculation != null) {
|
||||||
|
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||||
|
<mat-label>{{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[value]="savingsAccountProductTemplate.minBalanceForInterestCalculation | formatNumber"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
+169
@@ -0,0 +1,169 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.sps-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-section__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-body {
|
||||||
|
background: #fff;
|
||||||
|
padding: 18px 24px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-field {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&--full {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--compact {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-layout--balance {
|
||||||
|
gap: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-layout--additional {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
@media (width <= 1100px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-row {
|
||||||
|
display: grid;
|
||||||
|
gap: 18px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-row--three {
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.sps-field--compact:first-child {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-toggle--inline {
|
||||||
|
align-self: center;
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-row--two {
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.sps-field--compact {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 56px;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--full {
|
||||||
|
min-height: auto;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--inline {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--right {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-lockin-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-lockin-row mat-checkbox {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sps-lockin-fields {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
@media (width <= 640px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, Input, OnInit, inject } from '@angular/core';
|
||||||
|
import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl } from '@angular/forms';
|
||||||
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
|
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-savings-account-settings-step',
|
||||||
|
templateUrl: './savings-account-settings-step.component.html',
|
||||||
|
styleUrls: ['./savings-account-settings-step.component.scss'],
|
||||||
|
imports: [
|
||||||
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
MatCheckbox,
|
||||||
|
FormatNumberPipe
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SavingsAccountSettingsStepComponent implements OnInit {
|
||||||
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
|
|
||||||
|
private _savingsAccountProductTemplate: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set savingsAccountProductTemplate(value: any) {
|
||||||
|
this._savingsAccountProductTemplate = value;
|
||||||
|
if (value && this.savingsAccountSettingsForm) {
|
||||||
|
this.applyTemplate(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get savingsAccountProductTemplate(): any {
|
||||||
|
return this._savingsAccountProductTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
savingsAccountSettingsForm: UntypedFormGroup;
|
||||||
|
|
||||||
|
lockinPeriodFrequencyTypeData: any[] = [];
|
||||||
|
|
||||||
|
periodFrequencyData: any[] = [
|
||||||
|
{ value: 'Daily' },
|
||||||
|
{ value: 'Weekly' },
|
||||||
|
{ value: 'Monthly' },
|
||||||
|
{ value: 'Quarterly' },
|
||||||
|
{ value: 'Half Yearly' },
|
||||||
|
{ value: 'Yearly' }];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.createForm();
|
||||||
|
this.buildDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
createForm() {
|
||||||
|
this.savingsAccountSettingsForm = this.formBuilder.group({
|
||||||
|
minRequiredOpeningBalance: [''],
|
||||||
|
withdrawalFeeForTransfers: [false],
|
||||||
|
enforceMinRequiredBalance: [false],
|
||||||
|
minRequiredBalance: [''],
|
||||||
|
balanceFrequency: ['Monthly'],
|
||||||
|
enableLockinPeriod: [false],
|
||||||
|
lockinPeriodFrequency: [''],
|
||||||
|
lockinPeriodFrequencyType: [''],
|
||||||
|
accountClosedZeroBalanceInDayClosing: [false],
|
||||||
|
allowOverdraft: [false]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (this._savingsAccountProductTemplate) {
|
||||||
|
this.applyTemplate(this._savingsAccountProductTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyTemplate(t: any) {
|
||||||
|
this.lockinPeriodFrequencyTypeData = t.lockinPeriodFrequencyTypeOptions || [];
|
||||||
|
const hasLockin = !!(t.lockinPeriodFrequency && t.lockinPeriodFrequency > 0);
|
||||||
|
|
||||||
|
this.savingsAccountSettingsForm.patchValue({
|
||||||
|
minRequiredOpeningBalance: t.minRequiredOpeningBalance,
|
||||||
|
withdrawalFeeForTransfers: t.withdrawalFeeForTransfers || false,
|
||||||
|
enforceMinRequiredBalance: t.enforceMinRequiredBalance || false,
|
||||||
|
minRequiredBalance: t.minRequiredBalance,
|
||||||
|
balanceFrequency: t.balanceFrequency || 'Monthly',
|
||||||
|
enableLockinPeriod: hasLockin,
|
||||||
|
lockinPeriodFrequency: hasLockin ? t.lockinPeriodFrequency : '',
|
||||||
|
lockinPeriodFrequencyType: hasLockin ? t.lockinPeriodFrequencyType?.id || '' : '',
|
||||||
|
accountClosedZeroBalanceInDayClosing: t.accountClosedZeroBalanceInDayClosing || false,
|
||||||
|
allowOverdraft: t.allowOverdraft || false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (t.allowOverdraft) {
|
||||||
|
if (!this.savingsAccountSettingsForm.contains('minOverdraftForInterestCalculation')) {
|
||||||
|
this.savingsAccountSettingsForm.addControl('minOverdraftForInterestCalculation', new UntypedFormControl(''));
|
||||||
|
this.savingsAccountSettingsForm.addControl('nominalAnnualInterestRateOverdraft', new UntypedFormControl(''));
|
||||||
|
this.savingsAccountSettingsForm.addControl('overdraftLimit', new UntypedFormControl(''));
|
||||||
|
}
|
||||||
|
this.savingsAccountSettingsForm.patchValue({
|
||||||
|
minOverdraftForInterestCalculation: t.minOverdraftForInterestCalculation,
|
||||||
|
nominalAnnualInterestRateOverdraft: t.nominalAnnualInterestRateOverdraft,
|
||||||
|
overdraftLimit: t.overdraftLimit
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDependencies() {
|
||||||
|
this.savingsAccountSettingsForm.get('enableLockinPeriod').valueChanges.subscribe((enabled: boolean) => {
|
||||||
|
if (enabled) {
|
||||||
|
this.savingsAccountSettingsForm.get('lockinPeriodFrequency').setValidators([
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(1)
|
||||||
|
]);
|
||||||
|
this.savingsAccountSettingsForm.get('lockinPeriodFrequencyType').setValidators([Validators.required]);
|
||||||
|
} else {
|
||||||
|
this.savingsAccountSettingsForm.get('lockinPeriodFrequency').setValue('');
|
||||||
|
this.savingsAccountSettingsForm.get('lockinPeriodFrequencyType').setValue('');
|
||||||
|
this.savingsAccountSettingsForm.get('lockinPeriodFrequency').clearValidators();
|
||||||
|
this.savingsAccountSettingsForm.get('lockinPeriodFrequencyType').clearValidators();
|
||||||
|
}
|
||||||
|
this.savingsAccountSettingsForm.get('lockinPeriodFrequency').updateValueAndValidity();
|
||||||
|
this.savingsAccountSettingsForm.get('lockinPeriodFrequencyType').updateValueAndValidity();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.savingsAccountSettingsForm.get('allowOverdraft').valueChanges.subscribe((allowed: boolean) => {
|
||||||
|
if (allowed) {
|
||||||
|
this.savingsAccountSettingsForm.addControl('minOverdraftForInterestCalculation', new UntypedFormControl(''));
|
||||||
|
this.savingsAccountSettingsForm.addControl('nominalAnnualInterestRateOverdraft', new UntypedFormControl(''));
|
||||||
|
this.savingsAccountSettingsForm.addControl('overdraftLimit', new UntypedFormControl(''));
|
||||||
|
} else {
|
||||||
|
this.savingsAccountSettingsForm.removeControl('minOverdraftForInterestCalculation');
|
||||||
|
this.savingsAccountSettingsForm.removeControl('nominalAnnualInterestRateOverdraft');
|
||||||
|
this.savingsAccountSettingsForm.removeControl('overdraftLimit');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get savingsAccountSettings() {
|
||||||
|
const value = { ...this.savingsAccountSettingsForm.getRawValue() };
|
||||||
|
if (!value.enableLockinPeriod) {
|
||||||
|
value.lockinPeriodFrequency = null;
|
||||||
|
value.lockinPeriodFrequencyType = null;
|
||||||
|
}
|
||||||
|
delete value.enableLockinPeriod;
|
||||||
|
delete value.balanceFrequency;
|
||||||
|
delete value.accountClosedZeroBalanceInDayClosing;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
<!--
|
||||||
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<form [formGroup]="savingsAccountTermForm">
|
||||||
|
<div class="sat-section">
|
||||||
|
<div class="sat-section__header">
|
||||||
|
<span class="sat-section__badge"><i class="fas fa-chart-line"></i></span>
|
||||||
|
<span>Interest Rate & Posting Terms</span>
|
||||||
|
</div>
|
||||||
|
<div class="sat-body">
|
||||||
|
<!-- Banner showing current rate -->
|
||||||
|
<div class="sat-banner">
|
||||||
|
<span class="sat-banner__icon">ℹ️</span>
|
||||||
|
<span>
|
||||||
|
Savings Account base rate:
|
||||||
|
<b>{{ savingsAccountTermForm.get('nominalAnnualInterestRate').value || 0 }}%</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Row 1: Nominal Interest + Effective Date + Interest Calculation Type -->
|
||||||
|
<div class="sat-grid sat-grid--three">
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Nominal Annual Interest' | translate }}</mat-label>
|
||||||
|
<input type="number" matInput formControlName="nominalAnnualInterestRate" required min="0" step="0.01" />
|
||||||
|
@if (savingsAccountTermForm.get('nominalAnnualInterestRate').hasError('required')) {
|
||||||
|
<mat-error>Nominal Annual Interest is <strong>required</strong></mat-error>
|
||||||
|
}
|
||||||
|
@if (savingsAccountTermForm.get('nominalAnnualInterestRate').hasError('min')) {
|
||||||
|
<mat-error>Must be zero or greater</mat-error>
|
||||||
|
}
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sat-field" (click)="effectiveDatePicker.open()">
|
||||||
|
<mat-label>Effective Date</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="effectiveDatePicker"
|
||||||
|
formControlName="effectiveDate"
|
||||||
|
[min]="minDate"
|
||||||
|
[max]="maxDate"
|
||||||
|
/>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="effectiveDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #effectiveDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="interestCalculateSelector">
|
||||||
|
@for (opt of interestCalculateSelectorData; track opt.id) {
|
||||||
|
<mat-option [value]="opt.id">{{ opt.value }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Row 2: Interest Calculate (enum) + Average Period (conditional) + Interest Posting Period -->
|
||||||
|
<div class="sat-grid sat-grid--three">
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>Interest Calculate</mat-label>
|
||||||
|
<mat-select formControlName="interestCalculationType" required>
|
||||||
|
@for (opt of interestCalculationTypeData; track opt.id) {
|
||||||
|
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error
|
||||||
|
>{{ 'labels.inputs.Interest Calculation Type' | translate }} is <strong>required</strong></mat-error
|
||||||
|
>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
@if (savingsAccountTermForm.value.interestCalculationType === 2) {
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>Average Period</mat-label>
|
||||||
|
<mat-select formControlName="averagePeriod">
|
||||||
|
@for (period of periodFrequencyData; track period) {
|
||||||
|
<mat-option [value]="period.value">{{ period.value }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Interest Posting Period' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="interestPostingPeriodType" required>
|
||||||
|
@for (opt of interestPostingPeriodTypeData; track opt) {
|
||||||
|
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>{{ 'labels.inputs.Interest Posting Period' | translate }} is <strong>required</strong></mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Row 3: Interest Compounding + Days in Year -->
|
||||||
|
<div class="sat-grid sat-grid--three">
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Interest Compounding Period' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="interestCompoundingPeriodType" required>
|
||||||
|
@for (opt of interestCompoundingPeriodTypeData; track opt) {
|
||||||
|
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error
|
||||||
|
>{{ 'labels.inputs.Interest Compounding Period' | translate }} is <strong>required</strong></mat-error
|
||||||
|
>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Days in Year' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="interestCalculationDaysInYearType" required>
|
||||||
|
@for (opt of interestCalculationDaysInYearTypeData; track opt) {
|
||||||
|
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>{{ 'labels.inputs.Days in Year' | translate }} is <strong>required</strong></mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer: Auto Post + Frequency Fix Day + Last Interest Date -->
|
||||||
|
<div class="sat-footer">
|
||||||
|
<div class="sat-footer__grid">
|
||||||
|
<div class="sat-toggle-card">
|
||||||
|
<div>
|
||||||
|
<h4>Auto Interest Post</h4>
|
||||||
|
</div>
|
||||||
|
<div class="sat-toggle-card__choices">
|
||||||
|
@for (option of yesNoOptions; track option.value) {
|
||||||
|
<label class="sat-radio-label">
|
||||||
|
<input type="radio" formControlName="autoPost" [value]="option.value" />
|
||||||
|
<span>{{ option.label }}</span>
|
||||||
|
</label>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sat-field">
|
||||||
|
<mat-label>Frequency (Fix Day)</mat-label>
|
||||||
|
<input type="number" matInput formControlName="frequencyFixDay" min="0" />
|
||||||
|
<mat-hint>Days</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sat-field" (click)="lastInterestDatePicker.open()">
|
||||||
|
<mat-label>Last Interest Date</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="lastInterestDatePicker"
|
||||||
|
formControlName="lastInterestDate"
|
||||||
|
[min]="minDate"
|
||||||
|
[max]="maxDate"
|
||||||
|
/>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="lastInterestDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #lastInterestDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (savingsAccountTermForm.value.interestCalculationType === 1) {
|
||||||
|
<div class="sat-note">
|
||||||
|
<span>⚠️</span>
|
||||||
|
<span
|
||||||
|
>Daily product-based interest accrual enabled - interest calculated EOD on daily closing balance.</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$accent: #0ea5e9;
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
|
.sat-section {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-section__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-body {
|
||||||
|
background: #fff;
|
||||||
|
padding: 18px 24px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-banner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #eff6ff;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
font-size: 13px;
|
||||||
|
color: $gray-900;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 4px 16px;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid $border;
|
||||||
|
|
||||||
|
&__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr 1fr;
|
||||||
|
gap: 4px 16px;
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-toggle-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: $gray-50;
|
||||||
|
min-height: 56px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #64748b;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__choices {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-radio-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
[type='radio'] {
|
||||||
|
accent-color: $accent;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sat-note {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fffbeb;
|
||||||
|
border: 1px solid #fde68a;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #92400e;
|
||||||
|
}
|
||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* Copyright since 2025 Mifos Initiative
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, Input, OnInit, inject } from '@angular/core';
|
||||||
|
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'mifosx-savings-account-term-step',
|
||||||
|
templateUrl: './savings-account-term-step.component.html',
|
||||||
|
styleUrls: ['./savings-account-term-step.component.scss'],
|
||||||
|
imports: [...STANDALONE_SHARED_IMPORTS]
|
||||||
|
})
|
||||||
|
export class SavingsAccountTermStepComponent implements OnInit {
|
||||||
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
|
||||||
|
private _savingsAccountProductTemplate: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set savingsAccountProductTemplate(value: any) {
|
||||||
|
this._savingsAccountProductTemplate = value;
|
||||||
|
if (value && this.savingsAccountTermForm) {
|
||||||
|
this.applyTemplate(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get savingsAccountProductTemplate(): any {
|
||||||
|
return this._savingsAccountProductTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
savingsAccountTermForm: UntypedFormGroup;
|
||||||
|
|
||||||
|
interestCompoundingPeriodTypeData: any[] = [];
|
||||||
|
interestPostingPeriodTypeData: any[] = [];
|
||||||
|
interestCalculationTypeData: any[] = [];
|
||||||
|
interestCalculationDaysInYearTypeData: any[] = [];
|
||||||
|
interestCalculateSelectorData: any[] = [];
|
||||||
|
periodFrequencyData: any[] = [];
|
||||||
|
|
||||||
|
yesNoOptions = [
|
||||||
|
{ value: true, label: 'Yes' },
|
||||||
|
{ value: false, label: 'No' }
|
||||||
|
];
|
||||||
|
|
||||||
|
minDate = new Date(2000, 0, 1);
|
||||||
|
maxDate = new Date();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.createForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
createForm() {
|
||||||
|
this.savingsAccountTermForm = this.formBuilder.group({
|
||||||
|
currencyCode: [{ value: '', disabled: true }],
|
||||||
|
nominalAnnualInterestRate: [
|
||||||
|
'',
|
||||||
|
[
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(0)
|
||||||
|
]
|
||||||
|
],
|
||||||
|
interestCompoundingPeriodType: [
|
||||||
|
'',
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
interestPostingPeriodType: [
|
||||||
|
'',
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
interestCalculationType: [
|
||||||
|
'',
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
interestCalculationDaysInYearType: [
|
||||||
|
'',
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
effectiveDate: [''],
|
||||||
|
interestCalculateSelector: [1],
|
||||||
|
averagePeriod: ['Monthly'],
|
||||||
|
autoPost: [true],
|
||||||
|
frequencyFixDay: [
|
||||||
|
30,
|
||||||
|
[Validators.min(0)]
|
||||||
|
],
|
||||||
|
lastInterestDate: ['']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.maxDate = this.settingsService.businessDate;
|
||||||
|
// Apply template if it was set before the form was ready
|
||||||
|
if (this._savingsAccountProductTemplate) {
|
||||||
|
this.applyTemplate(this._savingsAccountProductTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyTemplate(t: any) {
|
||||||
|
this.maxDate = this.settingsService.businessDate;
|
||||||
|
|
||||||
|
this.interestCompoundingPeriodTypeData = t.interestCompoundingPeriodTypeOptions || [];
|
||||||
|
this.interestPostingPeriodTypeData = t.interestPostingPeriodTypeOptions || [];
|
||||||
|
this.interestCalculationTypeData = t.interestCalculationTypeOptions || [];
|
||||||
|
this.interestCalculateSelectorData = t.interestCalculationTypeOption || [];
|
||||||
|
this.interestCalculationDaysInYearTypeData = t.interestCalculationDaysInYearTypeOptions || [];
|
||||||
|
this.periodFrequencyData = t.periodFrequencyOptions?.length ? t.periodFrequencyOptions : [
|
||||||
|
{ value: 'Monthly' },
|
||||||
|
{ value: 'Quarterly' },
|
||||||
|
{ value: 'Half Yearly' },
|
||||||
|
{ value: 'Yearly' }];
|
||||||
|
|
||||||
|
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
||||||
|
const savedEnumTypeId = t.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1;
|
||||||
|
const savedSelectorId = t.interestCalculateSelector ?? this.interestCalculateSelectorData?.[0]?.id ?? null;
|
||||||
|
|
||||||
|
this.savingsAccountTermForm.patchValue({
|
||||||
|
currencyCode: t.currency?.code,
|
||||||
|
nominalAnnualInterestRate: t.nominalAnnualInterestRate,
|
||||||
|
interestCompoundingPeriodType: t.interestCompoundingPeriodType?.id,
|
||||||
|
interestPostingPeriodType: t.interestPostingPeriodType?.id,
|
||||||
|
interestCalculationType: savedEnumTypeId,
|
||||||
|
interestCalculationDaysInYearType: t.interestCalculationDaysInYearType?.id,
|
||||||
|
effectiveDate: t.effectiveDate ? new Date(t.effectiveDate) : '',
|
||||||
|
interestCalculateSelector: savedSelectorId,
|
||||||
|
averagePeriod: t.averagePeriod || defaultPeriod,
|
||||||
|
autoPost: t.autoPost ?? true,
|
||||||
|
frequencyFixDay: t.frequencyFixDay ?? 30,
|
||||||
|
lastInterestDate: t.lastInterestDate ? new Date(t.lastInterestDate) : ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get savingsAccountTerms() {
|
||||||
|
const value = { ...this.savingsAccountTermForm.getRawValue() };
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
|
||||||
|
if (value.effectiveDate instanceof Date) {
|
||||||
|
value.effectiveDate = this.dateUtils.formatDate(value.effectiveDate, dateFormat) || '';
|
||||||
|
}
|
||||||
|
if (value.lastInterestDate instanceof Date) {
|
||||||
|
value.lastInterestDate = this.dateUtils.formatDate(value.lastInterestDate, dateFormat) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
delete value.currencyCode;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
-7
@@ -152,13 +152,6 @@
|
|||||||
<mat-label>{{ 'labels.inputs.Minimum Balance' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Minimum Balance' | translate }}</mat-label>
|
||||||
<input type="number" matInput formControlName="minRequiredBalance" />
|
<input type="number" matInput formControlName="minRequiredBalance" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
@if (savingsAccountTermsForm.controls.minBalanceForInterestCalculation.value) {
|
|
||||||
<mat-form-field class="flex-48">
|
|
||||||
<mat-label>{{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}</mat-label>
|
|
||||||
<input type="number" matInput formControlName="minBalanceForInterestCalculation" />
|
|
||||||
</mat-form-field>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
export default {
|
export default {
|
||||||
'mifos_x': {
|
'mifos_x': {
|
||||||
'version': '260420',
|
'version': '260422',
|
||||||
'hash': 'fe61c925'
|
'hash': 'e9a93771'
|
||||||
},
|
},
|
||||||
'allow_switching_backend_instance': true
|
'allow_switching_backend_instance': true
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user