feat(cashier): integrate expected balance, settlement verification, and reconciliation checks
This commit is contained in:
@@ -26,10 +26,26 @@ interface TxnRow {
|
||||
isReversed: boolean;
|
||||
performedByUsername: string;
|
||||
createdAt: string;
|
||||
reversedBy: number | null;
|
||||
reversedAt: string | null;
|
||||
reverseReason: string | null;
|
||||
type: 'transfer' | 'settlement' | 'inter';
|
||||
}
|
||||
|
||||
/** Unified history of all cash movements with reversal support. */
|
||||
interface DenomDetail {
|
||||
denominationId: number | null;
|
||||
denominationValue: number;
|
||||
count: number;
|
||||
totalAmount: number;
|
||||
}
|
||||
|
||||
interface DetailView {
|
||||
row: TxnRow;
|
||||
denominations: DenomDetail[];
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
/** Unified history of all cash movements with reversal and detail view support. */
|
||||
@Component({
|
||||
selector: 'mifosx-cash-history',
|
||||
templateUrl: './cash-history.component.html',
|
||||
@@ -59,6 +75,8 @@ export class CashHistoryComponent implements OnInit {
|
||||
reversingType: string | null = null;
|
||||
reverseReason = '';
|
||||
|
||||
detail: DetailView | null = null;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.load();
|
||||
}
|
||||
@@ -80,6 +98,9 @@ export class CashHistoryComponent implements OnInit {
|
||||
isReversed: t.isReversed,
|
||||
performedByUsername: t.performedByUsername,
|
||||
createdAt: t.createdAt,
|
||||
reversedBy: t.reversedBy ?? null,
|
||||
reversedAt: t.reversedAt ?? null,
|
||||
reverseReason: t.reverseReason ?? null,
|
||||
type: 'transfer'
|
||||
}));
|
||||
this.settlements = (settlements as any[]).map((s) => ({
|
||||
@@ -92,6 +113,9 @@ export class CashHistoryComponent implements OnInit {
|
||||
isReversed: s.isReversed,
|
||||
performedByUsername: s.performedByUsername,
|
||||
createdAt: s.createdAt,
|
||||
reversedBy: s.reversedBy ?? null,
|
||||
reversedAt: s.reversedAt ?? null,
|
||||
reverseReason: s.reverseReason ?? null,
|
||||
type: 'settlement'
|
||||
}));
|
||||
this.interCashier = (inter as any[]).map((i) => ({
|
||||
@@ -104,12 +128,40 @@ export class CashHistoryComponent implements OnInit {
|
||||
isReversed: i.isReversed,
|
||||
performedByUsername: i.performedByUsername,
|
||||
createdAt: i.createdAt,
|
||||
reversedBy: i.reversedBy ?? null,
|
||||
reversedAt: i.reversedAt ?? null,
|
||||
reverseReason: i.reverseReason ?? null,
|
||||
type: 'inter'
|
||||
}));
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
viewDetail(row: TxnRow): void {
|
||||
this.detail = { row, denominations: [], loading: true };
|
||||
|
||||
let obs$;
|
||||
if (row.type === 'transfer') obs$ = this.service.getCashTransferById(row.id);
|
||||
else if (row.type === 'settlement') obs$ = this.service.getCashSettlementById(row.id);
|
||||
else obs$ = this.service.getInterCashierById(row.id);
|
||||
|
||||
obs$.pipe(catchError(() => of(null))).subscribe((data: any) => {
|
||||
if (this.detail) {
|
||||
this.detail.denominations = (data?.details || []).map((d: any) => ({
|
||||
denominationId: d.denominationId ?? null,
|
||||
denominationValue: d.denominationValue,
|
||||
count: d.count,
|
||||
totalAmount: d.totalAmount
|
||||
}));
|
||||
this.detail.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeDetail(): void {
|
||||
this.detail = null;
|
||||
}
|
||||
|
||||
startReverse(row: TxnRow): void {
|
||||
this.reversingId = row.id;
|
||||
this.reversingType = row.type;
|
||||
@@ -143,4 +195,12 @@ export class CashHistoryComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
typeLabel(type: string): string {
|
||||
return type === 'transfer' ? 'Cash Transfer' : type === 'settlement' ? 'Cash Settlement' : 'Inter-Cashier Transfer';
|
||||
}
|
||||
|
||||
totalCount(denoms: DenomDetail[]): number {
|
||||
return denoms.reduce((s, d) => s + d.count, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user