/** * 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/. */ import { Component, OnInit, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatIconModule } from '@angular/material/icon'; import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; import { MatTabsModule } from '@angular/material/tabs'; import { forkJoin, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { CashierService } from '../cashier.service'; interface TxnRow { id: number; date: string; fromParty: string; toParty: string; amount: number; note: string; isReversed: boolean; performedByUsername: string; createdAt: string; type: 'transfer' | 'settlement' | 'inter'; } /** Unified history of all cash movements with reversal support. */ @Component({ selector: 'mifosx-cash-history', templateUrl: './cash-history.component.html', styleUrls: [ '../cashier-shared.scss', './cash-history.component.scss' ], standalone: true, imports: [ CommonModule, FormsModule, MatIconModule, MatSnackBarModule, MatTabsModule ] }) export class CashHistoryComponent implements OnInit { private service = inject(CashierService); private snackBar = inject(MatSnackBar); transfers: TxnRow[] = []; settlements: TxnRow[] = []; interCashier: TxnRow[] = []; loading = false; reversingId: number | null = null; reversingType: string | null = null; reverseReason = ''; ngOnInit(): void { this.load(); } load(): void { this.loading = true; forkJoin({ transfers: this.service.getCashTransfers().pipe(catchError(() => of([]))), settlements: this.service.getCashSettlements().pipe(catchError(() => of([]))), inter: this.service.getInterCashierTransfers().pipe(catchError(() => of([]))) }).subscribe(({ transfers, settlements, inter }) => { this.transfers = (transfers as any[]).map((t) => ({ id: t.id, date: t.transferDate, fromParty: t.fromParty, toParty: t.toParty, amount: t.transferAmount, note: t.note, isReversed: t.isReversed, performedByUsername: t.performedByUsername, createdAt: t.createdAt, type: 'transfer' })); this.settlements = (settlements as any[]).map((s) => ({ id: s.id, date: s.settlementDate, fromParty: s.fromParty, toParty: s.toParty, amount: s.settlementAmount, note: s.note, isReversed: s.isReversed, performedByUsername: s.performedByUsername, createdAt: s.createdAt, type: 'settlement' })); this.interCashier = (inter as any[]).map((i) => ({ id: i.id, date: i.transferDate, fromParty: i.fromParty, toParty: i.toParty, amount: i.transferAmount, note: i.note, isReversed: i.isReversed, performedByUsername: i.performedByUsername, createdAt: i.createdAt, type: 'inter' })); this.loading = false; }); } startReverse(row: TxnRow): void { this.reversingId = row.id; this.reversingType = row.type; this.reverseReason = ''; } cancelReverse(): void { this.reversingId = null; this.reversingType = null; this.reverseReason = ''; } confirmReverse(): void { if (!this.reversingId || !this.reversingType) return; const id = this.reversingId; const type = this.reversingType; const reason = this.reverseReason; this.cancelReverse(); let obs$; if (type === 'transfer') obs$ = this.service.reverseCashTransfer(id, reason); else if (type === 'settlement') obs$ = this.service.reverseCashSettlement(id, reason); else obs$ = this.service.reverseInterCashierTransfer(id, reason); obs$.pipe(catchError(() => of(null))).subscribe((res: any) => { if (res?.reversed) { this.snackBar.open('Transaction reversed successfully', 'Close', { duration: 3000 }); this.load(); } else { this.snackBar.open('Reversal failed', 'Close', { duration: 3000 }); } }); } }