feat(ctsclearing): validation comparison popup, decided-item read-only view, and dialog-close fixes
Adds a "Comparison Details" popup on the inward decision dialog showing exactly what each automatic rule compared (amount figures vs words, instrument date vs its valid-until window, balance vs amount, RBI Positive Pay threshold, IQA sub-checks) instead of just the pass/fail pill, mirroring InwardClearingValidationService.java's actual logic. For an already-decided item, the stale/postdated "as of" date now shows the decision date rather than today's live date. Once a cheque is decided, the batch table's Validation column collapses to a single outcome pill instead of permanently showing all 7 rule badges, and the item-decision dialog itself switches to a read-only decision summary (outcome, decisioned-on, note, settlement status) instead of a re-openable Pass/Return form — the "Decide" action becomes "View" for decided rows. Also fixes two silently-broken directives: matTooltip and mat-dialog-close were used throughout the decision dialogs without MatTooltipModule / MatDialogClose being imported into the standalone components, so every tooltip and every close button did nothing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0207cf85a3
commit
a22f8e7e45
+179
-52
@@ -22,14 +22,34 @@
|
||||
<span class="k">{{ 'labels.inputs.Account' | translate }}</span
|
||||
><span>{{ item.accountNo || item.accountId || '–' }}</span>
|
||||
</div>
|
||||
@if (item.clientName) {
|
||||
<div>
|
||||
<span class="k">Account Holder</span><span>{{ item.clientName }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (accountInfo?.availableBalance !== null && accountInfo?.availableBalance !== undefined) {
|
||||
<div>
|
||||
<span class="k">Available Balance</span>
|
||||
<span>
|
||||
{{ accountInfo!.availableBalance }}
|
||||
@if (balanceSufficient !== null) {
|
||||
<span class="pill" [ngClass]="balanceSufficient ? 'ok' : 'bad'">
|
||||
{{ balanceSufficient ? 'Sufficient' : 'Insufficient' }}
|
||||
</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@if (
|
||||
accountInfo?.onHoldAmount !== null && accountInfo?.onHoldAmount !== undefined && accountInfo!.onHoldAmount !== 0
|
||||
) {
|
||||
<div>
|
||||
<span class="k">Existing Holds on Account</span><span>{{ accountInfo!.onHoldAmount }}</span>
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
<span class="k">Hold Placed</span><span>{{ item.holdPlaced ? (item.holdAmount ?? item.amount) : '–' }}</span>
|
||||
</div>
|
||||
@if (accountInfo?.availableBalance !== null && accountInfo?.availableBalance !== undefined) {
|
||||
<div>
|
||||
<span class="k">Available Balance</span><span>{{ accountInfo!.availableBalance }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<h3 class="section-title">Cheque Image</h3>
|
||||
@@ -142,12 +162,73 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
<h3 class="section-title">Automatic Validation</h3>
|
||||
<h3 class="section-title">Instrument Details</h3>
|
||||
<div class="info-grid">
|
||||
@if (item.micrCode) {
|
||||
<div>
|
||||
<span class="k">MICR Code</span><span>{{ item.micrCode }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (item.ifsc) {
|
||||
<div>
|
||||
<span class="k">IFSC</span><span>{{ item.ifsc }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (item.instrumentDate) {
|
||||
<div>
|
||||
<span class="k">Instrument Date</span><span>{{ item.instrumentDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (item.amountInWords) {
|
||||
<div>
|
||||
<span class="k">Amount in Words</span><span>{{ item.amountInWords }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (item.drawerAccountNumberMasked) {
|
||||
<div>
|
||||
<span class="k">Drawer A/C (as on cheque)</span><span>{{ item.drawerAccountNumberMasked }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (item.draweeBankName) {
|
||||
<div>
|
||||
<span class="k">Drawee Bank</span><span>{{ item.draweeBankName }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (item.instrumentTypeDescription || item.instrumentType) {
|
||||
<div>
|
||||
<span class="k">Instrument Type</span><span>{{ item.instrumentTypeDescription || item.instrumentType }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (item.ctsReferenceNumber) {
|
||||
<div>
|
||||
<span class="k">CTS Reference</span><span>{{ item.ctsReferenceNumber }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">Automatic Validation</h3>
|
||||
<button type="button" mat-stroked-button (click)="openValidationDetail()">Comparison Details</button>
|
||||
</div>
|
||||
@if (item.instrumentStopPaymentFlag) {
|
||||
<div class="gate-warning">Stop payment was flagged on this instrument by the depositing bank.</div>
|
||||
}
|
||||
<div class="check-badges">
|
||||
@for (rule of validationRules; track rule.key) {
|
||||
<span class="pill" [ngClass]="ruleClass(rule)" [title]="rule.label">{{ rule.label }}</span>
|
||||
}
|
||||
@if (item.iqaStatus && item.iqaStatus !== 'NA') {
|
||||
<span class="pill" [ngClass]="pillClass(item.iqaStatus)" matTooltip="Image Quality Assessment"
|
||||
>IQA: {{ item.iqaStatus }}</span
|
||||
>
|
||||
}
|
||||
</div>
|
||||
@if (item.iqaDetail) {
|
||||
<div class="muted">{{ item.iqaDetail }}</div>
|
||||
}
|
||||
@if (item.iqaSubChecks) {
|
||||
<div class="muted">{{ item.iqaSubChecks }}</div>
|
||||
}
|
||||
@if (item.validationDetail) {
|
||||
<div class="validation-detail muted">{{ item.validationDetail }}</div>
|
||||
}
|
||||
@@ -202,59 +283,105 @@
|
||||
}
|
||||
}
|
||||
|
||||
<div class="mode-toggle">
|
||||
<button mat-stroked-button type="button" [class.active]="mode === 'PASS'" color="primary" (click)="setMode('PASS')">
|
||||
{{ 'labels.buttons.Pass' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
[class.active]="mode === 'RETURN'"
|
||||
color="warn"
|
||||
(click)="setMode('RETURN')"
|
||||
>
|
||||
{{ 'labels.buttons.Return' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (mode === 'PASS') {
|
||||
<form [formGroup]="passForm">
|
||||
@if (!signatureVerified) {
|
||||
<div class="gate-warning">Signature verification required above before this cheque can be passed.</div>
|
||||
@if (item.decision) {
|
||||
<h3 class="section-title">Decision</h3>
|
||||
<div class="decided-summary" [ngClass]="pillClass(item.decision)">
|
||||
<div class="decided-headline">
|
||||
<span class="pill" [ngClass]="pillClass(item.decision)">{{ item.decision }}</span>
|
||||
@if (item.decisionedOn) {
|
||||
<span class="muted">on {{ item.decisionedOn | dateFormat }}</span>
|
||||
}
|
||||
</div>
|
||||
@if (item.returnReasonCode) {
|
||||
<div class="compare-row">
|
||||
<span class="compare-label">Return Reason Code</span
|
||||
><span class="compare-value">{{ item.returnReasonCode }}</span>
|
||||
</div>
|
||||
}
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{ 'labels.inputs.Note' | translate }}</mat-label>
|
||||
<input matInput formControlName="note" />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
@if (item.decisionReason) {
|
||||
<div class="compare-row">
|
||||
<span class="compare-label">Note</span><span class="compare-value">{{ item.decisionReason }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (item.settlementBatchId) {
|
||||
<div class="compare-row">
|
||||
<span class="compare-label">Settlement Batch</span
|
||||
><span class="compare-value">#{{ item.settlementBatchId }}</span>
|
||||
</div>
|
||||
} @else if (item.decision === 'PASS') {
|
||||
<div class="compare-row">
|
||||
<span class="compare-label">Settlement</span><span class="compare-value">Awaiting settlement</span>
|
||||
</div>
|
||||
}
|
||||
<div class="muted">
|
||||
This cheque has already been decided and cannot be decided again — this view is read-only.
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<form [formGroup]="returnForm">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{ 'labels.inputs.Return Reason Code' | translate }}</mat-label>
|
||||
<mat-select formControlName="returnReasonCode" required>
|
||||
@for (c of reasonCodes; track c.id) {
|
||||
<mat-option [value]="c.code">{{ c.code }} — {{ c.description }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{ 'labels.inputs.Reason' | translate }}</mat-label>
|
||||
<input matInput formControlName="reason" />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<div class="mode-toggle">
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
[class.active]="mode === 'PASS'"
|
||||
color="primary"
|
||||
(click)="setMode('PASS')"
|
||||
>
|
||||
{{ 'labels.buttons.Pass' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
[class.active]="mode === 'RETURN'"
|
||||
color="warn"
|
||||
(click)="setMode('RETURN')"
|
||||
>
|
||||
{{ 'labels.buttons.Return' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (mode === 'PASS') {
|
||||
<form [formGroup]="passForm">
|
||||
@if (!signatureVerified) {
|
||||
<div class="gate-warning">Signature verification required above before this cheque can be passed.</div>
|
||||
}
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{ 'labels.inputs.Note' | translate }}</mat-label>
|
||||
<input matInput formControlName="note" />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
} @else {
|
||||
<form [formGroup]="returnForm">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{ 'labels.inputs.Return Reason Code' | translate }}</mat-label>
|
||||
<mat-select formControlName="returnReasonCode" required>
|
||||
@for (c of reasonCodes; track c.id) {
|
||||
<mat-option [value]="c.code">{{ c.code }} — {{ c.description }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{ 'labels.inputs.Reason' | translate }}</mat-label>
|
||||
<input matInput formControlName="reason" />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
|
||||
<div #lightboxRoot style="display: none"></div>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions align="end">
|
||||
<button mat-raised-button type="button" mat-dialog-close>{{ 'labels.buttons.Cancel' | translate }}</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
[color]="mode === 'RETURN' ? 'warn' : 'primary'"
|
||||
(click)="submit()"
|
||||
[disabled]="busy || (mode === 'RETURN' ? returnForm.invalid : !signatureVerified)"
|
||||
>
|
||||
{{ (mode === 'PASS' ? 'labels.buttons.Pass' : 'labels.buttons.Return') | translate }}
|
||||
<button mat-raised-button type="button" mat-dialog-close>
|
||||
{{ (item.decision ? 'labels.buttons.Close' : 'labels.buttons.Cancel') | translate }}
|
||||
</button>
|
||||
@if (!item.decision) {
|
||||
<button
|
||||
mat-raised-button
|
||||
[color]="mode === 'RETURN' ? 'warn' : 'primary'"
|
||||
(click)="submit()"
|
||||
[disabled]="busy || (mode === 'RETURN' ? returnForm.invalid : !signatureVerified)"
|
||||
>
|
||||
{{ (mode === 'PASS' ? 'labels.buttons.Pass' : 'labels.buttons.Return') | translate }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user