- CTS clearing module: inward batches (validate/pass/return), outward presentment capture, positive pay, settlement, reconciliation - Outward Clearing merged into one screen: Pending Cheques + Batches tabs (a real clearing session per NPCI submission, either the 2-hourly job or manual send), drill into a batch to settle/dishonour its cheques - Date-range, text, and quick (Today/Yesterday/All) filters with pagination across inward batches, outward presentments, outward batches, and batch detail - NACH clearing module (mandates, batches, transaction decisioning) - Shell/sidenav wiring for both new modules Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
162 lines
4.3 KiB
TypeScript
162 lines
4.3 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 { 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 { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|
|
|
/** Custom Services */
|
|
import { ClearingService } from '../../clearing-network/clearing.service';
|
|
import { pillClass, clearingError } from '../../clearing-network/clearing-status';
|
|
|
|
/**
|
|
* CTS view of the shared net-settlement batches — prepared from validated inward batches, posted to the GL
|
|
* or rejected. clearingType is fixed to 'CTS'.
|
|
*/
|
|
@Component({
|
|
selector: 'mifosx-cts-settlement',
|
|
templateUrl: './settlement.component.html',
|
|
styleUrls: ['./settlement.component.scss'],
|
|
imports: [
|
|
...STANDALONE_SHARED_IMPORTS,
|
|
MatIconModule,
|
|
MatButtonModule,
|
|
MatTableModule,
|
|
MatPaginatorModule,
|
|
MatSnackBarModule,
|
|
FaIconComponent
|
|
]
|
|
})
|
|
export class SettlementComponent implements OnInit {
|
|
private formBuilder = inject(UntypedFormBuilder);
|
|
private clearingService = inject(ClearingService);
|
|
private snackBar = inject(MatSnackBar);
|
|
|
|
readonly clearingType = 'CTS';
|
|
pillClass = pillClass;
|
|
|
|
private notify(message: string, isError = false): void {
|
|
this.snackBar.open(message, 'Dismiss', { duration: isError ? 6000 : 3000 });
|
|
}
|
|
|
|
batches: any[] = [];
|
|
totalRecords = 0;
|
|
pageSize = 10;
|
|
pageIndex = 0;
|
|
pageSizeOptions = [
|
|
10,
|
|
25,
|
|
50,
|
|
100
|
|
];
|
|
statusFilter = '';
|
|
busy = false;
|
|
|
|
rejectForId: number | null = null;
|
|
rejectForm: UntypedFormGroup;
|
|
|
|
columns = [
|
|
'id',
|
|
'settlementDate',
|
|
'sourceReference',
|
|
'passed',
|
|
'returned',
|
|
'netAmount',
|
|
'status',
|
|
'actions'
|
|
];
|
|
|
|
constructor() {
|
|
this.rejectForm = this.formBuilder.group({
|
|
reason: [
|
|
'',
|
|
Validators.required
|
|
]
|
|
});
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
this.loadBatches();
|
|
}
|
|
|
|
loadBatches(): void {
|
|
this.clearingService
|
|
.getSettlementBatches(
|
|
this.clearingType,
|
|
this.statusFilter || undefined,
|
|
this.pageIndex * this.pageSize,
|
|
this.pageSize
|
|
)
|
|
.subscribe((res: any) => {
|
|
this.batches = res?.pageItems ?? (Array.isArray(res) ? res : []);
|
|
this.totalRecords = Number(res?.totalFilteredRecords ?? this.batches.length);
|
|
});
|
|
}
|
|
|
|
filterByStatus(status: string): void {
|
|
this.statusFilter = this.statusFilter === status ? '' : status;
|
|
this.pageIndex = 0;
|
|
this.loadBatches();
|
|
}
|
|
|
|
onPageChange(event: PageEvent): void {
|
|
this.pageSize = event.pageSize;
|
|
this.pageIndex = event.pageIndex;
|
|
this.loadBatches();
|
|
}
|
|
|
|
post(batch: any): void {
|
|
if (this.busy) {
|
|
return;
|
|
}
|
|
this.busy = true;
|
|
this.clearingService.postSettlement(batch.id).subscribe({
|
|
next: () => {
|
|
this.busy = false;
|
|
this.notify('Settlement posted');
|
|
this.loadBatches();
|
|
},
|
|
error: (err: any) => {
|
|
this.busy = false;
|
|
this.notify(clearingError(err, 'Post failed'), true);
|
|
}
|
|
});
|
|
}
|
|
|
|
toggleReject(batch: any): void {
|
|
this.rejectForId = this.rejectForId === batch.id ? null : batch.id;
|
|
this.rejectForm.reset();
|
|
}
|
|
|
|
submitReject(id: number): void {
|
|
if (this.rejectForm.invalid || this.busy) {
|
|
return;
|
|
}
|
|
this.busy = true;
|
|
this.clearingService.rejectSettlement(id, { reason: this.rejectForm.value.reason, locale: 'en' }).subscribe({
|
|
next: () => {
|
|
this.busy = false;
|
|
this.rejectForId = null;
|
|
this.notify('Settlement rejected');
|
|
this.loadBatches();
|
|
},
|
|
error: (err: any) => {
|
|
this.busy = false;
|
|
this.notify(clearingError(err, 'Reject failed'), true);
|
|
}
|
|
});
|
|
}
|
|
}
|