Files
cbsnexora/src/app/deposits/dds-approvals/dds-approval-review/dds-approval-review.component.ts
T

149 lines
4.6 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, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { OrganizationService } from 'app/organization/organization.service';
/**
* One DDS entry, laid out the way Disbursement & Transfers reviews a request - voucher and status on
* top, who made it and when, then the debit and credit entries it moves - and styled by the same
* sheet, so a checker reads both queues the same way.
*
* The checker approves it, which carries it out under the voucher numbered when it was sent, or
* rejects it, which leaves nothing done and cancels that number. Neither is offered to the user who
* made the entry, and the server refuses it as well.
*/
@Component({
selector: 'mifosx-dds-approval-review',
templateUrl: './dds-approval-review.component.html',
styleUrls: ['../../../clerk/disbursement-review/disbursement-review.component.scss'],
standalone: true,
imports: [
CommonModule,
MatDialogModule,
MatIconModule
]
})
export class DdsApprovalReviewComponent implements OnInit {
private organizationService = inject(OrganizationService);
private dialogRef = inject(MatDialogRef<DdsApprovalReviewComponent>);
data = inject<any>(MAT_DIALOG_DATA);
entry: any = this.data.entry;
/** The entry in full, with the debit and credit sides it moves. */
detail: any = null;
loading = true;
working = false;
confirmReject = false;
done = false;
error = '';
/** Set by an approval, in case the list row did not carry the number yet. */
approvedVoucher = '';
ngOnInit(): void {
this.load();
}
private load(): void {
this.organizationService.getDdsApproval(this.entry.id).subscribe({
next: (detail: any) => {
this.detail = detail;
this.entry = { ...this.entry, ...detail };
this.loading = false;
},
error: () => (this.loading = false)
});
}
get voucherNo(): string {
return this.approvedVoucher || this.entry?.voucherNo || '';
}
get debitLines(): any[] {
return (this.detail?.lines || []).filter((line: any) => line.side === 'DR');
}
get creditLines(): any[] {
return (this.detail?.lines || []).filter((line: any) => line.side === 'CR');
}
total(lines: any[]): number {
return lines.reduce((sum: number, line: any) => sum + Number(line.amount || 0), 0);
}
get isMine(): boolean {
return !!this.data.currentUserId && Number(this.entry.makerId) === Number(this.data.currentUserId);
}
get canAct(): boolean {
return this.entry.status === 'PENDING' && !this.isMine && !this.done && !this.working;
}
statusClass(status: string): string {
const map: Record<string, string> = {
PENDING: 'dri-status--pending',
APPROVED: 'dri-status--approved',
REJECTED: 'dri-status--rejected'
};
return map[status] ?? '';
}
act(command: 'approve' | 'reject'): void {
this.working = true;
this.error = '';
this.organizationService.actOnDdsApproval(this.entry.id, command).subscribe({
next: (result: any) => {
this.working = false;
this.done = true;
this.approvedVoucher = result?.voucherNo || '';
this.entry = {
...this.entry,
status: command === 'approve' ? 'APPROVED' : 'REJECTED',
voucherCancelled: command === 'reject' && !!this.voucherNo
};
// The approved entry's sides are now the voucher's own lines.
this.load();
},
error: (err: any) => {
this.working = false;
const body = err?.error;
this.error =
body?.errors?.[0]?.defaultUserMessage ||
body?.defaultUserMessage ||
body?.developerMessage ||
'Could not complete this.';
}
});
}
/** The same form Disbursement & Transfers prints its times in: 29 Sept 2026, 01:25 pm. */
formatDateTime(val: any): string {
if (!val) {
return '—';
}
const d = new Date(String(val).replace(' ', 'T'));
return isNaN(d.getTime())
? String(val)
: d.toLocaleString('en-IN', {
day: '2-digit',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
close(): void {
this.dialogRef.close(this.done);
}
}