From 97d0545f074da0188bedbc1b5144b40c151c21a3 Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Thu, 23 Jul 2026 17:08:10 +0530 Subject: [PATCH] fix(chequebook,ctsclearing): surface global stock allocation in UI; fix decided-state check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Receive Stock form now displays the actual highest-allocated serial across every series (not just silently prefilling the field), so the operator can see and trust the auto-suggested range start rather than taking it on faith. Fixes a real bug where item.decision (which InwardClearingItem.java always sets to the literal string "PENDING" at creation, never null) was checked with a plain truthy test everywhere a "has this been decided yet" branch was needed. Since "PENDING" is a non-empty string, that check was always true — silently breaking the Decide/View button label, the collapsed validation summary, the "Awaiting settlement" text, and the decision dialog itself (which never showed the actual Pass/Return form, even for still-pending items). Replaced with a proper isDecided() check everywhere. Co-Authored-By: Claude Sonnet 5 --- .../stock-management.component.html | 13 +++++++++++- .../stock-management.component.scss | 13 ++++++++++++ .../stock-management.component.ts | 21 ++++++++++++------- .../inward-batch-detail.component.html | 20 ++++++++++-------- .../inward-batch-detail.component.ts | 8 +++++++ .../item-decision-dialog.component.html | 6 +++--- .../item-decision-dialog.component.ts | 8 +++++++ src/assets/translations/en-US.json | 3 +++ src/environments/.env.ts | 4 ++-- 9 files changed, 73 insertions(+), 23 deletions(-) diff --git a/src/app/chequebook/stock-management/stock-management.component.html b/src/app/chequebook/stock-management/stock-management.component.html index 0fe1e7026..817bbbb29 100644 --- a/src/app/chequebook/stock-management/stock-management.component.html +++ b/src/app/chequebook/stock-management/stock-management.component.html @@ -45,6 +45,12 @@ @if (showReceiveForm) { +
+ {{ 'labels.text.Highest leaf serial allocated so far, across every series' | translate }}: + {{ globalHighestAllocated }} + — {{ 'labels.text.next available start' | translate }}: + {{ globalHighestAllocated + 1 }} +
@@ -82,7 +88,12 @@ {{ 'labels.inputs.Range Start' | translate }} - {{ 'labels.text.Auto-suggested to avoid overlap; editable' | translate }} + {{ 'labels.text.Auto-suggested to avoid overlap across every series; editable' | translate }} ({{ + globalHighestAllocated + }} + + 1) {{ 'labels.inputs.Range End' | translate }} diff --git a/src/app/chequebook/stock-management/stock-management.component.scss b/src/app/chequebook/stock-management/stock-management.component.scss index b27993936..aa34846e9 100644 --- a/src/app/chequebook/stock-management/stock-management.component.scss +++ b/src/app/chequebook/stock-management/stock-management.component.scss @@ -92,6 +92,19 @@ margin-bottom: 16px; } +.global-allocation-note { + margin-bottom: 14px; + padding: 8px 12px; + border-radius: 4px; + background: rgb(25 118 210 / 8%); + color: rgb(0 0 0 / 70%); + font-size: 0.85rem; + + strong { + font-variant-numeric: tabular-nums; + } +} + .form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); diff --git a/src/app/chequebook/stock-management/stock-management.component.ts b/src/app/chequebook/stock-management/stock-management.component.ts index e49ebf95f..98d7b8b88 100644 --- a/src/app/chequebook/stock-management/stock-management.component.ts +++ b/src/app/chequebook/stock-management/stock-management.component.ts @@ -281,17 +281,22 @@ export class StockManagementComponent implements OnInit { return start + leaves - 1; } + /** Highest leaf-serial allocated so far, across every series/office — the actual number the "Range Start" + * suggestion is based on. Shown directly in the form (not just silently applied to the field) so the operator + * can see and trust the reasoning instead of taking a pre-filled number on faith. */ + get globalHighestAllocated(): number { + return this.allStockLots.reduce((m, l) => Math.max(m, l.rangeEnd || 0), 0); + } + /** - * Next unused serial WITHIN this series, so a fresh receipt continues that series' own numbering — - * each series is numbered independently (a separate stationery order from the press), so a different - * series' stock is not relevant here. + * Next unused serial across EVERY series, not just the one being received — a real bank allocates leaf-serial + * numbers from one bank-wide numbering authority, so two different cheque designs never legitimately reuse the + * same range. Suggesting a start that only avoids the same series' own lots is exactly what let two series end + * up overlapping (and, once both got issued to the same account, an unresolvable serial collision) before the + * backend's own overlap check was widened to match (ChequeStockLotRepository.countOverlappingAnySeries). */ private nextFreeSerial(): number { - const seriesId = this.receiveForm.get('seriesId')?.value; - const maxEnd = this.allStockLots - .filter((l) => l.seriesId === seriesId) - .reduce((m, l) => Math.max(m, l.rangeEnd || 0), 0); - return maxEnd + 1; + return this.globalHighestAllocated + 1; } onReceiveSeriesChange(): void { diff --git a/src/app/ctsclearing/inward-batch-detail/inward-batch-detail.component.html b/src/app/ctsclearing/inward-batch-detail/inward-batch-detail.component.html index c2f22fa5d..7ff9e1f69 100644 --- a/src/app/ctsclearing/inward-batch-detail/inward-batch-detail.component.html +++ b/src/app/ctsclearing/inward-batch-detail/inward-batch-detail.component.html @@ -160,7 +160,7 @@ {{ 'labels.inputs.Validation' | translate }} - @if (it.decision) { + @if (isDecided(it)) { {{ hasFailedChecks(it) ? 'Some checks failed' : 'All checks passed' }} @@ -191,7 +191,7 @@ {{ 'labels.inputs.Decision' | translate }} - @if (it.decision) { + @if (isDecided(it)) { {{ it.decision }} } @else { {{ it.itemStatus || 'PENDING' }} @@ -199,16 +199,18 @@ @if (it.returnReasonCode) {
{{ it.returnReasonCode }}
} - @if (it.decisionedOn) { + @if (isDecided(it) && it.decisionedOn) {
{{ it.decisionedOn | dateFormat }}
} - @if (it.decisionReason) { + @if (isDecided(it) && it.decisionReason) {
{{ it.decisionReason }}
} - @if (it.settlementBatchId) { -
Settlement #{{ it.settlementBatchId }}
- } @else if (it.decision) { -
Awaiting settlement
+ @if (isDecided(it)) { + @if (it.settlementBatchId) { +
Settlement #{{ it.settlementBatchId }}
+ } @else { +
Awaiting settlement
+ } }
@@ -244,7 +246,7 @@
diff --git a/src/app/ctsclearing/inward-batch-detail/inward-batch-detail.component.ts b/src/app/ctsclearing/inward-batch-detail/inward-batch-detail.component.ts index b076a35f5..8ae8f5b38 100644 --- a/src/app/ctsclearing/inward-batch-detail/inward-batch-detail.component.ts +++ b/src/app/ctsclearing/inward-batch-detail/inward-batch-detail.component.ts @@ -209,6 +209,14 @@ export class InwardBatchDetailComponent implements OnInit, OnDestroy { return this.rules.some((rule) => this.ruleClass(item, rule) === 'bad') || item?.iqaStatus === 'FAIL'; } + /** item.decision is NEVER null/empty — InwardClearingItem.java sets it to the literal string "PENDING" at + * creation, then "PASS"/"RETURN" once decided. A plain truthy check on it.decision is always true, which + * silently broke every "is this item decided yet" branch (View-vs-Decide, read-only summary, collapsed + * validation) into always showing the decided state, even for a still-PENDING item. */ + isDecided(item: any): boolean { + return item?.decision === 'PASS' || item?.decision === 'RETURN'; + } + runValidation(): void { if (this.busy) { return; diff --git a/src/app/ctsclearing/inward-batch-detail/item-decision-dialog/item-decision-dialog.component.html b/src/app/ctsclearing/inward-batch-detail/item-decision-dialog/item-decision-dialog.component.html index f0c920fac..ef174ef10 100644 --- a/src/app/ctsclearing/inward-batch-detail/item-decision-dialog/item-decision-dialog.component.html +++ b/src/app/ctsclearing/inward-batch-detail/item-decision-dialog/item-decision-dialog.component.html @@ -283,7 +283,7 @@ } } - @if (item.decision) { + @if (isDecided) {

Decision

@@ -372,9 +372,9 @@
- @if (!item.decision) { + @if (!isDecided) {