987 lines
43 KiB
HTML
987 lines
43 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/.
|
||
-->
|
||
|
||
<form [formGroup]="currentAccountDetailsForm">
|
||
<!-- ══ Account Details ══════════════════════════════════════════ -->
|
||
<div class="sad-section">
|
||
<div class="sad-section__header">
|
||
<i class="fas fa-info-circle"></i>
|
||
<h3>Account Details</h3>
|
||
</div>
|
||
<div class="sad-grid">
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
||
<mat-select formControlName="productId" required>
|
||
@for (product of productData; track product) {
|
||
<mat-option [value]="product.id">{{ product.name }}</mat-option>
|
||
}
|
||
</mat-select>
|
||
<mat-error>
|
||
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||
</mat-error>
|
||
</mat-form-field>
|
||
|
||
@if (currentAccountProductSelected) {
|
||
<mat-form-field appearance="outline" class="sad-field" (click)="submittedOnDatePicker.open()">
|
||
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
||
<input
|
||
matInput
|
||
[min]="minDate"
|
||
[max]="maxDate"
|
||
[matDatepicker]="submittedOnDatePicker"
|
||
formControlName="submittedOnDate"
|
||
required
|
||
/>
|
||
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
||
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
||
<mat-error>
|
||
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||
</mat-error>
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
||
<mat-select formControlName="fieldOfficerId">
|
||
@for (officer of fieldOfficerData; track officer) {
|
||
<mat-option [value]="officer.id">{{ officer.displayName }}</mat-option>
|
||
}
|
||
</mat-select>
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>{{ 'labels.inputs.External ID' | translate }}</mat-label>
|
||
<input matInput formControlName="externalId" />
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="sad-field" (click)="establishmentDatePicker.open()">
|
||
<mat-label>Establishment Date</mat-label>
|
||
<input matInput [matDatepicker]="establishmentDatePicker" formControlName="establishmentDate" [max]="today" />
|
||
<mat-datepicker-toggle matSuffix [for]="establishmentDatePicker"></mat-datepicker-toggle>
|
||
<mat-datepicker #establishmentDatePicker></mat-datepicker>
|
||
<mat-hint>Date of entity / business establishment</mat-hint>
|
||
@if (currentAccountDetailsForm.get('establishmentDate')?.hasError('matDatepickerMax')) {
|
||
<mat-error>Establishment Date cannot be a future date.</mat-error>
|
||
}
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="sad-field" (click)="startOperationDatePicker.open()">
|
||
<mat-label>Start of Operation Date</mat-label>
|
||
<input
|
||
matInput
|
||
[matDatepicker]="startOperationDatePicker"
|
||
formControlName="startOperationDate"
|
||
[max]="today"
|
||
[min]="minStartOperationDate"
|
||
/>
|
||
<mat-datepicker-toggle matSuffix [for]="startOperationDatePicker"></mat-datepicker-toggle>
|
||
<mat-datepicker #startOperationDatePicker></mat-datepicker>
|
||
<mat-hint>Date when operations commenced</mat-hint>
|
||
@if (currentAccountDetailsForm.get('startOperationDate')?.hasError('matDatepickerMax')) {
|
||
<mat-error>Start of Operation Date cannot be a future date.</mat-error>
|
||
} @else if (currentAccountDetailsForm.get('startOperationDate')?.hasError('matDatepickerMin')) {
|
||
<mat-error>Start of Operation Date cannot be before Establishment Date.</mat-error>
|
||
}
|
||
</mat-form-field>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
@if (currentAccountProductSelected && productTemplate) {
|
||
<!-- ══ Product Classification (read-only) ══════════════════════ -->
|
||
<div class="sad-section sad-section--mt">
|
||
<div class="sad-section__header">
|
||
<i class="fas fa-tags"></i>
|
||
<h3>Product Classification</h3>
|
||
<span class="sad-badge">From Product</span>
|
||
</div>
|
||
<div class="sad-grid sad-grid--classification">
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>Account Type</mat-label>
|
||
<mat-select [(ngModel)]="selectedAccountType" [ngModelOptions]="{ standalone: true }" [disabled]="readOnly">
|
||
@for (opt of accountTypeOptions; track opt) {
|
||
<mat-option [value]="opt">{{ opt }}</mat-option>
|
||
}
|
||
</mat-select>
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>Customer Segment</mat-label>
|
||
<mat-select
|
||
[(ngModel)]="selectedCustomerSegment"
|
||
[ngModelOptions]="{ standalone: true }"
|
||
[disabled]="readOnly"
|
||
>
|
||
@for (opt of customerSegmentOptions; track opt) {
|
||
<mat-option [value]="opt">{{ opt }}</mat-option>
|
||
}
|
||
</mat-select>
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>Account No</mat-label>
|
||
<input
|
||
matInput
|
||
[value]="currentAccountTemplate?.accountNo || productTemplate?.previewAccountNo || 'Auto-generated'"
|
||
readonly
|
||
/>
|
||
</mat-form-field>
|
||
|
||
@if (productTemplate.effectiveDate) {
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>Effective Date</mat-label>
|
||
<input matInput [value]="formatTemplateDate(productTemplate.effectiveDate)" readonly />
|
||
</mat-form-field>
|
||
}
|
||
|
||
@if (productTemplate.effectiveTo) {
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>Effective To</mat-label>
|
||
<input matInput [value]="formatTemplateDate(productTemplate.effectiveTo)" readonly />
|
||
</mat-form-field>
|
||
}
|
||
|
||
@if (productTemplate.endDate) {
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>End Date</mat-label>
|
||
<input matInput [value]="formatTemplateDate(productTemplate.endDate)" readonly />
|
||
</mat-form-field>
|
||
}
|
||
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>Introducer Required</mat-label>
|
||
<input matInput [value]="productTemplate.introducerRequired ? 'Yes' : 'No'" readonly />
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="sad-field">
|
||
<mat-label>Share Linking Enabled</mat-label>
|
||
<mat-select [(ngModel)]="shareLinkingEnabled" [ngModelOptions]="{ standalone: true }" [disabled]="readOnly">
|
||
<mat-option [value]="true">Yes</mat-option>
|
||
<mat-option [value]="false">No</mat-option>
|
||
</mat-select>
|
||
</mat-form-field>
|
||
</div>
|
||
</div>
|
||
|
||
@if (productTemplate?.introducerRequired) {
|
||
<mifosx-introducer-search
|
||
[excludedClientIds]="primaryClientId ? [primaryClientId] : []"
|
||
[preselectedIntroducer]="
|
||
introducerId ? { clientId: introducerId, displayName: introducerName, cifNumber: introducerCif } : null
|
||
"
|
||
(introducerChange)="onIntroducerChange($event)"
|
||
[disabled]="readOnly"
|
||
></mifosx-introducer-search>
|
||
}
|
||
}
|
||
|
||
<!-- ══ Status of Account (RBI) — Individual Clients Only ════════ -->
|
||
@if (currentAccountProductSelected) {
|
||
<div class="soa-card">
|
||
<div class="soa-card__header">
|
||
<div class="soa-card__header-left">
|
||
<div class="soa-card__icon">
|
||
<i class="fas fa-id-card"></i>
|
||
</div>
|
||
<div>
|
||
<h3 class="soa-card__title">Status of Account</h3>
|
||
<span class="soa-card__sub">As per RBI guidelines — select account holder type</span>
|
||
</div>
|
||
</div>
|
||
<span class="soa-pill">RBI Compliant</span>
|
||
</div>
|
||
|
||
<!-- Auto-detected client info bar -->
|
||
@if (clientDob) {
|
||
<div class="soa-client-info-bar">
|
||
<div class="soa-client-info-bar__item">
|
||
<mat-icon>cake</mat-icon>
|
||
<span
|
||
>Date of Birth: <strong>{{ formatTemplateDate(clientDob) }}</strong></span
|
||
>
|
||
</div>
|
||
<div class="soa-client-info-bar__item">
|
||
<mat-icon>person_pin</mat-icon>
|
||
<span
|
||
>Age: <strong>{{ clientAge }} years</strong></span
|
||
>
|
||
</div>
|
||
@if (clientAge !== null && clientAge < 18) {
|
||
<span class="soa-auto-pill soa-auto-pill--minor">
|
||
<mat-icon>child_care</mat-icon> Auto-detected: Minor
|
||
</span>
|
||
} @else if (clientAge !== null && clientAge >= 60) {
|
||
<span class="soa-auto-pill soa-auto-pill--senior">
|
||
<mat-icon>elderly</mat-icon> Auto-detected: Senior Citizen
|
||
</span>
|
||
} @else {
|
||
<span class="soa-auto-pill soa-auto-pill--ok"><mat-icon>check_circle</mat-icon> Age Verified</span>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
<!-- Status selector tiles -->
|
||
<div class="soa-tiles">
|
||
@for (opt of accountStatusOptions; track opt.value) {
|
||
<button
|
||
type="button"
|
||
class="soa-tile"
|
||
[class.soa-tile--selected]="selectedAccountStatus === opt.value"
|
||
[class.soa-tile--disabled]="isTileDisabled(opt.value)"
|
||
[disabled]="isTileDisabled(opt.value)"
|
||
(click)="!isTileDisabled(opt.value) && (selectedAccountStatus = opt.value)"
|
||
[title]="getTileTooltip(opt.value)"
|
||
>
|
||
<mat-icon class="soa-tile__icon" [style.color]="isTileDisabled(opt.value) ? '#cbd5e1' : opt.color">
|
||
{{ opt.icon }}
|
||
</mat-icon>
|
||
<span class="soa-tile__label">{{ opt.label }}</span>
|
||
@if (isTileDisabled(opt.value)) {
|
||
<span class="soa-tile__lock"><mat-icon>lock</mat-icon></span>
|
||
}
|
||
@if (selectedAccountStatus === opt.value) {
|
||
<mat-icon class="soa-tile__check">check_circle</mat-icon>
|
||
}
|
||
</button>
|
||
}
|
||
</div>
|
||
|
||
<!-- ── JOINT ACCOUNT ─────────────────────────────────────────── -->
|
||
@if (selectedAccountStatus === 'JOINT') {
|
||
<div class="soa-block">
|
||
<div class="soa-block__head">
|
||
<div class="soa-block__head-left">
|
||
<span class="soa-block__letter" style="background: #8b5cf6">A</span>
|
||
<div>
|
||
<span class="soa-block__title">Joint Account Holders</span>
|
||
<span class="soa-block__hint">Add all co-applicants linked to this joint current account</span>
|
||
</div>
|
||
</div>
|
||
<button
|
||
mat-flat-button
|
||
color="primary"
|
||
type="button"
|
||
class="soa-add-btn"
|
||
[disabled]="totalJointSharePercentage >= 100 || readOnly"
|
||
(click)="openAddJointHolderDialog()"
|
||
>
|
||
<mat-icon>person_add</mat-icon> Add Holder
|
||
</button>
|
||
</div>
|
||
|
||
<div class="soa-table-wrap">
|
||
<table class="soa-tbl">
|
||
<thead>
|
||
<tr>
|
||
<th class="soa-th soa-th--sr">Sr.</th>
|
||
<th class="soa-th">Holder Name</th>
|
||
<th class="soa-th">CIF No.</th>
|
||
<th class="soa-th soa-th--center">Ownership %</th>
|
||
<th class="soa-th soa-th--center">Signing Authority</th>
|
||
<th class="soa-th soa-th--center">Role</th>
|
||
<th class="soa-th soa-th--center">Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<!-- Primary account holder row (non-removable) -->
|
||
<tr class="soa-tr soa-tr--primary">
|
||
<td class="soa-td soa-td--sr">1</td>
|
||
<td class="soa-td">
|
||
<div class="soa-client-cell">
|
||
<div class="soa-avatar soa-avatar--primary">
|
||
{{ primaryClientName ? primaryClientName[0].toUpperCase() : '?' }}
|
||
</div>
|
||
<div>
|
||
<span class="soa-client-name">{{ primaryClientName || 'Primary Holder' }}</span>
|
||
<span class="soa-primary-tag">Primary Account Holder</span>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td class="soa-td">
|
||
<span class="soa-mono">{{ primaryClientCif || '—' }}</span>
|
||
</td>
|
||
<td class="soa-td soa-td--center">
|
||
<input
|
||
class="soa-pct-input"
|
||
type="number"
|
||
min="1"
|
||
max="100"
|
||
[(ngModel)]="primaryHolderShare"
|
||
[ngModelOptions]="{ standalone: true }"
|
||
[disabled]="readOnly"
|
||
placeholder="0"
|
||
(input)="onPrimaryShareInput($event)"
|
||
(blur)="onPrimaryShareBlur($event)"
|
||
/>
|
||
<span class="soa-pct-unit">%</span>
|
||
@if (primaryHolderShare !== null && primaryHolderShare !== undefined && primaryHolderShare < 1) {
|
||
<div class="soa-pct-error">Min 1%</div>
|
||
}
|
||
</td>
|
||
<td class="soa-td soa-td--center">
|
||
<span class="soa-sign-toggle soa-sign-toggle--mandatory">
|
||
<mat-icon>lock</mat-icon> Mandatory
|
||
</span>
|
||
</td>
|
||
<td class="soa-td soa-td--center">Primary Account Holder</td>
|
||
<td class="soa-td soa-td--center"><span class="soa-no-action">—</span></td>
|
||
</tr>
|
||
|
||
<!-- Co-holders -->
|
||
@for (h of jointHolders; track h; let i = $index) {
|
||
<tr class="soa-tr" [class.soa-tr--mandatory]="h.isMandatory">
|
||
<td class="soa-td soa-td--sr">{{ i + 2 }}</td>
|
||
<td class="soa-td">
|
||
<div class="soa-client-cell">
|
||
<div class="soa-avatar" [class.soa-avatar--mandatory]="h.isMandatory">
|
||
{{ h.clientName[0].toUpperCase() }}
|
||
</div>
|
||
<div>
|
||
<span class="soa-client-name">{{ h.clientName }}</span>
|
||
@if (h.isMandatory) {
|
||
<span class="soa-mandatory-tag">Mandatory Signatory</span>
|
||
}
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td class="soa-td">
|
||
<span class="soa-mono">{{ h.clientCif || '—' }}</span>
|
||
</td>
|
||
<td class="soa-td soa-td--center">
|
||
<span class="soa-pct-badge" [class.soa-pct-badge--full]="h.sharePercentage === 100">
|
||
{{ h.sharePercentage }}%
|
||
</span>
|
||
</td>
|
||
<td class="soa-td soa-td--center">
|
||
<button
|
||
type="button"
|
||
class="soa-sign-toggle"
|
||
[class.soa-sign-toggle--mandatory]="h.isMandatory"
|
||
[class.soa-sign-toggle--optional]="!h.isMandatory"
|
||
[disabled]="readOnly"
|
||
(click)="!readOnly && toggleJointHolderMandatory(h)"
|
||
>
|
||
<mat-icon>{{ h.isMandatory ? 'lock' : 'lock_open' }}</mat-icon>
|
||
{{ h.isMandatory ? 'Mandatory' : 'Optional' }}
|
||
</button>
|
||
</td>
|
||
<td class="soa-td soa-td--center">Co-Holder</td>
|
||
<td class="soa-td soa-td--center">
|
||
<button
|
||
mat-icon-button
|
||
color="warn"
|
||
type="button"
|
||
[disabled]="readOnly"
|
||
(click)="removeJointHolder(i)"
|
||
>
|
||
<mat-icon>delete_outline</mat-icon>
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr class="soa-tr-total">
|
||
<td colspan="3" class="soa-td soa-td--total-label">Total Ownership</td>
|
||
<td class="soa-td soa-td--center">
|
||
<span
|
||
class="soa-pct-badge"
|
||
[class.soa-pct-badge--ok]="isJointOwnershipValid"
|
||
[class.soa-pct-badge--warn]="!isJointOwnershipValid"
|
||
>
|
||
{{ totalJointSharePercentage }}%
|
||
</span>
|
||
</td>
|
||
<td class="soa-td soa-td--center">
|
||
<span class="soa-mandatory-count">
|
||
<mat-icon>lock</mat-icon> {{ mandatoryJointCount + 1 }} Mandatory
|
||
</span>
|
||
</td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
|
||
@if (jointHolders.length === 0) {
|
||
<div class="soa-empty" style="margin-top: 8px">
|
||
<mat-icon>group_add</mat-icon>
|
||
<span>Click <strong>Add Holder</strong> to add joint account holders.</span>
|
||
</div>
|
||
}
|
||
|
||
@if (!isJointOwnershipValid) {
|
||
<div class="soa-alert soa-alert--warn" style="margin-top: 8px">
|
||
<mat-icon>warning_amber</mat-icon>
|
||
@if (totalJointSharePercentage !== 100) {
|
||
Total ownership is {{ totalJointSharePercentage }}%. It must equal 100%.
|
||
} @else {
|
||
Each holder must have at least 1% ownership share.
|
||
}
|
||
</div>
|
||
} @else {
|
||
<div class="soa-alert soa-alert--ok" style="margin-top: 8px">
|
||
<mat-icon>check_circle</mat-icon>
|
||
Ownership allocation is complete (100%).
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<!-- Mode of Operation -->
|
||
<div class="soa-block soa-block--sep">
|
||
<div class="soa-block__head">
|
||
<div class="soa-block__head-left">
|
||
<span class="soa-block__letter" style="background: #7c3aed">B</span>
|
||
<div>
|
||
<span class="soa-block__title">Mode of Operation</span>
|
||
<span class="soa-block__hint">Auto-set from mandatory holders — override manually if required</span>
|
||
</div>
|
||
</div>
|
||
@if (modeOfOperation) {
|
||
<span class="soa-auto-badge"><mat-icon>auto_awesome</mat-icon> Auto</span>
|
||
}
|
||
</div>
|
||
|
||
@if (jointHolders.length === 0) {
|
||
<div class="soa-empty">
|
||
<mat-icon>how_to_vote</mat-icon>
|
||
<span>Add joint holders in Section A first.</span>
|
||
</div>
|
||
} @else {
|
||
<div class="soa-mode-grid">
|
||
@for (opt of modeOfOperationOptions; track opt.value) {
|
||
<button
|
||
type="button"
|
||
class="soa-mode-card"
|
||
[class.soa-mode-card--selected]="modeOfOperation === opt.value"
|
||
[disabled]="readOnly"
|
||
(click)="!readOnly && (modeOfOperation = opt.value)"
|
||
>
|
||
<mat-icon class="soa-mode-card__icon">{{ opt.icon }}</mat-icon>
|
||
<div class="soa-mode-card__code">{{ opt.label }}</div>
|
||
<div class="soa-mode-card__desc">{{ opt.sublabel }}</div>
|
||
@if (modeOfOperation === opt.value) {
|
||
<mat-icon class="soa-mode-card__check">check_circle</mat-icon>
|
||
}
|
||
</button>
|
||
}
|
||
</div>
|
||
|
||
<div class="soa-mode-summary" [class.soa-mode-summary--set]="modeOfOperation">
|
||
@if (modeOfOperation) {
|
||
<mat-icon>how_to_vote</mat-icon>
|
||
<span
|
||
>Mode of Operation: <strong>{{ selectedModeLabel }}</strong></span
|
||
>
|
||
<span class="soa-mode-summary__from">
|
||
({{ mandatoryJointCount }} Mandatory + {{ jointHolders.length - mandatoryJointCount }} Optional)
|
||
</span>
|
||
} @else {
|
||
<mat-icon>info</mat-icon>
|
||
<span>Set Mandatory/Optional for holders above to auto-select mode.</span>
|
||
}
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
<!-- ── MINOR ACCOUNT ─────────────────────────────────────────── -->
|
||
@if (selectedAccountStatus === 'MINOR') {
|
||
<div class="soa-block">
|
||
<div class="soa-block__head">
|
||
<div class="soa-block__head-left">
|
||
<span class="soa-block__letter" style="background: #f59e0b">
|
||
<mat-icon style="font-size: 14px; height: 14px; width: 14px">child_care</mat-icon>
|
||
</span>
|
||
<div>
|
||
<span class="soa-block__title">Minor Account Details</span>
|
||
<span class="soa-block__hint">As per RBI — guardian must be an existing registered client</span>
|
||
</div>
|
||
</div>
|
||
<span class="soa-pill soa-pill--minor">Guardian Required</span>
|
||
</div>
|
||
|
||
<div class="soa-minor-grid">
|
||
<mat-form-field appearance="outline" class="soa-field" (click)="minorDobPicker.open()">
|
||
<mat-label>Minor's Date of Birth</mat-label>
|
||
<input
|
||
matInput
|
||
[matDatepicker]="minorDobPicker"
|
||
[max]="maxDate"
|
||
[(ngModel)]="minorDob"
|
||
[ngModelOptions]="{ standalone: true }"
|
||
[disabled]="readOnly"
|
||
/>
|
||
<mat-datepicker-toggle matSuffix [for]="minorDobPicker" [disabled]="readOnly"></mat-datepicker-toggle>
|
||
<mat-datepicker #minorDobPicker></mat-datepicker>
|
||
@if (clientDob) {
|
||
<mat-hint>Auto-filled from client profile</mat-hint>
|
||
}
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="soa-field">
|
||
<mat-label>Age</mat-label>
|
||
<input matInput [value]="clientAge !== null ? clientAge + ' years' : '—'" readonly />
|
||
<mat-hint>Must be below 18 years</mat-hint>
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="soa-field">
|
||
<mat-label>Date of Majority (Auto)</mat-label>
|
||
<input matInput [value]="majorityDate" readonly />
|
||
<mat-hint>Auto-calculated: DOB + 18 years</mat-hint>
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="soa-field">
|
||
<mat-label>Guardian Relationship</mat-label>
|
||
<mat-select
|
||
[(ngModel)]="guardianRelationship"
|
||
[ngModelOptions]="{ standalone: true }"
|
||
[disabled]="readOnly"
|
||
>
|
||
@for (rel of guardianRelationshipOptions; track rel) {
|
||
<mat-option [value]="rel">{{ rel }}</mat-option>
|
||
}
|
||
</mat-select>
|
||
</mat-form-field>
|
||
</div>
|
||
|
||
<div class="soa-guardian-search">
|
||
<div class="soa-guardian-search__label">
|
||
<mat-icon>manage_accounts</mat-icon>
|
||
<span>Search & Link Guardian (existing client)</span>
|
||
</div>
|
||
<mifosx-introducer-search
|
||
[excludedClientIds]="primaryClientId ? [primaryClientId] : []"
|
||
(introducerChange)="onGuardianChange($event)"
|
||
[disabled]="readOnly"
|
||
></mifosx-introducer-search>
|
||
|
||
@if (guardianName) {
|
||
<div class="soa-guardian-selected">
|
||
<mat-icon class="soa-guardian-selected__icon">verified_user</mat-icon>
|
||
<div>
|
||
<div class="soa-guardian-selected__name">{{ guardianName }}</div>
|
||
<div class="soa-guardian-selected__cif">CIF: {{ guardianCif || 'N/A' }}</div>
|
||
</div>
|
||
<span class="soa-pill soa-pill--ok">Guardian Linked</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<div class="soa-info-banner soa-info-banner--minor">
|
||
<mat-icon>info</mat-icon>
|
||
<ul>
|
||
<li>Minor below 10 years — Guardian must operate the account.</li>
|
||
<li>Minor above 10 years — Can operate independently (bank discretion).</li>
|
||
<li>Account must be converted to regular savings on attaining majority.</li>
|
||
<li>Fresh KYC of the minor must be done on majority.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── SENIOR CITIZEN ────────────────────────────────────────── -->
|
||
@if (selectedAccountStatus === 'SENIOR_CITIZEN') {
|
||
<div class="soa-block">
|
||
<div class="soa-block__head">
|
||
<div class="soa-block__head-left">
|
||
<span class="soa-block__letter" style="background: #10b981">
|
||
<mat-icon style="font-size: 14px; height: 14px; width: 14px">elderly</mat-icon>
|
||
</span>
|
||
<div>
|
||
<span class="soa-block__title">Senior Citizen Account</span>
|
||
<span class="soa-block__hint">Age ≥ 60 years — eligible for preferential banking services</span>
|
||
</div>
|
||
</div>
|
||
<span class="soa-pill soa-pill--senior">Age ≥ 60</span>
|
||
</div>
|
||
|
||
<div class="soa-minor-grid" style="margin-bottom: 16px">
|
||
<mat-form-field appearance="outline" class="soa-field">
|
||
<mat-label>Date of Birth</mat-label>
|
||
<input matInput [value]="formatTemplateDate(clientDob)" readonly />
|
||
<mat-hint>From client profile</mat-hint>
|
||
</mat-form-field>
|
||
|
||
<mat-form-field appearance="outline" class="soa-field">
|
||
<mat-label>Age</mat-label>
|
||
<input matInput [value]="clientAge !== null ? clientAge + ' years' : '—'" readonly />
|
||
<mat-hint>Age ≥ 60 required for Senior Citizen</mat-hint>
|
||
</mat-form-field>
|
||
</div>
|
||
|
||
<div class="soa-sc-benefits">
|
||
<div class="soa-sc-benefit">
|
||
<mat-icon>priority_high</mat-icon>
|
||
<div>
|
||
<strong>Priority Service</strong>
|
||
<span>Dedicated counters / assisted banking at branches</span>
|
||
</div>
|
||
</div>
|
||
<div class="soa-sc-benefit">
|
||
<mat-icon>percent</mat-icon>
|
||
<div>
|
||
<strong>Preferential Interest Rate</strong>
|
||
<span>Additional 0.25%–0.50% on deposits (as per product)</span>
|
||
</div>
|
||
</div>
|
||
<div class="soa-sc-benefit">
|
||
<mat-icon>health_and_safety</mat-icon>
|
||
<div>
|
||
<strong>Doorstep Banking</strong>
|
||
<span>Eligible for home banking services (RBI circular)</span>
|
||
</div>
|
||
</div>
|
||
<div class="soa-sc-benefit">
|
||
<mat-icon>gavel</mat-icon>
|
||
<div>
|
||
<strong>Nomination Mandatory</strong>
|
||
<span>RBI mandate — ensure nomination is registered</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="soa-info-banner soa-info-banner--senior">
|
||
<mat-icon>info</mat-icon>
|
||
<ul>
|
||
<li>Age proof (DOB document) mandatory at time of account opening.</li>
|
||
<li>Nomination registration is compulsory (Form DA-1).</li>
|
||
<li>Bank must offer dedicated helpline and priority grievance redressal.</li>
|
||
<li>Door-step banking services must be offered (RBI Master Direction 2023).</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── INDIVIDUAL MALE ───────────────────────────────────────── -->
|
||
@if (selectedAccountStatus === 'INDIVIDUAL_MALE') {
|
||
<div class="soa-block">
|
||
<div class="soa-block__head">
|
||
<div class="soa-block__head-left">
|
||
<span class="soa-block__letter" style="background: #3b82f6">
|
||
<mat-icon style="font-size: 14px; height: 14px; width: 14px">person</mat-icon>
|
||
</span>
|
||
<div>
|
||
<span class="soa-block__title">Individual Account (Male)</span>
|
||
<span class="soa-block__hint"
|
||
>Select special category if applicable — governs applicable schemes & benefits</span
|
||
>
|
||
</div>
|
||
</div>
|
||
<span class="soa-pill" style="background: #dbeafe; color: #1d4ed8; border-color: #bfdbfe"
|
||
>Special Category</span
|
||
>
|
||
</div>
|
||
|
||
<div class="soa-category-grid">
|
||
@for (opt of specialCategoryOptions; track opt.value) {
|
||
<button
|
||
type="button"
|
||
class="soa-cat-tile"
|
||
[class.soa-cat-tile--selected]="selectedSpecialCategory === opt.value"
|
||
[class.soa-cat-tile--male]="true"
|
||
[disabled]="readOnly"
|
||
(click)="!readOnly && (selectedSpecialCategory = opt.value)"
|
||
>
|
||
<mat-icon class="soa-cat-tile__icon">{{ opt.icon }}</mat-icon>
|
||
<span class="soa-cat-tile__label">{{ opt.label }}</span>
|
||
@if (selectedSpecialCategory === opt.value) {
|
||
<mat-icon class="soa-cat-tile__check">check_circle</mat-icon>
|
||
}
|
||
</button>
|
||
}
|
||
</div>
|
||
|
||
<div class="soa-info-banner soa-info-banner--male">
|
||
<mat-icon>info</mat-icon>
|
||
<ul>
|
||
<li>Valid KYC documents required (Aadhaar / PAN / Passport).</li>
|
||
<li>Nomination registration is mandatory (Form DA-1).</li>
|
||
<li>Defence / Ex-Serviceman — eligible for special savings schemes (DSSP).</li>
|
||
<li>NRI — FCNR / NRE / NRO account rules apply as per FEMA guidelines.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── INDIVIDUAL FEMALE ──────────────────────────────────────── -->
|
||
@if (selectedAccountStatus === 'INDIVIDUAL_FEMALE') {
|
||
<div class="soa-block">
|
||
<div class="soa-block__head">
|
||
<div class="soa-block__head-left">
|
||
<span class="soa-block__letter" style="background: #ec4899">
|
||
<mat-icon style="font-size: 14px; height: 14px; width: 14px">person_outline</mat-icon>
|
||
</span>
|
||
<div>
|
||
<span class="soa-block__title">Individual Account (Female)</span>
|
||
<span class="soa-block__hint"
|
||
>Select weaker section category if applicable — governs PMJDY & priority schemes</span
|
||
>
|
||
</div>
|
||
</div>
|
||
<span class="soa-pill" style="background: #fce7f3; color: #be185d; border-color: #f9a8d4"
|
||
>Weaker Section</span
|
||
>
|
||
</div>
|
||
|
||
<div class="soa-category-grid">
|
||
@for (opt of weakerSectionOptions; track opt.value) {
|
||
<button
|
||
type="button"
|
||
class="soa-cat-tile"
|
||
[class.soa-cat-tile--selected]="selectedWeakerSection === opt.value"
|
||
[class.soa-cat-tile--female]="true"
|
||
[disabled]="readOnly"
|
||
(click)="!readOnly && (selectedWeakerSection = opt.value)"
|
||
>
|
||
<mat-icon class="soa-cat-tile__icon">{{ opt.icon }}</mat-icon>
|
||
<span class="soa-cat-tile__label">{{ opt.label }}</span>
|
||
@if (selectedWeakerSection === opt.value) {
|
||
<mat-icon class="soa-cat-tile__check">check_circle</mat-icon>
|
||
}
|
||
</button>
|
||
}
|
||
</div>
|
||
|
||
<div class="soa-info-banner soa-info-banner--female">
|
||
<mat-icon>info</mat-icon>
|
||
<ul>
|
||
<li>Valid KYC documents required (Aadhaar / PAN / Passport).</li>
|
||
<li>Nomination registration is mandatory (Form DA-1).</li>
|
||
<li>BPL / SC-ST / Minority — eligible for zero-balance accounts (RBI Priority Sector norms).</li>
|
||
<li>PMJDY beneficiary — ₹10,000 overdraft facility & accident insurance cover applicable.</li>
|
||
<li>SHG members — group linkage lending & special deposit rates applicable.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
<!-- ══ Non-Individual Account Details (Indian Banking Style) ═════ -->
|
||
@if (isNonIndividualClient && currentAccountProductSelected) {
|
||
<div class="ni-card">
|
||
<!-- Card Header -->
|
||
<div class="ni-card__header">
|
||
<div class="ni-card__header-left">
|
||
<div class="ni-card__header-icon">
|
||
<i class="fas fa-building"></i>
|
||
</div>
|
||
<div>
|
||
<h3 class="ni-card__title">Non-Individual Account Details</h3>
|
||
<span class="ni-card__sub">As per RBI / Indian banking norms for non-individual entities</span>
|
||
</div>
|
||
</div>
|
||
<span class="ni-pill ni-pill--entity">Entity Account</span>
|
||
</div>
|
||
|
||
<!-- ── A. Partners / Members ─────────────────────────────── -->
|
||
<div class="ni-block">
|
||
<div class="ni-block__head">
|
||
<div class="ni-block__head-left">
|
||
<span class="ni-block__letter">A</span>
|
||
<div>
|
||
<span class="ni-block__title">Partners / Members</span>
|
||
<span class="ni-block__hint">Authorised persons linked to this entity account</span>
|
||
</div>
|
||
</div>
|
||
<button
|
||
mat-flat-button
|
||
color="primary"
|
||
type="button"
|
||
class="ni-add-btn"
|
||
[disabled]="totalSharePercentage >= 100 || readOnly"
|
||
(click)="openAddRelatedClientDialog()"
|
||
>
|
||
<mat-icon>person_add</mat-icon> Add Partner
|
||
</button>
|
||
</div>
|
||
|
||
@if (relatedClients.length > 0) {
|
||
<div class="ni-table-wrap">
|
||
<table class="ni-tbl">
|
||
<thead>
|
||
<tr>
|
||
<th class="ni-th ni-th--sr">Sr.</th>
|
||
<th class="ni-th">Partner Name</th>
|
||
<th class="ni-th">CIF No.</th>
|
||
<th class="ni-th ni-th--center">Ownership %</th>
|
||
<th class="ni-th ni-th--center">Signing Authority</th>
|
||
<th class="ni-th ni-th--center">Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (c of relatedClients; track c; let i = $index) {
|
||
<tr class="ni-tr" [class.ni-tr--mandatory]="c.isMandatory">
|
||
<td class="ni-td ni-td--sr">{{ i + 1 }}</td>
|
||
<td class="ni-td">
|
||
<div class="ni-client-cell">
|
||
<div class="ni-avatar" [class.ni-avatar--mandatory]="c.isMandatory">
|
||
{{ c.clientName[0].toUpperCase() }}
|
||
</div>
|
||
<div>
|
||
<span class="ni-client-name">{{ c.clientName }}</span>
|
||
@if (c.isMandatory) {
|
||
<span class="ni-mandatory-tag">Mandatory Signatory</span>
|
||
}
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td class="ni-td">
|
||
<span class="ni-mono">{{ c.clientCif || '—' }}</span>
|
||
</td>
|
||
<td class="ni-td ni-td--center">
|
||
<span class="ni-pct-badge" [class.ni-pct-badge--full]="c.sharePercentage === 100">
|
||
{{ c.sharePercentage }}%
|
||
</span>
|
||
</td>
|
||
<td class="ni-td ni-td--center">
|
||
<button
|
||
type="button"
|
||
class="ni-sign-toggle"
|
||
[class.ni-sign-toggle--mandatory]="c.isMandatory"
|
||
[class.ni-sign-toggle--optional]="!c.isMandatory"
|
||
[disabled]="readOnly"
|
||
(click)="!readOnly && toggleMandatory(c)"
|
||
[title]="c.isMandatory ? 'Click to set Optional' : 'Click to set Mandatory'"
|
||
>
|
||
<mat-icon>{{ c.isMandatory ? 'lock' : 'lock_open' }}</mat-icon>
|
||
{{ c.isMandatory ? 'Mandatory' : 'Optional' }}
|
||
</button>
|
||
</td>
|
||
<td class="ni-td ni-td--center">
|
||
<button
|
||
mat-icon-button
|
||
color="warn"
|
||
type="button"
|
||
[disabled]="readOnly"
|
||
(click)="removeRelatedClient(i)"
|
||
class="ni-del-btn"
|
||
>
|
||
<mat-icon>delete_outline</mat-icon>
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr class="ni-tr-total">
|
||
<td colspan="3" class="ni-td ni-td--total-label">Total Ownership</td>
|
||
<td class="ni-td ni-td--center">
|
||
<span
|
||
class="ni-pct-badge"
|
||
[class.ni-pct-badge--ok]="totalSharePercentage === 100"
|
||
[class.ni-pct-badge--warn]="totalSharePercentage !== 100"
|
||
>
|
||
{{ totalSharePercentage }}%
|
||
</span>
|
||
</td>
|
||
<td class="ni-td ni-td--center">
|
||
<span class="ni-mandatory-count-badge">
|
||
<mat-icon>lock</mat-icon> {{ mandatoryCount }} Mandatory
|
||
</span>
|
||
</td>
|
||
<td></td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
@if (totalSharePercentage !== 100) {
|
||
<div class="ni-alert ni-alert--warn">
|
||
<mat-icon>warning_amber</mat-icon>
|
||
Total ownership is {{ totalSharePercentage }}%. It must equal 100%.
|
||
</div>
|
||
} @else {
|
||
<div class="ni-alert ni-alert--ok">
|
||
<mat-icon>check_circle</mat-icon>
|
||
Ownership allocation is complete (100%).
|
||
</div>
|
||
}
|
||
} @else {
|
||
<div class="ni-empty-state">
|
||
<mat-icon>group_add</mat-icon>
|
||
<span>No partners added yet. Click <strong>Add Partner</strong> to begin.</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<!-- ── B. Mode of Operation / Signatory Rights (auto from partners) ── -->
|
||
<div class="ni-block ni-block--sep">
|
||
<div class="ni-block__head">
|
||
<div class="ni-block__head-left">
|
||
<span class="ni-block__letter ni-block__letter--b">B</span>
|
||
<div>
|
||
<span class="ni-block__title">Mode of Operation / Signatory Rights</span>
|
||
<span class="ni-block__hint">Auto-computed from Mandatory partners — you can override manually</span>
|
||
</div>
|
||
</div>
|
||
@if (signatoryRights) {
|
||
<span class="ni-auto-badge"> <mat-icon>auto_awesome</mat-icon> Auto </span>
|
||
}
|
||
</div>
|
||
|
||
@if (relatedClients.length === 0) {
|
||
<div class="ni-empty-state">
|
||
<mat-icon>how_to_vote</mat-icon>
|
||
<span>Add partners in Section A and set Mandatory/Optional to auto-compute the mode.</span>
|
||
</div>
|
||
} @else {
|
||
<div class="ni-signatory-grid">
|
||
@for (opt of signatoryRightsOptions; track opt.value) {
|
||
<button
|
||
type="button"
|
||
class="ni-sig-card"
|
||
[class.ni-sig-card--selected]="signatoryRights === opt.value"
|
||
[disabled]="readOnly"
|
||
(click)="!readOnly && (signatoryRights = opt.value)"
|
||
>
|
||
<mat-icon class="ni-sig-card__icon">{{ opt.icon }}</mat-icon>
|
||
<div class="ni-sig-card__code">{{ opt.label }}</div>
|
||
<div class="ni-sig-card__desc">{{ opt.sublabel }}</div>
|
||
@if (signatoryRights === opt.value) {
|
||
<mat-icon class="ni-sig-card__check">check_circle</mat-icon>
|
||
}
|
||
</button>
|
||
}
|
||
</div>
|
||
|
||
<div class="ni-mode-summary" [class.ni-mode-summary--set]="signatoryRights">
|
||
@if (signatoryRights) {
|
||
<mat-icon>how_to_vote</mat-icon>
|
||
<span
|
||
>Mode of Operation: <strong>{{ selectedSignatoryLabel }}</strong></span
|
||
>
|
||
<span class="ni-mode-summary__from">
|
||
({{ mandatoryCount }} Mandatory + {{ relatedClients.length - mandatoryCount }} Optional partner(s))
|
||
</span>
|
||
} @else {
|
||
<mat-icon>info</mat-icon>
|
||
<span>Set Mandatory/Optional for partners above to auto-select mode.</span>
|
||
}
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
@if (!hideButtons) {
|
||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||
<button mat-raised-button matStepperPrevious disabled>
|
||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||
{{ 'labels.buttons.Previous' | translate }}
|
||
</button>
|
||
<button mat-raised-button matStepperNext [disabled]="currentAccountDetailsForm.invalid">
|
||
{{ 'labels.buttons.Next' | translate }}
|
||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||
</button>
|
||
</div>
|
||
}
|
||
</form>
|