Four independent, self-contained changes bundled from this session's work:
1. Auto-derive Senior Citizen / Minor / Customer Segment instead of manual entry
Senior Citizen was a manual, staff-editable checkbox on FD/RD account
creation with no validation against the client's actual age -- this let
non-senior clients get the senior-citizen interest rate by mistake (found
twice on live test accounts, where m_savings_account.is_senior_citizen
was true despite the client being confirmed non-senior at the client
record level). Likewise "minor" client classification was hardcoded to
the age-18 threshold with no single source of truth, and FD/RD account
creation's "Customer Segment" (Individual / Non-Individual) had no
connection to the Individual/Non-Individual Legal Form already captured
at client creation.
- New src/app/clients/shared/client-age.util.ts centralises age/DOB
derivation: MINOR_AGE_THRESHOLD (now 10, was 18), SENIOR_CITIZEN_AGE_
THRESHOLD (60), AGE_OF_MAJORITY (18, kept separate from the minor
threshold -- majority date math is unrelated to the minor/adult
classification threshold), calculateAge/isMinorAge/isSeniorCitizenAge/
isIndividualClient/deriveSeniorCitizenStatus.
- FD/RD interest-rate-chart steps: Senior Citizen checkbox is now derived
from the client's DOB (age >= 60, individual clients only) and
disabled for manual editing; a hint explains why it is checked/
unchecked.
- Client general step and all four account-details-steps (savings,
current, FD, RD): replaced hardcoded 18/60 literals with the shared
helpers; "Must be below 18" hints now read the actual threshold.
- FD/RD account-details-steps: Customer Segment auto-selects to match
the client's Legal Form (exact match, not substring -- a substring
match would misclassify "Individual" as matching "Non-Individual").
- RD preview step: fixed a pre-existing display bug that always showed
the product's default Customer Segment instead of what was actually
selected on the account.
2. Fix wrong Account Name / Account Type on Disbursement Review screens
Clerk disbursement review and "My Disbursement Requests" showed the
account NUMBER instead of the holder's name, and always labelled the
account type "SAVINGS" regardless of the real account type (FD/RD/
Share/Loan), because transaction-entry.component.ts's account-type
mapping only checked for CA/CB and its account-name fallback chain
never tried clientName. Added creditEntryAccountType() covering
GL/CA/CB/FD/RD/SHR/LA, and accountTypeLabel() display helpers on both
review screens.
3. Stop reversed transactions from distorting displayed running balances
Passbook and Transactions tabs were including reversed transactions in
their client-side running-balance recalculation, so a reversed posting
(visible struck-through) still shifted the balance shown for every
transaction after it. Both now filter out txn.reversed before computing
running balances; Transactions tab also defaults "Hide Reversed" to on.
4. New Interest Schedule dialog on the FD account view
Added custom-dialogs/interest-schedule-dialog, opened from the FD
General tab, showing the account's period-by-period interest schedule.
Excludes src/environments/.env.ts (auto-generated build version/hash
stamp, regenerated on every build -- not meaningful to hand-commit).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
912 lines
41 KiB
HTML
912 lines
41 KiB
HTML
<!--
|
||
Copyright since 2025 Mifos Initiative
|
||
|
||
This Source Code Form is subject to the terms of the Mozilla Public
|
||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||
-->
|
||
|
||
<div class="preview-wrapper">
|
||
<!-- ══ 1. Account Details ════════════════════════════════════════ -->
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-info-circle preview-section__icon"></i>
|
||
<span class="preview-section__title">{{ 'labels.heading.Details' | translate }}</span>
|
||
</div>
|
||
<div class="preview-body">
|
||
<div class="preview-grid preview-grid--three">
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Product' | translate }}</span>
|
||
<span class="preview-value">{{
|
||
recurringDepositAccountData?.productId
|
||
| find: recurringDepositsAccountTemplate?.productOptions : 'id' : 'name'
|
||
}}</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Submitted On' | translate }}</span>
|
||
<span class="preview-value">{{ recurringDepositAccountData?.submittedOnDate | dateFormat }}</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Field Officer' | translate }}</span>
|
||
<span class="preview-value">{{
|
||
recurringDepositAccountData?.fieldOfficerId
|
||
| find: recurringDepositsAccountProductTemplate?.fieldOfficerOptions : 'id' : 'displayName'
|
||
}}</span>
|
||
</div>
|
||
@if (recurringDepositAccountData?.externalId) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.External Id' | translate }}</span>
|
||
<span class="preview-value">{{ recurringDepositAccountData.externalId }}</span>
|
||
</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 ════════════════════════════════ -->
|
||
@if (
|
||
recurringDepositsAccountProductTemplate?.accountType ||
|
||
recurringDepositsAccountProductTemplate?.customerSegment ||
|
||
recurringDepositsAccountProductTemplate?.productCode ||
|
||
recurringDepositsAccountProductTemplate?.effectiveDate ||
|
||
recurringDepositsAccountProductTemplate?.endDate ||
|
||
recurringDepositsAccountProductTemplate?.introducerRequired !== undefined ||
|
||
recurringDepositsAccountProductTemplate?.shareLinkingEnabled !== undefined
|
||
) {
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-tags preview-section__icon"></i>
|
||
<span class="preview-section__title">Product Classification</span>
|
||
<span class="preview-section__badge">From Product</span>
|
||
</div>
|
||
<div class="preview-body">
|
||
<div class="preview-grid preview-grid--three">
|
||
@if (recurringDepositsAccountProductTemplate?.accountType) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Account Type</span>
|
||
<span class="preview-value">{{ recurringDepositsAccountProductTemplate.accountType }}</span>
|
||
</div>
|
||
}
|
||
<!-- Prefer what the details step actually selected (derived from the client's legal form) over
|
||
the product's default, which is only the starting suggestion. -->
|
||
@if (
|
||
recurringDepositAccountData?.customerSegment || recurringDepositsAccountProductTemplate?.customerSegment
|
||
) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Customer Segment</span>
|
||
<span class="preview-value">{{
|
||
recurringDepositAccountData?.customerSegment ?? recurringDepositsAccountProductTemplate.customerSegment
|
||
}}</span>
|
||
</div>
|
||
}
|
||
@if (recurringDepositsAccountProductTemplate?.productCode) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Product Code</span>
|
||
<span class="preview-value preview-mono">{{ recurringDepositsAccountProductTemplate.productCode }}</span>
|
||
</div>
|
||
}
|
||
@if (recurringDepositsAccountProductTemplate?.effectiveDate) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Effective Date</span>
|
||
<span class="preview-value">{{
|
||
recurringDepositsAccountProductTemplate.effectiveDate | dateFormat
|
||
}}</span>
|
||
</div>
|
||
}
|
||
@if (recurringDepositsAccountProductTemplate?.endDate) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">End Date</span>
|
||
<span class="preview-value">{{ recurringDepositsAccountProductTemplate.endDate | dateFormat }}</span>
|
||
</div>
|
||
}
|
||
@if (
|
||
recurringDepositsAccountProductTemplate?.introducerRequired !== undefined &&
|
||
recurringDepositsAccountProductTemplate?.introducerRequired !== null
|
||
) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Introducer Required</span>
|
||
<span
|
||
class="preview-value preview-badge"
|
||
[class.preview-badge--yes]="recurringDepositsAccountProductTemplate.introducerRequired"
|
||
>
|
||
{{ recurringDepositsAccountProductTemplate.introducerRequired | yesNo }}
|
||
</span>
|
||
</div>
|
||
}
|
||
@if (
|
||
recurringDepositsAccountProductTemplate?.shareLinkingEnabled !== undefined &&
|
||
recurringDepositsAccountProductTemplate?.shareLinkingEnabled !== null
|
||
) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Deposit Account Link</span>
|
||
<span
|
||
class="preview-value preview-badge"
|
||
[class.preview-badge--yes]="recurringDepositsAccountProductTemplate.shareLinkingEnabled"
|
||
>
|
||
{{ recurringDepositsAccountProductTemplate.shareLinkingEnabled | yesNo }}
|
||
</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ══ 3. Introducer Details (if selected) ══════════════════════ -->
|
||
@if (recurringDepositAccountData?.introducerId) {
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-user-tie preview-section__icon"></i>
|
||
<span class="preview-section__title">Introducer Details</span>
|
||
</div>
|
||
<div class="preview-body">
|
||
<div class="preview-grid preview-grid--three">
|
||
<div class="preview-field">
|
||
<span class="preview-label">Name</span>
|
||
<span class="preview-value">{{ recurringDepositAccountData?.introducerName || '—' }}</span>
|
||
</div>
|
||
@if (recurringDepositAccountData?.introducerCif) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">CIF No</span>
|
||
<span class="preview-value preview-mono">{{ recurringDepositAccountData.introducerCif }}</span>
|
||
</div>
|
||
}
|
||
<div class="preview-field">
|
||
<span class="preview-label">Client ID</span>
|
||
<span class="preview-value preview-mono">{{ recurringDepositAccountData.introducerId }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ══ 4. Currency & Account Flags ══════════════════════════════ -->
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-coins preview-section__icon"></i>
|
||
<span class="preview-section__title">{{ 'labels.heading.Currency' | translate }}</span>
|
||
</div>
|
||
<div class="preview-body">
|
||
<div class="preview-grid preview-grid--three">
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Currency' | translate }}</span>
|
||
<span class="preview-value">{{ currencyName }}</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Decimal Places' | translate }}</span>
|
||
<span class="preview-value">{{ decimalPlaces }}</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Currency In Multiples Of' | translate }}</span>
|
||
<span class="preview-value">{{ inMultiplesOf }}</span>
|
||
</div>
|
||
@if (recurringDepositsAccountProductTemplate?.withHoldTax) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Is Withhold Tax Applicable' | translate }}</span>
|
||
<span
|
||
class="preview-value preview-badge"
|
||
[class.preview-badge--yes]="recurringDepositAccountData?.withHoldTax"
|
||
>
|
||
{{ recurringDepositAccountData?.withHoldTax | yesNo }}
|
||
</span>
|
||
</div>
|
||
}
|
||
@if (recurringDepositAccountData?.withHoldTax && recurringDepositsAccountProductTemplate?.taxGroup?.name) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Tax Group' | translate }}</span>
|
||
<span class="preview-value">{{ recurringDepositsAccountProductTemplate.taxGroup.name }}</span>
|
||
</div>
|
||
}
|
||
<div class="preview-field">
|
||
<span class="preview-label">Auto Debit Facility</span>
|
||
<span class="preview-value preview-badge" [class.preview-badge--yes]="rdCurrencyFlags?.autoDebitFacility">
|
||
{{ rdCurrencyFlags?.autoDebitFacility | yesNo }}
|
||
</span>
|
||
</div>
|
||
@if (
|
||
recurringDepositsAccountProductTemplate?.dormancyAlert !== undefined &&
|
||
recurringDepositsAccountProductTemplate?.dormancyAlert !== null
|
||
) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Dormancy Alert</span>
|
||
<span
|
||
class="preview-value preview-badge"
|
||
[class.preview-badge--yes]="recurringDepositsAccountProductTemplate.dormancyAlert"
|
||
>
|
||
{{ recurringDepositsAccountProductTemplate.dormancyAlert | yesNo }}
|
||
</span>
|
||
</div>
|
||
}
|
||
@if (
|
||
recurringDepositsAccountProductTemplate?.nominationFacility !== undefined &&
|
||
recurringDepositsAccountProductTemplate?.nominationFacility !== null
|
||
) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Nomination Facility</span>
|
||
<span
|
||
class="preview-value preview-badge"
|
||
[class.preview-badge--yes]="recurringDepositsAccountProductTemplate.nominationFacility"
|
||
>
|
||
{{ recurringDepositsAccountProductTemplate.nominationFacility | yesNo }}
|
||
</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══ 5. Dormancy Monitoring ════════════════════════════════════ -->
|
||
@if (
|
||
recurringDepositsAccountProductTemplate?.dormancyAlert ||
|
||
recurringDepositsAccountProductTemplate?.isDormancyTrackingActive
|
||
) {
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-moon preview-section__icon"></i>
|
||
<span class="preview-section__title">Dormancy Monitoring</span>
|
||
<span
|
||
class="preview-section__badge"
|
||
[class.preview-section__badge--active]="recurringDepositsAccountProductTemplate?.isDormancyTrackingActive"
|
||
>
|
||
{{ recurringDepositsAccountProductTemplate?.isDormancyTrackingActive ? 'Active' : 'Inactive' }}
|
||
</span>
|
||
</div>
|
||
<div class="preview-body">
|
||
<div class="preview-dormancy-timeline">
|
||
<div class="preview-dorm-step">
|
||
<div class="preview-dorm-step__dot preview-dorm-step__dot--inactive"></div>
|
||
<div class="preview-dorm-step__body">
|
||
<span class="preview-dorm-step__label">Inactive</span>
|
||
<span class="preview-dorm-step__days">
|
||
@if (recurringDepositsAccountProductTemplate?.daysToInactive != null) {
|
||
After {{ recurringDepositsAccountProductTemplate.daysToInactive }} days of no transaction
|
||
} @else {
|
||
Not configured
|
||
}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="preview-dorm-connector"></div>
|
||
<div class="preview-dorm-step">
|
||
<div class="preview-dorm-step__dot preview-dorm-step__dot--dormant"></div>
|
||
<div class="preview-dorm-step__body">
|
||
<span class="preview-dorm-step__label">Dormant</span>
|
||
<span class="preview-dorm-step__days">
|
||
@if (recurringDepositsAccountProductTemplate?.daysToDormancy != null) {
|
||
{{ recurringDepositsAccountProductTemplate.daysToDormancy }} days after inactive
|
||
} @else {
|
||
Not configured
|
||
}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="preview-dorm-connector"></div>
|
||
<div class="preview-dorm-step">
|
||
<div class="preview-dorm-step__dot preview-dorm-step__dot--escheat"></div>
|
||
<div class="preview-dorm-step__body">
|
||
<span class="preview-dorm-step__label">Escheat</span>
|
||
<span class="preview-dorm-step__days">
|
||
@if (recurringDepositsAccountProductTemplate?.daysToEscheat != null) {
|
||
{{ recurringDepositsAccountProductTemplate.daysToEscheat }} days after dormant
|
||
} @else {
|
||
Not configured
|
||
}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="preview-dorm-flags">
|
||
<span
|
||
class="preview-dorm-flag"
|
||
[class.preview-dorm-flag--on]="recurringDepositsAccountProductTemplate?.dormancyAlert"
|
||
>
|
||
<i class="fas fa-bell"></i> Alert
|
||
{{ recurringDepositsAccountProductTemplate?.dormancyAlert ? 'Enabled' : 'Disabled' }}
|
||
</span>
|
||
<span
|
||
class="preview-dorm-flag"
|
||
[class.preview-dorm-flag--on]="recurringDepositsAccountProductTemplate?.isDormancyTrackingActive"
|
||
>
|
||
<i class="fas fa-eye"></i> Tracking
|
||
{{ recurringDepositsAccountProductTemplate?.isDormancyTrackingActive ? 'Active' : 'Inactive' }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ══ 6. Nominee Details (if filled) ═══════════════════════════ -->
|
||
@if (rdNominees?.length > 0) {
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-user-shield preview-section__icon"></i>
|
||
<span class="preview-section__title">Nominee Details</span>
|
||
<span class="preview-section__badge">{{ rdNominees.length }} Nominee(s)</span>
|
||
</div>
|
||
<div class="preview-body">
|
||
@for (nom of rdNominees; track nom.nomineeClientId; let i = $index) {
|
||
@if (i > 0) {
|
||
<hr class="preview-nom-divider" />
|
||
}
|
||
<div class="preview-grid preview-grid--three">
|
||
<div class="preview-field">
|
||
<span class="preview-label">Name</span>
|
||
<span class="preview-value">{{ nom.nomineeDisplayName || '—' }}</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">Client ID</span>
|
||
<span class="preview-value preview-mono">{{ nom.nomineeClientId || '—' }}</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">Relationship</span>
|
||
<span class="preview-value">{{ nom.relationshipName || '—' }}</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">Share %</span>
|
||
<span class="preview-value">{{ nom.percentageShare ?? '—' }}%</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">Minor</span>
|
||
<span class="preview-value">{{ nom.nomineeIsMinor ? 'Yes' : 'No' }}</span>
|
||
</div>
|
||
@if (nom.guardianDisplayName) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Guardian</span>
|
||
<span class="preview-value">{{ nom.guardianDisplayName }}</span>
|
||
</div>
|
||
}
|
||
@if (nom.remarks) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Remarks</span>
|
||
<span class="preview-value">{{ nom.remarks }}</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ══ 6. Maturity Summary ═══════════════════════════════════ -->
|
||
@if (rdMaturityAmount) {
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-piggy-bank preview-section__icon"></i>
|
||
<span class="preview-section__title">Maturity Summary</span>
|
||
<span class="preview-section__badge preview-section__badge--active">Projected</span>
|
||
</div>
|
||
<div class="preview-body">
|
||
<div class="preview-grid preview-grid--three">
|
||
<div class="preview-field">
|
||
<span class="preview-label">Total Deposit Amount</span>
|
||
<span class="preview-value"
|
||
>{{ rdMaturityAmount - rdInterestEarned | formatNumber }} {{ currencyName }}</span
|
||
>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">
|
||
@if (rdIsSeniorCitizen && rdSeniorCitizenBonus > 0) {
|
||
<i class="fas fa-user-shield" style="color: #5b21b6; margin-right: 4px"></i>Sr. Citizen Rate
|
||
} @else {
|
||
Applicable Interest Rate
|
||
}
|
||
</span>
|
||
<span class="preview-value" [style.color]="rdIsSeniorCitizen && rdSeniorCitizenBonus > 0 ? '#5b21b6' : ''">
|
||
{{ rdAppliedInterestRate | formatNumber }}%
|
||
</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">Tenure</span>
|
||
<span class="preview-value">{{ selectedTenureLabel }}</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">Estimated Interest Earned</span>
|
||
<span class="preview-value" style="color: var(--theme-success, #16a34a); font-weight: 500">
|
||
+ {{ rdInterestEarned | formatNumber }} {{ currencyName }}
|
||
</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">Days in Year</span>
|
||
<span class="preview-value">{{ rdDaysInYear ?? 365 }} Days</span>
|
||
</div>
|
||
<div class="preview-field" style="grid-column: span 1">
|
||
<span class="preview-label">Projected Maturity Amount</span>
|
||
<span class="preview-value preview-mono" style="font-weight: 600; font-size: 1.25rem">
|
||
{{ rdMaturityAmount | formatNumber }} {{ currencyName }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ══ 7. Interest Rate Chart ═══════════════════════════════════ -->
|
||
@if (interestRateChartData?.length > 0) {
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-chart-line preview-section__icon"></i>
|
||
<span class="preview-section__title">{{ 'labels.heading.Interest Rate Chart' | translate }}</span>
|
||
@if (rdTenureDays) {
|
||
<span class="preview-section__badge preview-section__badge--tenure">
|
||
<i class="fas fa-calendar-check"></i> Selected Tenure: {{ selectedTenureLabel }}
|
||
</span>
|
||
}
|
||
</div>
|
||
<div class="preview-body">
|
||
<!-- Selected Tenure + Rate Banner -->
|
||
@if (rdTenureDays && matchedSlabIndex >= 0) {
|
||
<!-- Senior Citizen Status Banner -->
|
||
@if (rdIsSeniorCitizen) {
|
||
@if (rdSeniorCitizenBonus > 0) {
|
||
<div class="preview-sc-status preview-sc-status--applied">
|
||
<i class="fas fa-user-shield"></i>
|
||
<span>Senior Citizen Rate Applied</span>
|
||
<span class="preview-sc-status__bonus">+{{ rdSeniorCitizenBonus | formatNumber }}% bonus</span>
|
||
</div>
|
||
} @else {
|
||
<div class="preview-sc-status preview-sc-status--notice">
|
||
<i class="fas fa-info-circle"></i>
|
||
<span>Senior Citizen selected — no additional rate configured for this slab</span>
|
||
</div>
|
||
}
|
||
}
|
||
|
||
<div
|
||
class="preview-tenure-banner"
|
||
[class.preview-tenure-banner--sc]="rdIsSeniorCitizen && rdSeniorCitizenBonus > 0"
|
||
>
|
||
<div class="preview-tenure-banner__left">
|
||
<i class="fas fa-calendar-check"></i>
|
||
<div>
|
||
<span class="preview-tenure-banner__label">Your RD Tenure</span>
|
||
<span class="preview-tenure-banner__value">{{ selectedTenureLabel }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="preview-tenure-banner__divider"></div>
|
||
<div class="preview-tenure-banner__right">
|
||
<span class="preview-tenure-banner__rate-label">
|
||
@if (rdIsSeniorCitizen && rdSeniorCitizenBonus > 0) {
|
||
<i class="fas fa-user-shield" style="color: #5b21b6; margin-right: 4px"></i>Sr. Citizen Rate
|
||
} @else {
|
||
Applicable Interest Rate
|
||
}
|
||
</span>
|
||
<span
|
||
class="preview-tenure-banner__rate"
|
||
[style.color]="rdIsSeniorCitizen && rdSeniorCitizenBonus > 0 ? '#5b21b6' : ''"
|
||
>{{ rdAppliedInterestRate | formatNumber }}%</span
|
||
>
|
||
</div>
|
||
<div class="preview-tenure-banner__slab">
|
||
<i class="fas fa-layer-group"></i>
|
||
Slab: {{ interestRateChartData[matchedSlabIndex]?.fromPeriod }}–{{
|
||
interestRateChartData[matchedSlabIndex]?.toPeriod ?? '∞'
|
||
}}
|
||
{{ interestRateChartData[matchedSlabIndex]?.periodType?.value }}
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<div class="preview-grid preview-grid--three" style="margin-bottom: 1rem">
|
||
@if (chartName) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.name' | translate }}</span>
|
||
<span class="preview-value">{{ chartName }}</span>
|
||
</div>
|
||
}
|
||
@if (chartFromDate) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">Valid From</span>
|
||
<span class="preview-value">{{ chartFromDate | dateFormat }}</span>
|
||
</div>
|
||
}
|
||
<div class="preview-field">
|
||
<span class="preview-label">Primary Grouping by Amount</span>
|
||
<span class="preview-value preview-badge" [class.preview-badge--yes]="isPrimaryGroupingByAmount">
|
||
{{ isPrimaryGroupingByAmount | yesNo }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
@if (interestRateChartData?.length) {
|
||
<table
|
||
class="mat-elevation-z1 irc-table preview-table"
|
||
mat-table
|
||
[dataSource]="interestRateChartData"
|
||
multiTemplateDataRows
|
||
>
|
||
<ng-container matColumnDef="period">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Period' | translate }}</th>
|
||
<td mat-cell *matCellDef="let chartSlab">
|
||
{{ chartSlab.fromPeriod }} - {{ chartSlab.toPeriod }} {{
|
||
chartSlab.periodType?.value | translateKey: 'catalogs'
|
||
}}
|
||
</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="amountRange">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount Range' | translate }}</th>
|
||
<td mat-cell *matCellDef="let chartSlab">
|
||
{{ chartSlab.amountRangeFrom | formatNumber }} - {{ chartSlab.amountRangeTo | formatNumber }}
|
||
</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="interest">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Interest' | translate }}</th>
|
||
<td mat-cell *matCellDef="let chartSlab">{{ chartSlab.annualInterestRate | formatNumber }} %</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="description">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Description' | translate }}</th>
|
||
<td mat-cell *matCellDef="let chartSlab">{{ chartSlab.description }}</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="actions">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
||
<td mat-cell *matCellDef="let chartSlab; let chartSlabIndex = dataIndex">
|
||
<button
|
||
mat-button
|
||
color="primary"
|
||
(click)="expandChartSlabIndex = expandChartSlabIndex === chartSlabIndex ? null : chartSlabIndex"
|
||
>
|
||
<fa-icon
|
||
[icon]="expandChartSlabIndex === chartSlabIndex ? 'eye-slash' : 'eye'"
|
||
class="m-r-10"
|
||
></fa-icon>
|
||
{{
|
||
(expandChartSlabIndex === chartSlabIndex
|
||
? 'labels.buttons.Hide Incentives'
|
||
: 'labels.buttons.View Incentives'
|
||
) | translate
|
||
}}
|
||
</button>
|
||
</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="incentives">
|
||
<td
|
||
mat-cell
|
||
*matCellDef="let chartSlab; let chartSlabIndex = dataIndex"
|
||
[attr.colspan]="chartSlabsDisplayedColumns?.length"
|
||
>
|
||
<div
|
||
class="layout-row-wrap flex-100 incentives"
|
||
[@expandChartSlab]="chartSlabIndex === expandChartSlabIndex ? 'expanded' : 'collapsed'"
|
||
>
|
||
<mat-card class="layout-row-wrap flex-100">
|
||
<h4 class="m-b-10 flex-13">{{ 'labels.heading.Incentives' | translate }}</h4>
|
||
<table
|
||
class="flex-100 mat-elevation-z1"
|
||
mat-table
|
||
[dataSource]="chartSlab.incentives"
|
||
*ngIf="chartSlab.incentives?.length"
|
||
>
|
||
<ng-container matColumnDef="entityType">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Entity Type' | translate }}</th>
|
||
<td mat-cell *matCellDef="let incentive">{{ incentive.entityType?.value }}</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="attributeName">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Attribute Name' | translate }}</th>
|
||
<td mat-cell *matCellDef="let incentive">{{ incentive.attributeName?.value }}</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="conditionType">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Condition Type' | translate }}</th>
|
||
<td mat-cell *matCellDef="let incentive">{{ incentive.conditionType?.value | titlecase }}</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="attributeValue">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Attribute Value' | translate }}</th>
|
||
<td mat-cell *matCellDef="let incentive">{{ incentive.attributeValueDesc }}</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="incentiveType">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Incentive Type' | translate }}</th>
|
||
<td mat-cell *matCellDef="let incentive">{{ incentive.incentiveType?.value }}</td>
|
||
</ng-container>
|
||
<ng-container matColumnDef="amount">
|
||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Interest' | translate }}</th>
|
||
<td mat-cell *matCellDef="let incentive">{{ incentive.amount | formatNumber }}</td>
|
||
</ng-container>
|
||
<tr mat-header-row *matHeaderRowDef="incentivesDisplayedColumns"></tr>
|
||
<tr mat-row *matRowDef="let row; columns: incentivesDisplayedColumns"></tr>
|
||
</table>
|
||
</mat-card>
|
||
</div>
|
||
</td>
|
||
</ng-container>
|
||
<tr mat-header-row *matHeaderRowDef="chartSlabsDisplayedColumns"></tr>
|
||
<tr
|
||
mat-row
|
||
*matRowDef="let row; columns: chartSlabsDisplayedColumns; let i = dataIndex"
|
||
[class.preview-slab-row--matched]="i === matchedSlabIndex"
|
||
></tr>
|
||
<tr mat-row *matRowDef="let row; columns: chartSlabsIncentivesDisplayedColumns" class="incentives-row"></tr>
|
||
</table>
|
||
@if (rdTenureDays && matchedSlabIndex >= 0) {
|
||
<div class="preview-slab-legend">
|
||
<span class="preview-slab-legend__dot"></span>
|
||
Highlighted row is the slab applicable for your selected tenure of
|
||
<strong>{{ selectedTenureLabel }}</strong>
|
||
</div>
|
||
}
|
||
}
|
||
</div>
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<div class="preview-section">
|
||
<div class="preview-section__header">
|
||
<i class="fas fa-flag-checkered preview-section__icon"></i>
|
||
<span class="preview-section__title">Maturity & Pre-mature Closure</span>
|
||
</div>
|
||
<div class="preview-body">
|
||
<div class="preview-grid preview-grid--three">
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Maturity Instructions' | translate }}</span>
|
||
<span class="preview-value">{{ closureMaturityInstructionLabel }}</span>
|
||
</div>
|
||
@if (closureSettings?.transferToSavingsId) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Transfer to savings account' | translate }}</span>
|
||
<span class="preview-value">{{ closureTransferToSavingsLabel }}</span>
|
||
</div>
|
||
}
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Apply Penal Interest (less)' | translate }}</span>
|
||
<span class="preview-value">{{ closureSettings?.preClosurePenalApplicable | yesNo }}</span>
|
||
</div>
|
||
@if (closureSettings?.preClosurePenalApplicable) {
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Penal Interest' | translate }} (%)</span>
|
||
<span class="preview-value">{{ closureSettings?.preClosurePenalInterest }}%</span>
|
||
</div>
|
||
<div class="preview-field">
|
||
<span class="preview-label">{{ 'labels.inputs.Period' | translate }}</span>
|
||
<span class="preview-value">{{ closurePenalInterestOnTypeLabel }}</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@if (!hideButtons) {
|
||
<div class="layout-row responsive-column align-center gap-2px margin-t">
|
||
<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 [routerLink]="['../..']">
|
||
{{ 'labels.buttons.Cancel' | translate }}
|
||
</button>
|
||
<button mat-raised-button color="primary" class="preview-submit-row__primary" (click)="submitEvent.emit()">
|
||
<fa-icon icon="check" class="m-r-10"></fa-icon>
|
||
{{ 'labels.buttons.Submit' | translate }}
|
||
</button>
|
||
</div>
|
||
}
|