feat(cashier): complete maker-checker withdrawal disbursement flow

- Clerk manually enters token no with today-date unique validation (backend enforced)
- Cashier token lookup: auto-fill amount/account, lock amount field, status-specific errors
- Confirm dialog shows CASH DISBURSEMENT (blue) with no balance re-deduction warning
- Disbursement denomination saved to m_savings_txn_denomination for vault position update
- Block direct cashier withdrawal without approved maker-checker token
- DISBURSED status shown in My Requests and Checker inbox views with cashier/time info
- Remove getNextToken() service method and m_daily_token_counter table (0326 migration)
- Race condition protection: UPDATE WHERE status=APPROVED prevents double disbursement

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-11 15:57:33 +05:30
co-authored by Claude Sonnet 4.6
parent de420bb8ab
commit 61cb09f352
26 changed files with 4822 additions and 248 deletions
+16 -3
View File
@@ -57,9 +57,9 @@
<!-- Toolbar always visible (shows CBSNexora, Branch, Session info) -->
<mifosx-toolbar [sidenav]="sidenav" (collapse)="toggleCollapse($event)"></mifosx-toolbar>
<!-- Simplified menu bar — cashier OR clerk fullscreen mode -->
@if (hideShellLayout) {
<!-- Clerk Dashboard fullscreen nav — only visible at /clerk/dashboard -->
<!-- Simplified menu bar — cashier OR clerk fullscreen mode. The review screen renders its own nav. -->
@if (hideShellLayout && !isWithdrawalReviewUrl) {
<!-- Clerk Dashboard fullscreen nav — only visible at /clerk/* -->
@if (currentUrl.includes('/clerk/')) {
<nav class="cashier-nav cashier-nav--clerk">
<a
@@ -69,6 +69,19 @@
>
<fa-icon icon="id-card"></fa-icon> Clerk Dashboard
</a>
<a
class="cashier-nav__item cashier-nav__item--sec"
[class.cashier-nav__item--active]="currentUrl.includes('/clerk/withdrawal-request')"
[routerLink]="['/clerk/withdrawal-requests']"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<line x1="9" y1="14" x2="15" y2="14" />
<line x1="9" y1="18" x2="13" y2="18" />
</svg>
My Requests
</a>
@if (clerkAccountLoaded) {
<a
class="cashier-nav__item cashier-nav__item--sec"
+10 -4
View File
@@ -134,8 +134,9 @@ export class ShellComponent implements OnInit, OnDestroy {
url.includes('/clerk/account-facilities') ||
url.includes('/clerk/photo-signature') ||
url.includes('/clerk/standing-instructions') ||
url.includes('/clerk/today-activity');
this.hideShellLayout = isCashierOnly || isClerkRoute;
url.includes('/clerk/today-activity') ||
url.includes('/clerk/withdrawal-request');
this.hideShellLayout = isCashierOnly || isClerkRoute || this.isWithdrawalReviewUrl;
this.cdr.detectChanges();
};
this.routeSub = this.router.events.pipe(filter((e) => e instanceof NavigationEnd)).subscribe(() => checkLayout());
@@ -167,10 +168,15 @@ export class ShellComponent implements OnInit, OnDestroy {
return this.router.url;
}
/** ESC key — exit fullscreen at Clerk Dashboard, restoring full shell header. */
/** True on the manager's full-screen withdrawal-request review screen. */
get isWithdrawalReviewUrl(): boolean {
return /\/checker-inbox-and-tasks\/withdrawal-requests\/\d+\/review/.test(this.router.url);
}
/** ESC key — exit fullscreen at Clerk Dashboard / review screen, restoring full shell header. */
@HostListener('document:keydown.escape')
onEscapeKey(): void {
if (this.hideShellLayout && this.router.url.includes('/clerk/')) {
if (this.hideShellLayout && (this.router.url.includes('/clerk/') || this.isWithdrawalReviewUrl)) {
this.hideShellLayout = false;
this.cdr.detectChanges();
}