/** * 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 { 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'; import { businessToday } from 'app/core/utils/business-today'; interface DenomRow { denominationId: number; denominationValue: number; kind: string; count: number; amount: number; } interface VaultPos { tellerId: number; tellerName?: string; asOfDate: string; anchorDate?: string; hasBreakdown: boolean; totalAmount: number; denominations: DenomRow[]; } /** * Denomination-wise stock a vault (teller) is currently holding — "how much of what does the bank * have", the same question cashier-position answers for a cashier's own float. */ @Component({ selector: 'mifosx-vault-position', templateUrl: './vault-position.component.html', styleUrls: [ '../cashier-shared.scss', '../cashier-position/cashier-position.component.scss' ], standalone: true, imports: [ CommonModule, FormsModule, MatIconModule, MatFormFieldModule, MatSelectModule, MatInputModule ] }) export class VaultPositionComponent implements OnInit { private service = inject(CashierService); tellers: { id: number; name: string }[] = []; selectedTellerId: number | null = null; asOfDate = ''; loading = false; results: VaultPos[] = []; errorMsg = ''; ngOnInit(): void { const d = businessToday(); this.asOfDate = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; 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.selectedTellerId = this.tellers[0].id; } this.load(); }); } load(): void { this.loading = true; this.results = []; this.errorMsg = ''; this.service .vaultPosition(this.selectedTellerId ?? undefined, this.asOfDate) .pipe(catchError(() => of(null))) .subscribe((data: any) => { this.loading = false; if (!data) { this.errorMsg = 'Failed to load vault position.'; return; } const arr: any[] = Array.isArray(data) ? data : [data]; this.results = arr; }); } labelForTeller(id: number): string { const t = this.tellers.find((x) => x.id === id); return t ? t.name : `Vault #${id}`; } notes(pos: VaultPos): DenomRow[] { return pos.denominations.filter((d) => d.kind !== 'coin'); } coins(pos: VaultPos): DenomRow[] { return pos.denominations.filter((d) => d.kind === 'coin'); } }