Adds cheque series creation, stock receive, cheque leaf request/lookup, and stop-payment/lost-reporting flows with Maker-Checker approval and a checker-inbox tab for pending chequebook requests.
177 lines
5.0 KiB
TypeScript
177 lines
5.0 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/.
|
|
*/
|
|
|
|
/** Angular Imports */
|
|
import { Component, OnInit, inject } from '@angular/core';
|
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
|
import { RouterLink } from '@angular/router';
|
|
import { MatIconModule } from '@angular/material/icon';
|
|
import { MatButtonModule } from '@angular/material/button';
|
|
import { MatTableModule } from '@angular/material/table';
|
|
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
|
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
|
import { MatDialog } from '@angular/material/dialog';
|
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|
|
|
/** Custom Services */
|
|
import { ChequeBookService } from '../chequebook.service';
|
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
|
|
|
/** Cheque-leaf stationery master — leaves per book, CTS-2010 compliance, low-stock reorder threshold. */
|
|
@Component({
|
|
selector: 'mifosx-cheque-series',
|
|
templateUrl: './cheque-series.component.html',
|
|
styleUrls: ['./cheque-series.component.scss'],
|
|
imports: [
|
|
...STANDALONE_SHARED_IMPORTS,
|
|
RouterLink,
|
|
MatIconModule,
|
|
MatButtonModule,
|
|
MatTableModule,
|
|
MatPaginatorModule,
|
|
MatSnackBarModule,
|
|
FaIconComponent
|
|
]
|
|
})
|
|
export class ChequeSeriesComponent implements OnInit {
|
|
private formBuilder = inject(UntypedFormBuilder);
|
|
private chequeBookService = inject(ChequeBookService);
|
|
private dialog = inject(MatDialog);
|
|
private snackBar = inject(MatSnackBar);
|
|
|
|
private notify(message: string, isError = false): void {
|
|
this.snackBar.open(message, 'Dismiss', { duration: isError ? 6000 : 3000 });
|
|
}
|
|
|
|
seriesList: any[] = [];
|
|
showForm = false;
|
|
busy = false;
|
|
|
|
statusFilter = '';
|
|
pageSize = 10;
|
|
pageIndex = 0;
|
|
totalRecords = 0;
|
|
pageSizeOptions = [
|
|
10,
|
|
25,
|
|
50,
|
|
100
|
|
];
|
|
|
|
seriesForm: UntypedFormGroup;
|
|
|
|
columns = [
|
|
'seriesCode',
|
|
'name',
|
|
'leavesPerBook',
|
|
'applicableAccountType',
|
|
'ctsCompliant',
|
|
'reorderLevel',
|
|
'active',
|
|
'actions'
|
|
];
|
|
|
|
constructor() {
|
|
this.seriesForm = this.formBuilder.group({
|
|
seriesCode: [
|
|
'',
|
|
Validators.required
|
|
],
|
|
name: [
|
|
'',
|
|
Validators.required
|
|
],
|
|
leavesPerBook: [
|
|
25,
|
|
Validators.required
|
|
],
|
|
applicableAccountType: [
|
|
'',
|
|
Validators.required
|
|
],
|
|
ctsCompliant: [true],
|
|
reorderLevel: [10]
|
|
});
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
this.loadSeries();
|
|
}
|
|
|
|
loadSeries(): void {
|
|
this.chequeBookService
|
|
.getSeries(this.statusFilter || undefined, this.pageIndex * this.pageSize, this.pageSize)
|
|
.subscribe((res: any) => {
|
|
this.seriesList = res?.pageItems ?? [];
|
|
this.totalRecords = Number(res?.totalFilteredRecords ?? 0);
|
|
});
|
|
}
|
|
|
|
filterByStatus(status: string): void {
|
|
this.statusFilter = this.statusFilter === status ? '' : status;
|
|
this.pageIndex = 0;
|
|
this.loadSeries();
|
|
}
|
|
|
|
onPageChange(event: PageEvent): void {
|
|
this.pageSize = event.pageSize;
|
|
this.pageIndex = event.pageIndex;
|
|
this.loadSeries();
|
|
}
|
|
|
|
submit(): void {
|
|
if (this.seriesForm.invalid || this.busy) {
|
|
return;
|
|
}
|
|
this.busy = true;
|
|
const raw = this.seriesForm.value;
|
|
const payload: any = {
|
|
seriesCode: raw.seriesCode,
|
|
name: raw.name,
|
|
leavesPerBook: raw.leavesPerBook,
|
|
applicableAccountType: raw.applicableAccountType,
|
|
ctsCompliant: !!raw.ctsCompliant,
|
|
locale: 'en'
|
|
};
|
|
if (raw.reorderLevel !== '' && raw.reorderLevel != null) payload.reorderLevel = raw.reorderLevel;
|
|
|
|
this.chequeBookService.createSeries(payload).subscribe({
|
|
next: () => {
|
|
this.busy = false;
|
|
this.showForm = false;
|
|
this.seriesForm.reset({ leavesPerBook: 25, applicableAccountType: '', ctsCompliant: true, reorderLevel: 10 });
|
|
this.notify('Submitted for approval — see Approvals > Cheque Book Requests');
|
|
this.loadSeries();
|
|
},
|
|
error: () => {
|
|
this.busy = false;
|
|
this.notify('Failed to add cheque series', true);
|
|
}
|
|
});
|
|
}
|
|
|
|
deactivate(series: any): void {
|
|
const dialogRef = this.dialog.open(DeleteDialogComponent, {
|
|
data: { deleteContext: `cheque series ${series.seriesCode}` }
|
|
});
|
|
dialogRef.afterClosed().subscribe((response: any) => {
|
|
if (!response?.delete) {
|
|
return;
|
|
}
|
|
this.chequeBookService.deactivateSeries(series.id).subscribe({
|
|
next: () => {
|
|
this.notify('Submitted for approval — see Approvals > Cheque Book Requests');
|
|
this.loadSeries();
|
|
},
|
|
error: () => this.notify('Failed to submit for approval', true)
|
|
});
|
|
});
|
|
}
|
|
}
|