Files
cbsnexora/src/app/cashier/voucher-view/voucher-view.component.ts
T
keshav 9f95d01196
build-run / Run Lint, Build and Deploy (push) Canceled after 0s
Publish Image in Docker Hub / build (push) Canceled after 0s
Playwright E2E Tests / Run Playwright Tests (push) Canceled after 0s
Tests / testRigor (push) Canceled after 0s
done
2026-08-30 11:09:14 +05:30

144 lines
4.1 KiB
TypeScript

/**
* 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, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
import { catchError, of } from 'rxjs';
import { CashierService } from '../cashier.service';
/** One side of a voucher: a debit or a credit against a single account. */
interface VoucherLine {
lineNo: number;
entryType: 'DR' | 'CR';
glAccountId: number | null;
savingsAccountId: number | null;
loanAccountId: number | null;
partyName: string | null;
accountNo: string | null;
amount: number;
description: string | null;
}
interface Voucher {
voucherNo: string;
voucherType: string | null;
referenceType: string | null;
referenceId: number | null;
amount: number | null;
branchOfficeId: number | null;
branchName: string | null;
createdBy: string | null;
createdAt: string | null;
verificationStatus: string | null;
lines: VoucherLine[];
totalDebit: number;
totalCredit: number;
balanced: boolean;
}
/**
* One voucher, as a slip: which accounts were debited, which credited, and for how much.
*
* Looked up by voucher number rather than listed, because that is how a voucher is reached — from a
* paper slip in someone's hand, or from a number quoted on a statement.
*/
@Component({
selector: 'mifosx-voucher-view',
templateUrl: './voucher-view.component.html',
styleUrls: [
'../cashier-shared.scss',
'./voucher-view.component.scss'
],
standalone: true,
imports: [
CommonModule,
FormsModule,
MatIconModule
]
})
export class VoucherViewComponent {
private service = inject(CashierService);
private route = inject(ActivatedRoute);
private router = inject(Router);
voucherNo = '';
voucher: Voucher | null = null;
loading = false;
/** Shown in place of the slip — a bad number and a server error read differently to the user. */
error: string | null = null;
constructor() {
// A voucher number in the URL means someone arrived from a link or a statement rather than by
// typing, so the slip is fetched straight away instead of showing them an empty search box.
const fromUrl = this.route.snapshot.queryParamMap.get('voucherNo');
if (fromUrl) {
this.voucherNo = fromUrl;
this.search();
}
}
search(): void {
const trimmed = (this.voucherNo || '').trim();
if (!trimmed) {
this.error = 'Enter a voucher number.';
this.voucher = null;
return;
}
this.loading = true;
this.error = null;
// Kept in the URL so the slip can be linked to and survives a refresh.
this.router.navigate([], {
relativeTo: this.route,
queryParams: { voucherNo: trimmed },
replaceUrl: true
});
this.service
.voucherDetail(trimmed)
.pipe(
catchError((err: any) => {
this.error =
err?.error?.errors?.[0]?.defaultUserMessage ??
err?.error?.developerMessage ??
`Voucher ${trimmed} could not be loaded.`;
return of(null);
})
)
.subscribe((res: Voucher | null) => {
this.loading = false;
this.voucher = res;
});
}
/** Debits first, then credits — the order a slip is read in. */
get debitLines(): VoucherLine[] {
return (this.voucher?.lines ?? []).filter((l) => l.entryType === 'DR');
}
get creditLines(): VoucherLine[] {
return (this.voucher?.lines ?? []).filter((l) => l.entryType === 'CR');
}
/**
* A voucher raised before its sides were recorded. The voucher is real and its number valid — only
* the posting behind it was never captured — so this reads as an older record, not a broken one.
*/
get hasNoLines(): boolean {
return !!this.voucher && this.voucher.lines.length === 0;
}
print(): void {
window.print();
}
}