/** * 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 { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { MatInputModule } from '@angular/material/input'; import { of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { CashierService } from '../cashier.service'; interface Denom { id: number; value: number; kind: 'note' | 'coin'; label: string; } interface Cashier { id: number; label: string; } /** Move cash denomination-wise between two cashiers under the same teller. */ @Component({ selector: 'mifosx-inter-cashier-transfer', templateUrl: './inter-cashier-transfer.component.html', styleUrls: ['../cashier-shared.scss'], standalone: true, imports: [ CommonModule, FormsModule, MatIconModule, MatSnackBarModule, MatFormFieldModule, MatSelectModule, MatInputModule ] }) export class InterCashierTransferComponent implements OnInit { private service = inject(CashierService); private snackBar = inject(MatSnackBar); notes: Denom[] = []; coins: Denom[] = []; counts: Record = {}; transferDate = ''; note = ''; saving = false; tellers: { id: number; name: string }[] = []; allCashiers: Cashier[] = []; selectedTellerId: number | null = null; fromCashierId: number | null = null; toCashierId: number | null = null; fromParty = ''; toParty = ''; loadingCashiers = false; get denoms(): Denom[] { return [ ...this.notes, ...this.coins ]; } get availableFrom(): Cashier[] { return this.allCashiers; } get availableTo(): Cashier[] { return this.allCashiers.filter((c) => c.id !== this.fromCashierId); } ngOnInit(): void { const d = new Date(); this.transferDate = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; this.service.getActiveDenominations().subscribe({ next: (list) => { const rows: Denom[] = (list || []).map((x: any) => ({ id: x.id, value: x.denomination, kind: x.kind, label: '₹' + x.denomination })); this.notes = rows.filter((r) => r.kind === 'note').sort((a, b) => b.value - a.value); this.coins = rows.filter((r) => r.kind === 'coin').sort((a, b) => b.value - a.value); }, error: () => { this.notes = []; this.coins = []; } }); this.service .getTellers() .pipe(catchError(() => of([] as any[]))) .subscribe((list) => { this.tellers = (list || []).map((t: any) => ({ id: t.id, name: t.name })); if (this.tellers.length === 1) { this.onTellerSelect(this.tellers[0].id); } }); } onTellerSelect(tellerId: number): void { this.selectedTellerId = tellerId; this.fromCashierId = null; this.toCashierId = null; this.fromParty = ''; this.toParty = ''; this.allCashiers = []; if (!tellerId) return; this.loadingCashiers = true; this.service .getCashiersForTeller(tellerId) .pipe(catchError(() => of({ cashiers: [] }))) .subscribe((r: any) => { this.allCashiers = (r?.cashiers || []).map((c: any) => ({ id: c.id, label: c.staffName })); this.loadingCashiers = false; }); } onFromSelect(cashierId: number): void { this.fromCashierId = cashierId; const c = this.allCashiers.find((x) => x.id === cashierId); this.fromParty = c ? c.label : ''; if (this.toCashierId === cashierId) { this.toCashierId = null; this.toParty = ''; } } onToSelect(cashierId: number): void { this.toCashierId = cashierId; const c = this.allCashiers.find((x) => x.id === cashierId); this.toParty = c ? c.label : ''; } countFor(d: Denom): number { return this.counts[d.id] || 0; } subtotal(d: Denom): number { return this.countFor(d) * d.value; } onChange(d: Denom, raw: any): void { this.counts[d.id] = Math.max(0, Math.floor(Number(raw) || 0)); } bump(d: Denom, delta: number): void { this.counts[d.id] = Math.max(0, (this.counts[d.id] || 0) + delta); } get total(): number { return this.denoms.reduce((s, d) => s + this.subtotal(d), 0); } get valid(): boolean { return this.total > 0 && !!this.selectedTellerId && !!this.fromCashierId && !!this.toCashierId; } clear(): void { this.counts = {}; } submit(): void { if (!this.valid || this.saving) return; this.saving = true; const payload = { transferDate: this.transferDate, tellerId: this.selectedTellerId, fromCashierId: this.fromCashierId, toCashierId: this.toCashierId, fromParty: this.fromParty.trim(), toParty: this.toParty.trim(), note: this.note || null, denominations: this.denoms .filter((d) => this.countFor(d) > 0) .map((d) => ({ denominationId: d.id, denominationValue: d.value, count: this.countFor(d) })) }; this.service.createInterCashierTransfer(payload).subscribe({ next: () => { this.snackBar.open('Inter-cashier transfer recorded', 'Close', { duration: 3000 }); this.clear(); this.note = ''; this.fromCashierId = null; this.toCashierId = null; this.fromParty = ''; this.toParty = ''; this.saving = false; }, error: () => { this.saving = false; } }); } }