Files
cbsnexora/src/app/ctsclearing/settlement/settlement.component.ts
T
Jaanu423andClaude Sonnet 5 6d205c0ed2
Playwright E2E Tests / Run Playwright Tests (push) Canceled after 0s
Tests / testRigor (push) Canceled after 0s
build-run / Run Lint, Build and Deploy (push) Canceled after 0s
Publish Image in Docker Hub / build (push) Canceled after 0s
feat(clearing): add CTS/NACH clearing UI with real NPCI batch sessions
- 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>
2026-07-21 15:57:45 +05:30

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);
}
});
}
}