diff --git a/.prettierrc b/.prettierrc
index 754ccd867..fb9acc4a1 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -4,5 +4,6 @@
"prettier-plugin-multiline-arrays"
],
"singleQuote": true,
- "trailingComma": "none"
+ "trailingComma": "none",
+ "endOfLine": "auto"
}
diff --git a/src/app/clients/clients-view/general-tab/general-tab.component.html b/src/app/clients/clients-view/general-tab/general-tab.component.html
index 0da32a41a..2123fb7ae 100644
--- a/src/app/clients/clients-view/general-tab/general-tab.component.html
+++ b/src/app/clients/clients-view/general-tab/general-tab.component.html
@@ -65,7 +65,7 @@
diff --git a/src/app/current-accounts/current-account-stepper/current-account-details-step/current-account-details-step.component.ts b/src/app/current-accounts/current-account-stepper/current-account-details-step/current-account-details-step.component.ts
index cc44792be..5c031ba97 100644
--- a/src/app/current-accounts/current-account-stepper/current-account-details-step/current-account-details-step.component.ts
+++ b/src/app/current-accounts/current-account-stepper/current-account-details-step/current-account-details-step.component.ts
@@ -22,6 +22,7 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { IntroducerSearchComponent } from 'app/shared/introducer-search/introducer-search.component';
import { AddRelatedClientDialogComponent } from './add-related-client-dialog/add-related-client-dialog.component';
+import { AlertService } from 'app/core/alert/alert.service';
/**
* Current Account Details Step
@@ -46,6 +47,7 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
private settingsService = inject(SettingsService);
private clientsService = inject(ClientsService);
private dialog = inject(MatDialog);
+ private alertService = inject(AlertService);
private dateUtils = inject(Dates);
private cdr = inject(ChangeDetectorRef);
@@ -365,9 +367,13 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
});
ref.afterClosed().subscribe((result) => {
if (result) {
- const duplicate = this.jointHolders.some((h) => h.clientCif && h.clientCif === result.clientCif);
+ const isPrimary = this.primaryClientCif && this.primaryClientCif === result.clientCif;
+ const duplicate = isPrimary || this.jointHolders.some((h) => h.clientCif && h.clientCif === result.clientCif);
if (duplicate) {
- alert(`Account holder with CIF ${result.clientCif} is already added.`);
+ this.alertService.alert({
+ type: 'Error',
+ message: `Account holder with CIF ${result.clientCif} is already added.`
+ });
return;
}
this.jointHolders = [
@@ -407,6 +413,17 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
return this.modeOfOperationOptions.find((o) => o.value === this.modeOfOperation)?.sublabel ?? '';
}
+ get today(): Date {
+ const d = new Date();
+ d.setHours(0, 0, 0, 0);
+ return d;
+ }
+
+ get minStartOperationDate(): Date | null {
+ const val = this.currentAccountDetailsForm?.get('establishmentDate')?.value;
+ return val ? new Date(val) : null;
+ }
+
get majorityDate(): string {
const dob = this.minorDob ?? this.clientDob;
if (!dob) return '';
@@ -426,7 +443,7 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
if (result) {
const duplicate = this.relatedClients.some((c) => c.clientCif && c.clientCif === result.clientCif);
if (duplicate) {
- alert(`Partner with CIF ${result.clientCif} is already added.`);
+ this.alertService.alert({ type: 'Error', message: `Partner with CIF ${result.clientCif} is already added.` });
return;
}
this.relatedClients = [
@@ -520,7 +537,7 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
value.guardianName = this.guardianName;
value.guardianCifNumber = this.guardianCif;
value.guardianRelationship = this.guardianRelationship;
- value.minorDateOfBirth = this.minorDob ? this.dateUtils.formatDate(this.minorDob, 'yyyy-MM-dd') : null;
+ value.minorDateOfBirth = this.minorDob ? this.dateUtils.formatDate(this.minorDob, 'dd MMMM yyyy') : null;
}
// non-individual: partners are a separate dataset
diff --git a/src/app/current-accounts/current-account-stepper/current-account-preview-step/current-account-preview-step.component.html b/src/app/current-accounts/current-account-stepper/current-account-preview-step/current-account-preview-step.component.html
index 8265860bf..1fd091156 100644
--- a/src/app/current-accounts/current-account-stepper/current-account-preview-step/current-account-preview-step.component.html
+++ b/src/app/current-accounts/current-account-stepper/current-account-preview-step/current-account-preview-step.component.html
@@ -649,54 +649,6 @@
}
-
- @if (currentAccount?.partners?.length > 0) {
-
-
-
-
-
Partners / Members
-
-
-
-
- Partner Name
- CIF No
- Ownership %
- Signing Authority
-
-
-
- @for (p of currentAccount.partners; track p.clientId) {
-
- {{ p.clientName || '—' }}
- {{ p.clientCif || '—' }}
- {{ p.sharePercentage }}%
-
-
- {{ p.isMandatory ? 'Mandatory' : 'Optional' }}
-
-
-
- }
-
-
-
-
- @if (currentAccount?.signatoryRights) {
-
-
- Mode of Operation:
- {{ currentAccount.signatoryRights }}
-
- }
-
-
- }
-
@if (hasDigitalSection) {
diff --git a/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.ts b/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.ts
index 2761036f7..44c48f58f 100644
--- a/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.ts
+++ b/src/app/deposits/fixed-deposits/create-fixed-deposit-account/create-fixed-deposit-account.component.ts
@@ -13,6 +13,7 @@ import { ActivatedRoute, Router } from '@angular/router';
/** Custom Services */
import { FixedDepositsService } from '../fixed-deposits.service';
import { SettingsService } from 'app/settings/settings.service';
+import { AlertService } from 'app/core/alert/alert.service';
/** Custom Components */
import { FixedDepositAccountDetailsStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component';
@@ -59,6 +60,7 @@ export class CreateFixedDepositAccountComponent {
private dateUtils = inject(Dates);
private fixedDepositsService = inject(FixedDepositsService);
private settingsService = inject(SettingsService);
+ private alertService = inject(AlertService);
@ViewChild(FixedDepositAccountDetailsStepComponent, { static: true })
fixedDepositsAccountDetailsStep: FixedDepositAccountDetailsStepComponent;
@@ -70,7 +72,14 @@ export class CreateFixedDepositAccountComponent {
/** Fixed Deposit Account Product Template */
fixedDepositsAccountProductTemplate: any;
/** Tenure selected from the interest rate chart step */
- selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number } | null = null;
+ selectedTenure: {
+ depositPeriod: number;
+ depositPeriodFrequencyId: number;
+ depositAmount?: number;
+ maturityAmount?: number;
+ interestEarned?: number;
+ appliedRate?: number;
+ } | null = null;
/**
* Fetches FD account template from `resolve`
@@ -94,7 +103,14 @@ export class CreateFixedDepositAccountComponent {
this.fixedDepositsAccountProductTemplate = $event;
}
- onTenureSelected($event: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number }) {
+ onTenureSelected($event: {
+ depositPeriod: number;
+ depositPeriodFrequencyId: number;
+ depositAmount?: number;
+ maturityAmount?: number;
+ interestEarned?: number;
+ appliedRate?: number;
+ }) {
this.selectedTenure = $event;
}
@@ -126,6 +142,14 @@ export class CreateFixedDepositAccountComponent {
* Submits the fixed deposit form and creates a new fixed deposit account
*/
submit() {
+ const detailsStep = this.fixedDepositsAccountDetailsStep;
+ if (detailsStep?.selectedAccountStatus === 'JOINT' && detailsStep.totalJointSharePercentage !== 100) {
+ this.alertService.alert({
+ type: 'Error',
+ message: `Total ownership is ${detailsStep.totalJointSharePercentage}%. It must equal 100% before submitting.`
+ });
+ return;
+ }
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const monthDayFormat = 'dd MMMM';
@@ -178,7 +202,12 @@ export class CreateFixedDepositAccountComponent {
...this.fixedDepositAccount,
...terms,
...settings,
- ...(this.selectedTenure ?? {}),
+ ...(this.selectedTenure
+ ? {
+ depositPeriod: this.selectedTenure.depositPeriod,
+ depositPeriodFrequencyId: this.selectedTenure.depositPeriodFrequencyId
+ }
+ : {}),
clientId: this.fixedDepositsAccountTemplate.clientId,
charges: rawCharges.map((charge: any) => ({
chargeId: charge.chargeId ?? charge.id,
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component.ts
index 832f3d752..297db0824 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component.ts
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component.ts
@@ -22,6 +22,7 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { MatIcon } from '@angular/material/icon';
import { AddRelatedClientDialogComponent } from 'app/current-accounts/current-account-stepper/current-account-details-step/add-related-client-dialog/add-related-client-dialog.component';
+import { AlertService } from 'app/core/alert/alert.service';
/**
* Fixed Deposits Account Details Step
@@ -48,6 +49,7 @@ export class FixedDepositAccountDetailsStepComponent implements OnInit {
private cdr = inject(ChangeDetectorRef);
private clientsService = inject(ClientsService);
private dialog = inject(MatDialog);
+ private alertService = inject(AlertService);
/** Fixed Deposits Account Template */
@Input() fixedDepositsAccountTemplate: any;
@@ -221,15 +223,17 @@ export class FixedDepositAccountDetailsStepComponent implements OnInit {
this.dalLoading = true;
this.clientsService.getClientAccountData(clientId).subscribe({
next: (data: any) => {
- this.dalAllSavings = (data?.savingsAccounts ?? []).map((a: any) => ({
- id: a.id,
- accountNo: a.accountNo,
- productName: a.productName ?? '',
- accountType: 'SAVINGS' as const,
- status: a.status?.value ?? '',
- balance: a.accountBalance ?? 0,
- currencyCode: a.currency?.code ?? ''
- }));
+ this.dalAllSavings = (data?.savingsAccounts ?? [])
+ .filter((a: any) => a.depositType?.code === 'depositAccountType.savingsDeposit')
+ .map((a: any) => ({
+ id: a.id,
+ accountNo: a.accountNo,
+ productName: a.productName ?? '',
+ accountType: 'SAVINGS' as const,
+ status: a.status?.value ?? '',
+ balance: a.accountBalance ?? 0,
+ currencyCode: a.currency?.code ?? ''
+ }));
this.dalAllCurrent = (data?.currentAccounts ?? []).map((a: any) => ({
id: a.id,
accountNo: a.accountNo,
@@ -469,9 +473,13 @@ export class FixedDepositAccountDetailsStepComponent implements OnInit {
});
ref.afterClosed().subscribe((result) => {
if (result) {
- const duplicate = this.jointHolders.some((h) => h.clientCif && h.clientCif === result.clientCif);
+ const isPrimary = this.primaryClientCif && this.primaryClientCif === result.clientCif;
+ const duplicate = isPrimary || this.jointHolders.some((h) => h.clientCif && h.clientCif === result.clientCif);
if (duplicate) {
- alert(`Account holder with CIF ${result.clientCif} is already added.`);
+ this.alertService.alert({
+ type: 'Error',
+ message: `Account holder with CIF ${result.clientCif} is already added.`
+ });
return;
}
this.jointHolders = [
@@ -582,7 +590,7 @@ export class FixedDepositAccountDetailsStepComponent implements OnInit {
value.guardianName = this.guardianName;
value.guardianCifNumber = this.guardianCif;
value.guardianRelationship = this.guardianRelationship;
- value.minorDateOfBirth = this.minorDob ? this.dateUtils.formatDate(this.minorDob, 'yyyy-MM-dd') : null;
+ value.minorDateOfBirth = this.minorDob ? this.dateUtils.formatDate(this.minorDob, 'dd MMMM yyyy') : null;
}
if (this.selectedLinkedAccount) {
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.ts
index cf45ee1d1..6d683dc40 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.ts
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component.ts
@@ -76,6 +76,9 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
depositPeriod: number;
depositPeriodFrequencyId: number;
depositAmount?: number;
+ maturityAmount?: number;
+ interestEarned?: number;
+ appliedRate?: number;
}>();
interestRateChartData: any[] = [];
@@ -254,13 +257,16 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
this.matchResult = result;
- // Emit selected tenure and principal amount to parent for backend submission
+ // Emit selected tenure and calculated maturity to parent
const frequencyId = this.resolveFrequencyId(this.tenurePeriodUnit);
if (frequencyId !== null) {
this.tenureSelected.emit({
depositPeriod: this.tenureValue,
depositPeriodFrequencyId: frequencyId,
- depositAmount: this.principalAmount || undefined
+ depositAmount: this.principalAmount || undefined,
+ maturityAmount: result.maturityAmount,
+ interestEarned: result.interestEarned,
+ appliedRate: result.appliedRate
});
}
}
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.html b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.html
index b05e5d69c..9cf7782fc 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.html
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.html
@@ -95,7 +95,7 @@
}
- Share Linking Enabled
+ Deposit Account Link
- @if (fixedDepositsAccountProductTemplate?.introducerRequired !== undefined) {
-
- Introducer Required
-
- {{ fixedDepositsAccountProductTemplate?.introducerRequired ? 'Yes' : 'No' }}
-
-
- }
@@ -316,12 +305,19 @@
- @if (fixedDepositsAccountProductTemplate?.dormancyAlert) {
+ @if (
+ fixedDepositsAccountProductTemplate?.dormancyAlert || fixedDepositsAccountProductTemplate?.isDormancyTrackingActive
+ ) {
@@ -330,7 +326,11 @@
Inactive
- After {{ fixedDepositsAccountProductTemplate?.daysToInactiveFromProduct || 30 }} days of no transaction
+ @if (fixedDepositsAccountProductTemplate?.daysToInactive != null) {
+ After {{ fixedDepositsAccountProductTemplate.daysToInactive }} days of no transaction
+ } @else {
+ Not configured
+ }
@@ -340,7 +340,11 @@
Dormant
- {{ fixedDepositsAccountProductTemplate?.daysToDormancyFromProduct || 60 }} days after inactive
+ @if (fixedDepositsAccountProductTemplate?.daysToDormancy != null) {
+ {{ fixedDepositsAccountProductTemplate.daysToDormancy }} days after inactive
+ } @else {
+ Not configured
+ }
@@ -350,11 +354,31 @@
Escheat
- {{ fixedDepositsAccountProductTemplate?.daysToEscheatFromProduct || 90 }} days after dormant
+ @if (fixedDepositsAccountProductTemplate?.daysToEscheat != null) {
+ {{ fixedDepositsAccountProductTemplate.daysToEscheat }} days after dormant
+ } @else {
+ Not configured
+ }
+
+
+ Alert
+ {{ fixedDepositsAccountProductTemplate?.dormancyAlert ? 'Enabled' : 'Disabled' }}
+
+
+ Tracking
+ {{ fixedDepositsAccountProductTemplate?.isDormancyTrackingActive ? 'Active' : 'Inactive' }}
+
+
}
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.scss b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.scss
index c7c88cafb..cc19947d5 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.scss
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.scss
@@ -423,31 +423,25 @@
.preview-dormancy-timeline {
display: flex;
align-items: flex-start;
- gap: 12px;
- margin: 1.25rem 0;
- overflow-x: auto;
- padding-bottom: 8px;
+ margin-bottom: 14px;
}
.preview-dorm-step {
display: flex;
flex-direction: column;
align-items: center;
- flex: 0 0 140px;
- gap: 10px;
+ flex: 1;
+ gap: 6px;
&__dot {
- width: 32px;
- height: 32px;
+ width: 36px;
+ height: 36px;
border-radius: 50%;
+ border: 3px solid;
display: flex;
align-items: center;
justify-content: center;
- border: 3px solid #e2e8f0;
- background: #f8fafc;
- position: relative;
- box-shadow: 0 0 0 4px #fff;
- z-index: 2;
+ flex-shrink: 0;
&--inactive {
border-color: #f59e0b;
@@ -517,3 +511,32 @@
margin-top: 16px;
border-radius: 2px;
}
+
+.preview-dorm-flags {
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+.preview-dorm-flag {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 4px 12px;
+ border-radius: 20px;
+ font-size: 11.5px;
+ font-weight: 600;
+ background: #f1f5f9;
+ color: #94a3b8;
+ border: 1px solid #e2e8f0;
+
+ i {
+ font-size: 11px;
+ }
+
+ &--on {
+ background: #f0fdf4;
+ color: #15803d;
+ border-color: #bbf7d0;
+ }
+}
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.ts
index 7232f2d38..ee134eab3 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.ts
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component.ts
@@ -76,9 +76,15 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
@Input() fixedDepositsAccountProductTemplate: any;
/** Fixed Deposits Account Data */
@Input() fixedDepositAccountData: any;
- /** Tenure selected from the interest rate chart step */
- @Input() selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number } | null =
- null;
+ /** Tenure selected from the interest rate chart step (includes pre-calculated maturity if user used calculator) */
+ @Input() selectedTenure: {
+ depositPeriod: number;
+ depositPeriodFrequencyId: number;
+ depositAmount?: number;
+ maturityAmount?: number;
+ interestEarned?: number;
+ appliedRate?: number;
+ } | null = null;
/** Charges Displayed Columns */
chargesDisplayedColumns: string[] = [
@@ -237,6 +243,20 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
this.fixedDepositsAccountProductTemplate?.depositAmount ??
0;
+ // If the chart step already calculated maturity, use those values directly
+ if (this.selectedTenure?.maturityAmount != null && this.selectedTenure.maturityAmount > 0) {
+ const interestRate =
+ this.selectedTenure.appliedRate ?? this.fixedDepositsAccountProductTemplate?.nominalAnnualInterestRate ?? 0;
+ return {
+ depositAmount,
+ maturityAmount: this.selectedTenure.maturityAmount,
+ totalInterest: this.selectedTenure.interestEarned ?? this.selectedTenure.maturityAmount - depositAmount,
+ interestRate,
+ tenureLabel: this.selectedTenureLabel
+ };
+ }
+
+ // Fallback: recalculate using product template settings
const chartRate =
this.matchedSlabIndex >= 0 ? this.interestRateChartData[this.matchedSlabIndex]?.annualInterestRate : null;
const interestRate = chartRate ?? this.fixedDepositsAccountProductTemplate?.nominalAnnualInterestRate ?? 0;
@@ -248,20 +268,15 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
this.fixedDepositsAccountProductTemplate?.interestCalculationDaysInYearType?.id === 360 ? 360 : 365;
if (depositPeriodFrequencyId === 0) {
- // Days
t = depositPeriod / daysInYear;
} else if (depositPeriodFrequencyId === 1) {
- // Weeks
t = (depositPeriod * 7) / daysInYear;
} else if (depositPeriodFrequencyId === 2) {
- // Months
t = depositPeriod / 12;
} else if (depositPeriodFrequencyId === 3) {
- // Years
t = depositPeriod;
}
- // Compounding frequency (n)
const compPeriodValue = (
this.fixedDepositsAccountProductTemplate?.interestCompoundingPeriodType?.value || ''
).toLowerCase();
@@ -273,7 +288,6 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
else if (compPeriodValue.includes('biannual')) n = 2;
else if (compPeriodValue.includes('annual')) n = 1;
- // Interest Calculation Type
const calcTypeValue = (
this.fixedDepositsAccountProductTemplate?.interestCalculationType?.value || ''
).toLowerCase();
@@ -283,7 +297,6 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
if (isSimple) {
maturityAmount = depositAmount + depositAmount * r * t;
} else {
- // Compound interest: A = P(1 + r/n)^(nt)
maturityAmount = depositAmount * Math.pow(1 + r / n, n * t);
}
diff --git a/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.ts b/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.ts
index bfc5384a9..995587be2 100644
--- a/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.ts
+++ b/src/app/deposits/recurring-deposits/create-recurring-deposits-account/create-recurring-deposits-account.component.ts
@@ -14,6 +14,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { RecurringDepositsService } from '../recurring-deposits.service';
import { SettingsService } from 'app/settings/settings.service';
import { ProductsService } from 'app/products/products.service';
+import { AlertService } from 'app/core/alert/alert.service';
/** Step Components */
import { RecurringDepositsAccountDetailsStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component';
@@ -61,6 +62,7 @@ export class CreateRecurringDepositsAccountComponent {
private recurringDepositsService = inject(RecurringDepositsService);
private settingsService = inject(SettingsService);
private productsService = inject(ProductsService);
+ private alertService = inject(AlertService);
@ViewChild(RecurringDepositsAccountDetailsStepComponent, { static: true })
recurringDepositsAccountDetailsStep: RecurringDepositsAccountDetailsStepComponent;
@@ -157,6 +159,14 @@ export class CreateRecurringDepositsAccountComponent {
* Submits the recurring deposit form to create a new recurring deposit account
*/
submit() {
+ const detailsStep = this.recurringDepositsAccountDetailsStep;
+ if (detailsStep.selectedAccountStatus === 'JOINT' && detailsStep.totalJointSharePercentage !== 100) {
+ this.alertService.alert({
+ type: 'Error',
+ message: `Total ownership is ${detailsStep.totalJointSharePercentage}%. It must equal 100% before submitting.`
+ });
+ return;
+ }
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const monthDayFormat = 'dd MMMM';
diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component.ts b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component.ts
index 3bd5b04a2..389803067 100644
--- a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component.ts
+++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component.ts
@@ -22,6 +22,7 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { MatIcon } from '@angular/material/icon';
import { AddRelatedClientDialogComponent } from 'app/current-accounts/current-account-stepper/current-account-details-step/add-related-client-dialog/add-related-client-dialog.component';
+import { AlertService } from 'app/core/alert/alert.service';
/**
* Recurring Deposits Account Details Step
@@ -48,6 +49,7 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
private cdr = inject(ChangeDetectorRef);
private clientsService = inject(ClientsService);
private dialog = inject(MatDialog);
+ private alertService = inject(AlertService);
/** Recurring Deposits Account Template */
@Input() recurringDepositsAccountTemplate: any;
@@ -198,15 +200,17 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
this.dalLoading = true;
this.clientsService.getClientAccountData(clientId).subscribe({
next: (data: any) => {
- this.dalAllSavings = (data?.savingsAccounts ?? []).map((a: any) => ({
- id: a.id,
- accountNo: a.accountNo,
- productName: a.productName ?? '',
- accountType: 'SAVINGS' as const,
- status: a.status?.value ?? '',
- balance: a.accountBalance ?? 0,
- currencyCode: a.currency?.code ?? ''
- }));
+ this.dalAllSavings = (data?.savingsAccounts ?? [])
+ .filter((a: any) => a.depositType?.code === 'depositAccountType.savingsDeposit')
+ .map((a: any) => ({
+ id: a.id,
+ accountNo: a.accountNo,
+ productName: a.productName ?? '',
+ accountType: 'SAVINGS' as const,
+ status: a.status?.value ?? '',
+ balance: a.accountBalance ?? 0,
+ currencyCode: a.currency?.code ?? ''
+ }));
this.dalAllCurrent = (data?.currentAccounts ?? []).map((a: any) => ({
id: a.id,
accountNo: a.accountNo,
@@ -430,9 +434,13 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
});
ref.afterClosed().subscribe((result) => {
if (result) {
- const duplicate = this.jointHolders.some((h) => h.clientCif && h.clientCif === result.clientCif);
+ const isPrimary = this.primaryClientCif && this.primaryClientCif === result.clientCif;
+ const duplicate = isPrimary || this.jointHolders.some((h) => h.clientCif && h.clientCif === result.clientCif);
if (duplicate) {
- alert(`Account holder with CIF ${result.clientCif} is already added.`);
+ this.alertService.alert({
+ type: 'Error',
+ message: `Account holder with CIF ${result.clientCif} is already added.`
+ });
return;
}
this.jointHolders = [
@@ -540,7 +548,7 @@ export class RecurringDepositsAccountDetailsStepComponent implements OnInit {
value.guardianName = this.guardianName;
value.guardianCifNumber = this.guardianCif;
value.guardianRelationship = this.guardianRelationship;
- value.minorDateOfBirth = this.minorDob ? this.dateUtils.formatDate(this.minorDob, 'yyyy-MM-dd') : null;
+ value.minorDateOfBirth = this.minorDob ? this.dateUtils.formatDate(this.minorDob, 'dd MMMM yyyy') : null;
}
if (this.selectedLinkedAccount) {
diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component.html b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component.html
index 824777e6b..e26d30815 100644
--- a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component.html
+++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component.html
@@ -463,7 +463,7 @@
@if (
recurringDepositsAccountProductTemplate?.dormancyAlert ||
- recurringDepositsAccountProductTemplate?.isDormancyTrackingActiveFromProduct
+ recurringDepositsAccountProductTemplate?.isDormancyTrackingActive
) {
@@ -485,8 +483,8 @@
Inactive
- @if (recurringDepositsAccountProductTemplate?.daysToInactiveFromProduct !== null) {
- After {{ recurringDepositsAccountProductTemplate.daysToInactiveFromProduct }} days of no transaction
+ @if (recurringDepositsAccountProductTemplate?.daysToInactive != null) {
+ After {{ recurringDepositsAccountProductTemplate.daysToInactive }} days of no transaction
} @else {
Not configured
}
@@ -499,8 +497,8 @@
Dormant
- @if (recurringDepositsAccountProductTemplate?.daysToDormancyFromProduct !== null) {
- {{ recurringDepositsAccountProductTemplate.daysToDormancyFromProduct }} days after inactive
+ @if (recurringDepositsAccountProductTemplate?.daysToDormancy != null) {
+ {{ recurringDepositsAccountProductTemplate.daysToDormancy }} days after inactive
} @else {
Not configured
}
@@ -513,8 +511,8 @@
Escheat
- @if (recurringDepositsAccountProductTemplate?.daysToEscheatFromProduct !== null) {
- {{ recurringDepositsAccountProductTemplate.daysToEscheatFromProduct }} days after dormant
+ @if (recurringDepositsAccountProductTemplate?.daysToEscheat != null) {
+ {{ recurringDepositsAccountProductTemplate.daysToEscheat }} days after dormant
} @else {
Not configured
}
@@ -532,10 +530,10 @@
Tracking
- {{ recurringDepositsAccountProductTemplate?.isDormancyTrackingActiveFromProduct ? 'Active' : 'Inactive' }}
+ {{ recurringDepositsAccountProductTemplate?.isDormancyTrackingActive ? 'Active' : 'Inactive' }}
diff --git a/src/app/products/loan-products/create-loan-product/create-loan-product.component.scss b/src/app/products/loan-products/create-loan-product/create-loan-product.component.scss
index d05cedb1a..68e4c9ac5 100644
--- a/src/app/products/loan-products/create-loan-product/create-loan-product.component.scss
+++ b/src/app/products/loan-products/create-loan-product/create-loan-product.component.scss
@@ -9,7 +9,7 @@
$primary: #1353a0;
.lp-container {
- max-width: 960px;
+ max-width: 1200px;
margin: 0 auto;
padding: 24px 16px;
}
@@ -53,11 +53,11 @@ $primary: #1353a0;
}
.mat-mdc-tab-body {
- overflow: visible !important;
+ overflow: hidden visible !important;
}
.mat-mdc-tab-body-content {
- overflow: visible !important;
+ overflow: hidden visible !important;
}
}
}
@@ -69,6 +69,8 @@ $primary: #1353a0;
.lp-tab-content {
padding: 24px 16px;
+ min-width: 0;
+ overflow-x: hidden;
}
.lp-action-bar {
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.html
index b5e37990a..434640cec 100644
--- a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.html
+++ b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.html
@@ -90,4 +90,34 @@
+
+
+
+
+
+
+ Product Type Group
+
+ @for (group of productTypeGroups; track group.id) {
+ {{ group.label }}
+ }
+
+
+
+ @if (loanProductDetailsForm.get('productTypeGroup')?.value) {
+
+ GL Account ({{ glClassificationLabel }})
+
+ @for (acc of availableGlAccounts; track acc.id) {
+ ({{ acc.glCode }}) {{ acc.name }}
+ }
+
+ GL Account is required
+
+ }
+
+
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.scss
index e0ff5016a..f6a3f62d5 100644
--- a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.scss
+++ b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.scss
@@ -68,3 +68,140 @@ $gray-900: #202124;
align-items: center;
padding: 4px 0;
}
+
+// GL Section
+.lpd-gl-section {
+ border-top: 2px solid #e0f2fe;
+}
+
+.lpd-gl-subtype-badge {
+ margin-left: auto;
+ padding: 3px 12px;
+ background: #eff6ff;
+ border: 1px solid #bfdbfe;
+ border-radius: 20px;
+ font-size: 11.5px;
+ font-weight: 600;
+ color: #2563eb;
+}
+
+.lpd-gl-body {
+ padding: 16px 24px 20px;
+}
+
+.lpd-gl-hint {
+ font-size: 12.5px;
+ color: #64748b;
+ margin: 0 0 16px;
+}
+
+.lpd-gl-cards {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.lpd-gl-card {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding: 14px 18px;
+ border: 2px solid #e2e8f0;
+ border-radius: 10px;
+ cursor: pointer;
+ background: #fff;
+ transition:
+ border-color 0.15s,
+ box-shadow 0.15s,
+ background 0.15s;
+ user-select: none;
+
+ &:hover {
+ border-color: #93c5fd;
+ background: #f8faff;
+ }
+
+ &--selected {
+ border-color: #2563eb !important;
+ background: #eff6ff !important;
+ box-shadow: 0 0 0 3px rgb(37 99 235 / 10%);
+
+ .lpd-gl-card__check {
+ opacity: 1;
+ color: #2563eb;
+ }
+ }
+}
+
+.lpd-gl-radio {
+ display: none;
+}
+
+.lpd-gl-card__icon {
+ width: 40px;
+ height: 40px;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 16px;
+ flex-shrink: 0;
+
+ &--asset {
+ background: #dcfce7;
+ color: #15803d;
+ }
+
+ &--liability {
+ background: #fee2e2;
+ color: #b91c1c;
+ }
+}
+
+.lpd-gl-card__info {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ flex: 1;
+}
+
+.lpd-gl-card__title {
+ font-size: 14px;
+ font-weight: 700;
+ color: #1e293b;
+}
+
+.lpd-gl-card__desc {
+ font-size: 12px;
+ color: #64748b;
+}
+
+.lpd-gl-card__check {
+ font-size: 18px;
+ opacity: 0;
+ transition: opacity 0.15s;
+ flex-shrink: 0;
+}
+
+.lpd-gl-account-field {
+ width: 100%;
+ max-width: 480px;
+}
+
+.lpd-gl-type-badge {
+ padding: 3px 12px;
+ border-radius: 20px;
+ font-size: 11.5px;
+ font-weight: 700;
+ margin-left: 6px;
+
+ &--asset {
+ background: #dcfce7;
+ color: #15803d;
+ }
+
+ &--liability {
+ background: #fee2e2;
+ color: #b91c1c;
+ }
+}
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.ts
index 5548aee15..68373a59d 100644
--- a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.ts
+++ b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.ts
@@ -57,6 +57,68 @@ export class LoanProductDetailsStepComponent implements OnInit {
startDate: this.loanProductsTemplate.startDate && new Date(this.loanProductsTemplate.startDate),
closeDate: this.loanProductsTemplate.closeDate && new Date(this.loanProductsTemplate.closeDate)
});
+
+ const mappingOptions = this.loanProductsTemplate?.accountingMappingOptions ?? {};
+ this.assetAccountOptions = mappingOptions.assetAccountOptions ?? [];
+ this.liabilityAccountOptions = mappingOptions.liabilityAccountOptions ?? [];
+ }
+
+ /** Product Classification data */
+ readonly productTypeGroups = [
+ { id: 'loans', label: 'Loan' },
+ { id: 'advanced', label: 'Advanced' }
+ ];
+
+ readonly productTypeSubGroups: Record
= {
+ loans: [
+ { id: 'emi', label: 'EMI' },
+ { id: 'cc', label: 'CC (Cash Credit)' },
+ { id: 'overdraft', label: 'Overdraft' },
+ { id: 'warehouse', label: 'Warehouse' },
+ { id: 'gold', label: 'Gold' }
+ ],
+ advanced: [
+ { id: 'fd', label: 'FD (Fixed Deposit)' },
+ { id: 'rd', label: 'RD (Recurring Deposit)' }
+ ]
+ };
+
+ /** Default GL per product type group */
+ private readonly glDefaultMap: Record = {
+ loans: 'asset',
+ advanced: 'asset'
+ };
+
+ get availableSubGroups() {
+ const group = this.loanProductDetailsForm.get('productTypeGroup')?.value;
+ return group ? (this.productTypeSubGroups[group] ?? []) : [];
+ }
+
+ get glOptions(): string[] {
+ return [
+ 'asset',
+ 'liability'
+ ];
+ }
+
+ assetAccountOptions: any[] = [];
+ liabilityAccountOptions: any[] = [];
+
+ get availableGlAccounts(): any[] {
+ const type = this.loanProductDetailsForm.get('glClassification')?.value;
+ if (type === 'asset') return this.assetAccountOptions;
+ if (type === 'liability') return this.liabilityAccountOptions;
+ return [];
+ }
+
+ get glClassificationLabel(): string {
+ const type = this.loanProductDetailsForm.get('glClassification')?.value;
+ return type === 'liability' ? 'Liability' : 'Asset';
+ }
+
+ get selectedGroupLabel(): string {
+ const group = this.loanProductDetailsForm.get('productTypeGroup')?.value;
+ return this.productTypeGroups.find((g) => g.id === group)?.label ?? '';
}
createLoanProductDetailsForm() {
@@ -71,7 +133,18 @@ export class LoanProductDetailsStepComponent implements OnInit {
],
description: [''],
startDate: [''],
- closeDate: ['']
+ closeDate: [''],
+ productTypeGroup: [''],
+ productTypeSubGroup: [''],
+ glClassification: ['asset'],
+ glAccount: ['']
+ });
+
+ // Auto-set GL based on product type group
+ this.loanProductDetailsForm.get('productTypeGroup')?.valueChanges.subscribe((group: string) => {
+ const defaultVal = this.glDefaultMap[group] ?? 'asset';
+ this.loanProductDetailsForm.get('glClassification')?.setValue(defaultVal);
+ this.loanProductDetailsForm.get('glAccount')?.setValue('');
});
}
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html
index a1259fe05..16658c103 100644
--- a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html
+++ b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html
@@ -467,7 +467,7 @@
-
+
Against Interest Calculation Enabled
YES
@@ -499,7 +499,7 @@
-
+
Consider Debit Closing Amount For Against Clearing
YES
@@ -517,7 +517,7 @@
-
+
Penalty Interest Calculation Enabled
YES
@@ -525,7 +525,7 @@
-
+
Penalty Interest Calculation Method
Daywise ( 36500 )
@@ -559,7 +559,7 @@
-
+
Interest On Credit Balance
YES
@@ -567,7 +567,7 @@
-
+
Penalty Interest Date Different Than Regular Interest
YES
@@ -575,7 +575,7 @@
-
+
Fixed Deposit Overdraft Certificate Sort Order
Order on Interest Rate
@@ -599,12 +599,12 @@
-
+
Minimum Normal Interest Amount
-
+
Minimum Interest Calculation Method
Flat Amount
@@ -613,12 +613,12 @@
-
+
Minimum Against Clearing Interest Amount
-
+
Limit Increase Upto Interest Amount In Interest Calculation
YES
@@ -626,22 +626,22 @@
-
+
Minimum Penalty Interest Amount
-
+
Days For Limit Increase Upto Interest Amount
-
+
Additional Interest Rate For Fixed Deposit Overdraft
-
+
Consider DD/Payorder In Against Clearing Interest Calculation
YES
@@ -649,12 +649,12 @@
-
+
Grace Days After Renewal Date
-
+
Apply Undebited Interest Concept Enabled
YES
@@ -671,12 +671,12 @@
-
+
Normal Grace Days After Overdue
-
+
Allow Against Clearing Entry
YES
@@ -684,7 +684,7 @@
-
+
Allow No Balance Entry
YES
@@ -692,7 +692,7 @@
-
+
Fixed Deposit Overdraft Penalty Interest Combined With Normal Interest
YES
@@ -700,7 +700,7 @@
-
+
Allow No Balance Entry With Password
YES
@@ -708,7 +708,7 @@
-
+
Undebited Simple Interest Account Wise
YES
@@ -716,7 +716,7 @@
-
+
Daily Interest Calculation Enabled
YES
@@ -724,12 +724,12 @@
-
+
Daily Interest Calculation Start Date
-
+
Non Performing Asset Penal Interest Rate Update
YES
@@ -737,12 +737,12 @@
-
+
Non Performing Asset Penal Interest Rate
-
+
Fixed Deposit Overdraft Penal Interest Rate Update In Branch Process
YES
@@ -750,13 +750,116 @@
-
+
Add Penal Interest Rate
+
+
+
+
+
+ Penal Charge Method
+
+ RBI Compliant Penal Charges (Non-Compounding)
+ Legacy Penal Interest (Compounding)
+
+
+
+
+
+ Is Floating Rate Loan? (Individual)
+
+
+
+
+ Foreclosure Charges Applicable
+
+ YES
+ NO
+
+ RBI: No charges for Floating Rate Loans to Individuals
+
+
+
+
+ Enable Key Fact Statement (KFS)
+
+
+
+
+
+ Calculate APR
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Particular
+
+
+
+
+
+ @for (genGroup of slabGroups; track $index) {
+
+
{{ interestSlabDefinitions[$index].label }}
+
+
+ }
+
+
+
+
+
+
+