feat: merge feature/merge-updates into feature/ui-reports-product
- FD product stepper steps updated to match RD layout (Details, Currency, Terms, Settings, Charges, Accounting) - FD account creation stepper restructured with conditional steps and Previous/Next navigation - RD account details step and preview step updates - Theme and environment updates
This commit is contained in:
+85
-35
@@ -34,7 +34,8 @@
|
|||||||
<fa-icon icon="eye" size="sm"></fa-icon>
|
<fa-icon icon="eye" size="sm"></fa-icon>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<mat-step [stepControl]="fixedDepositAccountDetailsForm">
|
<!-- Step 1: Details -->
|
||||||
|
<mat-step>
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
||||||
|
|
||||||
<mifosx-fixed-deposit-account-details-step
|
<mifosx-fixed-deposit-account-details-step
|
||||||
@@ -44,48 +45,97 @@
|
|||||||
</mifosx-fixed-deposit-account-details-step>
|
</mifosx-fixed-deposit-account-details-step>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
<mat-step [stepControl]="fixedDepositAccountTermsForm">
|
@if (fixedDepositsAccountProductTemplate) {
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
<!-- Step 2: Terms -->
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
||||||
|
|
||||||
<mifosx-fixed-deposit-account-terms-step
|
<mifosx-fixed-deposit-account-terms-step
|
||||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
|
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
|
||||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||||
>
|
>
|
||||||
</mifosx-fixed-deposit-account-terms-step>
|
</mifosx-fixed-deposit-account-terms-step>
|
||||||
</mat-step>
|
|
||||||
|
|
||||||
<mat-step [stepControl]="fixedDepositAccountSettingsForm">
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.SETTINGS' | translate }}</ng-template>
|
<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>
|
||||||
|
|
||||||
<mifosx-fixed-deposit-account-settings-step
|
<!-- Step 3: Settings -->
|
||||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
|
<mat-step>
|
||||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
<ng-template matStepLabel>{{ 'labels.inputs.SETTINGS' | translate }}</ng-template>
|
||||||
>
|
|
||||||
</mifosx-fixed-deposit-account-settings-step>
|
|
||||||
</mat-step>
|
|
||||||
|
|
||||||
<mat-step>
|
<mifosx-fixed-deposit-account-settings-step
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.INTEREST RATE CHART' | translate }}</ng-template>
|
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
|
||||||
|
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||||
|
>
|
||||||
|
</mifosx-fixed-deposit-account-settings-step>
|
||||||
|
|
||||||
<mifosx-fixed-deposit-account-interest-rate-chart-step
|
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
|
<button mat-raised-button matStepperPrevious>
|
||||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||||
>
|
{{ 'labels.buttons.Previous' | translate }}
|
||||||
</mifosx-fixed-deposit-account-interest-rate-chart-step>
|
</button>
|
||||||
</mat-step>
|
<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 4: Interest Rate Chart -->
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>{{ 'labels.inputs.INTEREST RATE CHART' | translate }}</ng-template>
|
||||||
|
|
||||||
<mifosx-fixed-deposit-account-charges-step
|
<mifosx-fixed-deposit-account-interest-rate-chart-step
|
||||||
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
|
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
|
||||||
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||||
[fixedDepositAccountFormValid]="fixedDepositAccountFormValid"
|
>
|
||||||
>
|
</mifosx-fixed-deposit-account-interest-rate-chart-step>
|
||||||
</mifosx-fixed-deposit-account-charges-step>
|
|
||||||
</mat-step>
|
|
||||||
|
|
||||||
@if (fixedDepositAccountFormValid) {
|
<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: Charges -->
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
||||||
|
|
||||||
|
<mifosx-fixed-deposit-account-charges-step
|
||||||
|
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
|
||||||
|
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
|
||||||
|
[fixedDepositAccountFormValid]="fixedDepositAccountFormValid"
|
||||||
|
>
|
||||||
|
</mifosx-fixed-deposit-account-charges-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: Preview -->
|
||||||
<mat-step state="preview" completed>
|
<mat-step state="preview" completed>
|
||||||
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
||||||
<mifosx-fixed-deposit-account-preview-step
|
<mifosx-fixed-deposit-account-preview-step
|
||||||
|
|||||||
+13
-8
@@ -20,7 +20,14 @@ import { FixedDepositAccountTermsStepComponent } from '../fixed-deposit-account-
|
|||||||
import { FixedDepositAccountSettingsStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-settings-step/fixed-deposit-account-settings-step.component';
|
import { FixedDepositAccountSettingsStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-settings-step/fixed-deposit-account-settings-step.component';
|
||||||
import { FixedDepositAccountChargesStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-charges-step/fixed-deposit-account-charges-step.component';
|
import { FixedDepositAccountChargesStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-charges-step/fixed-deposit-account-charges-step.component';
|
||||||
import { Dates } from 'app/core/utils/dates';
|
import { Dates } from 'app/core/utils/dates';
|
||||||
import { MatStepper, MatStepperIcon, MatStep, MatStepLabel } from '@angular/material/stepper';
|
import {
|
||||||
|
MatStepper,
|
||||||
|
MatStepperIcon,
|
||||||
|
MatStep,
|
||||||
|
MatStepLabel,
|
||||||
|
MatStepperNext,
|
||||||
|
MatStepperPrevious
|
||||||
|
} from '@angular/material/stepper';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { FixedDepositAccountInterestRateChartStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component';
|
import { FixedDepositAccountInterestRateChartStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component';
|
||||||
import { FixedDepositAccountPreviewStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component';
|
import { FixedDepositAccountPreviewStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component';
|
||||||
@@ -37,6 +44,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
...STANDALONE_SHARED_IMPORTS,
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
MatStepper,
|
MatStepper,
|
||||||
MatStepperIcon,
|
MatStepperIcon,
|
||||||
|
MatStepperNext,
|
||||||
|
MatStepperPrevious,
|
||||||
FaIconComponent,
|
FaIconComponent,
|
||||||
MatStep,
|
MatStep,
|
||||||
MatStepLabel,
|
MatStepLabel,
|
||||||
@@ -55,17 +64,13 @@ export class CreateFixedDepositAccountComponent {
|
|||||||
private fixedDepositsService = inject(FixedDepositsService);
|
private fixedDepositsService = inject(FixedDepositsService);
|
||||||
private settingsService = inject(SettingsService);
|
private settingsService = inject(SettingsService);
|
||||||
|
|
||||||
/** Fixed Deposits Account Details Step */
|
|
||||||
@ViewChild(FixedDepositAccountDetailsStepComponent, { static: true })
|
@ViewChild(FixedDepositAccountDetailsStepComponent, { static: true })
|
||||||
fixedDepositsAccountDetailsStep: FixedDepositAccountDetailsStepComponent;
|
fixedDepositsAccountDetailsStep: FixedDepositAccountDetailsStepComponent;
|
||||||
/** Fixed Deposits Account Terms Step */
|
@ViewChild(FixedDepositAccountTermsStepComponent)
|
||||||
@ViewChild(FixedDepositAccountTermsStepComponent, { static: true })
|
|
||||||
fixedDepositAccountTermsStep: FixedDepositAccountTermsStepComponent;
|
fixedDepositAccountTermsStep: FixedDepositAccountTermsStepComponent;
|
||||||
/** Fixed Deposits Account Settings Step */
|
@ViewChild(FixedDepositAccountSettingsStepComponent)
|
||||||
@ViewChild(FixedDepositAccountSettingsStepComponent, { static: true })
|
|
||||||
fixedDepositAccountSettingsStep: FixedDepositAccountSettingsStepComponent;
|
fixedDepositAccountSettingsStep: FixedDepositAccountSettingsStepComponent;
|
||||||
/** Fixed Deposits Account Charges Step */
|
@ViewChild(FixedDepositAccountChargesStepComponent)
|
||||||
@ViewChild(FixedDepositAccountChargesStepComponent, { static: true })
|
|
||||||
fixedDepositAccountChargesStep: FixedDepositAccountChargesStepComponent;
|
fixedDepositAccountChargesStep: FixedDepositAccountChargesStepComponent;
|
||||||
|
|
||||||
/** Fixed Deposits Account Template */
|
/** Fixed Deposits Account Template */
|
||||||
|
|||||||
+28
-9
@@ -145,22 +145,41 @@ export class CreateRecurringDepositsAccountComponent {
|
|||||||
const locale = this.settingsService.language.code;
|
const locale = this.settingsService.language.code;
|
||||||
const dateFormat = this.settingsService.dateFormat;
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
const monthDayFormat = 'dd MMMM';
|
const monthDayFormat = 'dd MMMM';
|
||||||
const recurringDepositAccount = {
|
|
||||||
...this.recurringDepositAccount,
|
const details = this.recurringDepositAccount;
|
||||||
...this.recurringDepositsAccountCurrencyStep.currencyStepData,
|
const currencyStep = this.recurringDepositsAccountCurrencyStep;
|
||||||
|
|
||||||
|
const recurringDepositAccount: any = {
|
||||||
|
...details,
|
||||||
clientId: this.recurringDepositsAccountTemplate.clientId,
|
clientId: this.recurringDepositsAccountTemplate.clientId,
|
||||||
charges: [],
|
charges: [],
|
||||||
isCalendarInherited: false,
|
isCalendarInherited: false,
|
||||||
submittedOnDate: this.dateUtils.formatDate(this.recurringDepositAccount.submittedOnDate, dateFormat),
|
mandatoryRecommendedDepositAmount: 0,
|
||||||
expectedFirstDepositOnDate: this.dateUtils.formatDate(
|
recurringFrequency: 1,
|
||||||
this.recurringDepositAccount.expectedFirstDepositOnDate,
|
recurringFrequencyType: 2,
|
||||||
dateFormat
|
submittedOnDate: this.dateUtils.formatDate(details.submittedOnDate, dateFormat),
|
||||||
),
|
expectedFirstDepositOnDate: this.dateUtils.formatDate(details.expectedFirstDepositOnDate, dateFormat),
|
||||||
dateFormat,
|
dateFormat,
|
||||||
monthDayFormat,
|
monthDayFormat,
|
||||||
locale
|
locale,
|
||||||
|
// Currency step overrides
|
||||||
|
balanceSwapOption: currencyStep?.balanceSwapOption ?? details.balanceSwapOption ?? false,
|
||||||
|
msme: currencyStep?.msme ?? details.msme ?? false,
|
||||||
|
autoDebitFacility: currencyStep?.autoDebitFacility ?? details.autoDebitFacility ?? false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tenure from interest rate chart step (depositPeriod in days)
|
||||||
|
const tenureDays = this.rdTenureDays;
|
||||||
|
if (tenureDays && tenureDays > 0) {
|
||||||
|
recurringDepositAccount.depositPeriod = tenureDays;
|
||||||
|
recurringDepositAccount.depositPeriodFrequencyId = 0; // 0 = Days
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nominee from currency step
|
||||||
|
if (currencyStep?.nomineeData) {
|
||||||
|
recurringDepositAccount.nominee = currencyStep.nomineeData;
|
||||||
|
}
|
||||||
|
|
||||||
this.recurringDepositsService.createRecurringDepositAccount(recurringDepositAccount).subscribe((response: any) => {
|
this.recurringDepositsService.createRecurringDepositAccount(recurringDepositAccount).subscribe((response: any) => {
|
||||||
this.router.navigate(
|
this.router.navigate(
|
||||||
[
|
[
|
||||||
|
|||||||
+13
-6
@@ -494,6 +494,13 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
get recurringDepositAccountDetails() {
|
get recurringDepositAccountDetails() {
|
||||||
const value = this.recurringDepositAccountDetailsForm.value;
|
const value = this.recurringDepositAccountDetailsForm.value;
|
||||||
|
|
||||||
|
if (this.selectedAccountType) {
|
||||||
|
value.accountType = this.selectedAccountType;
|
||||||
|
}
|
||||||
|
if (this.selectedCustomerSegment) {
|
||||||
|
value.customerSegment = this.selectedCustomerSegment;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.selectedAccountStatus) {
|
if (this.selectedAccountStatus) {
|
||||||
value.accountStatus = this.selectedAccountStatus;
|
value.accountStatus = this.selectedAccountStatus;
|
||||||
}
|
}
|
||||||
@@ -503,8 +510,8 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
value.jointHolders = [
|
value.jointHolders = [
|
||||||
{
|
{
|
||||||
clientId: this.primaryClientId,
|
clientId: this.primaryClientId,
|
||||||
clientName: this.primaryClientName,
|
displayName: this.primaryClientName,
|
||||||
clientCif: this.primaryClientCif,
|
cifNumber: this.primaryClientCif,
|
||||||
sharePercentage: this.primaryHolderShare,
|
sharePercentage: this.primaryHolderShare,
|
||||||
isMandatory: true,
|
isMandatory: true,
|
||||||
isPrimary: true,
|
isPrimary: true,
|
||||||
@@ -513,8 +520,8 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
...this.jointHolders.map((h) => ({
|
...this.jointHolders.map((h) => ({
|
||||||
clientId: h.clientId,
|
clientId: h.clientId,
|
||||||
clientName: h.clientName,
|
displayName: h.clientName,
|
||||||
clientCif: h.clientCif,
|
cifNumber: h.clientCif,
|
||||||
sharePercentage: h.sharePercentage,
|
sharePercentage: h.sharePercentage,
|
||||||
isMandatory: h.isMandatory,
|
isMandatory: h.isMandatory,
|
||||||
isPrimary: false,
|
isPrimary: false,
|
||||||
@@ -535,9 +542,9 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
|
|||||||
if (this.selectedAccountStatus === 'MINOR') {
|
if (this.selectedAccountStatus === 'MINOR') {
|
||||||
value.guardianClientId = this.guardianClientId;
|
value.guardianClientId = this.guardianClientId;
|
||||||
value.guardianName = this.guardianName;
|
value.guardianName = this.guardianName;
|
||||||
value.guardianCif = this.guardianCif;
|
value.guardianCifNumber = this.guardianCif;
|
||||||
value.guardianRelationship = this.guardianRelationship;
|
value.guardianRelationship = this.guardianRelationship;
|
||||||
value.minorDob = this.minorDob ? this.dateUtils.formatDate(this.minorDob, 'yyyy-MM-dd') : null;
|
value.minorDateOfBirth = this.minorDob ? this.dateUtils.formatDate(this.minorDob, 'yyyy-MM-dd') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selectedLinkedAccount) {
|
if (this.selectedLinkedAccount) {
|
||||||
|
|||||||
+231
@@ -43,6 +43,237 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ══ 1b. Status of Account ════════════════════════════════════ -->
|
||||||
|
@if (recurringDepositAccountData?.accountStatus) {
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<i class="fas fa-id-card preview-section__icon"></i>
|
||||||
|
<span class="preview-section__title">Status of Account</span>
|
||||||
|
<span class="preview-section__badge">RBI Guidelines</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<!-- Status Tile -->
|
||||||
|
<div class="preview-soa-status-row">
|
||||||
|
<span class="preview-soa-tile">
|
||||||
|
@switch (recurringDepositAccountData.accountStatus) {
|
||||||
|
@case ('INDIVIDUAL_MALE') {
|
||||||
|
<i class="fas fa-male"></i> Individual (Male)
|
||||||
|
}
|
||||||
|
@case ('INDIVIDUAL_FEMALE') {
|
||||||
|
<i class="fas fa-female"></i> Individual (Female)
|
||||||
|
}
|
||||||
|
@case ('SINGLE') {
|
||||||
|
<i class="fas fa-user-circle"></i> Single Account
|
||||||
|
}
|
||||||
|
@case ('JOINT') {
|
||||||
|
<i class="fas fa-users"></i> Joint Account
|
||||||
|
}
|
||||||
|
@case ('MINOR') {
|
||||||
|
<i class="fas fa-child"></i> Minor Account
|
||||||
|
}
|
||||||
|
@case ('SENIOR_CITIZEN') {
|
||||||
|
<i class="fas fa-user-clock"></i> Senior Citizen
|
||||||
|
}
|
||||||
|
@default {
|
||||||
|
{{ recurringDepositAccountData.accountStatus }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- JOINT: Mode of Operation + Holders Table -->
|
||||||
|
@if (recurringDepositAccountData.accountStatus === 'JOINT') {
|
||||||
|
<div class="preview-grid preview-grid--three" style="margin-top: 12px">
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Mode of Operation</span>
|
||||||
|
<span class="preview-value">
|
||||||
|
@switch (recurringDepositAccountData.modeOfOperation) {
|
||||||
|
@case ('EITHER_OR_SURVIVOR') {
|
||||||
|
Either or Survivor
|
||||||
|
}
|
||||||
|
@case ('FORMER_OR_SURVIVOR') {
|
||||||
|
Former or Survivor
|
||||||
|
}
|
||||||
|
@case ('JOINTLY') {
|
||||||
|
Jointly
|
||||||
|
}
|
||||||
|
@case ('ANYONE_OR_SURVIVOR') {
|
||||||
|
Anyone or Survivor
|
||||||
|
}
|
||||||
|
@default {
|
||||||
|
{{ recurringDepositAccountData.modeOfOperation || '—' }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (recurringDepositAccountData.jointHolders?.length) {
|
||||||
|
<div class="preview-soa-table-wrap">
|
||||||
|
<table class="preview-soa-tbl">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="preview-soa-th">Sr.</th>
|
||||||
|
<th class="preview-soa-th">Holder Name</th>
|
||||||
|
<th class="preview-soa-th">CIF No.</th>
|
||||||
|
<th class="preview-soa-th">Ownership %</th>
|
||||||
|
<th class="preview-soa-th">Signing</th>
|
||||||
|
<th class="preview-soa-th">Role</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (holder of recurringDepositAccountData.jointHolders; track holder.clientId; let i = $index) {
|
||||||
|
<tr [class.preview-soa-tr--primary]="holder.isPrimary">
|
||||||
|
<td class="preview-soa-td">{{ i + 1 }}</td>
|
||||||
|
<td class="preview-soa-td">
|
||||||
|
{{ holder.displayName || holder.clientName || '—' }}
|
||||||
|
@if (holder.isPrimary) {
|
||||||
|
<span class="preview-soa-primary-tag">Primary</span>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td class="preview-soa-td preview-mono">{{ holder.cifNumber || holder.clientCif || '—' }}</td>
|
||||||
|
<td class="preview-soa-td">{{ holder.sharePercentage ?? '—' }}%</td>
|
||||||
|
<td class="preview-soa-td">{{ holder.isMandatory ? 'Mandatory' : 'Optional' }}</td>
|
||||||
|
<td class="preview-soa-td">{{ holder.isPrimary ? 'Primary Account Holder' : 'Co-Holder' }}</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- INDIVIDUAL_MALE: Special Category -->
|
||||||
|
@if (
|
||||||
|
recurringDepositAccountData.accountStatus === 'INDIVIDUAL_MALE' && recurringDepositAccountData.specialCategory
|
||||||
|
) {
|
||||||
|
<div class="preview-grid preview-grid--three" style="margin-top: 12px">
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Special Category</span>
|
||||||
|
<span class="preview-value">
|
||||||
|
@switch (recurringDepositAccountData.specialCategory) {
|
||||||
|
@case ('DEFENCE') {
|
||||||
|
Defence Personnel
|
||||||
|
}
|
||||||
|
@case ('EX_SERVICEMAN') {
|
||||||
|
Ex-Serviceman
|
||||||
|
}
|
||||||
|
@case ('GOVT_EMPLOYEE') {
|
||||||
|
Government Employee
|
||||||
|
}
|
||||||
|
@case ('PHYSICALLY_CHALLENGED') {
|
||||||
|
Physically Challenged
|
||||||
|
}
|
||||||
|
@case ('NRI') {
|
||||||
|
NRI (Non-Resident Indian)
|
||||||
|
}
|
||||||
|
@case ('NONE') {
|
||||||
|
None / General
|
||||||
|
}
|
||||||
|
@default {
|
||||||
|
{{ recurringDepositAccountData.specialCategory }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- INDIVIDUAL_FEMALE: Weaker Section -->
|
||||||
|
@if (
|
||||||
|
recurringDepositAccountData.accountStatus === 'INDIVIDUAL_FEMALE' && recurringDepositAccountData.weakerSection
|
||||||
|
) {
|
||||||
|
<div class="preview-grid preview-grid--three" style="margin-top: 12px">
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Weaker Section</span>
|
||||||
|
<span class="preview-value">
|
||||||
|
@switch (recurringDepositAccountData.weakerSection) {
|
||||||
|
@case ('BPL') {
|
||||||
|
BPL (Below Poverty Line)
|
||||||
|
}
|
||||||
|
@case ('SC_ST') {
|
||||||
|
SC / ST Category
|
||||||
|
}
|
||||||
|
@case ('MINORITY') {
|
||||||
|
Minority Community
|
||||||
|
}
|
||||||
|
@case ('SHG') {
|
||||||
|
Self Help Group Member
|
||||||
|
}
|
||||||
|
@case ('PMJDY') {
|
||||||
|
PMJDY Beneficiary
|
||||||
|
}
|
||||||
|
@case ('NONE') {
|
||||||
|
None / General
|
||||||
|
}
|
||||||
|
@default {
|
||||||
|
{{ recurringDepositAccountData.weakerSection }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- MINOR: Guardian Details -->
|
||||||
|
@if (recurringDepositAccountData.accountStatus === 'MINOR') {
|
||||||
|
<div class="preview-grid preview-grid--three" style="margin-top: 12px">
|
||||||
|
@if (recurringDepositAccountData.guardianName) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Guardian Name</span>
|
||||||
|
<span class="preview-value">{{ recurringDepositAccountData.guardianName }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (recurringDepositAccountData.guardianCifNumber) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Guardian CIF</span>
|
||||||
|
<span class="preview-value preview-mono">{{ recurringDepositAccountData.guardianCifNumber }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (recurringDepositAccountData.guardianRelationship) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Relationship</span>
|
||||||
|
<span class="preview-value">{{ recurringDepositAccountData.guardianRelationship }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (recurringDepositAccountData.minorDateOfBirth) {
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Minor DOB</span>
|
||||||
|
<span class="preview-value">{{ recurringDepositAccountData.minorDateOfBirth }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- ══ 1c. Linked Account ════════════════════════════════════════ -->
|
||||||
|
@if (recurringDepositAccountData?.linkedAccountId) {
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-section__header">
|
||||||
|
<i class="fas fa-link preview-section__icon"></i>
|
||||||
|
<span class="preview-section__title">Linked Deposit Account</span>
|
||||||
|
<span class="preview-section__badge">{{ recurringDepositAccountData.linkedAccountType }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body">
|
||||||
|
<div class="preview-grid preview-grid--three">
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Account No</span>
|
||||||
|
<span class="preview-value preview-mono">{{ recurringDepositAccountData.linkedAccountNo }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Account Type</span>
|
||||||
|
<span class="preview-value">{{ recurringDepositAccountData.linkedAccountType }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-field">
|
||||||
|
<span class="preview-label">Account ID</span>
|
||||||
|
<span class="preview-value preview-mono">{{ recurringDepositAccountData.linkedAccountId }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- ══ 2. Product Classification ════════════════════════════════ -->
|
<!-- ══ 2. Product Classification ════════════════════════════════ -->
|
||||||
@if (
|
@if (
|
||||||
recurringDepositsAccountProductTemplate?.accountType ||
|
recurringDepositsAccountProductTemplate?.accountType ||
|
||||||
|
|||||||
+102
-22
@@ -16,18 +16,18 @@
|
|||||||
|
|
||||||
// ── Section card ─────────────────────────────────────────────────────────────
|
// ── Section card ─────────────────────────────────────────────────────────────
|
||||||
.preview-section {
|
.preview-section {
|
||||||
border: 1px solid #e0e0e0;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 1px 3px rgb(0 0 0 / 6%);
|
box-shadow: 0 1px 4px rgb(0 0 0 / 5%);
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
background: #f5f5f5;
|
background: #f8fafc;
|
||||||
border-bottom: 1px solid #e0e0e0;
|
border-bottom: 1px solid #e2e8f0;
|
||||||
padding: 0.6rem 1rem;
|
padding: 0.6rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,11 +40,11 @@
|
|||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 0.95rem;
|
font-size: 0.82rem;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
color: #333;
|
color: #1e293b;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.06em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__badge {
|
&__badge {
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
// ── Grid ─────────────────────────────────────────────────────────────────────
|
// ── Grid ─────────────────────────────────────────────────────────────────────
|
||||||
.preview-grid {
|
.preview-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.75rem 1.5rem;
|
gap: 0.85rem 1.5rem;
|
||||||
|
|
||||||
&--three {
|
&--three {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
@@ -116,33 +116,38 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preview-label {
|
.preview-label {
|
||||||
font-size: 0.72rem;
|
font-size: 0.68rem;
|
||||||
color: #888;
|
color: #94a3b8;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-value {
|
.preview-value {
|
||||||
font-size: 0.92rem;
|
font-size: 0.9rem;
|
||||||
color: #222;
|
color: #1e293b;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Yes/No badge ─────────────────────────────────────────────────────────────
|
// ── Yes/No badge ─────────────────────────────────────────────────────────────
|
||||||
.preview-badge {
|
.preview-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.1rem 0.55rem;
|
padding: 0.15rem 0.6rem;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
font-size: 0.78rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
background: #ffebee;
|
background: #fef2f2;
|
||||||
color: #c62828;
|
color: #dc2626;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
|
||||||
&--yes {
|
&--yes {
|
||||||
background: #e8f5e9;
|
background: #f0fdf4;
|
||||||
color: #2e7d32;
|
color: #15803d;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--warn {
|
||||||
|
background: #fef3c7;
|
||||||
|
color: #92400e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,6 +344,81 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Status of Account (SOA) ───────────────────────────────────────────────────
|
||||||
|
.preview-soa-status-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-soa-tile {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 20px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #eff6ff;
|
||||||
|
color: #1d4ed8;
|
||||||
|
border: 2px solid #bfdbfe;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-soa-table-wrap {
|
||||||
|
margin-top: 14px;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-soa-tbl {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-soa-th {
|
||||||
|
background: #f8fafc;
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #475569;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-soa-td {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-bottom: 1px solid #f1f5f9;
|
||||||
|
color: #0f172a;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-soa-tr--primary {
|
||||||
|
background: #f0f9ff;
|
||||||
|
|
||||||
|
.preview-soa-td {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-soa-primary-tag {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 6px;
|
||||||
|
padding: 1px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1d4ed8;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Product feature flag rows (same style as details step) ────────────────────
|
// ── Product feature flag rows (same style as details step) ────────────────────
|
||||||
.preview-feature-list {
|
.preview-feature-list {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
|||||||
+53
-5
@@ -67,12 +67,60 @@
|
|||||||
<mat-tab>
|
<mat-tab>
|
||||||
<ng-template mat-tab-label>
|
<ng-template mat-tab-label>
|
||||||
<i class="fas fa-chart-area fdp-tab-icon"></i>
|
<i class="fas fa-chart-area fdp-tab-icon"></i>
|
||||||
{{ 'labels.inputs.INTEREST RATE CHART' | translate }}
|
CBS Base Chart
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<div class="fdp-tab-content">
|
<div class="fdp-tab-content">
|
||||||
<mifosx-fixed-deposit-product-interest-rate-chart-step
|
@if (activeBaseVersion) {
|
||||||
[fixedDepositProductsTemplate]="fixedDepositProductsTemplate"
|
<div class="base-rate-card">
|
||||||
></mifosx-fixed-deposit-product-interest-rate-chart-step>
|
<div class="base-rate-header">
|
||||||
|
<div>
|
||||||
|
<h3 class="base-rate-title">{{ activeBaseVersion.versionLabel }}</h3>
|
||||||
|
<p class="base-rate-meta">
|
||||||
|
Active CBS base chart
|
||||||
|
<span>Effective from {{ activeBaseVersion.effectiveFromDate }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span class="base-rate-badge">Active</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (activeBaseVersion.remarks) {
|
||||||
|
<p class="base-rate-remarks">{{ activeBaseVersion.remarks }}</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<table class="base-rate-table" mat-table [dataSource]="activeBaseVersion.slabs">
|
||||||
|
<ng-container matColumnDef="srNo">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Sr.</th>
|
||||||
|
<td mat-cell *matCellDef="let slab">{{ slab.srNo }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="description">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Particular</th>
|
||||||
|
<td mat-cell *matCellDef="let slab">{{ slab.description }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="fromPeriod">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>From</th>
|
||||||
|
<td mat-cell *matCellDef="let slab">{{ slab.fromPeriod }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="toPeriod">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||||
|
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? 'N/A' }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="periodTypeLabel">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Period Type</th>
|
||||||
|
<td mat-cell *matCellDef="let slab">{{ slab.periodTypeLabel }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="annualInterestRate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Annual Rate (%)</th>
|
||||||
|
<td mat-cell *matCellDef="let slab">
|
||||||
|
<strong>{{ slab.annualInterestRate | number: '1.2-2' }}</strong>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="fdSlabColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: fdSlabColumns"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<p>Loading CBS base chart...</p>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|
||||||
@@ -112,7 +160,7 @@
|
|||||||
<div class="fdp-tab-content">
|
<div class="fdp-tab-content">
|
||||||
<mifosx-fixed-deposit-product-preview-step
|
<mifosx-fixed-deposit-product-preview-step
|
||||||
[fixedDepositProductsTemplate]="fixedDepositProductsTemplate"
|
[fixedDepositProductsTemplate]="fixedDepositProductsTemplate"
|
||||||
[chartSlabsDisplayedColumns]="fixedDepositProductInterestRateChartStep.chartSlabsDisplayedColumns"
|
[chartSlabsDisplayedColumns]="fdSlabColumns"
|
||||||
[accountingRuleData]="accountingRuleData"
|
[accountingRuleData]="accountingRuleData"
|
||||||
[fixedDepositProduct]="fixedDepositProduct"
|
[fixedDepositProduct]="fixedDepositProduct"
|
||||||
(submitEvent)="submit()"
|
(submitEvent)="submit()"
|
||||||
|
|||||||
+48
@@ -71,6 +71,54 @@ $primary: #1353a0;
|
|||||||
padding: 24px 16px;
|
padding: 24px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.base-rate-card {
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid #dbe4f0;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: linear-gradient(180deg, #f8fbff 0%, #fff 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-rate-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-rate-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-rate-meta {
|
||||||
|
margin: 6px 0 0;
|
||||||
|
color: #5b6b7f;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-rate-badge {
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #dcfce7;
|
||||||
|
color: #166534;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-rate-remarks {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-rate-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
.fdp-action-bar {
|
.fdp-action-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
+51
-21
@@ -15,9 +15,20 @@ import { FixedDepositProductDetailsStepComponent } from '../fixed-deposit-produc
|
|||||||
import { FixedDepositProductCurrencyStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component';
|
import { FixedDepositProductCurrencyStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-currency-step/fixed-deposit-product-currency-step.component';
|
||||||
import { FixedDepositProductTermsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component';
|
import { FixedDepositProductTermsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component';
|
||||||
import { FixedDepositProductSettingsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component';
|
import { FixedDepositProductSettingsStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component';
|
||||||
import { FixedDepositProductInterestRateChartStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-interest-rate-chart-step/fixed-deposit-product-interest-rate-chart-step.component';
|
|
||||||
import { FixedDepositProductChargesStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component';
|
import { FixedDepositProductChargesStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component';
|
||||||
import { FixedDepositProductAccountingStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component';
|
import { FixedDepositProductAccountingStepComponent } from '../fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component';
|
||||||
|
import {
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatCell,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRowDef,
|
||||||
|
MatRow
|
||||||
|
} from '@angular/material/table';
|
||||||
|
|
||||||
/** Custom Services */
|
/** Custom Services */
|
||||||
import { ProductsService } from 'app/products/products.service';
|
import { ProductsService } from 'app/products/products.service';
|
||||||
@@ -40,10 +51,19 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
FixedDepositProductCurrencyStepComponent,
|
FixedDepositProductCurrencyStepComponent,
|
||||||
FixedDepositProductTermsStepComponent,
|
FixedDepositProductTermsStepComponent,
|
||||||
FixedDepositProductSettingsStepComponent,
|
FixedDepositProductSettingsStepComponent,
|
||||||
FixedDepositProductInterestRateChartStepComponent,
|
|
||||||
FixedDepositProductChargesStepComponent,
|
FixedDepositProductChargesStepComponent,
|
||||||
FixedDepositProductAccountingStepComponent,
|
FixedDepositProductAccountingStepComponent,
|
||||||
FixedDepositProductPreviewStepComponent
|
FixedDepositProductPreviewStepComponent,
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatCell,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRowDef,
|
||||||
|
MatRow
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CreateFixedDepositProductComponent {
|
export class CreateFixedDepositProductComponent {
|
||||||
@@ -61,8 +81,6 @@ export class CreateFixedDepositProductComponent {
|
|||||||
fixedDepositProductTermsStep: FixedDepositProductTermsStepComponent;
|
fixedDepositProductTermsStep: FixedDepositProductTermsStepComponent;
|
||||||
@ViewChild(FixedDepositProductSettingsStepComponent, { static: true })
|
@ViewChild(FixedDepositProductSettingsStepComponent, { static: true })
|
||||||
fixedDepositProductSettingsStep: FixedDepositProductSettingsStepComponent;
|
fixedDepositProductSettingsStep: FixedDepositProductSettingsStepComponent;
|
||||||
@ViewChild(FixedDepositProductInterestRateChartStepComponent, { static: true })
|
|
||||||
fixedDepositProductInterestRateChartStep: FixedDepositProductInterestRateChartStepComponent;
|
|
||||||
@ViewChild(FixedDepositProductChargesStepComponent, { static: true })
|
@ViewChild(FixedDepositProductChargesStepComponent, { static: true })
|
||||||
fixedDepositProductChargesStep: FixedDepositProductChargesStepComponent;
|
fixedDepositProductChargesStep: FixedDepositProductChargesStepComponent;
|
||||||
@ViewChild(FixedDepositProductAccountingStepComponent, { static: true })
|
@ViewChild(FixedDepositProductAccountingStepComponent, { static: true })
|
||||||
@@ -71,19 +89,29 @@ export class CreateFixedDepositProductComponent {
|
|||||||
fixedDepositProductsTemplate: any;
|
fixedDepositProductsTemplate: any;
|
||||||
accountingRuleData: string[] = [];
|
accountingRuleData: string[] = [];
|
||||||
selectedTabIndex = 0;
|
selectedTabIndex = 0;
|
||||||
|
fdInterestRates: any[] = [];
|
||||||
/**
|
readonly fdSlabColumns = [
|
||||||
* @param {ActivatedRoute} route Activated Route.
|
'srNo',
|
||||||
* @param {ProductsService} productsService Products Service.
|
'description',
|
||||||
* @param {Router} router Router for navigation.
|
'fromPeriod',
|
||||||
* @param {SettingsService} settingsService Settings Service.
|
'toPeriod',
|
||||||
*/
|
'periodTypeLabel',
|
||||||
|
'annualInterestRate'
|
||||||
|
];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.route.data.subscribe((data: { fixedDepositProductsTemplate: any }) => {
|
this.route.data.subscribe((data: { fixedDepositProductsTemplate: any }) => {
|
||||||
this.fixedDepositProductsTemplate = data.fixedDepositProductsTemplate;
|
this.fixedDepositProductsTemplate = data.fixedDepositProductsTemplate;
|
||||||
});
|
});
|
||||||
this.accountingRuleData = this.accounting.getAccountingRulesForSavings();
|
this.accountingRuleData = this.accounting.getAccountingRulesForSavings();
|
||||||
|
this.productsService.getRdBaseInterestRates().subscribe({
|
||||||
|
next: (rates: any[]) => {
|
||||||
|
this.fdInterestRates = rates || [];
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.fdInterestRates = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get fixedDepositProductDetailsForm() {
|
get fixedDepositProductDetailsForm() {
|
||||||
@@ -102,10 +130,6 @@ export class CreateFixedDepositProductComponent {
|
|||||||
return this.fixedDepositProductSettingsStep.fixedDepositProductSettingsForm;
|
return this.fixedDepositProductSettingsStep.fixedDepositProductSettingsForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fixedDepositProductInterestRateChartForm() {
|
|
||||||
return this.fixedDepositProductInterestRateChartStep.fixedDepositProductInterestRateChartForm;
|
|
||||||
}
|
|
||||||
|
|
||||||
get fixedDepositProductAccountingForm() {
|
get fixedDepositProductAccountingForm() {
|
||||||
return this.fixedDepositProductAccountingStep.fixedDepositProductAccountingForm;
|
return this.fixedDepositProductAccountingStep.fixedDepositProductAccountingForm;
|
||||||
}
|
}
|
||||||
@@ -120,29 +144,35 @@ export class CreateFixedDepositProductComponent {
|
|||||||
this.fixedDepositProductCurrencyForm.valid &&
|
this.fixedDepositProductCurrencyForm.valid &&
|
||||||
this.fixedDepositProductTermsForm.valid &&
|
this.fixedDepositProductTermsForm.valid &&
|
||||||
this.fixedDepositProductSettingsForm.valid &&
|
this.fixedDepositProductSettingsForm.valid &&
|
||||||
this.fixedDepositProductInterestRateChartForm.valid &&
|
|
||||||
this.fixedDepositProductAccountingForm.valid
|
this.fixedDepositProductAccountingForm.valid
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get activeBaseVersion() {
|
||||||
|
return this.fdInterestRates.find((v: any) => v.isActive) ?? this.fdInterestRates[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get activeBaseSlabs() {
|
||||||
|
return this.activeBaseVersion?.slabs || [];
|
||||||
|
}
|
||||||
|
|
||||||
get fixedDepositProduct() {
|
get fixedDepositProduct() {
|
||||||
return {
|
return {
|
||||||
...this.fixedDepositProductDetailsStep.fixedDepositProductDetails,
|
...this.fixedDepositProductDetailsStep.fixedDepositProductDetails,
|
||||||
...this.fixedDepositProductCurrencyStep.fixedDepositProductCurrency,
|
...this.fixedDepositProductCurrencyStep.fixedDepositProductCurrency,
|
||||||
...this.fixedDepositProductTermsStep.fixedDepositProductTerms,
|
...this.fixedDepositProductTermsStep.fixedDepositProductTerms,
|
||||||
...this.fixedDepositProductSettingsStep.fixedDepositProductSettings,
|
...this.fixedDepositProductSettingsStep.fixedDepositProductSettings,
|
||||||
...this.fixedDepositProductInterestRateChartStep.fixedDepositProductInterestRateChart,
|
|
||||||
...this.fixedDepositProductChargesStep.fixedDepositProductCharges,
|
...this.fixedDepositProductChargesStep.fixedDepositProductCharges,
|
||||||
...this.fixedDepositProductAccountingStep.fixedDepositProductAccounting
|
...this.fixedDepositProductAccountingStep.fixedDepositProductAccounting,
|
||||||
|
fdSlabs: this.activeBaseSlabs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
// TODO: Update once language and date settings are setup
|
|
||||||
const fixedDepositProduct = {
|
const fixedDepositProduct = {
|
||||||
...this.fixedDepositProduct,
|
...this.fixedDepositProduct,
|
||||||
charges: this.fixedDepositProduct.charges.map((charge: any) => ({ id: charge.id })),
|
charges: this.fixedDepositProduct.charges.map((charge: any) => ({ id: charge.id })),
|
||||||
locale: this.settingsService.language.code // locale required for depositAmount
|
locale: this.settingsService.language.code
|
||||||
};
|
};
|
||||||
delete fixedDepositProduct.advancedAccountingRules;
|
delete fixedDepositProduct.advancedAccountingRules;
|
||||||
this.productsService.createFixedDepositProduct(fixedDepositProduct).subscribe((response: any) => {
|
this.productsService.createFixedDepositProduct(fixedDepositProduct).subscribe((response: any) => {
|
||||||
|
|||||||
+93
-292
@@ -9,310 +9,111 @@
|
|||||||
<form [formGroup]="fixedDepositProductAccountingForm">
|
<form [formGroup]="fixedDepositProductAccountingForm">
|
||||||
<div class="fdpa-section">
|
<div class="fdpa-section">
|
||||||
<div class="fdpa-section__header">
|
<div class="fdpa-section__header">
|
||||||
<i class="fas fa-book"></i>
|
<span class="fdpa-section__icon">
|
||||||
<h3>{{ 'labels.heading.Accounting' | translate }}</h3>
|
<i class="fas fa-book"></i>
|
||||||
</div>
|
</span>
|
||||||
<div class="fdpa-grid">
|
<h3>Accounting</h3>
|
||||||
<div class="fdpa-field fdpa-field--full">
|
|
||||||
<mat-radio-group class="fdpa-radio-group" formControlName="accountingRule">
|
|
||||||
@for (accountingRule of accountingRuleData; track accountingRule; let i = $index) {
|
|
||||||
<mat-radio-button [value]="i + 1">
|
|
||||||
{{ 'labels.accounting.' + accountingRule | translate }}
|
|
||||||
</mat-radio-button>
|
|
||||||
}
|
|
||||||
</mat-radio-group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (isCashOrAccrualAccounting()) {
|
|
||||||
<div class="fdpa-section">
|
|
||||||
<div class="fdpa-section__header">
|
|
||||||
<i class="fas fa-landmark"></i>
|
|
||||||
<h3>{{ 'labels.heading.Assets' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdpa-grid">
|
|
||||||
<mifosx-gl-account-selector
|
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.savingsReferenceAccountId"
|
|
||||||
[glAccountList]="assetAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Saving reference'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
@if (isAccrualAccounting()) {
|
|
||||||
<mifosx-gl-account-selector
|
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.feesReceivableAccountId"
|
|
||||||
[glAccountList]="assetAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Fees Receivable'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
}
|
|
||||||
@if (isAccrualAccounting()) {
|
|
||||||
<mifosx-gl-account-selector
|
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.penaltiesReceivableAccountId"
|
|
||||||
[glAccountList]="assetAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Penalties Receivable'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="fdpa-section">
|
<div class="fdpa-section__body">
|
||||||
<div class="fdpa-section__header">
|
<div class="fdpa-group">
|
||||||
<i class="fas fa-balance-scale"></i>
|
<div class="fdpa-group__header">
|
||||||
<h3>{{ 'labels.heading.Liabilities' | translate }}</h3>
|
<h4>Accounting Rule</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="fdpa-grid">
|
<div class="fdpa-radio-group">
|
||||||
<mifosx-gl-account-selector
|
<mat-radio-button [value]="2" [checked]="true" [disabled]="true">Cash</mat-radio-button>
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.savingsControlAccountId"
|
|
||||||
[glAccountList]="liabilityAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Saving control'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
<mifosx-gl-account-selector
|
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.transfersInSuspenseAccountId"
|
|
||||||
[glAccountList]="liabilityAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Savings transfers in suspense'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
@if (isAccrualAccounting()) {
|
|
||||||
<mifosx-gl-account-selector
|
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.interestPayableAccountId"
|
|
||||||
[glAccountList]="liabilityAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Interest Payable'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fdpa-section">
|
|
||||||
<div class="fdpa-section__header">
|
|
||||||
<i class="fas fa-arrow-circle-down"></i>
|
|
||||||
<h3>{{ 'labels.heading.Expenses' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdpa-grid">
|
|
||||||
<mifosx-gl-account-selector
|
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.interestOnSavingsAccountId"
|
|
||||||
[glAccountList]="expenseAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Interest on savings'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fdpa-section">
|
|
||||||
<div class="fdpa-section__header">
|
|
||||||
<i class="fas fa-arrow-circle-up"></i>
|
|
||||||
<h3>{{ 'labels.heading.Income' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdpa-grid">
|
|
||||||
<mifosx-gl-account-selector
|
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.incomeFromFeeAccountId"
|
|
||||||
[glAccountList]="incomeAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Income from fees'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
<mifosx-gl-account-selector
|
|
||||||
class="fdpa-field"
|
|
||||||
[inputFormControl]="fixedDepositProductAccountingForm.controls.incomeFromPenaltyAccountId"
|
|
||||||
[glAccountList]="incomeAccountData"
|
|
||||||
[required]="true"
|
|
||||||
[inputLabel]="'Income from penalties'"
|
|
||||||
>
|
|
||||||
</mifosx-gl-account-selector>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fdpa-section">
|
|
||||||
<div class="fdpa-section__header">
|
|
||||||
<i class="fas fa-cogs"></i>
|
|
||||||
<h3>{{ 'labels.heading.Advanced Accounting Rules' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdpa-grid">
|
|
||||||
<div class="fdpa-field fdpa-field--full fdpa-checkbox-row">
|
|
||||||
<mat-checkbox formControlName="advancedAccountingRules"></mat-checkbox>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (fixedDepositProductAccountingForm.value.advancedAccountingRules) {
|
@if (isCashOrAccrualAccounting()) {
|
||||||
<!-- Payment Fund Source -->
|
@for (section of accountingSectionConfigs; track section.key) {
|
||||||
<div class="fdpa-subsection">
|
<div class="fdpa-group">
|
||||||
<div class="fdpa-subsection__header">
|
<div class="fdpa-group__header">
|
||||||
<h4>{{ 'labels.heading.Configure Fund Sources for Payment Channels' | translate }}</h4>
|
<h4>{{ section.title }}</h4>
|
||||||
<button
|
<button type="button" class="fdpa-add-btn" (click)="addOtherGlMapping(section.otherMappingsControlName)">
|
||||||
type="button"
|
+ Add Other
|
||||||
mat-raised-button
|
|
||||||
color="primary"
|
|
||||||
class="fdpa-add-btn"
|
|
||||||
(click)="add('PaymentFundSource', paymentChannelToFundSourceMappings)"
|
|
||||||
>
|
|
||||||
<fa-icon icon="plus" class="m-r-10"></fa-icon>
|
|
||||||
{{ 'labels.buttons.Add' | translate }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
@if (paymentChannelToFundSourceMappings.value.length !== 0) {
|
|
||||||
<div class="fdpa-table-wrapper">
|
|
||||||
<table mat-table [dataSource]="paymentChannelToFundSourceMappings.value">
|
|
||||||
<ng-container matColumnDef="paymentTypeId">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.heading.Payment Type' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let paymentFundSource">
|
|
||||||
{{ paymentFundSource.paymentTypeId | find: paymentTypeData : 'id' : 'name' }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="fundSourceAccountId">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Fund Source' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let paymentFundSource">
|
|
||||||
{{ paymentFundSource.fundSourceAccountId | find: assetAccountData : 'id' : 'name' }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="actions">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.buttons.Actions' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let paymentFundSource; let i = index">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
color="primary"
|
|
||||||
(click)="edit('PaymentFundSource', paymentChannelToFundSourceMappings, i)"
|
|
||||||
>
|
|
||||||
<fa-icon icon="edit"></fa-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button color="warn" (click)="delete(paymentChannelToFundSourceMappings, i)">
|
|
||||||
<fa-icon icon="trash"></fa-icon>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns"></tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (existCharges()) {
|
|
||||||
<!-- Fees Income -->
|
|
||||||
<div class="fdpa-subsection">
|
|
||||||
<div class="fdpa-subsection__header">
|
|
||||||
<h4>{{ 'labels.heading.Map Fees to Specific Income Accounts' | translate }}</h4>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
mat-raised-button
|
|
||||||
color="primary"
|
|
||||||
class="fdpa-add-btn"
|
|
||||||
(click)="add('FeesIncome', feeToIncomeAccountMappings)"
|
|
||||||
>
|
|
||||||
<fa-icon icon="plus" class="m-r-10"></fa-icon>
|
|
||||||
{{ 'labels.buttons.Add' | translate }}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@if (feeToIncomeAccountMappings.value.length !== 0) {
|
<div class="fdpa-list">
|
||||||
<div class="fdpa-table-wrapper">
|
@for (row of section.rows; track row.key) {
|
||||||
<table mat-table [dataSource]="feeToIncomeAccountMappings.value">
|
@if (!row.accrualOnly || isAccrualAccounting()) {
|
||||||
<ng-container matColumnDef="chargeId">
|
<div class="fdpa-row">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.catalogs.Fees' | translate }}</th>
|
<div class="fdpa-row__meta">
|
||||||
<td mat-cell *matCellDef="let feesIncome">
|
<span
|
||||||
{{ feesIncome.chargeId | find: chargeData : 'id' : 'name' }}
|
class="fdpa-pill"
|
||||||
</td>
|
[class.fdpa-pill--green]="section.pillClass === 'green'"
|
||||||
</ng-container>
|
[class.fdpa-pill--amber]="section.pillClass === 'amber'"
|
||||||
<ng-container matColumnDef="incomeAccountId">
|
[class.fdpa-pill--blue]="section.pillClass === 'blue'"
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Income Account' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let feesIncome">
|
|
||||||
{{ feesIncome.incomeAccountId | find: incomeAccountData : 'id' : 'name' }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="actions">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.buttons.Actions' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let feesIncome; let i = index">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
color="primary"
|
|
||||||
(click)="edit('FeesIncome', feeToIncomeAccountMappings, i)"
|
|
||||||
>
|
>
|
||||||
<fa-icon icon="edit"></fa-icon>
|
{{ section.pillLabel }}
|
||||||
</button>
|
</span>
|
||||||
<button mat-icon-button color="warn" (click)="delete(feeToIncomeAccountMappings, i)">
|
<span class="fdpa-row__label">{{ row.label }}</span>
|
||||||
<fa-icon icon="trash"></fa-icon>
|
</div>
|
||||||
</button>
|
<mifosx-gl-account-selector
|
||||||
</td>
|
class="fdpa-row__input"
|
||||||
</ng-container>
|
[inputFormControl]="fixedDepositProductAccountingForm.get(row.controlName)"
|
||||||
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
|
[glAccountList]="row.options"
|
||||||
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns"></tr>
|
[required]="row.required"
|
||||||
</table>
|
[inputLabel]="row.inputLabel"
|
||||||
</div>
|
></mifosx-gl-account-selector>
|
||||||
}
|
</div>
|
||||||
</div>
|
}
|
||||||
|
}
|
||||||
<!-- Penalty Income -->
|
|
||||||
<div class="fdpa-subsection">
|
|
||||||
<div class="fdpa-subsection__header">
|
|
||||||
<h4>
|
|
||||||
{{ 'labels.catalogs.Penalties' | translate }}
|
|
||||||
{{ 'labels.heading.Map Penalties to Specific Income Accounts' | translate }}
|
|
||||||
</h4>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
mat-raised-button
|
|
||||||
color="primary"
|
|
||||||
class="fdpa-add-btn"
|
|
||||||
(click)="add('PenaltyIncome', penaltyToIncomeAccountMappings)"
|
|
||||||
>
|
|
||||||
<fa-icon icon="plus" class="m-r-10"></fa-icon>
|
|
||||||
{{ 'labels.buttons.Add' | translate }}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
@if (penaltyToIncomeAccountMappings.value.length !== 0) {
|
|
||||||
<div class="fdpa-table-wrapper">
|
<div class="fdpa-list" [formArrayName]="section.otherMappingsControlName">
|
||||||
<table mat-table [dataSource]="penaltyToIncomeAccountMappings.value">
|
@for (
|
||||||
<ng-container matColumnDef="chargeId">
|
otherRow of getOtherGlMappingsArray(section.otherMappingsControlName).controls;
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Penalty' | translate }}</th>
|
track $index;
|
||||||
<td mat-cell *matCellDef="let penaltyIncome">
|
let i = $index
|
||||||
{{ penaltyIncome.chargeId | find: penaltyData : 'id' : 'name' }}
|
) {
|
||||||
</td>
|
<div class="fdpa-other-row-wrap">
|
||||||
</ng-container>
|
<div class="fdpa-row fdpa-row--other" [formGroupName]="i">
|
||||||
<ng-container matColumnDef="incomeAccountId">
|
<div class="fdpa-row__meta fdpa-row__meta--other">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Income Account' | translate }}</th>
|
<span
|
||||||
<td mat-cell *matCellDef="let penaltyIncome">
|
class="fdpa-pill"
|
||||||
{{ penaltyIncome.incomeAccountId | find: incomeAccountData : 'id' : 'name' }}
|
[class.fdpa-pill--green]="section.pillClass === 'green'"
|
||||||
</td>
|
[class.fdpa-pill--amber]="section.pillClass === 'amber'"
|
||||||
</ng-container>
|
[class.fdpa-pill--blue]="section.pillClass === 'blue'"
|
||||||
<ng-container matColumnDef="actions">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.buttons.Actions' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let penaltyIncome; let i = index">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
color="primary"
|
|
||||||
(click)="edit('PenaltyIncome', penaltyToIncomeAccountMappings, i)"
|
|
||||||
>
|
>
|
||||||
<fa-icon icon="edit"></fa-icon>
|
{{ section.pillLabel }}
|
||||||
</button>
|
</span>
|
||||||
<button mat-icon-button color="warn" (click)="delete(penaltyToIncomeAccountMappings, i)">
|
<input
|
||||||
<fa-icon icon="trash"></fa-icon>
|
class="fdpa-row__name fdpa-row__name--other-account"
|
||||||
</button>
|
formControlName="accountName"
|
||||||
</td>
|
placeholder="Enter account name..."
|
||||||
</ng-container>
|
type="text"
|
||||||
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
|
/>
|
||||||
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns"></tr>
|
</div>
|
||||||
</table>
|
|
||||||
</div>
|
<div class="fdpa-row__actions fdpa-row__actions--other">
|
||||||
}
|
<mat-form-field class="fdpa-row__other-field" appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<mat-label>GL Code</mat-label>
|
||||||
|
<mat-select
|
||||||
|
[formControl]="getOtherGlMappingControl(section.otherMappingsControlName, i, 'glAccountId')"
|
||||||
|
>
|
||||||
|
@for (glAccount of section.otherAccountOptions; track glAccount.id) {
|
||||||
|
<mat-option [value]="glAccount.id">
|
||||||
|
{{ '(' + glAccount.glCode + ') ' + glAccount.name }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="fdpa-remove-btn fdpa-remove-btn--side"
|
||||||
|
(click)="removeOtherGlMapping(section.otherMappingsControlName, i)"
|
||||||
|
>
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+267
-40
@@ -6,8 +6,14 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$border: #e2e8f0;
|
||||||
|
$gray-50: #f8f9fa;
|
||||||
|
$gray-200: #e8eaed;
|
||||||
|
$gray-700: #334155;
|
||||||
|
$gray-900: #202124;
|
||||||
|
|
||||||
.fdpa-section {
|
.fdpa-section {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid $border;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -15,45 +21,244 @@
|
|||||||
& + & {
|
& + & {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-section__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: $gray-50;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-section__icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
color: #2563eb;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-section__body {
|
||||||
|
padding: 16px 24px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-group {
|
||||||
|
& + & {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
justify-content: space-between;
|
||||||
padding: 12px 24px;
|
gap: 16px;
|
||||||
background: #f8f9fa;
|
margin: 0 0 12px;
|
||||||
border-bottom: 1px solid #e8eaed;
|
}
|
||||||
|
|
||||||
i {
|
h4 {
|
||||||
font-size: 14px;
|
margin: 0 0 12px;
|
||||||
color: #0ea5e9;
|
font-size: 12px;
|
||||||
}
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
h3 {
|
text-transform: uppercase;
|
||||||
margin: 0;
|
color: #94a3b8;
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #202124;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdpa-grid {
|
.fdpa-add-btn {
|
||||||
display: grid;
|
border: 0;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
background: transparent;
|
||||||
gap: 4px 16px;
|
color: #2563eb;
|
||||||
padding: 16px 24px;
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
@media (width <= 600px) {
|
.fdpa-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-list + .fdpa-list {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row:has(mifosx-gl-account-selector) {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row--other {
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
background: #f8fafc;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 16px;
|
||||||
|
|
||||||
|
@media (width <= 1100px) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdpa-field {
|
.fdpa-other-row-wrap {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
@media (width <= 1100px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__meta--other {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__input {
|
||||||
|
flex: 0 0 22rem;
|
||||||
|
max-width: 22rem;
|
||||||
|
min-width: 18rem;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__input :is(mifosx-gl-account-selector) {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__input :is(mifosx-gl-account-selector) :is(.layout-column) {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__input :is(mifosx-gl-account-selector) :is(mat-form-field) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__actions--other {
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-self: stretch;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&--full {
|
@media (width <= 1100px) {
|
||||||
grid-column: 1 / -1;
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__name {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 24rem;
|
||||||
|
height: 40px;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__name--other-account {
|
||||||
|
max-width: 100%;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__other-field {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__other-field :is(mat-form-field) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__other-field :is(.mat-mdc-form-field-required-marker) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-remove-btn {
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: #ef4444;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-remove-btn--side {
|
||||||
|
place-self: center end;
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
@media (width <= 1100px) {
|
||||||
|
justify-self: start;
|
||||||
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,23 +266,34 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
@media (width <= 600px) {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdpa-checkbox-row {
|
.fdpa-pill {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px 0;
|
justify-content: center;
|
||||||
}
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
.fdpa-add-btn {
|
&--green {
|
||||||
height: 36px;
|
background: #dcfce7;
|
||||||
border-radius: 8px;
|
color: #16a34a;
|
||||||
font-size: 13px;
|
}
|
||||||
font-weight: 600;
|
|
||||||
|
&--amber {
|
||||||
|
background: #fef3c7;
|
||||||
|
color: #d97706;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--blue {
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #2563eb;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdpa-subsection {
|
.fdpa-subsection {
|
||||||
@@ -95,13 +311,13 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #202124;
|
color: $gray-900;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdpa-table-wrapper {
|
.fdpa-table-wrapper {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid $border;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
@@ -109,3 +325,14 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
.fdpa-row__meta {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpa-row__input {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+330
-373
@@ -8,434 +8,391 @@
|
|||||||
|
|
||||||
import { Component, Input, OnInit, inject } from '@angular/core';
|
import { Component, Input, OnInit, inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
UntypedFormArray,
|
UntypedFormArray,
|
||||||
UntypedFormBuilder,
|
UntypedFormBuilder,
|
||||||
UntypedFormControl,
|
UntypedFormControl,
|
||||||
UntypedFormGroup,
|
UntypedFormGroup,
|
||||||
Validators,
|
Validators
|
||||||
ReactiveFormsModule
|
|
||||||
} from '@angular/forms';
|
} from '@angular/forms';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatRadioButton, MatRadioGroup } from '@angular/material/radio';
|
||||||
|
import { GlAccountSelectorComponent } from 'app/shared/accounting/gl-account-selector/gl-account-selector.component';
|
||||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
|
||||||
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { Accounting } from 'app/core/utils/accounting';
|
import { Accounting } from 'app/core/utils/accounting';
|
||||||
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
import { GLAccount } from 'app/shared/models/general.model';
|
||||||
import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base';
|
|
||||||
import { MatRadioGroup, MatRadioButton } from '@angular/material/radio';
|
|
||||||
import { GlAccountSelectorComponent } from '../../../../shared/accounting/gl-account-selector/gl-account-selector.component';
|
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
|
||||||
import { MatButton, MatIconButton } from '@angular/material/button';
|
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
|
||||||
import {
|
|
||||||
MatTable,
|
|
||||||
MatColumnDef,
|
|
||||||
MatHeaderCellDef,
|
|
||||||
MatHeaderCell,
|
|
||||||
MatCellDef,
|
|
||||||
MatCell,
|
|
||||||
MatHeaderRowDef,
|
|
||||||
MatHeaderRow,
|
|
||||||
MatRowDef,
|
|
||||||
MatRow
|
|
||||||
} from '@angular/material/table';
|
|
||||||
import { FindPipe } from '../../../../pipes/find.pipe';
|
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
|
type AccountingSectionKey = 'assets' | 'liabilities' | 'expenses' | 'income';
|
||||||
|
|
||||||
|
type AccountingRowConfig = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
inputLabel: string;
|
||||||
|
controlName: string;
|
||||||
|
options: GLAccount[];
|
||||||
|
required?: boolean;
|
||||||
|
accrualOnly?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AccountingSectionConfig = {
|
||||||
|
key: AccountingSectionKey;
|
||||||
|
title: string;
|
||||||
|
pillLabel: string;
|
||||||
|
pillClass: 'green' | 'amber' | 'blue';
|
||||||
|
rows: AccountingRowConfig[];
|
||||||
|
otherMappingsControlName: string;
|
||||||
|
otherAccountOptions: GLAccount[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type OtherGlMappingValue = {
|
||||||
|
accountName: string;
|
||||||
|
glAccountId: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'mifosx-fixed-deposit-product-accounting-step',
|
selector: 'mifosx-fixed-deposit-product-accounting-step',
|
||||||
templateUrl: './fixed-deposit-product-accounting-step.component.html',
|
templateUrl: './fixed-deposit-product-accounting-step.component.html',
|
||||||
styleUrls: ['./fixed-deposit-product-accounting-step.component.scss'],
|
styleUrls: ['./fixed-deposit-product-accounting-step.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
...STANDALONE_SHARED_IMPORTS,
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
MatRadioGroup,
|
MatRadioGroup,
|
||||||
MatRadioButton,
|
MatRadioButton,
|
||||||
GlAccountSelectorComponent,
|
GlAccountSelectorComponent
|
||||||
MatCheckbox,
|
|
||||||
FaIconComponent,
|
|
||||||
MatTable,
|
|
||||||
MatColumnDef,
|
|
||||||
MatHeaderCellDef,
|
|
||||||
MatHeaderCell,
|
|
||||||
MatCellDef,
|
|
||||||
MatCell,
|
|
||||||
MatIconButton,
|
|
||||||
MatHeaderRowDef,
|
|
||||||
MatHeaderRow,
|
|
||||||
MatRowDef,
|
|
||||||
MatRow,
|
|
||||||
FindPipe
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class FixedDepositProductAccountingStepComponent implements OnInit {
|
export class FixedDepositProductAccountingStepComponent implements OnInit {
|
||||||
private formBuilder = inject(UntypedFormBuilder);
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
dialog = inject(MatDialog);
|
|
||||||
private accounting = inject(Accounting);
|
private accounting = inject(Accounting);
|
||||||
private translateService = inject(TranslateService);
|
|
||||||
|
|
||||||
@Input() fixedDepositProductsTemplate: any;
|
@Input() fixedDepositProductsTemplate: any;
|
||||||
@Input() accountingRuleData: any;
|
@Input() accountingRuleData: any;
|
||||||
@Input() fixedDepositProductFormValid: boolean;
|
@Input() fixedDepositProductFormValid: boolean;
|
||||||
|
|
||||||
fixedDepositProductAccountingForm: UntypedFormGroup;
|
fixedDepositProductAccountingForm: UntypedFormGroup;
|
||||||
|
assetAccountOptions: GLAccount[] = [];
|
||||||
chargeData: any;
|
liabilityAccountOptions: GLAccount[] = [];
|
||||||
penaltyData: any;
|
expenseAccountOptions: GLAccount[] = [];
|
||||||
paymentTypeData: any;
|
incomeAccountOptions: GLAccount[] = [];
|
||||||
assetAccountData: any;
|
accountingSectionConfigs: AccountingSectionConfig[] = [];
|
||||||
incomeAccountData: any;
|
|
||||||
expenseAccountData: any;
|
|
||||||
liabilityAccountData: any;
|
|
||||||
|
|
||||||
paymentFundSourceDisplayedColumns: string[] = [
|
|
||||||
'paymentTypeId',
|
|
||||||
'fundSourceAccountId',
|
|
||||||
'actions'
|
|
||||||
];
|
|
||||||
feesPenaltyIncomeDisplayedColumns: string[] = [
|
|
||||||
'chargeId',
|
|
||||||
'incomeAccountId',
|
|
||||||
'actions'
|
|
||||||
];
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createfixedDepositProductAccountingForm();
|
this.createFixedDepositProductAccountingForm();
|
||||||
this.setConditionalControls();
|
this.setConditionalControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.chargeData = this.fixedDepositProductsTemplate.chargeOptions || [];
|
const accountingMappings = this.fixedDepositProductsTemplate?.accountingMappings || {};
|
||||||
this.penaltyData = this.fixedDepositProductsTemplate.penaltyOptions || [];
|
const mappings = {
|
||||||
this.paymentTypeData = this.fixedDepositProductsTemplate.paymentTypeOptions || [];
|
...accountingMappings,
|
||||||
this.assetAccountData = this.fixedDepositProductsTemplate.accountingMappingOptions.assetAccountOptions || [];
|
otherGlMappingsMap: this.fixedDepositProductsTemplate?.otherGlMappingsMap
|
||||||
this.incomeAccountData = this.fixedDepositProductsTemplate.accountingMappingOptions.incomeAccountOptions || [];
|
};
|
||||||
this.expenseAccountData = this.fixedDepositProductsTemplate.accountingMappingOptions.expenseAccountOptions || [];
|
const accountingMappingOptions = this.fixedDepositProductsTemplate?.accountingMappingOptions || {};
|
||||||
this.liabilityAccountData =
|
const accountingRuleId = 2; // Always Cash
|
||||||
this.fixedDepositProductsTemplate.accountingMappingOptions.liabilityAccountOptions || [];
|
|
||||||
if (!(this.fixedDepositProductsTemplate === undefined) && this.fixedDepositProductsTemplate.id) {
|
this.assetAccountOptions = accountingMappingOptions.assetAccountOptions || [];
|
||||||
this.assignAccountingStepData();
|
this.liabilityAccountOptions = accountingMappingOptions.liabilityAccountOptions || [];
|
||||||
}
|
this.expenseAccountOptions = accountingMappingOptions.expenseAccountOptions || [];
|
||||||
|
this.incomeAccountOptions = accountingMappingOptions.incomeAccountOptions || [];
|
||||||
|
|
||||||
|
this.buildAccountingSectionConfigs();
|
||||||
|
|
||||||
|
this.fixedDepositProductAccountingForm.patchValue({ accountingRule: accountingRuleId });
|
||||||
|
this.syncAccountingControls(accountingRuleId);
|
||||||
|
this.patchAccountingValues(mappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
assignAccountingStepData() {
|
createFixedDepositProductAccountingForm() {
|
||||||
this.fixedDepositProductAccountingForm.patchValue({
|
|
||||||
accountingRule: this.fixedDepositProductsTemplate.accountingRule.id
|
|
||||||
});
|
|
||||||
if (this.isCashOrAccrualAccounting()) {
|
|
||||||
this.fixedDepositProductAccountingForm.patchValue({
|
|
||||||
savingsReferenceAccountId: this.fixedDepositProductsTemplate.accountingMappings.savingsReferenceAccount.id,
|
|
||||||
savingsControlAccountId: this.fixedDepositProductsTemplate.accountingMappings.savingsControlAccount.id,
|
|
||||||
transfersInSuspenseAccountId:
|
|
||||||
this.fixedDepositProductsTemplate.accountingMappings.transfersInSuspenseAccount.id,
|
|
||||||
incomeFromFeeAccountId: this.fixedDepositProductsTemplate.accountingMappings.incomeFromFeeAccount.id,
|
|
||||||
incomeFromPenaltyAccountId: this.fixedDepositProductsTemplate.accountingMappings.incomeFromPenaltyAccount.id,
|
|
||||||
interestOnSavingsAccountId: this.fixedDepositProductsTemplate.accountingMappings.interestOnSavingsAccount.id
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.isAccrualAccounting()) {
|
|
||||||
this.fixedDepositProductAccountingForm.patchValue({
|
|
||||||
feesReceivableAccountId: this.fixedDepositProductsTemplate.accountingMappings.feeReceivableAccount.id,
|
|
||||||
penaltiesReceivableAccountId:
|
|
||||||
this.fixedDepositProductsTemplate.accountingMappings.penaltyReceivableAccount.id,
|
|
||||||
interestPayableAccountId: this.fixedDepositProductsTemplate.accountingMappings.interestPayableAccount.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.fixedDepositProductsTemplate.paymentChannelToFundSourceMappings ||
|
|
||||||
this.fixedDepositProductsTemplate.feeToIncomeAccountMappings ||
|
|
||||||
this.fixedDepositProductsTemplate.penaltyToIncomeAccountMappings
|
|
||||||
) {
|
|
||||||
this.fixedDepositProductAccountingForm.patchValue({
|
|
||||||
advancedAccountingRules: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.fixedDepositProductsTemplate.paymentChannelToFundSourceMappings) {
|
|
||||||
this.fixedDepositProductsTemplate.paymentChannelToFundSourceMappings.forEach(
|
|
||||||
(paymentChannelToFundSourceMapping: any) => {
|
|
||||||
const paymentChannelToFundSourceMappingData = this.formBuilder.group({
|
|
||||||
paymentTypeId: [
|
|
||||||
paymentChannelToFundSourceMapping.paymentType.id,
|
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
fundSourceAccountId: [
|
|
||||||
paymentChannelToFundSourceMapping.fundSourceAccount.id,
|
|
||||||
Validators.required
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const formArray = this.fixedDepositProductAccountingForm.controls[
|
|
||||||
'paymentChannelToFundSourceMappings'
|
|
||||||
] as UntypedFormArray;
|
|
||||||
formArray.push(paymentChannelToFundSourceMappingData);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (this.fixedDepositProductsTemplate.feeToIncomeAccountMappings) {
|
|
||||||
this.fixedDepositProductsTemplate.feeToIncomeAccountMappings.forEach((feeToIncomeAccountMapping: any) => {
|
|
||||||
const feeToIncomeAccountMappingData = this.formBuilder.group({
|
|
||||||
chargeId: [
|
|
||||||
feeToIncomeAccountMapping.charge.id,
|
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
incomeAccountId: [
|
|
||||||
feeToIncomeAccountMapping.incomeAccount.id,
|
|
||||||
Validators.required
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const formArray = this.fixedDepositProductAccountingForm.controls[
|
|
||||||
'feeToIncomeAccountMappings'
|
|
||||||
] as UntypedFormArray;
|
|
||||||
formArray.push(feeToIncomeAccountMappingData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.fixedDepositProductsTemplate.penaltyToIncomeAccountMappings) {
|
|
||||||
this.fixedDepositProductsTemplate.penaltyToIncomeAccountMappings.forEach(
|
|
||||||
(penaltyToIncomeAccountMapping: any) => {
|
|
||||||
const penaltyToIncomeAccountMappingData = this.formBuilder.group({
|
|
||||||
chargeId: [
|
|
||||||
penaltyToIncomeAccountMapping.charge.id,
|
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
incomeAccountId: [
|
|
||||||
penaltyToIncomeAccountMapping.incomeAccount.id,
|
|
||||||
Validators.required
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const formArray = this.fixedDepositProductAccountingForm.controls[
|
|
||||||
'penaltyToIncomeAccountMappings'
|
|
||||||
] as UntypedFormArray;
|
|
||||||
formArray.push(penaltyToIncomeAccountMappingData);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createfixedDepositProductAccountingForm() {
|
|
||||||
this.fixedDepositProductAccountingForm = this.formBuilder.group({
|
this.fixedDepositProductAccountingForm = this.formBuilder.group({
|
||||||
accountingRule: [1]
|
accountingRule: [2],
|
||||||
|
assetsOtherGlMappings: this.formBuilder.array([]),
|
||||||
|
liabilitiesOtherGlMappings: this.formBuilder.array([]),
|
||||||
|
expensesOtherGlMappings: this.formBuilder.array([]),
|
||||||
|
incomeOtherGlMappings: this.formBuilder.array([])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
existCharges(): boolean {
|
private setConditionalControls() {
|
||||||
return this.chargeData.length > 0;
|
this.fixedDepositProductAccountingForm.get('accountingRule')?.valueChanges.subscribe((rule: number) => {
|
||||||
}
|
this.syncAccountingControls(rule);
|
||||||
|
|
||||||
setConditionalControls() {
|
|
||||||
this.fixedDepositProductAccountingForm.get('accountingRule').valueChanges.subscribe((accountingRule: any) => {
|
|
||||||
if (accountingRule === 2 || accountingRule === 3) {
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'savingsReferenceAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'savingsControlAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'transfersInSuspenseAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'interestOnSavingsAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'incomeFromFeeAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'incomeFromPenaltyAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl('advancedAccountingRules', new UntypedFormControl(false));
|
|
||||||
|
|
||||||
if (accountingRule === 3) {
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'feesReceivableAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'penaltiesReceivableAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'interestPayableAccountId',
|
|
||||||
new UntypedFormControl('', Validators.required)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fixedDepositProductAccountingForm
|
|
||||||
.get('advancedAccountingRules')
|
|
||||||
.valueChanges.subscribe((advancedAccountingRules: boolean) => {
|
|
||||||
if (advancedAccountingRules) {
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'paymentChannelToFundSourceMappings',
|
|
||||||
this.formBuilder.array([])
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'feeToIncomeAccountMappings',
|
|
||||||
this.formBuilder.array([])
|
|
||||||
);
|
|
||||||
this.fixedDepositProductAccountingForm.addControl(
|
|
||||||
'penaltyToIncomeAccountMappings',
|
|
||||||
this.formBuilder.array([])
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('paymentChannelToFundSourceMappings');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('feeToIncomeAccountMappings');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('penaltyToIncomeAccountMappings');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('savingsReferenceAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('overdraftPortfolioControlId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('savingsControlAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('transfersInSuspenseAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('interestOnSavingsAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('writeOffAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('incomeFromFeeAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('incomeFromPenaltyAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('incomeFromInterestId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('advancedAccountingRules');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('escheatLiabilityId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('feesReceivableAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('penaltiesReceivableAccountId');
|
|
||||||
this.fixedDepositProductAccountingForm.removeControl('interestPayableAccountId');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get paymentChannelToFundSourceMappings(): UntypedFormArray {
|
private patchAccountingValues(mappings: any) {
|
||||||
return this.fixedDepositProductAccountingForm.get('paymentChannelToFundSourceMappings') as UntypedFormArray;
|
this.fixedDepositProductAccountingForm.patchValue({
|
||||||
}
|
savingsReferenceAccountId: mappings.savingsReferenceAccount?.id || '',
|
||||||
|
savingsControlAccountId: mappings.savingsControlAccount?.id || '',
|
||||||
get feeToIncomeAccountMappings(): UntypedFormArray {
|
transfersInSuspenseAccountId: mappings.transfersInSuspenseAccount?.id || '',
|
||||||
return this.fixedDepositProductAccountingForm.get('feeToIncomeAccountMappings') as UntypedFormArray;
|
interestOnSavingsAccountId: mappings.interestOnSavingsAccount?.id || '',
|
||||||
}
|
incomeFromFeeAccountId: mappings.incomeFromFeeAccount?.id || '',
|
||||||
|
incomeFromPenaltyAccountId: mappings.incomeFromPenaltyAccount?.id || '',
|
||||||
get penaltyToIncomeAccountMappings(): UntypedFormArray {
|
feesReceivableAccountId: mappings.feeReceivableAccount?.id || '',
|
||||||
return this.fixedDepositProductAccountingForm.get('penaltyToIncomeAccountMappings') as UntypedFormArray;
|
penaltiesReceivableAccountId: mappings.penaltyReceivableAccount?.id || '',
|
||||||
}
|
interestPayableAccountId: mappings.interestPayableAccount?.id || ''
|
||||||
|
|
||||||
add(formType: string, formArray: UntypedFormArray) {
|
|
||||||
const data = { ...this.getData(formType), pristine: false };
|
|
||||||
const dialogRef = this.dialog.open(FormDialogComponent, { data });
|
|
||||||
dialogRef.afterClosed().subscribe((response: any) => {
|
|
||||||
if (response.data) {
|
|
||||||
formArray.push(response.data);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
edit(formType: string, formArray: UntypedFormArray, index: number) {
|
if (this.isCashOrAccrualAccounting()) {
|
||||||
const data = { ...this.getData(formType, formArray.at(index).value), layout: { addButtonText: 'Edit' } };
|
const glMappings = mappings.otherGlMappingsMap || mappings.otherGlMappings;
|
||||||
const dialogRef = this.dialog.open(FormDialogComponent, { data });
|
this.setOtherGlMappings('assetsOtherGlMappings', glMappings?.assets || [], this.assetAccountOptions);
|
||||||
dialogRef.afterClosed().subscribe((response: any) => {
|
this.setOtherGlMappings(
|
||||||
if (response.data) {
|
'liabilitiesOtherGlMappings',
|
||||||
formArray.at(index).patchValue(response.data.value);
|
glMappings?.liabilities || [],
|
||||||
}
|
this.liabilityAccountOptions
|
||||||
});
|
);
|
||||||
}
|
this.setOtherGlMappings('expensesOtherGlMappings', glMappings?.expenses || [], this.expenseAccountOptions);
|
||||||
|
this.setOtherGlMappings('incomeOtherGlMappings', glMappings?.income || [], this.incomeAccountOptions);
|
||||||
delete(formArray: UntypedFormArray, index: number) {
|
|
||||||
const dialogRef = this.dialog.open(DeleteDialogComponent, {
|
|
||||||
data: { deleteContext: this.translateService.instant('labels.text.this') }
|
|
||||||
});
|
|
||||||
dialogRef.afterClosed().subscribe((response: any) => {
|
|
||||||
if (response.delete) {
|
|
||||||
formArray.removeAt(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getData(formType: string, values?: any) {
|
|
||||||
switch (formType) {
|
|
||||||
case 'PaymentFundSource':
|
|
||||||
return {
|
|
||||||
title: 'Configure Fund Sources for Payment Channels',
|
|
||||||
formfields: this.getPaymentFundSourceFormfields(values)
|
|
||||||
};
|
|
||||||
case 'FeesIncome':
|
|
||||||
return { title: 'Map Fees to Income Accounts', formfields: this.getFeesIncomeFormfields(values) };
|
|
||||||
case 'PenaltyIncome':
|
|
||||||
return {
|
|
||||||
title: 'Map Penalties to Specific Income Accounts',
|
|
||||||
formfields: this.getPenaltyIncomeFormfields(values)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPaymentFundSourceFormfields(values?: any) {
|
private createOtherGlMappingGroup(otherGlMapping: Partial<OtherGlMappingValue> = {}) {
|
||||||
const formfields: FormfieldBase[] = [
|
return this.formBuilder.group({
|
||||||
new SelectBase({
|
accountName: [
|
||||||
controlName: 'paymentTypeId',
|
otherGlMapping.accountName || '',
|
||||||
label: 'Payment Type',
|
Validators.required
|
||||||
value: values ? values.paymentTypeId : this.paymentTypeData[0].id,
|
],
|
||||||
options: { label: 'name', value: 'id', data: this.paymentTypeData },
|
glAccountId: [
|
||||||
required: true,
|
otherGlMapping.glAccountId || '',
|
||||||
order: 1
|
Validators.required
|
||||||
}),
|
]
|
||||||
new SelectBase({
|
});
|
||||||
controlName: 'fundSourceAccountId',
|
|
||||||
label: 'Fund Source',
|
|
||||||
value: values ? values.fundSourceAccountId : this.assetAccountData[0].id,
|
|
||||||
options: { label: 'name', value: 'id', data: this.assetAccountData },
|
|
||||||
required: true,
|
|
||||||
order: 2
|
|
||||||
})
|
|
||||||
];
|
|
||||||
return formfields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFeesIncomeFormfields(values?: any) {
|
getOtherGlMappingsArray(controlName: string): UntypedFormArray {
|
||||||
const formfields: FormfieldBase[] = [
|
return this.fixedDepositProductAccountingForm.get(controlName) as UntypedFormArray;
|
||||||
new SelectBase({
|
|
||||||
controlName: 'chargeId',
|
|
||||||
label: 'Fees',
|
|
||||||
value: values ? values.chargeId : this.chargeData[0].id,
|
|
||||||
options: { label: 'name', value: 'id', data: this.chargeData },
|
|
||||||
required: true,
|
|
||||||
order: 1
|
|
||||||
}),
|
|
||||||
new SelectBase({
|
|
||||||
controlName: 'incomeAccountId',
|
|
||||||
label: 'Income Account',
|
|
||||||
value: values ? values.incomeAccountId : this.incomeAccountData[0].id,
|
|
||||||
options: { label: 'name', value: 'id', data: this.incomeAccountData },
|
|
||||||
required: true,
|
|
||||||
order: 2
|
|
||||||
})
|
|
||||||
];
|
|
||||||
return formfields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPenaltyIncomeFormfields(values?: any) {
|
private setOtherGlMappings(controlName: string, otherGlMappings: any[], options: GLAccount[]) {
|
||||||
const formfields: FormfieldBase[] = [
|
const arr = this.getOtherGlMappingsArray(controlName);
|
||||||
new SelectBase({
|
arr.clear();
|
||||||
controlName: 'chargeId',
|
(otherGlMappings || []).forEach((m) => {
|
||||||
label: 'Penalty',
|
arr.push(
|
||||||
value: values ? values.chargeId : this.penaltyData[0].id,
|
this.createOtherGlMappingGroup({
|
||||||
options: { label: 'name', value: 'id', data: this.penaltyData },
|
accountName: m.accountName,
|
||||||
required: true,
|
glAccountId: m.glAccountId || options.find((o) => o.glCode === m.glCode)?.id || null
|
||||||
order: 1
|
})
|
||||||
}),
|
);
|
||||||
new SelectBase({
|
});
|
||||||
controlName: 'incomeAccountId',
|
}
|
||||||
label: 'Income Account',
|
|
||||||
value: values ? values.incomeAccountId : this.incomeAccountData[0].id,
|
addOtherGlMapping(controlName: string) {
|
||||||
options: { label: 'name', value: 'id', data: this.incomeAccountData },
|
this.getOtherGlMappingsArray(controlName).push(this.createOtherGlMappingGroup());
|
||||||
required: true,
|
}
|
||||||
order: 2
|
|
||||||
})
|
removeOtherGlMapping(controlName: string, index: number) {
|
||||||
|
this.getOtherGlMappingsArray(controlName).removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOtherGlMappingControl(controlName: string, index: number, fieldName: string): UntypedFormControl {
|
||||||
|
return this.getOtherGlMappingsArray(controlName).at(index).get(fieldName) as UntypedFormControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearOtherGlMappings() {
|
||||||
|
[
|
||||||
|
'assetsOtherGlMappings',
|
||||||
|
'liabilitiesOtherGlMappings',
|
||||||
|
'expensesOtherGlMappings',
|
||||||
|
'incomeOtherGlMappings'
|
||||||
|
].forEach((c) => this.getOtherGlMappingsArray(c).clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOtherGlMappings() {
|
||||||
|
const map = (controlName: string, options: GLAccount[]) =>
|
||||||
|
this.getOtherGlMappingsArray(controlName)
|
||||||
|
.value.filter((item: OtherGlMappingValue) => !!item.accountName || !!item.glAccountId)
|
||||||
|
.map((item: OtherGlMappingValue) => ({
|
||||||
|
accountName: item.accountName,
|
||||||
|
glCode: options.find((o) => o.id === item.glAccountId)?.glCode || ''
|
||||||
|
}))
|
||||||
|
.filter((item: { accountName: string; glCode: string }) => !!item.accountName || !!item.glCode);
|
||||||
|
|
||||||
|
const otherGlMappings = {
|
||||||
|
assets: map('assetsOtherGlMappings', this.assetAccountOptions),
|
||||||
|
liabilities: map('liabilitiesOtherGlMappings', this.liabilityAccountOptions),
|
||||||
|
expenses: map('expensesOtherGlMappings', this.expenseAccountOptions),
|
||||||
|
income: map('incomeOtherGlMappings', this.incomeAccountOptions)
|
||||||
|
};
|
||||||
|
return Object.values(otherGlMappings).some((v) => v.length) ? otherGlMappings : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private syncAccountingControls(accountingRule: number) {
|
||||||
|
const addControl = (name: string, required = false) => {
|
||||||
|
if (!this.fixedDepositProductAccountingForm.contains(name)) {
|
||||||
|
this.fixedDepositProductAccountingForm.addControl(
|
||||||
|
name,
|
||||||
|
new UntypedFormControl('', required ? Validators.required : undefined)
|
||||||
|
);
|
||||||
|
} else if (required) {
|
||||||
|
this.fixedDepositProductAccountingForm.get(name)?.setValidators(Validators.required);
|
||||||
|
this.fixedDepositProductAccountingForm.get(name)?.updateValueAndValidity({ emitEvent: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const removeControl = (name: string) => {
|
||||||
|
if (this.fixedDepositProductAccountingForm.contains(name)) {
|
||||||
|
this.fixedDepositProductAccountingForm.removeControl(name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.accounting.isCashOrAccrualAccountingRuleId(accountingRule)) {
|
||||||
|
addControl('savingsReferenceAccountId', true);
|
||||||
|
addControl('savingsControlAccountId', true);
|
||||||
|
addControl('transfersInSuspenseAccountId', true);
|
||||||
|
addControl('interestOnSavingsAccountId', true);
|
||||||
|
addControl('incomeFromFeeAccountId', true);
|
||||||
|
addControl('incomeFromPenaltyAccountId', true);
|
||||||
|
|
||||||
|
if (this.accounting.isAccrualAccountingRuleId(accountingRule)) {
|
||||||
|
addControl('feesReceivableAccountId', true);
|
||||||
|
addControl('penaltiesReceivableAccountId', true);
|
||||||
|
addControl('interestPayableAccountId', true);
|
||||||
|
} else {
|
||||||
|
removeControl('feesReceivableAccountId');
|
||||||
|
removeControl('penaltiesReceivableAccountId');
|
||||||
|
removeControl('interestPayableAccountId');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[
|
||||||
|
'savingsReferenceAccountId',
|
||||||
|
'savingsControlAccountId',
|
||||||
|
'transfersInSuspenseAccountId',
|
||||||
|
'interestOnSavingsAccountId',
|
||||||
|
'incomeFromFeeAccountId',
|
||||||
|
'incomeFromPenaltyAccountId',
|
||||||
|
'feesReceivableAccountId',
|
||||||
|
'penaltiesReceivableAccountId',
|
||||||
|
'interestPayableAccountId'
|
||||||
|
].forEach(removeControl);
|
||||||
|
this.clearOtherGlMappings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAccountingSectionConfigs() {
|
||||||
|
this.accountingSectionConfigs = [
|
||||||
|
{
|
||||||
|
key: 'assets',
|
||||||
|
title: 'Assets',
|
||||||
|
pillLabel: 'Asset',
|
||||||
|
pillClass: 'green',
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: 'savingsReferenceAccountId',
|
||||||
|
label: 'Savings Reference Account',
|
||||||
|
inputLabel: 'Savings reference account',
|
||||||
|
controlName: 'savingsReferenceAccountId',
|
||||||
|
options: this.assetAccountOptions,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'feesReceivableAccountId',
|
||||||
|
label: 'Fees Receivable Account',
|
||||||
|
inputLabel: 'Fees receivable account',
|
||||||
|
controlName: 'feesReceivableAccountId',
|
||||||
|
options: this.assetAccountOptions,
|
||||||
|
accrualOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'penaltiesReceivableAccountId',
|
||||||
|
label: 'Penalties Receivable Account',
|
||||||
|
inputLabel: 'Penalties receivable account',
|
||||||
|
controlName: 'penaltiesReceivableAccountId',
|
||||||
|
options: this.assetAccountOptions,
|
||||||
|
accrualOnly: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
otherMappingsControlName: 'assetsOtherGlMappings',
|
||||||
|
otherAccountOptions: this.assetAccountOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'liabilities',
|
||||||
|
title: 'Liabilities',
|
||||||
|
pillLabel: 'Liability',
|
||||||
|
pillClass: 'green',
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: 'savingsControlAccountId',
|
||||||
|
label: 'Savings Control Account',
|
||||||
|
inputLabel: 'Savings control account',
|
||||||
|
controlName: 'savingsControlAccountId',
|
||||||
|
options: this.liabilityAccountOptions,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'transfersInSuspenseAccountId',
|
||||||
|
label: 'Transfers In Suspense Account',
|
||||||
|
inputLabel: 'Transfers in suspense account',
|
||||||
|
controlName: 'transfersInSuspenseAccountId',
|
||||||
|
options: this.liabilityAccountOptions,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'interestPayableAccountId',
|
||||||
|
label: 'Interest Payable Account',
|
||||||
|
inputLabel: 'Interest payable account',
|
||||||
|
controlName: 'interestPayableAccountId',
|
||||||
|
options: this.liabilityAccountOptions,
|
||||||
|
accrualOnly: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
otherMappingsControlName: 'liabilitiesOtherGlMappings',
|
||||||
|
otherAccountOptions: this.liabilityAccountOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'expenses',
|
||||||
|
title: 'Expenses',
|
||||||
|
pillLabel: 'Expense',
|
||||||
|
pillClass: 'amber',
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: 'interestOnSavingsAccountId',
|
||||||
|
label: 'Interest On Savings Account',
|
||||||
|
inputLabel: 'Interest on savings account',
|
||||||
|
controlName: 'interestOnSavingsAccountId',
|
||||||
|
options: this.expenseAccountOptions,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
otherMappingsControlName: 'expensesOtherGlMappings',
|
||||||
|
otherAccountOptions: this.expenseAccountOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'income',
|
||||||
|
title: 'Income',
|
||||||
|
pillLabel: 'Income',
|
||||||
|
pillClass: 'blue',
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: 'incomeFromFeeAccountId',
|
||||||
|
label: 'Income From Fees',
|
||||||
|
inputLabel: 'Income from fees',
|
||||||
|
controlName: 'incomeFromFeeAccountId',
|
||||||
|
options: this.incomeAccountOptions,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'incomeFromPenaltyAccountId',
|
||||||
|
label: 'Income From Penalties',
|
||||||
|
inputLabel: 'Income from penalties',
|
||||||
|
controlName: 'incomeFromPenaltyAccountId',
|
||||||
|
options: this.incomeAccountOptions,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
otherMappingsControlName: 'incomeOtherGlMappings',
|
||||||
|
otherAccountOptions: this.incomeAccountOptions
|
||||||
|
}
|
||||||
];
|
];
|
||||||
return formfields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get fixedDepositProductAccounting() {
|
get fixedDepositProductAccounting() {
|
||||||
return this.fixedDepositProductAccountingForm.value;
|
const {
|
||||||
|
assetsOtherGlMappings,
|
||||||
|
liabilitiesOtherGlMappings,
|
||||||
|
expensesOtherGlMappings,
|
||||||
|
incomeOtherGlMappings,
|
||||||
|
...accounting
|
||||||
|
} = this.fixedDepositProductAccountingForm.value;
|
||||||
|
const otherGlMappings = this.getOtherGlMappings();
|
||||||
|
return { ...accounting, ...(otherGlMappings ? { otherGlMappings } : {}) };
|
||||||
}
|
}
|
||||||
|
|
||||||
isCashOrAccrualAccounting(): boolean {
|
isCashOrAccrualAccounting(): boolean {
|
||||||
|
|||||||
+60
-61
@@ -8,22 +8,21 @@
|
|||||||
|
|
||||||
<div class="fdpch-section">
|
<div class="fdpch-section">
|
||||||
<div class="fdpch-section__header">
|
<div class="fdpch-section__header">
|
||||||
<i class="fas fa-money-check-alt"></i>
|
<i class="fa fa-file-invoice-dollar"></i>
|
||||||
<h3>{{ 'labels.inputs.Charge' | translate }}</h3>
|
<h3>{{ 'labels.heading.Charges' | translate }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="fdpch-grid">
|
<div class="fdpch-section__body">
|
||||||
<mat-form-field appearance="outline" class="fdpch-field">
|
<div class="fdpch-add-row">
|
||||||
<mat-label>{{ 'labels.inputs.Charge' | translate }}</mat-label>
|
<mat-form-field appearance="outline" class="fdpch-add-row__select">
|
||||||
<mat-select #charge>
|
<mat-label>{{ 'labels.inputs.Charge' | translate }}</mat-label>
|
||||||
@for (charge of chargeData | chargesFilter: chargesDataSource : currencyCode.value; track charge) {
|
<mat-select #charge>
|
||||||
<mat-option [value]="charge">
|
@for (charge of chargeData | chargesFilter: chargesDataSource : currencyCode.value; track charge) {
|
||||||
{{ charge.name }}
|
<mat-option [value]="charge">
|
||||||
</mat-option>
|
{{ charge.name }}
|
||||||
}
|
</mat-option>
|
||||||
</mat-select>
|
}
|
||||||
</mat-form-field>
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
<div class="fdpch-field fdpch-btn-wrap">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
mat-raised-button
|
mat-raised-button
|
||||||
@@ -36,51 +35,51 @@
|
|||||||
{{ 'labels.buttons.Add' | translate }}
|
{{ 'labels.buttons.Add' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (chargesDataSource.length > 0) {
|
||||||
|
<div class="fdpch-table-wrapper">
|
||||||
|
<table mat-table [dataSource]="chargesDataSource">
|
||||||
|
<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 }}
|
||||||
|
</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="action">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let charge">
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteCharge(charge)">
|
||||||
|
<fa-icon icon="trash"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (chargesDataSource.length > 0) {
|
|
||||||
<div class="fdpch-table-wrapper">
|
|
||||||
<table mat-table [dataSource]="chargesDataSource">
|
|
||||||
<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 }}
|
|
||||||
</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="action">
|
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
|
||||||
<td mat-cell *matCellDef="let charge">
|
|
||||||
<button mat-icon-button color="warn" (click)="deleteCharge(charge)">
|
|
||||||
<fa-icon icon="trash"></fa-icon>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+11
-23
@@ -12,10 +12,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
||||||
& + & {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -36,30 +32,22 @@
|
|||||||
color: #202124;
|
color: #202124;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.fdpch-grid {
|
&__body {
|
||||||
display: grid;
|
padding: 16px 24px;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 4px 16px;
|
|
||||||
padding: 16px 24px;
|
|
||||||
|
|
||||||
@media (width <= 600px) {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdpch-field {
|
.fdpch-add-row {
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&--full {
|
|
||||||
grid-column: 1 / -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fdpch-btn-wrap {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&__select {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdpch-add-btn {
|
.fdpch-add-btn {
|
||||||
@@ -73,7 +61,7 @@
|
|||||||
border: 1px solid #e2e8f0;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 0 24px 16px;
|
margin-top: 12px;
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
+90
-48
@@ -10,60 +10,102 @@
|
|||||||
<div class="fdpc-section">
|
<div class="fdpc-section">
|
||||||
<div class="fdpc-section__header">
|
<div class="fdpc-section__header">
|
||||||
<i class="fas fa-coins"></i>
|
<i class="fas fa-coins"></i>
|
||||||
<h3>{{ 'labels.inputs.Currency' | translate }}</h3>
|
<span>Currency & Account Status Configuration</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="fdpc-grid">
|
<div class="fdpc-body">
|
||||||
<mat-form-field appearance="outline" class="fdpc-field">
|
<div class="fdpc-grid">
|
||||||
<mat-label>{{ 'labels.inputs.Currency' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="currencyCode" required>
|
|
||||||
@for (currency of currencyData; track currency) {
|
|
||||||
<mat-option [value]="currency.code">
|
|
||||||
{{ currency.name }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
<mat-error>
|
|
||||||
{{ 'labels.inputs.Currency' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdpc-field">
|
|
||||||
<mat-label>{{ 'labels.inputs.Decimal Places' | translate }}</mat-label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
matInput
|
|
||||||
matTooltip="{{ 'tooltips.Decimal places to be used to track and report' | translate }}"
|
|
||||||
formControlName="digitsAfterDecimal"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<mat-error *ngIf="fixedDepositProductCurrencyForm.get('digitsAfterDecimal')?.hasError('required')">
|
|
||||||
{{ 'labels.inputs.Decimal Places' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
<mat-error *ngIf="fixedDepositProductCurrencyForm.get('digitsAfterDecimal')?.hasError('min')">
|
|
||||||
{{ 'labels.inputs.Decimal Places' | translate }} {{ 'labels.commons.Minimum Value must be' | translate }}
|
|
||||||
<strong>0</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<div class="fdpc-field fdpc-field--full fdpc-checkbox-row">
|
|
||||||
<mat-checkbox labelPosition="before" formControlName="setMultiples">
|
|
||||||
{{ 'labels.inputs.Set the fixed deposit installment in multiples of' | translate }}
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (fixedDepositProductCurrencyForm.value.setMultiples) {
|
|
||||||
<mat-form-field appearance="outline" class="fdpc-field">
|
<mat-form-field appearance="outline" class="fdpc-field">
|
||||||
<mat-label>{{ 'labels.inputs.Currency in multiples of' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Currency' | translate }}</mat-label>
|
||||||
<input type="number" min="1" matInput formControlName="inMultiplesOf" />
|
<mat-select
|
||||||
|
formControlName="currencyCode"
|
||||||
|
matTooltip="{{ 'tooltips.The currency in which the deposit will be disbursed' | translate }}"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
@for (currency of currencyData; track currency) {
|
||||||
|
<mat-option [value]="currency.code">
|
||||||
|
{{ currency.name }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-hint>Default: Indian Rupee - auto-set for domestic products</mat-hint>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{ 'labels.inputs.Currency in multiples of' | translate }} {{ 'labels.commons.is' | translate }}
|
{{ 'labels.inputs.Currency' | 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>
|
||||||
}
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpc-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Decimal Places' | translate }}</mat-label>
|
||||||
|
<mat-select
|
||||||
|
formControlName="digitsAfterDecimal"
|
||||||
|
matTooltip="{{ 'tooltips.Decimal places to be used to track and report' | translate }}"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
@for (places of decimalPlacesData; track places) {
|
||||||
|
<mat-option [value]="places">{{ places }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Decimal Places' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdpc-alert">
|
||||||
|
<div class="fdpc-alert__content">
|
||||||
|
<h4>Dormancy Alert</h4>
|
||||||
|
<p>Enable automated notifications when an account becomes inactive based on product rules.</p>
|
||||||
|
</div>
|
||||||
|
<mat-radio-group class="fdpc-alert__choices" formControlName="dormancyAlert">
|
||||||
|
<mat-radio-button [value]="true">yes</mat-radio-button>
|
||||||
|
<mat-radio-button [value]="false">no</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdpc-alert">
|
||||||
|
<div class="fdpc-alert__content">
|
||||||
|
<h4>Balance Swap Option</h4>
|
||||||
|
<p>Enable automated transfer of excess funds to linked deposits or vice-versa to maintain liquidity.</p>
|
||||||
|
</div>
|
||||||
|
<mat-radio-group class="fdpc-alert__choices" formControlName="balanceSwapOption">
|
||||||
|
<mat-radio-button [value]="true">yes</mat-radio-button>
|
||||||
|
<mat-radio-button [value]="false">no</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdpc-alert">
|
||||||
|
<div class="fdpc-alert__content">
|
||||||
|
<h4>MSME</h4>
|
||||||
|
<p>Micro, Small and Medium Enterprises</p>
|
||||||
|
</div>
|
||||||
|
<mat-radio-group class="fdpc-alert__choices" formControlName="msme">
|
||||||
|
<mat-radio-button [value]="true">yes</mat-radio-button>
|
||||||
|
<mat-radio-button [value]="false">no</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdpc-alert">
|
||||||
|
<div class="fdpc-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>
|
||||||
|
<mat-radio-group class="fdpc-alert__choices" formControlName="nominationFacility">
|
||||||
|
<mat-radio-button [value]="true">yes</mat-radio-button>
|
||||||
|
<mat-radio-button [value]="false">no</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdpc-alert">
|
||||||
|
<div class="fdpc-alert__content">
|
||||||
|
<h4>Auto Debit Facility</h4>
|
||||||
|
<p>Enable automatic deduction of funds for recurring payments, utility bills, or EMI obligations.</p>
|
||||||
|
</div>
|
||||||
|
<mat-radio-group class="fdpc-alert__choices" formControlName="autoDebitFacility">
|
||||||
|
<mat-radio-button [value]="true">yes</mat-radio-button>
|
||||||
|
<mat-radio-button [value]="false">no</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+51
-12
@@ -6,8 +6,14 @@
|
|||||||
* 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;
|
||||||
|
|
||||||
.fdpc-section {
|
.fdpc-section {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid $border;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -21,23 +27,27 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 12px 24px;
|
padding: 12px 24px;
|
||||||
background: #f8f9fa;
|
background: $gray-50;
|
||||||
border-bottom: 1px solid #e8eaed;
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #0ea5e9;
|
color: $accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
span {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #202124;
|
color: $gray-900;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fdpc-body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.fdpc-grid {
|
.fdpc-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
@@ -51,14 +61,43 @@
|
|||||||
|
|
||||||
.fdpc-field {
|
.fdpc-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&--full {
|
|
||||||
grid-column: 1 / -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdpc-checkbox-row {
|
.fdpc-alert {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px 0;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-44
@@ -6,12 +6,10 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnInit, Input, inject, DestroyRef } from '@angular/core';
|
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
import { startWith } from 'rxjs/operators';
|
|
||||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
|
import { MatRadioGroup, MatRadioButton } from '@angular/material/radio';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -21,18 +19,23 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
imports: [
|
imports: [
|
||||||
...STANDALONE_SHARED_IMPORTS,
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
MatTooltip,
|
MatTooltip,
|
||||||
MatCheckbox
|
MatRadioGroup,
|
||||||
|
MatRadioButton
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class FixedDepositProductCurrencyStepComponent implements OnInit {
|
export class FixedDepositProductCurrencyStepComponent implements OnInit {
|
||||||
private formBuilder = inject(UntypedFormBuilder);
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
private destroyRef = inject(DestroyRef);
|
|
||||||
|
|
||||||
@Input() fixedDepositProductsTemplate: any;
|
@Input() fixedDepositProductsTemplate: any;
|
||||||
|
|
||||||
fixedDepositProductCurrencyForm: UntypedFormGroup;
|
fixedDepositProductCurrencyForm: UntypedFormGroup;
|
||||||
|
|
||||||
currencyData: any;
|
currencyData: any;
|
||||||
|
decimalPlacesData = [
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createFixedDepositProductCurrencyForm();
|
this.createFixedDepositProductCurrencyForm();
|
||||||
@@ -42,13 +45,17 @@ export class FixedDepositProductCurrencyStepComponent implements OnInit {
|
|||||||
this.currencyData = this.fixedDepositProductsTemplate.currencyOptions;
|
this.currencyData = this.fixedDepositProductsTemplate.currencyOptions;
|
||||||
|
|
||||||
this.fixedDepositProductCurrencyForm.patchValue({
|
this.fixedDepositProductCurrencyForm.patchValue({
|
||||||
currencyCode: this.fixedDepositProductsTemplate.currency?.code || this.currencyData[0].code,
|
currencyCode: this.fixedDepositProductsTemplate.currency?.code || this.currencyData?.[0]?.code || '',
|
||||||
digitsAfterDecimal: this.fixedDepositProductsTemplate.digitsAfterDecimal ?? '',
|
digitsAfterDecimal:
|
||||||
setMultiples: !!this.fixedDepositProductsTemplate.inMultiplesOf,
|
this.fixedDepositProductsTemplate.digitsAfterDecimal ??
|
||||||
inMultiplesOf: this.fixedDepositProductsTemplate.inMultiplesOf ?? ''
|
this.fixedDepositProductsTemplate.currency?.decimalPlaces ??
|
||||||
|
2,
|
||||||
|
dormancyAlert: this.fixedDepositProductsTemplate.dormancyAlert ?? false,
|
||||||
|
balanceSwapOption: this.fixedDepositProductsTemplate.balanceSwapOption ?? false,
|
||||||
|
msme: this.fixedDepositProductsTemplate.msme ?? false,
|
||||||
|
nominationFacility: this.fixedDepositProductsTemplate.nominationFacility ?? false,
|
||||||
|
autoDebitFacility: this.fixedDepositProductsTemplate.autoDebitFacility ?? false
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setupConditionalValidation();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createFixedDepositProductCurrencyForm() {
|
createFixedDepositProductCurrencyForm() {
|
||||||
@@ -58,48 +65,30 @@ export class FixedDepositProductCurrencyStepComponent implements OnInit {
|
|||||||
Validators.required
|
Validators.required
|
||||||
],
|
],
|
||||||
digitsAfterDecimal: [
|
digitsAfterDecimal: [
|
||||||
'',
|
2,
|
||||||
[
|
[
|
||||||
Validators.required,
|
Validators.required,
|
||||||
Validators.min(0)
|
Validators.min(0)
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
setMultiples: [false],
|
dormancyAlert: [false],
|
||||||
inMultiplesOf: ['']
|
balanceSwapOption: [false],
|
||||||
|
msme: [false],
|
||||||
|
nominationFacility: [false],
|
||||||
|
autoDebitFacility: [false]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setupConditionalValidation() {
|
|
||||||
const inMultiplesOfControl = this.fixedDepositProductCurrencyForm.get('inMultiplesOf');
|
|
||||||
const setMultiplesControl = this.fixedDepositProductCurrencyForm.get('setMultiples');
|
|
||||||
|
|
||||||
setMultiplesControl?.valueChanges
|
|
||||||
.pipe(startWith(setMultiplesControl.value), takeUntilDestroyed(this.destroyRef))
|
|
||||||
.subscribe((checked) => {
|
|
||||||
if (checked) {
|
|
||||||
inMultiplesOfControl?.setValidators([
|
|
||||||
Validators.required,
|
|
||||||
Validators.min(1)
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
inMultiplesOfControl?.clearValidators();
|
|
||||||
inMultiplesOfControl?.setValue('');
|
|
||||||
}
|
|
||||||
inMultiplesOfControl?.updateValueAndValidity();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get fixedDepositProductCurrency() {
|
get fixedDepositProductCurrency() {
|
||||||
const formValue = this.fixedDepositProductCurrencyForm.value;
|
const formValue = this.fixedDepositProductCurrencyForm.value;
|
||||||
const result: any = {
|
return {
|
||||||
currencyCode: formValue.currencyCode,
|
currencyCode: formValue.currencyCode,
|
||||||
digitsAfterDecimal: formValue.digitsAfterDecimal
|
digitsAfterDecimal: formValue.digitsAfterDecimal,
|
||||||
|
dormancyAlert: formValue.dormancyAlert,
|
||||||
|
balanceSwapOption: formValue.balanceSwapOption,
|
||||||
|
msme: formValue.msme,
|
||||||
|
nominationFacility: formValue.nominationFacility,
|
||||||
|
autoDebitFacility: formValue.autoDebitFacility
|
||||||
};
|
};
|
||||||
|
|
||||||
if (formValue.inMultiplesOf !== '' && formValue.inMultiplesOf !== null && formValue.inMultiplesOf !== undefined) {
|
|
||||||
result.inMultiplesOf = formValue.inMultiplesOf;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -9,13 +9,18 @@
|
|||||||
<form [formGroup]="fixedDepositProductDetailsForm">
|
<form [formGroup]="fixedDepositProductDetailsForm">
|
||||||
<div class="fdpd-section">
|
<div class="fdpd-section">
|
||||||
<div class="fdpd-section__header">
|
<div class="fdpd-section__header">
|
||||||
<i class="fas fa-info-circle"></i>
|
<i class="fa fa-info-circle"></i>
|
||||||
<h3>{{ 'labels.inputs.Product Name' | translate }} & {{ 'labels.inputs.Description' | translate }}</h3>
|
<h3>{{ 'labels.inputs.Product Name' | translate }} & {{ 'labels.inputs.Description' | translate }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="fdpd-grid">
|
<div class="fdpd-grid">
|
||||||
<mat-form-field appearance="outline" class="fdpd-field">
|
<mat-form-field appearance="outline" class="fdpd-field">
|
||||||
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
||||||
<input matInput formControlName="name" required />
|
<input
|
||||||
|
matInput
|
||||||
|
formControlName="name"
|
||||||
|
matTooltip="{{ 'tooltips.Product name is a unique identifier' | translate }}"
|
||||||
|
required
|
||||||
|
/>
|
||||||
@if (fixedDepositProductDetailsForm.controls.name.hasError('required')) {
|
@if (fixedDepositProductDetailsForm.controls.name.hasError('required')) {
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
@@ -26,7 +31,13 @@
|
|||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdpd-field">
|
<mat-form-field appearance="outline" class="fdpd-field">
|
||||||
<mat-label>{{ 'labels.inputs.Short Name' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Short Name' | translate }}</mat-label>
|
||||||
<input matInput formControlName="shortName" maxlength="4" required />
|
<input
|
||||||
|
matInput
|
||||||
|
formControlName="shortName"
|
||||||
|
maxlength="4"
|
||||||
|
matTooltip="{{ 'tooltips.Short name is a unique identifier' | translate }}"
|
||||||
|
required
|
||||||
|
/>
|
||||||
@if (fixedDepositProductDetailsForm.controls.shortName.hasError('required')) {
|
@if (fixedDepositProductDetailsForm.controls.shortName.hasError('required')) {
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{ 'labels.inputs.Short Name' | translate }} {{ 'labels.commons.is' | translate }}
|
{{ 'labels.inputs.Short Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
@@ -37,14 +48,80 @@
|
|||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdpd-field fdpd-field--full">
|
<mat-form-field appearance="outline" class="fdpd-field fdpd-field--full">
|
||||||
<mat-label>{{ 'labels.inputs.Description' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Description' | translate }}</mat-label>
|
||||||
<textarea matInput formControlName="description" required></textarea>
|
<textarea
|
||||||
@if (fixedDepositProductDetailsForm.controls.description.hasError('required')) {
|
matInput
|
||||||
<mat-error>
|
formControlName="description"
|
||||||
{{ 'labels.inputs.Description' | translate }} {{ 'labels.commons.is' | translate }}
|
matTooltip="{{
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
'tooltips.Description is used to provide additional information regarding the purpose' | translate
|
||||||
</mat-error>
|
}}"
|
||||||
}
|
cdkTextareaAutosize
|
||||||
|
cdkAutosizeMinRows="2"
|
||||||
|
></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="fdpd-section">
|
||||||
|
<div class="fdpd-section__header">
|
||||||
|
<i class="fa fa-tags"></i>
|
||||||
|
<h3>Product Classification</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fdpd-grid fdpd-grid--classification">
|
||||||
|
<mat-form-field appearance="outline" class="fdpd-field">
|
||||||
|
<mat-label>Account Type</mat-label>
|
||||||
|
<mat-select formControlName="accountType" required>
|
||||||
|
@for (accountType of accountTypeData; track accountType) {
|
||||||
|
<mat-option [value]="accountType">{{ accountType }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>Account Type <strong>is required</strong></mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpd-field">
|
||||||
|
<mat-label>Customer Segment</mat-label>
|
||||||
|
<mat-select formControlName="customerSegment">
|
||||||
|
@for (customerSegment of customerSegmentData; track customerSegment) {
|
||||||
|
<mat-option [value]="customerSegment">{{ customerSegment }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpd-field">
|
||||||
|
<mat-label>GL Account (Liability)</mat-label>
|
||||||
|
<mat-select formControlName="glAccountId" [compareWith]="compareById">
|
||||||
|
@for (account of liabilityAccountOptions; track account.id) {
|
||||||
|
<mat-option [value]="account.id">{{ account.name }} ({{ account.glCode }})</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpd-field" (click)="effectiveToPicker.open()">
|
||||||
|
<mat-label>Effective To</mat-label>
|
||||||
|
<input matInput [matDatepicker]="effectiveToPicker" formControlName="effectiveTo" />
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="effectiveToPicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #effectiveToPicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpd-field" (click)="endDatePicker.open()">
|
||||||
|
<mat-label>End Date</mat-label>
|
||||||
|
<input matInput [matDatepicker]="endDatePicker" formControlName="endDate" />
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="endDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #endDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpd-field">
|
||||||
|
<mat-label>Introducer Required</mat-label>
|
||||||
|
<mat-select formControlName="introducerRequired">
|
||||||
|
@for (option of yesNoOptions; track option.value) {
|
||||||
|
<mat-option [value]="option.value">{{ option.label }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="fdpd-field fdpd-field--full fdpd-checkbox-row">
|
||||||
|
<mat-checkbox formControlName="shareLinkingEnabled">Deposit Account Link</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+29
-5
@@ -6,8 +6,14 @@
|
|||||||
* 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;
|
||||||
|
|
||||||
.fdpd-section {
|
.fdpd-section {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid $border;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -21,19 +27,19 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 12px 24px;
|
padding: 12px 24px;
|
||||||
background: #f8f9fa;
|
background: $gray-50;
|
||||||
border-bottom: 1px solid #e8eaed;
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #0ea5e9;
|
color: $accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #202124;
|
color: $gray-900;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,6 +55,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fdpd-grid--classification {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.fdpd-field {
|
.fdpd-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@@ -56,3 +74,9 @@
|
|||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fdpd-checkbox-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
|||||||
+84
-14
@@ -6,8 +6,12 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnInit, Input, inject } from '@angular/core';
|
import { Component, OnInit, OnChanges, Input, SimpleChanges, inject, ChangeDetectorRef } from '@angular/core';
|
||||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { CdkTextareaAutosize } from '@angular/cdk/text-field';
|
||||||
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -15,27 +19,41 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
templateUrl: './fixed-deposit-product-details-step.component.html',
|
templateUrl: './fixed-deposit-product-details-step.component.html',
|
||||||
styleUrls: ['./fixed-deposit-product-details-step.component.scss'],
|
styleUrls: ['./fixed-deposit-product-details-step.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
...STANDALONE_SHARED_IMPORTS
|
...STANDALONE_SHARED_IMPORTS,
|
||||||
|
CdkTextareaAutosize,
|
||||||
|
MatTooltip
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class FixedDepositProductDetailsStepComponent implements OnInit {
|
export class FixedDepositProductDetailsStepComponent implements OnInit, OnChanges {
|
||||||
private formBuilder = inject(UntypedFormBuilder);
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
private cdr = inject(ChangeDetectorRef);
|
||||||
|
|
||||||
@Input() fixedDepositProductsTemplate: any;
|
@Input() fixedDepositProductsTemplate: any;
|
||||||
|
|
||||||
fixedDepositProductDetailsForm: UntypedFormGroup;
|
fixedDepositProductDetailsForm: UntypedFormGroup;
|
||||||
|
accountTypeData: string[] = [];
|
||||||
|
customerSegmentData: string[] = [];
|
||||||
|
liabilityAccountOptions: any[] = [];
|
||||||
|
yesNoOptions: Array<{ value: boolean; label: string }> = [
|
||||||
|
{ value: true, label: 'Yes' },
|
||||||
|
{ value: false, label: 'No' }
|
||||||
|
];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createFixedDepositProductDetailsForm();
|
this.createFixedDepositProductDetailsForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (!(this.fixedDepositProductsTemplate === undefined) && this.fixedDepositProductsTemplate.id) {
|
if (this.fixedDepositProductsTemplate) {
|
||||||
this.fixedDepositProductDetailsForm.patchValue({
|
this.applyTemplate(this.fixedDepositProductsTemplate);
|
||||||
name: this.fixedDepositProductsTemplate.name,
|
}
|
||||||
shortName: this.fixedDepositProductsTemplate.shortName,
|
}
|
||||||
description: this.fixedDepositProductsTemplate.description
|
|
||||||
});
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes.fixedDepositProductsTemplate?.currentValue) {
|
||||||
|
this.applyTemplate(changes.fixedDepositProductsTemplate.currentValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,14 +67,66 @@ export class FixedDepositProductDetailsStepComponent implements OnInit {
|
|||||||
'',
|
'',
|
||||||
Validators.required
|
Validators.required
|
||||||
],
|
],
|
||||||
description: [
|
description: [''],
|
||||||
'',
|
accountType: [
|
||||||
|
null,
|
||||||
Validators.required
|
Validators.required
|
||||||
]
|
],
|
||||||
|
customerSegment: [null],
|
||||||
|
glAccountId: [null],
|
||||||
|
effectiveTo: [''],
|
||||||
|
endDate: [''],
|
||||||
|
introducerRequired: [false],
|
||||||
|
shareLinkingEnabled: [false]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOptionNames(options: any[]): string[] {
|
||||||
|
if (!Array.isArray(options)) return [];
|
||||||
|
return options.map((o) => o?.name ?? o?.value ?? o).filter((v) => typeof v === 'string' && v.trim().length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseDate(dateValue: any): Date | null {
|
||||||
|
if (!dateValue) return null;
|
||||||
|
if (Array.isArray(dateValue)) return new Date(dateValue[0], dateValue[1] - 1, dateValue[2]);
|
||||||
|
if (dateValue instanceof Date) return dateValue;
|
||||||
|
return new Date(dateValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
compareById(o1: any, o2: any): boolean {
|
||||||
|
return o1 != null && o2 != null && Number(o1) === Number(o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyTemplate(template: any) {
|
||||||
|
this.accountTypeData = this.getOptionNames(template.accountTypeOptions);
|
||||||
|
this.customerSegmentData = this.getOptionNames(template.customerSegmentOptions);
|
||||||
|
this.liabilityAccountOptions = template.accountingMappingOptions?.liabilityAccountOptions ?? [];
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
|
||||||
|
const isEditing = !!template.id;
|
||||||
|
this.fixedDepositProductDetailsForm.patchValue({
|
||||||
|
name: isEditing ? template.name : template.name || '',
|
||||||
|
shortName: isEditing ? template.shortName : template.shortName || '',
|
||||||
|
description: template.description || '',
|
||||||
|
accountType: template.accountType ?? this.accountTypeData[0] ?? null,
|
||||||
|
customerSegment: template.customerSegment ?? this.customerSegmentData[0] ?? null,
|
||||||
|
glAccountId: template.glAccountId ?? null,
|
||||||
|
effectiveTo: this.parseDate(template.effectiveTo),
|
||||||
|
endDate: this.parseDate(template.endDate),
|
||||||
|
introducerRequired: template.introducerRequired === true || template.introducerRequired === 'Yes',
|
||||||
|
shareLinkingEnabled: !!template.shareLinkingEnabled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get fixedDepositProductDetails() {
|
get fixedDepositProductDetails() {
|
||||||
return this.fixedDepositProductDetailsForm.value;
|
const formValue = { ...this.fixedDepositProductDetailsForm.value };
|
||||||
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
|
if (formValue.effectiveTo instanceof Date) {
|
||||||
|
formValue.effectiveTo = this.dateUtils.formatDate(formValue.effectiveTo, dateFormat) || '';
|
||||||
|
}
|
||||||
|
if (formValue.endDate instanceof Date) {
|
||||||
|
formValue.endDate = this.dateUtils.formatDate(formValue.endDate, dateFormat) || '';
|
||||||
|
}
|
||||||
|
return formValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+105
-185
@@ -7,204 +7,124 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="fixedDepositProductSettingsForm">
|
<form [formGroup]="fixedDepositProductSettingsForm">
|
||||||
|
<!-- Premature option -->
|
||||||
<div class="fdps-section">
|
<div class="fdps-section">
|
||||||
<div class="fdps-section__header">
|
<div class="fdps-section__header">
|
||||||
<i class="fas fa-lock"></i>
|
<span class="fdps-section__badge"><i class="fas fa-balance-scale"></i></span>
|
||||||
<h3>{{ 'labels.inputs.Lock-in Period' | translate }}</h3>
|
<span>Premature option</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="fdps-grid">
|
<div class="fdps-body">
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
<div class="fdps-premature-row">
|
||||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
<mat-checkbox formControlName="loanFacility">Loan Facility</mat-checkbox>
|
||||||
<input
|
<mat-checkbox formControlName="prematureWithdrawalOption">Premature Withdrawal option</mat-checkbox>
|
||||||
type="number"
|
<mat-checkbox formControlName="tenureFlexibility">Tenure Flexibility</mat-checkbox>
|
||||||
min="0"
|
<mat-checkbox formControlName="lienMarked">Lien Marked</mat-checkbox>
|
||||||
matInput
|
<mat-checkbox formControlName="withdrawalFeeForTransfers">Apply Withdrawal Fee for Transfers</mat-checkbox>
|
||||||
formControlName="lockinPeriodFrequency"
|
</div>
|
||||||
matTooltip="{{ 'tooltips.A period of time during which a fixed deposit account' | translate }}"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
|
||||||
<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 }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Lock-in Period -->
|
||||||
<div class="fdps-section">
|
<div class="fdps-section">
|
||||||
<div class="fdps-section__header">
|
<div class="fdps-section__header">
|
||||||
<i class="fas fa-calendar-check"></i>
|
<span class="fdps-section__badge"><i class="fas fa-lock"></i></span>
|
||||||
<h3>{{ 'labels.inputs.Minimum Deposit Term' | translate }}</h3>
|
<span>{{ 'labels.inputs.Lock-in Period' | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="fdps-grid">
|
<div class="fdps-body">
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
<div class="fdps-lockin-row">
|
||||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
<span>Enable Lock-in Period</span>
|
||||||
<input
|
<mat-checkbox formControlName="enableLockinPeriod"></mat-checkbox>
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
matInput
|
|
||||||
formControlName="minDepositTerm"
|
|
||||||
required
|
|
||||||
matTooltip="{{ 'tooltips.The minimum length of time' | translate }}"
|
|
||||||
/>
|
|
||||||
<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 appearance="outline" class="fdps-field">
|
|
||||||
<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 }}
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fdps-section">
|
|
||||||
<div class="fdps-section__header">
|
|
||||||
<i class="fas fa-layer-group"></i>
|
|
||||||
<h3>{{ 'labels.heading.And thereafter, in Multiples of' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdps-grid">
|
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
|
||||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
matInput
|
|
||||||
formControlName="inMultiplesOfDepositTerm"
|
|
||||||
matTooltip="{{ 'tooltips.After the minimum deposit term has passed' | translate }}"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
|
||||||
<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 }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fdps-section">
|
|
||||||
<div class="fdps-section__header">
|
|
||||||
<i class="fas fa-calendar-alt"></i>
|
|
||||||
<h3>{{ 'labels.inputs.Maximum Deposit Term' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdps-grid">
|
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
|
||||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
matInput
|
|
||||||
formControlName="maxDepositTerm"
|
|
||||||
matTooltip="{{ 'tooltips.The maximum length of time funds' | translate }}"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
|
||||||
<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 }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fdps-section">
|
|
||||||
<div class="fdps-section__header">
|
|
||||||
<i class="fas fa-exclamation-triangle"></i>
|
|
||||||
<h3>{{ 'labels.heading.For Pre-mature closure' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdps-grid">
|
|
||||||
<div class="fdps-field fdps-field--full fdps-checkbox-row">
|
|
||||||
<mat-checkbox
|
|
||||||
labelPosition="before"
|
|
||||||
formControlName="preClosurePenalApplicable"
|
|
||||||
matTooltip="{{ 'tooltips.This sections defines an interest penalty' | translate }}"
|
|
||||||
>
|
|
||||||
{{ 'labels.inputs.Apply Penal Interest (less)' | translate }}
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
@if (fixedDepositProductSettingsForm.value.enableLockinPeriod) {
|
||||||
<mat-label>{{ 'labels.inputs.Penal Interest' | translate }} (%)</mat-label>
|
<div class="fdps-lockin-fields">
|
||||||
<input type="number" min="0" matInput formControlName="preClosurePenalInterest" />
|
<mat-form-field appearance="outline" class="fdps-field">
|
||||||
</mat-form-field>
|
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
matInput
|
||||||
|
formControlName="lockinPeriodFrequency"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
matTooltip="{{ 'tooltips.A period of time during which a fixed deposit account' | translate }}"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
<mat-form-field appearance="outline" class="fdps-field">
|
||||||
<mat-label>{{ 'labels.inputs.Period' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label>
|
||||||
<mat-select formControlName="preClosurePenalInterestOnTypeId">
|
<mat-select formControlName="lockinPeriodFrequencyType">
|
||||||
@for (
|
@for (lockinPeriodFrequencyType of lockinPeriodFrequencyTypeData; track lockinPeriodFrequencyType) {
|
||||||
preClosurePenalInterestOnType of preClosurePenalInterestOnTypeData;
|
<mat-option [value]="lockinPeriodFrequencyType.id">
|
||||||
track preClosurePenalInterestOnType
|
{{ lockinPeriodFrequencyType.value | translateKey: 'catalogs' }}
|
||||||
) {
|
</mat-option>
|
||||||
<mat-option [value]="preClosurePenalInterestOnType.id">
|
}
|
||||||
{{ preClosurePenalInterestOnType.value }}
|
</mat-select>
|
||||||
</mat-option>
|
</mat-form-field>
|
||||||
}
|
</div>
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fdps-section">
|
|
||||||
<div class="fdps-section__header">
|
|
||||||
<i class="fas fa-file-invoice-dollar"></i>
|
|
||||||
<h3>{{ 'labels.inputs.Is Withhold Tax Applicable' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdps-grid">
|
|
||||||
<div class="fdps-field fdps-field--full fdps-checkbox-row">
|
|
||||||
<mat-checkbox
|
|
||||||
labelPosition="before"
|
|
||||||
formControlName="withHoldTax"
|
|
||||||
matTooltip="{{ 'tooltips.An boolean flag to attach' | translate }}"
|
|
||||||
>
|
|
||||||
{{ 'labels.inputs.Is Withhold Tax Applicable' | translate }}
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (fixedDepositProductSettingsForm.value.withHoldTax) {
|
|
||||||
<mat-form-field appearance="outline" class="fdps-field">
|
|
||||||
<mat-label>{{ 'labels.inputs.Tax Group' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="taxGroupId" required>
|
|
||||||
@for (taxGroup of taxGroupData; track taxGroup) {
|
|
||||||
<mat-option [value]="taxGroup.id">
|
|
||||||
{{ taxGroup.name }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
<mat-error>
|
|
||||||
{{ 'labels.inputs.Tax Group' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Maturity Instructions -->
|
||||||
|
<div class="fdps-section">
|
||||||
|
<div class="fdps-section__header">
|
||||||
|
<span class="fdps-section__badge"><i class="fas fa-lock"></i></span>
|
||||||
|
<span>Maturity Instructions</span>
|
||||||
|
</div>
|
||||||
|
<div class="fdps-body fdps-body--list">
|
||||||
|
<div class="fdps-list-row">
|
||||||
|
<span>Maturity Instructions</span>
|
||||||
|
<mat-checkbox formControlName="maturityInstructions"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="fdps-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="fdps-list-row">
|
||||||
|
<span>Penalty / Grace Period for Missed Installments</span>
|
||||||
|
<mat-checkbox formControlName="penaltyGracePeriodMissedInstallments"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="fdps-list-row">
|
||||||
|
<span>Panel Request Applicable</span>
|
||||||
|
<mat-checkbox formControlName="panelRequestApplicable"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="fdps-list-row">
|
||||||
|
<span>No penalty for nominee / legal heir closure</span>
|
||||||
|
<mat-checkbox formControlName="noPenaltyNomineeHeirClosure"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="fdps-list-row">
|
||||||
|
<span>Deceased Settlement</span>
|
||||||
|
<mat-checkbox formControlName="deceasedSettlement"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="fdps-list-row">
|
||||||
|
<span>Allow Debit Txn after due date</span>
|
||||||
|
<mat-checkbox formControlName="allowDebitTxnAfterDueDate"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Additional Rules -->
|
||||||
|
<div class="fdps-section">
|
||||||
|
<div class="fdps-section__header">
|
||||||
|
<span class="fdps-section__badge"><i class="fas fa-cog"></i></span>
|
||||||
|
<span>Additional Rules</span>
|
||||||
|
</div>
|
||||||
|
<div class="fdps-body">
|
||||||
|
<div class="fdps-layout fdps-layout--additional">
|
||||||
|
<div class="fdps-toggle fdps-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="fdps-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>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+112
-16
@@ -6,8 +6,14 @@
|
|||||||
* 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;
|
||||||
|
|
||||||
.fdps-section {
|
.fdps-section {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid $border;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -21,32 +27,41 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 12px 24px;
|
padding: 12px 24px;
|
||||||
background: #f8f9fa;
|
background: $gray-50;
|
||||||
border-bottom: 1px solid #e8eaed;
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #0ea5e9;
|
color: $accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
span:not(.fdps-section__badge) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #202124;
|
color: $gray-900;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdps-grid {
|
.fdps-section__badge {
|
||||||
display: grid;
|
display: inline-flex;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
align-items: center;
|
||||||
gap: 4px 16px;
|
justify-content: center;
|
||||||
padding: 16px 24px;
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (width <= 600px) {
|
.fdps-body {
|
||||||
grid-template-columns: 1fr;
|
background: #fff;
|
||||||
}
|
padding: 18px 24px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdps-body--list {
|
||||||
|
padding: 0 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdps-field {
|
.fdps-field {
|
||||||
@@ -55,10 +70,91 @@
|
|||||||
&--full {
|
&--full {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--compact {
|
||||||
|
width: 200px;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fdps-checkbox-row {
|
.fdps-premature-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px 0;
|
gap: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdps-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdps-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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdps-lockin-fields {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
@media (width <= 640px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdps-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdps-layout--additional {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
|
||||||
|
@media (width <= 1100px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdps-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 40px;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--inline {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-89
@@ -7,13 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnInit, Input, inject } from '@angular/core';
|
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||||
import {
|
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
UntypedFormGroup,
|
|
||||||
UntypedFormBuilder,
|
|
||||||
UntypedFormControl,
|
|
||||||
Validators,
|
|
||||||
ReactiveFormsModule
|
|
||||||
} from '@angular/forms';
|
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
@@ -36,113 +30,71 @@ export class FixedDepositProductSettingsStepComponent implements OnInit {
|
|||||||
fixedDepositProductSettingsForm: UntypedFormGroup;
|
fixedDepositProductSettingsForm: UntypedFormGroup;
|
||||||
|
|
||||||
lockinPeriodFrequencyTypeData: any;
|
lockinPeriodFrequencyTypeData: any;
|
||||||
periodFrequencyTypeData: any;
|
|
||||||
preClosurePenalInterestOnTypeData: any;
|
|
||||||
taxGroupData: any;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createFixedDepositProductSettingsForm();
|
this.createFixedDepositProductSettingsForm();
|
||||||
this.setConditionalControls();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.lockinPeriodFrequencyTypeData = this.fixedDepositProductsTemplate.lockinPeriodFrequencyTypeOptions;
|
this.lockinPeriodFrequencyTypeData = this.fixedDepositProductsTemplate.lockinPeriodFrequencyTypeOptions;
|
||||||
this.periodFrequencyTypeData = this.fixedDepositProductsTemplate.periodFrequencyTypeOptions.slice(0, -1);
|
|
||||||
this.preClosurePenalInterestOnTypeData = this.fixedDepositProductsTemplate.preClosurePenalInterestOnTypeOptions;
|
|
||||||
this.taxGroupData = this.fixedDepositProductsTemplate.taxGroupOptions;
|
|
||||||
|
|
||||||
if (!(this.fixedDepositProductsTemplate === undefined) && this.fixedDepositProductsTemplate.id) {
|
if (this.fixedDepositProductsTemplate?.id) {
|
||||||
this.fixedDepositProductSettingsForm.patchValue({
|
this.fixedDepositProductSettingsForm.patchValue({
|
||||||
isMandatoryDeposit: this.fixedDepositProductsTemplate.isMandatoryDeposit,
|
loanFacility: this.fixedDepositProductsTemplate.loanFacility ?? false,
|
||||||
adjustAdvanceTowardsFuturePayments: this.fixedDepositProductsTemplate.adjustAdvanceTowardsFuturePayments,
|
prematureWithdrawalOption: this.fixedDepositProductsTemplate.prematureWithdrawalOption ?? false,
|
||||||
allowWithdrawal: this.fixedDepositProductsTemplate.allowWithdrawal,
|
tenureFlexibility: this.fixedDepositProductsTemplate.tenureFlexibility ?? false,
|
||||||
lockinPeriodFrequency: this.fixedDepositProductsTemplate.lockinPeriodFrequency,
|
lienMarked: this.fixedDepositProductsTemplate.lienMarked ?? false,
|
||||||
lockinPeriodFrequencyType: this.fixedDepositProductsTemplate.lockinPeriodFrequencyType
|
withdrawalFeeForTransfers: this.fixedDepositProductsTemplate.withdrawalFeeForTransfers ?? false,
|
||||||
? this.fixedDepositProductsTemplate.lockinPeriodFrequencyType.id
|
enableLockinPeriod: this.fixedDepositProductsTemplate.enableLockinPeriod ?? false,
|
||||||
: '',
|
lockinPeriodFrequency: this.fixedDepositProductsTemplate.lockinPeriodFrequency ?? '',
|
||||||
minDepositTerm: this.fixedDepositProductsTemplate.minDepositTerm,
|
lockinPeriodFrequencyType: this.fixedDepositProductsTemplate.lockinPeriodFrequencyType?.id ?? '',
|
||||||
minDepositTermTypeId: this.fixedDepositProductsTemplate.minDepositTermType
|
maturityInstructions: this.fixedDepositProductsTemplate.maturityInstructions ?? false,
|
||||||
? this.fixedDepositProductsTemplate.minDepositTermType.id
|
creditMaturityProceeds: this.fixedDepositProductsTemplate.creditMaturityProceeds ?? false,
|
||||||
: '',
|
penaltyGracePeriodMissedInstallments:
|
||||||
inMultiplesOfDepositTerm: this.fixedDepositProductsTemplate.inMultiplesOfDepositTerm,
|
this.fixedDepositProductsTemplate.penaltyGracePeriodMissedInstallments ?? false,
|
||||||
inMultiplesOfDepositTermTypeId: this.fixedDepositProductsTemplate.inMultiplesOfDepositTermType
|
panelRequestApplicable: this.fixedDepositProductsTemplate.panelRequestApplicable ?? false,
|
||||||
? this.fixedDepositProductsTemplate.inMultiplesOfDepositTerm.id
|
noPenaltyNomineeHeirClosure: this.fixedDepositProductsTemplate.noPenaltyNomineeHeirClosure ?? false,
|
||||||
: '',
|
deceasedSettlement: this.fixedDepositProductsTemplate.deceasedSettlement ?? false,
|
||||||
maxDepositTerm: this.fixedDepositProductsTemplate.maxDepositTerm,
|
allowDebitTxnAfterDueDate: this.fixedDepositProductsTemplate.allowDebitTxnAfterDueDate ?? false,
|
||||||
maxDepositTermTypeId: this.fixedDepositProductsTemplate.maxDepositTermType
|
accountClosedZeroBalanceInDayClosing:
|
||||||
? this.fixedDepositProductsTemplate.minDepositTermType.id
|
this.fixedDepositProductsTemplate.accountClosedZeroBalanceInDayClosing ?? false,
|
||||||
: '',
|
minBalanceForInterestCalculation: this.fixedDepositProductsTemplate.minBalanceForInterestCalculation ?? ''
|
||||||
preClosurePenalApplicable: this.fixedDepositProductsTemplate.preClosurePenalApplicable,
|
|
||||||
preClosurePenalInterest: this.fixedDepositProductsTemplate.preClosurePenalInterest,
|
|
||||||
preClosurePenalInterestOnTypeId: this.fixedDepositProductsTemplate.preClosurePenalInterestOnType
|
|
||||||
? this.fixedDepositProductsTemplate.preClosurePenalInterestOnType.id
|
|
||||||
: '',
|
|
||||||
withHoldTax: this.fixedDepositProductsTemplate.withHoldTax
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.fixedDepositProductsTemplate.withHoldTax) {
|
|
||||||
this.fixedDepositProductSettingsForm.patchValue({
|
|
||||||
taxGroupId: this.fixedDepositProductsTemplate.taxGroup ? this.fixedDepositProductsTemplate.taxGroup.id : ''
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createFixedDepositProductSettingsForm() {
|
createFixedDepositProductSettingsForm() {
|
||||||
this.fixedDepositProductSettingsForm = this.formBuilder.group({
|
this.fixedDepositProductSettingsForm = this.formBuilder.group({
|
||||||
|
loanFacility: [false],
|
||||||
|
prematureWithdrawalOption: [false],
|
||||||
|
tenureFlexibility: [false],
|
||||||
|
lienMarked: [false],
|
||||||
|
withdrawalFeeForTransfers: [false],
|
||||||
|
enableLockinPeriod: [false],
|
||||||
lockinPeriodFrequency: [
|
lockinPeriodFrequency: [
|
||||||
'',
|
'',
|
||||||
Validators.min(0)
|
Validators.min(0)
|
||||||
],
|
],
|
||||||
lockinPeriodFrequencyType: [''],
|
lockinPeriodFrequencyType: [''],
|
||||||
minDepositTerm: [
|
maturityInstructions: [false],
|
||||||
'',
|
creditMaturityProceeds: [false],
|
||||||
[
|
penaltyGracePeriodMissedInstallments: [false],
|
||||||
Validators.required,
|
panelRequestApplicable: [false],
|
||||||
Validators.min(0)
|
noPenaltyNomineeHeirClosure: [false],
|
||||||
]
|
deceasedSettlement: [false],
|
||||||
],
|
allowDebitTxnAfterDueDate: [false],
|
||||||
minDepositTermTypeId: [
|
accountClosedZeroBalanceInDayClosing: [false],
|
||||||
'',
|
minBalanceForInterestCalculation: ['']
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
inMultiplesOfDepositTerm: [
|
|
||||||
'',
|
|
||||||
Validators.min(0)
|
|
||||||
],
|
|
||||||
inMultiplesOfDepositTermTypeId: [''],
|
|
||||||
maxDepositTerm: [
|
|
||||||
'',
|
|
||||||
Validators.min(0)
|
|
||||||
],
|
|
||||||
maxDepositTermTypeId: [''],
|
|
||||||
preClosurePenalApplicable: [false],
|
|
||||||
preClosurePenalInterest: [
|
|
||||||
'',
|
|
||||||
Validators.min(0)
|
|
||||||
],
|
|
||||||
preClosurePenalInterestOnTypeId: [''],
|
|
||||||
withHoldTax: [false]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setConditionalControls() {
|
|
||||||
this.fixedDepositProductSettingsForm.get('withHoldTax').valueChanges.subscribe((withHoldTax: any) => {
|
|
||||||
if (withHoldTax) {
|
|
||||||
this.fixedDepositProductSettingsForm.addControl('taxGroupId', new UntypedFormControl('', Validators.required));
|
|
||||||
} else {
|
|
||||||
this.fixedDepositProductSettingsForm.removeControl('taxGroupId');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get fixedDepositProductSettings() {
|
get fixedDepositProductSettings() {
|
||||||
const fixedDepositProductSettings = this.fixedDepositProductSettingsForm.value;
|
const settings = { ...this.fixedDepositProductSettingsForm.value };
|
||||||
for (const key in fixedDepositProductSettings) {
|
for (const key in settings) {
|
||||||
if (fixedDepositProductSettings[key] === '') {
|
if (settings[key] === '') {
|
||||||
delete fixedDepositProductSettings[key];
|
delete settings[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fixedDepositProductSettings;
|
return settings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+134
-125
@@ -7,139 +7,148 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="fixedDepositProductTermsForm">
|
<form [formGroup]="fixedDepositProductTermsForm">
|
||||||
|
<!-- Interest Rate & Posting Terms -->
|
||||||
<div class="fdpt-section">
|
<div class="fdpt-section">
|
||||||
<div class="fdpt-section__header">
|
<div class="fdpt-section__header">
|
||||||
<i class="fas fa-wallet"></i>
|
<span class="fdpt-section__badge"><i class="fas fa-chart-line"></i></span>
|
||||||
<h3>{{ 'labels.inputs.Deposit Amount' | translate }}</h3>
|
<span>Interest Rate & Posting Terms</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="fdpt-grid">
|
<div class="fdpt-body">
|
||||||
<mat-form-field appearance="outline" class="fdpt-field">
|
<div class="fdpt-banner">
|
||||||
<mat-label>{{ 'labels.inputs.Minimum' | translate }}</mat-label>
|
<span class="fdpt-banner__icon">ℹ️</span>
|
||||||
<input
|
<span>
|
||||||
type="number"
|
Fixed Deposit base rate:
|
||||||
min="0"
|
<b>{{ fixedDepositProductTermsForm.get('nominalAnnualInterestRate').value }}%</b>
|
||||||
matInput
|
</span>
|
||||||
formControlName="minDepositAmount"
|
</div>
|
||||||
matTooltip="{{ 'tooltips.The minimum deposit amount required to open a fixed deposit' | translate }}"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdpt-field">
|
<div class="fdpt-grid fdpt-grid--three">
|
||||||
<mat-label>{{ 'labels.inputs.Default' | translate }}</mat-label>
|
<mat-form-field appearance="outline" class="fdpt-field">
|
||||||
<input
|
<mat-label>{{ 'labels.inputs.Nominal Annual Interest' | translate }}</mat-label>
|
||||||
type="number"
|
<input
|
||||||
min="0"
|
type="number"
|
||||||
matInput
|
matInput
|
||||||
formControlName="depositAmount"
|
matTooltip="{{ 'tooltips.The default interest rate set' | translate }}"
|
||||||
matTooltip="{{ 'tooltips.The default deposit amount expected' | translate }}"
|
formControlName="nominalAnnualInterestRate"
|
||||||
required
|
required
|
||||||
/>
|
min="0"
|
||||||
<mat-error>
|
step="0.01"
|
||||||
{{ 'labels.inputs.Default' | translate }} {{ 'labels.inputs.Deposit Amount' | translate }}
|
/>
|
||||||
{{ 'labels.commons.is' | translate }} <strong>{{ 'labels.commons.required' | translate }}</strong>
|
@if (fixedDepositProductTermsForm.get('nominalAnnualInterestRate').hasError('required')) {
|
||||||
</mat-error>
|
<mat-error>
|
||||||
</mat-form-field>
|
{{ 'labels.inputs.Nominal Annual Interest' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
<mat-form-field appearance="outline" class="fdpt-field">
|
</mat-error>
|
||||||
<mat-label>{{ 'labels.inputs.Maximum' | translate }}</mat-label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
matInput
|
|
||||||
formControlName="maxDepositAmount"
|
|
||||||
matTooltip="{{ 'tooltips.The maximum deposit amount allowed when a fixed deposit' | translate }}"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="fdpt-section">
|
|
||||||
<div class="fdpt-section__header">
|
|
||||||
<i class="fas fa-percentage"></i>
|
|
||||||
<h3>{{ 'labels.inputs.Interest Compounding Period' | translate }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="fdpt-grid">
|
|
||||||
<mat-form-field appearance="outline" class="fdpt-field">
|
|
||||||
<mat-label>{{ 'labels.inputs.Interest Compounding Period' | translate }}</mat-label>
|
|
||||||
<mat-select
|
|
||||||
formControlName="interestCompoundingPeriodType"
|
|
||||||
matTooltip="{{ 'tooltips.The period at which interest rate' | translate }}"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
@for (
|
|
||||||
interestCompoundingPeriodType of interestCompoundingPeriodTypeData;
|
|
||||||
track interestCompoundingPeriodType
|
|
||||||
) {
|
|
||||||
<mat-option [value]="interestCompoundingPeriodType.id">
|
|
||||||
{{ interestCompoundingPeriodType.value }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
}
|
||||||
</mat-select>
|
@if (fixedDepositProductTermsForm.get('nominalAnnualInterestRate').hasError('min')) {
|
||||||
<mat-error>
|
<mat-error>{{ 'labels.inputs.Nominal Annual Interest' | translate }} must be zero or greater</mat-error>
|
||||||
{{ 'labels.inputs.Interest Compounding Period' | translate }} {{ 'labels.commons.is' | translate }}
|
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="fdpt-field">
|
|
||||||
<mat-label>{{ 'labels.inputs.Interest Posting Period' | translate }}</mat-label>
|
|
||||||
<mat-select
|
|
||||||
formControlName="interestPostingPeriodType"
|
|
||||||
matTooltip="{{ 'tooltips.The period at which interest rate is posted or credited' | translate }}"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
@for (interestPostingPeriodType of interestPostingPeriodTypeData; track interestPostingPeriodType) {
|
|
||||||
<mat-option [value]="interestPostingPeriodType.id">
|
|
||||||
{{ interestPostingPeriodType.value }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
}
|
||||||
</mat-select>
|
</mat-form-field>
|
||||||
<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 appearance="outline" class="fdpt-field">
|
<mat-form-field appearance="outline" class="fdpt-field">
|
||||||
<mat-label>{{ 'labels.inputs.Interest Calculated using' | translate }}</mat-label>
|
<mat-label>Effective Date</mat-label>
|
||||||
<mat-select
|
<input matInput [matDatepicker]="effectiveDatePicker" formControlName="effectiveDate" />
|
||||||
formControlName="interestCalculationType"
|
<mat-datepicker-toggle matSuffix [for]="effectiveDatePicker"></mat-datepicker-toggle>
|
||||||
matTooltip="{{ 'tooltips.The method used to calculate interest' | translate }}"
|
<mat-datepicker #effectiveDatePicker></mat-datepicker>
|
||||||
required
|
</mat-form-field>
|
||||||
>
|
|
||||||
@for (interestCalculationType of interestCalculationTypeData; track interestCalculationType) {
|
|
||||||
<mat-option [value]="interestCalculationType.id">
|
|
||||||
{{ interestCalculationType.value }}
|
|
||||||
</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="fdpt-field">
|
<mat-form-field appearance="outline" class="fdpt-field">
|
||||||
<mat-label>{{ 'labels.inputs.Days in Year' | translate }}</mat-label>
|
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
||||||
<mat-select
|
<mat-select
|
||||||
formControlName="interestCalculationDaysInYearType"
|
formControlName="interestCalculateSelector"
|
||||||
matTooltip="{{ 'tooltips.The setting for number of days' | translate }}"
|
matTooltip="{{ 'tooltips.The method used to calculate interest' | translate }}"
|
||||||
required
|
>
|
||||||
>
|
@for (opt of interestCalculationTypeOptionData; track opt.id) {
|
||||||
@for (
|
<mat-option [value]="opt.id">{{ opt.value }}</mat-option>
|
||||||
interestCalculationDaysInYearType of interestCalculationDaysInYearTypeData;
|
}
|
||||||
track interestCalculationDaysInYearType
|
</mat-select>
|
||||||
) {
|
</mat-form-field>
|
||||||
<mat-option [value]="interestCalculationDaysInYearType.id">
|
</div>
|
||||||
{{ interestCalculationDaysInYearType.value }}
|
|
||||||
</mat-option>
|
<div class="fdpt-grid fdpt-grid--three">
|
||||||
}
|
<mat-form-field appearance="outline" class="fdpt-field">
|
||||||
</mat-select>
|
<mat-label>Interest Calculate</mat-label>
|
||||||
<mat-error>
|
<mat-select formControlName="interestCalculationType" required>
|
||||||
{{ 'labels.inputs.Days in Year' | translate }} {{ 'labels.commons.is' | translate }}
|
@for (mode of interestCalculationTypeData; track mode.id) {
|
||||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
<mat-option [value]="mode.id">{{ mode.value | translateKey: 'catalogs' }}</mat-option>
|
||||||
</mat-error>
|
}
|
||||||
</mat-form-field>
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
{{ 'labels.inputs.Interest Calculation Type' | translate }} {{ 'labels.commons.is' | translate }}
|
||||||
|
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
@if (fixedDepositProductTermsForm.value.interestCalculationType === 2) {
|
||||||
|
<mat-form-field appearance="outline" class="fdpt-field">
|
||||||
|
<mat-label>Average Period</mat-label>
|
||||||
|
<mat-select formControlName="averagePeriod">
|
||||||
|
@for (period of periodFrequencyData; track period.id) {
|
||||||
|
<mat-option [value]="period.value">{{ period.value }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
}
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpt-field">
|
||||||
|
<mat-label>{{ 'labels.inputs.Interest Posting Period' | translate }}</mat-label>
|
||||||
|
<mat-select
|
||||||
|
formControlName="interestPostingPeriodType"
|
||||||
|
matTooltip="{{ 'tooltips.Interest rate is posted or credited to a saving account' | translate }}"
|
||||||
|
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>
|
||||||
|
|
||||||
|
<div class="fdpt-footer">
|
||||||
|
<div class="fdpt-footer__grid">
|
||||||
|
<div class="fdpt-toggle-card">
|
||||||
|
<div>
|
||||||
|
<h4>Auto Interest Post</h4>
|
||||||
|
</div>
|
||||||
|
<div class="fdpt-toggle-card__choices">
|
||||||
|
@for (option of yesNoOptions; track option.value) {
|
||||||
|
<label class="inline-flex items-center cursor-pointer">
|
||||||
|
<input type="radio" class="w-4 h-4" formControlName="autoPost" [value]="option.value" />
|
||||||
|
<span class="ml-2 text-sm">{{ option.label }}</span>
|
||||||
|
</label>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpt-field">
|
||||||
|
<mat-label>Frequency (Fix Day)</mat-label>
|
||||||
|
<input type="number" matInput formControlName="frequencyFixDay" />
|
||||||
|
<mat-hint>Days</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="fdpt-field">
|
||||||
|
<mat-label>Last Interest Date</mat-label>
|
||||||
|
<input matInput [matDatepicker]="lastInterestDatePicker" formControlName="lastInterestDate" />
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="lastInterestDatePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #lastInterestDatePicker></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (fixedDepositProductTermsForm.value.interestCalculationType === 1) {
|
||||||
|
<div class="fdpt-note">
|
||||||
|
<span>⚠️</span>
|
||||||
|
<span
|
||||||
|
>Daily product-based interest accrual enabled - interest calculated EOD on daily closing balance.</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+99
-6
@@ -6,8 +6,14 @@
|
|||||||
* 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;
|
||||||
|
|
||||||
.fdpt-section {
|
.fdpt-section {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid $border;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -21,31 +27,54 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 12px 24px;
|
padding: 12px 24px;
|
||||||
background: #f8f9fa;
|
background: $gray-50;
|
||||||
border-bottom: 1px solid #e8eaed;
|
border-bottom: 1px solid $gray-200;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #0ea5e9;
|
color: $accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
span:not(.fdpt-section__badge) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #202124;
|
color: $gray-900;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fdpt-section__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpt-body {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.fdpt-grid {
|
.fdpt-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: 4px 16px;
|
gap: 4px 16px;
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
|
|
||||||
|
&--three {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
@media (width <= 600px) {
|
@media (width <= 600px) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|
||||||
|
&--three {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,3 +85,67 @@
|
|||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fdpt-footer {
|
||||||
|
padding: 0 24px 24px;
|
||||||
|
border-top: 1px solid #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpt-footer__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
padding-top: 18px;
|
||||||
|
|
||||||
|
@media (width <= 900px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpt-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpt-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fdpt-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;
|
||||||
|
}
|
||||||
|
|||||||
+76
-12
@@ -7,8 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnInit, Input, inject } from '@angular/core';
|
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
|
import { Dates } from 'app/core/utils/dates';
|
||||||
|
import { SettingsService } from 'app/settings/settings.service';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -22,6 +24,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
})
|
})
|
||||||
export class FixedDepositProductTermsStepComponent implements OnInit {
|
export class FixedDepositProductTermsStepComponent implements OnInit {
|
||||||
private formBuilder = inject(UntypedFormBuilder);
|
private formBuilder = inject(UntypedFormBuilder);
|
||||||
|
private dateUtils = inject(Dates);
|
||||||
|
private settingsService = inject(SettingsService);
|
||||||
|
|
||||||
@Input() fixedDepositProductsTemplate: any;
|
@Input() fixedDepositProductsTemplate: any;
|
||||||
|
|
||||||
@@ -30,7 +34,13 @@ export class FixedDepositProductTermsStepComponent implements OnInit {
|
|||||||
interestCompoundingPeriodTypeData: any;
|
interestCompoundingPeriodTypeData: any;
|
||||||
interestPostingPeriodTypeData: any;
|
interestPostingPeriodTypeData: any;
|
||||||
interestCalculationTypeData: any;
|
interestCalculationTypeData: any;
|
||||||
|
interestCalculationTypeOptionData: any;
|
||||||
interestCalculationDaysInYearTypeData: any;
|
interestCalculationDaysInYearTypeData: any;
|
||||||
|
periodFrequencyData: any[] = [];
|
||||||
|
yesNoOptions: Array<{ value: boolean; label: string }> = [
|
||||||
|
{ value: true, label: 'Yes' },
|
||||||
|
{ value: false, label: 'No' }
|
||||||
|
];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createFixedDepositProductTermsForm();
|
this.createFixedDepositProductTermsForm();
|
||||||
@@ -40,10 +50,22 @@ export class FixedDepositProductTermsStepComponent implements OnInit {
|
|||||||
this.interestCompoundingPeriodTypeData = this.fixedDepositProductsTemplate.interestCompoundingPeriodTypeOptions;
|
this.interestCompoundingPeriodTypeData = this.fixedDepositProductsTemplate.interestCompoundingPeriodTypeOptions;
|
||||||
this.interestPostingPeriodTypeData = this.fixedDepositProductsTemplate.interestPostingPeriodTypeOptions;
|
this.interestPostingPeriodTypeData = this.fixedDepositProductsTemplate.interestPostingPeriodTypeOptions;
|
||||||
this.interestCalculationTypeData = this.fixedDepositProductsTemplate.interestCalculationTypeOptions;
|
this.interestCalculationTypeData = this.fixedDepositProductsTemplate.interestCalculationTypeOptions;
|
||||||
|
this.interestCalculationTypeOptionData = this.fixedDepositProductsTemplate.interestCalculationTypeOption || [];
|
||||||
this.interestCalculationDaysInYearTypeData =
|
this.interestCalculationDaysInYearTypeData =
|
||||||
this.fixedDepositProductsTemplate.interestCalculationDaysInYearTypeOptions;
|
this.fixedDepositProductsTemplate.interestCalculationDaysInYearTypeOptions;
|
||||||
|
this.periodFrequencyData = this.fixedDepositProductsTemplate.periodFrequencyOptions || [];
|
||||||
|
|
||||||
if (!(this.fixedDepositProductsTemplate === undefined) && this.fixedDepositProductsTemplate.id) {
|
const isEditing = !!this.fixedDepositProductsTemplate.id;
|
||||||
|
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
||||||
|
const savedEnumTypeId =
|
||||||
|
this.fixedDepositProductsTemplate.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1;
|
||||||
|
const savedSelectorId = isEditing
|
||||||
|
? (this.fixedDepositProductsTemplate.interestCalculateSelector ??
|
||||||
|
this.interestCalculationTypeOptionData?.[0]?.id ??
|
||||||
|
null)
|
||||||
|
: (this.interestCalculationTypeOptionData?.[0]?.id ?? null);
|
||||||
|
|
||||||
|
if (isEditing) {
|
||||||
this.fixedDepositProductTermsForm.patchValue({
|
this.fixedDepositProductTermsForm.patchValue({
|
||||||
minDepositAmount: this.fixedDepositProductsTemplate.minDepositAmount,
|
minDepositAmount: this.fixedDepositProductsTemplate.minDepositAmount,
|
||||||
depositAmount: this.fixedDepositProductsTemplate.depositAmount,
|
depositAmount: this.fixedDepositProductsTemplate.depositAmount,
|
||||||
@@ -52,13 +74,31 @@ export class FixedDepositProductTermsStepComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.fixedDepositProductTermsForm.patchValue({
|
this.fixedDepositProductTermsForm.patchValue({
|
||||||
interestCompoundingPeriodType: this.fixedDepositProductsTemplate.interestCompoundingPeriodType.id,
|
nominalAnnualInterestRate: this.fixedDepositProductsTemplate.nominalAnnualInterestRate ?? 0,
|
||||||
interestPostingPeriodType: this.fixedDepositProductsTemplate.interestPostingPeriodType.id,
|
interestCompoundingPeriodType: this.fixedDepositProductsTemplate.interestCompoundingPeriodType?.id,
|
||||||
interestCalculationType: this.fixedDepositProductsTemplate.interestCalculationType.id,
|
interestPostingPeriodType: this.fixedDepositProductsTemplate.interestPostingPeriodType?.id,
|
||||||
interestCalculationDaysInYearType: this.fixedDepositProductsTemplate.interestCalculationDaysInYearType.id
|
interestCalculationType: savedEnumTypeId,
|
||||||
|
interestCalculationDaysInYearType: this.fixedDepositProductsTemplate.interestCalculationDaysInYearType?.id,
|
||||||
|
effectiveDate: this.fixedDepositProductsTemplate.effectiveDate
|
||||||
|
? this.parseDate(this.fixedDepositProductsTemplate.effectiveDate)
|
||||||
|
: '',
|
||||||
|
interestCalculateSelector: savedSelectorId,
|
||||||
|
averagePeriod: isEditing ? this.fixedDepositProductsTemplate.averagePeriod || defaultPeriod : defaultPeriod,
|
||||||
|
autoPost: isEditing ? (this.fixedDepositProductsTemplate.autoPost ?? true) : true,
|
||||||
|
frequencyFixDay: isEditing ? (this.fixedDepositProductsTemplate.frequencyFixDay ?? 30) : 30,
|
||||||
|
lastInterestDate: this.fixedDepositProductsTemplate.lastInterestDate
|
||||||
|
? this.parseDate(this.fixedDepositProductsTemplate.lastInterestDate)
|
||||||
|
: ''
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseDate(dateValue: any): Date | null {
|
||||||
|
if (!dateValue) return null;
|
||||||
|
if (Array.isArray(dateValue)) return new Date(dateValue[0], dateValue[1] - 1, dateValue[2]);
|
||||||
|
if (dateValue instanceof Date) return dateValue;
|
||||||
|
return new Date(dateValue);
|
||||||
|
}
|
||||||
|
|
||||||
createFixedDepositProductTermsForm() {
|
createFixedDepositProductTermsForm() {
|
||||||
this.fixedDepositProductTermsForm = this.formBuilder.group({
|
this.fixedDepositProductTermsForm = this.formBuilder.group({
|
||||||
minDepositAmount: [
|
minDepositAmount: [
|
||||||
@@ -76,6 +116,13 @@ export class FixedDepositProductTermsStepComponent implements OnInit {
|
|||||||
'',
|
'',
|
||||||
Validators.min(0)
|
Validators.min(0)
|
||||||
],
|
],
|
||||||
|
nominalAnnualInterestRate: [
|
||||||
|
'',
|
||||||
|
[
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(0)
|
||||||
|
]
|
||||||
|
],
|
||||||
interestCompoundingPeriodType: [
|
interestCompoundingPeriodType: [
|
||||||
'',
|
'',
|
||||||
Validators.required
|
Validators.required
|
||||||
@@ -91,17 +138,34 @@ export class FixedDepositProductTermsStepComponent implements OnInit {
|
|||||||
interestCalculationDaysInYearType: [
|
interestCalculationDaysInYearType: [
|
||||||
'',
|
'',
|
||||||
Validators.required
|
Validators.required
|
||||||
]
|
],
|
||||||
|
effectiveDate: [''],
|
||||||
|
interestCalculateSelector: [1],
|
||||||
|
averagePeriod: ['Monthly'],
|
||||||
|
autoPost: [true],
|
||||||
|
frequencyFixDay: [
|
||||||
|
30,
|
||||||
|
[Validators.min(0)]
|
||||||
|
],
|
||||||
|
lastInterestDate: ['']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get fixedDepositProductTerms() {
|
get fixedDepositProductTerms() {
|
||||||
const fixedDepositProductTerms = this.fixedDepositProductTermsForm.value;
|
const formValue = { ...this.fixedDepositProductTermsForm.value };
|
||||||
for (const key in fixedDepositProductTerms) {
|
const dateFormat = this.settingsService.dateFormat;
|
||||||
if (fixedDepositProductTerms[key] === '') {
|
|
||||||
delete fixedDepositProductTerms[key];
|
if (formValue.effectiveDate instanceof Date) {
|
||||||
|
formValue.effectiveDate = this.dateUtils.formatDate(formValue.effectiveDate, dateFormat) || '';
|
||||||
|
}
|
||||||
|
if (formValue.lastInterestDate instanceof Date) {
|
||||||
|
formValue.lastInterestDate = this.dateUtils.formatDate(formValue.lastInterestDate, dateFormat) || '';
|
||||||
|
}
|
||||||
|
for (const key in formValue) {
|
||||||
|
if (formValue[key] === '') {
|
||||||
|
delete formValue[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fixedDepositProductTerms;
|
return formValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -9,8 +9,8 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
export default {
|
export default {
|
||||||
'mifos_x': {
|
'mifos_x': {
|
||||||
'version': '260429',
|
'version': '260430',
|
||||||
'hash': '0d3d8379'
|
'hash': '2d4fb423'
|
||||||
},
|
},
|
||||||
'allow_switching_backend_instance': true
|
'allow_switching_backend_instance': true
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,7 +31,8 @@
|
|||||||
mifosx-loans-view,
|
mifosx-loans-view,
|
||||||
mifosx-shares-account-view,
|
mifosx-shares-account-view,
|
||||||
mifosx-recurring-deposits-account-view,
|
mifosx-recurring-deposits-account-view,
|
||||||
mifosx-savings-account-view {
|
mifosx-savings-account-view,
|
||||||
|
mifosx-current-account-account-view {
|
||||||
.account-card {
|
.account-card {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 90rem;
|
max-width: 90rem;
|
||||||
@@ -98,7 +99,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
.savings-account-tables {
|
.savings-account-tables,
|
||||||
|
.current-account-tables {
|
||||||
padding: 1%;
|
padding: 1%;
|
||||||
margin: 1%;
|
margin: 1%;
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@
|
|||||||
mifosx-loans-view,
|
mifosx-loans-view,
|
||||||
mifosx-shares-account-view,
|
mifosx-shares-account-view,
|
||||||
mifosx-recurring-deposits-account-view,
|
mifosx-recurring-deposits-account-view,
|
||||||
mifosx-savings-account-view {
|
mifosx-savings-account-view,
|
||||||
|
mifosx-current-account-account-view {
|
||||||
.account-card {
|
.account-card {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 90rem;
|
max-width: 90rem;
|
||||||
@@ -137,7 +138,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
.savings-account-tables {
|
.savings-account-tables,
|
||||||
|
.current-account-tables {
|
||||||
padding: 1%;
|
padding: 1%;
|
||||||
margin: 1%;
|
margin: 1%;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user