/** * 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 { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { CashierService } from '../cashier.service'; /** Day-wise denomination-wise closing cash collected from deposits. */ @Component({ selector: 'mifosx-closing-cash-report', templateUrl: './closing-cash-report.component.html', styleUrls: ['../cashier-shared.scss'], standalone: true, imports: [ CommonModule, FormsModule, MatIconModule, MatPaginatorModule ] }) export class ClosingCashReportComponent implements OnInit { private service = inject(CashierService); date = ''; rows: any[] = []; exchangeRows: any[] = []; depositTotal = 0; withdrawalTotal = 0; netTotal = 0; loading = false; loaded = false; // GL View showGl = false; glRows: any[] = []; glTotalDr = 0; glTotalCr = 0; glLoading = false; glLoaded = false; // GL Pagination glTotal = 0; glOffset = 0; glPageSize = 20; glPageSizeOptions = [ 10, 20, 50, 100 ]; // Filters for GL tellers: any[] = []; cashiers: any[] = []; selectedTellerId: number | null = null; selectedCashierId: number | null = null; get noteRows(): any[] { return this.rows.filter((r) => r.kind !== 'coin'); } get coinRows(): any[] { return this.rows.filter((r) => r.kind === 'coin'); } get exchangeNoteRows(): any[] { return this.exchangeRows.filter((r) => r.kind !== 'coin'); } get exchangeCoinRows(): any[] { return this.exchangeRows.filter((r) => r.kind === 'coin'); } ngOnInit(): void { const d = new Date(); this.date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; this.load(); this.service.getTellers().subscribe({ next: (t: any[]) => (this.tellers = t || []), error: () => {} }); } load(): void { this.loading = true; this.service.closingCashReport(this.date).subscribe({ next: (res: any) => { this.rows = res?.rows || []; this.exchangeRows = res?.exchangeRows || []; this.depositTotal = Number(res?.depositTotal || 0); this.withdrawalTotal = Number(res?.withdrawalTotal || 0); this.netTotal = Number(res?.netTotal || 0); this.loading = false; this.loaded = true; }, error: () => { this.rows = []; this.exchangeRows = []; this.depositTotal = 0; this.withdrawalTotal = 0; this.netTotal = 0; this.loading = false; this.loaded = true; } }); // GL Entries is scoped to `this.date` too, but was never re-fetched here -- changing the date and // clicking Load left glLoaded=true from whatever date was open before, so a stale, wrong-date GL // table stayed on screen under a header that had already relabelled itself to the new date. If // the panel was open, refresh it for the date just loaded; otherwise just invalidate it so the // next time it's opened it fetches fresh instead of showing leftover rows. this.glOffset = 0; this.glLoaded = false; if (this.showGl) { this.loadGl(); } else { this.glRows = []; } } toggleGl(): void { this.showGl = !this.showGl; if (this.showGl && !this.glLoaded) { this.loadGl(); } } onTellerChange(): void { this.selectedCashierId = null; this.cashiers = []; if (this.selectedTellerId) { this.service.getCashiersForTeller(this.selectedTellerId).subscribe({ next: (res: any) => (this.cashiers = res?.cashiers || res || []), error: () => {} }); } this.glOffset = 0; this.glLoaded = false; this.loadGl(); } onCashierChange(): void { this.glOffset = 0; this.glLoaded = false; this.loadGl(); } onGlPageChange(event: PageEvent): void { this.glPageSize = event.pageSize; this.glOffset = event.pageIndex * event.pageSize; this.loadGl(); } loadGl(): void { this.glLoading = true; this.service .getGlEntries( this.date, this.selectedCashierId || undefined, this.selectedTellerId || undefined, this.glOffset, this.glPageSize ) .subscribe({ next: (res: any) => { this.glRows = res?.pageItems || []; this.glTotal = Number(res?.totalFilteredRecords || 0); this.glTotalDr = Number(res?.totalDr || 0); this.glTotalCr = Number(res?.totalCr || 0); this.glLoading = false; this.glLoaded = true; }, error: () => { this.glRows = []; this.glTotal = 0; this.glTotalDr = 0; this.glTotalCr = 0; this.glLoading = false; this.glLoaded = true; } }); } }