feat(chequebook): add cheque book module with Maker-Checker workflow

Adds cheque series creation, stock receive, cheque leaf request/lookup,
and stop-payment/lost-reporting flows with Maker-Checker approval and
a checker-inbox tab for pending chequebook requests.
This commit is contained in:
Jaanu423
2026-07-15 11:02:46 +05:30
parent b9856a1c93
commit 7abfa394df
40 changed files with 4892 additions and 2 deletions
@@ -0,0 +1,128 @@
<!--
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="container">
<div class="page-header">
<h2>{{ 'labels.inputs.Leaf Lookup' | translate }}</h2>
<button mat-raised-button [routerLink]="['/chequebook']">{{ 'labels.buttons.Back' | translate }}</button>
</div>
<mat-card class="search-card">
<mat-card-content>
<form [formGroup]="searchForm" (ngSubmit)="search()" class="search-row">
<mat-form-field appearance="fill">
<mat-label>{{ 'labels.inputs.Cheque Leaf Serial No' | translate }}</mat-label>
<input matInput type="number" formControlName="serialNo" required />
</mat-form-field>
<button mat-raised-button color="primary" type="submit" [disabled]="searchForm.invalid || busy">
<fa-icon icon="magnifying-glass"></fa-icon>&nbsp;{{ 'labels.buttons.Search' | translate }}
</button>
</form>
</mat-card-content>
</mat-card>
@if (results.length > 1) {
<div class="multi-match-note">
{{
'labels.text.More than one series has a leaf with this serial number — check the series code against the physical cheque.'
| translate
}}
</div>
}
@for (result of results; track result.seriesCode + '-' + result.leafSerialNo) {
<mat-card class="result-card">
<mat-card-content>
<div class="result-header">
@if (result.seriesCode) {
<span class="series-tag">{{ result.seriesCode }}</span>
}
<span
class="pill big"
[ngClass]="
result.status === 'ACTIVE'
? 'ok'
: result.status === 'USED'
? 'used'
: result.status === 'STOP_PAYMENT'
? 'warn'
: result.status === 'LOST'
? 'bad'
: 'neutral'
"
>{{ result.status }}</span
>
</div>
<div class="info-grid">
<div>
<span class="k">{{ 'labels.inputs.Serial No' | translate }}</span
><span>{{ result.leafSerialNo }}</span>
</div>
<div>
<span class="k">{{ 'labels.inputs.Series' | translate }}</span
><span>{{ result.seriesCode || '–' }}</span>
</div>
@if (result.chequeBookId) {
<div>
<span class="k">{{ 'labels.inputs.Cheque Book' | translate }}</span
><span>#{{ result.chequeBookId }}</span>
</div>
<div>
<span class="k">{{ 'labels.inputs.Account' | translate }}</span
><span>{{ result.accountType }} {{ result.accountNo || result.accountId }}</span>
</div>
@if (result.clientName) {
<div>
<span class="k">{{ 'labels.inputs.Client' | translate }}</span
><span>{{ result.clientName }}</span>
</div>
}
} @else {
<div class="warn-note">
{{
'labels.text.This leaf was never issued to a customer — reported lost/stolen from stock.' | translate
}}
</div>
}
<div>
<span class="k">{{ 'labels.inputs.Office' | translate }}</span
><span>{{ result.officeName }}</span>
</div>
@if (result.status === 'STOP_PAYMENT') {
<div>
<span class="k">{{ 'labels.inputs.Stop Reason' | translate }}</span
><span>{{ result.stopReason }}</span>
</div>
}
@if (result.status === 'LOST') {
<div>
<span class="k">{{ 'labels.inputs.Lost Reason' | translate }}</span
><span>{{ result.lostReason }}</span>
</div>
}
@if (result.status === 'CANCELLED') {
<div>
<span class="k">{{ 'labels.inputs.Cancelled Reason' | translate }}</span
><span>{{ result.cancelledReason }}</span>
</div>
}
@if (result.status === 'USED' && result.usedReason) {
<div>
<span class="k">{{ 'labels.inputs.Used Reason' | translate }}</span
><span>{{ result.usedReason }}</span>
</div>
}
</div>
</mat-card-content>
</mat-card>
} @empty {
@if (searched && !busy) {
<div class="empty-state">{{ 'labels.text.No records to display' | translate }}</div>
}
}
</div>