feat(cashier): maker-checker on cash transfer, real branches, compact Approval Center
- Cash Transfer now raises a request ("Send for approval") instead of posting. A My Requests button in the
page header opens the caller's requests with their status, and a Cash Transfers approvals page (review
popup with the full denomination breakdown, approve, reject with a reason) lets a different manager release
the cash.
- Transaction Entry's Branch dropdown lists the installation's real offices instead of a fixed list. A login
that cannot read offices is offered only its own branch (read-only), so no "Unauthorized Request" popup.
- Approval Center header and tab bar made compact so more tabs fit on a 100% window.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,9 +6,10 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { Component, OnInit, TemplateRef, ViewChild, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
@@ -39,6 +40,7 @@ interface Denom {
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
MatIconModule,
|
||||
MatDialogModule,
|
||||
MatSnackBarModule,
|
||||
MatFormFieldModule,
|
||||
MatSelectModule,
|
||||
@@ -48,6 +50,10 @@ interface Denom {
|
||||
export class CashTransferComponent implements OnInit {
|
||||
private service = inject(CashierService);
|
||||
private snackBar = inject(MatSnackBar);
|
||||
private dialog = inject(MatDialog);
|
||||
|
||||
@ViewChild('requestsDialog') requestsDialog!: TemplateRef<unknown>;
|
||||
private requestsRef: MatDialogRef<unknown> | null = null;
|
||||
|
||||
notes: Denom[] = [];
|
||||
coins: Denom[] = [];
|
||||
@@ -82,7 +88,34 @@ export class CashTransferComponent implements OnInit {
|
||||
vaultHasBreakdown = true;
|
||||
loadingVault = false;
|
||||
|
||||
/** The caller's own transfer requests, newest first, so a request can be followed to its approval. */
|
||||
myRequests: any[] = [];
|
||||
|
||||
loadMyRequests(): void {
|
||||
this.service
|
||||
.getCashTransferRequests(undefined, true)
|
||||
.pipe(catchError(() => of([] as any[])))
|
||||
.subscribe((rows) => (this.myRequests = rows.slice(0, 20)));
|
||||
}
|
||||
|
||||
/** How many of this person's requests are still waiting for a manager. */
|
||||
get pendingCount(): number {
|
||||
return this.myRequests.filter((r) => r.status === 'PENDING').length;
|
||||
}
|
||||
|
||||
/** Opens the list of this person's requests, freshly read, so a request can be followed to its approval. */
|
||||
openRequests(): void {
|
||||
this.loadMyRequests();
|
||||
this.requestsRef?.close();
|
||||
this.requestsRef = this.dialog.open(this.requestsDialog, { width: '720px', maxWidth: '95vw', autoFocus: false });
|
||||
}
|
||||
|
||||
closeRequests(): void {
|
||||
this.requestsRef?.close();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadMyRequests();
|
||||
const d = businessToday();
|
||||
this.transferDate = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
this.service.getActiveDenominations().subscribe({
|
||||
@@ -245,16 +278,11 @@ export class CashTransferComponent implements OnInit {
|
||||
.filter((d) => this.countFor(d) > 0)
|
||||
.map((d) => ({ denominationId: d.id, denominationValue: d.value, count: this.countFor(d) }))
|
||||
};
|
||||
this.service.createCashTransfer(payload).subscribe({
|
||||
next: (res: any) => {
|
||||
if (res?.glWarning) {
|
||||
this.snackBar.open(`Transfer saved (GL warning: ${res.glWarning})`, 'Close', {
|
||||
duration: 8000,
|
||||
panelClass: ['snack-warn']
|
||||
});
|
||||
} else {
|
||||
this.snackBar.open('Cash transfer recorded', 'Close', { duration: 3000 });
|
||||
}
|
||||
this.service.createCashTransferRequest(payload).subscribe({
|
||||
next: () => {
|
||||
// Nothing has moved yet: a manager approves the allocation first, and only then does the cash leave the vault.
|
||||
this.snackBar.open('Cash transfer sent for manager approval', 'Close', { duration: 5000 });
|
||||
this.loadMyRequests();
|
||||
this.clear();
|
||||
this.note = '';
|
||||
this.fromParty = '';
|
||||
|
||||
Reference in New Issue
Block a user