feat(chequebook): add cheque book module with Maker-Checker workflow
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.
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* 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 { Injectable, inject } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Cheque Book Inventory — stationery master, a unified stock ledger (HO receipt / branch transfer-in share
|
||||
* one drawdown-cursor concept), account issuance with per-leaf serial tracking, stop-payment and
|
||||
* lost/stolen hotlisting (both issued and unissued stock).
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ChequeBookService {
|
||||
private http = inject(HttpClient);
|
||||
|
||||
// ----- Series -----
|
||||
|
||||
/** status: 'ACTIVE' | 'INACTIVE'. Server-side paginated — always returns { pageItems, totalFilteredRecords }. */
|
||||
getSeries(status?: string, offset = 0, limit = 20): Observable<any> {
|
||||
let params = new HttpParams().set('offset', offset).set('limit', limit);
|
||||
if (status) params = params.set('status', status);
|
||||
return this.http.get('/chequeseries', { params });
|
||||
}
|
||||
|
||||
createSeries(payload: any): Observable<any> {
|
||||
return this.http.post('/chequeseries', payload);
|
||||
}
|
||||
|
||||
updateSeries(id: string | number, payload: any): Observable<any> {
|
||||
return this.http.put(`/chequeseries/${id}`, payload);
|
||||
}
|
||||
|
||||
deactivateSeries(id: string | number): Observable<any> {
|
||||
return this.http.delete(`/chequeseries/${id}`);
|
||||
}
|
||||
|
||||
// ----- Stock lots -----
|
||||
|
||||
/** Server-side paginated — always returns { pageItems, totalFilteredRecords }. */
|
||||
getStockLots(
|
||||
officeId?: string | number,
|
||||
seriesId?: string | number,
|
||||
status?: string,
|
||||
offset = 0,
|
||||
limit = 20
|
||||
): Observable<any> {
|
||||
let params = new HttpParams().set('offset', offset).set('limit', limit);
|
||||
if (officeId) params = params.set('officeId', officeId);
|
||||
if (seriesId) params = params.set('seriesId', seriesId);
|
||||
if (status) params = params.set('status', status);
|
||||
return this.http.get('/chequestocklots', { params });
|
||||
}
|
||||
|
||||
receiveStock(payload: any): Observable<any> {
|
||||
return this.http.post('/chequestocklots', payload);
|
||||
}
|
||||
|
||||
reportStockLotLost(lotId: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequestocklots/${lotId}`, payload, {
|
||||
params: new HttpParams().set('command', 'reportlost')
|
||||
});
|
||||
}
|
||||
|
||||
// ----- Stock transfers -----
|
||||
|
||||
/** Server-side paginated — always returns { pageItems, totalFilteredRecords }. */
|
||||
getStockTransfers(status?: string, offset = 0, limit = 20): Observable<any> {
|
||||
let params = new HttpParams().set('offset', offset).set('limit', limit);
|
||||
if (status) params = params.set('status', status);
|
||||
return this.http.get('/chequestocktransfers', { params });
|
||||
}
|
||||
|
||||
dispatchStockTransfer(payload: any): Observable<any> {
|
||||
return this.http.post('/chequestocktransfers', payload);
|
||||
}
|
||||
|
||||
receiveStockTransfer(transferId: string | number): Observable<any> {
|
||||
return this.http.post(
|
||||
`/chequestocktransfers/${transferId}`,
|
||||
{},
|
||||
{ params: new HttpParams().set('command', 'receive') }
|
||||
);
|
||||
}
|
||||
|
||||
rejectStockTransfer(transferId: string | number): Observable<any> {
|
||||
return this.http.post(
|
||||
`/chequestocktransfers/${transferId}`,
|
||||
{},
|
||||
{ params: new HttpParams().set('command', 'reject') }
|
||||
);
|
||||
}
|
||||
|
||||
// ----- Cheque books -----
|
||||
|
||||
getBooks(
|
||||
accountType?: string,
|
||||
accountId?: string | number,
|
||||
officeId?: string | number,
|
||||
status?: string,
|
||||
offset = 0,
|
||||
limit = 20
|
||||
): Observable<any> {
|
||||
let params = new HttpParams().set('offset', offset).set('limit', limit);
|
||||
if (accountType) params = params.set('accountType', accountType);
|
||||
if (accountId) params = params.set('accountId', accountId);
|
||||
if (officeId) params = params.set('officeId', officeId);
|
||||
if (status) params = params.set('status', status);
|
||||
return this.http.get('/chequebooks', { params });
|
||||
}
|
||||
|
||||
getBook(id: string | number): Observable<any> {
|
||||
return this.http.get(`/chequebooks/${id}`);
|
||||
}
|
||||
|
||||
getLeaves(bookId: string | number): Observable<any> {
|
||||
return this.http.get(`/chequebooks/${bookId}/leaves`);
|
||||
}
|
||||
|
||||
requestBook(payload: any): Observable<any> {
|
||||
return this.http.post('/chequebooks', payload);
|
||||
}
|
||||
|
||||
issueBook(bookId: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequebooks/${bookId}`, payload, { params: new HttpParams().set('command', 'issue') });
|
||||
}
|
||||
|
||||
rejectBook(bookId: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequebooks/${bookId}`, payload, { params: new HttpParams().set('command', 'reject') });
|
||||
}
|
||||
|
||||
withdrawBook(bookId: string | number): Observable<any> {
|
||||
return this.http.post(`/chequebooks/${bookId}`, {}, { params: new HttpParams().set('command', 'withdraw') });
|
||||
}
|
||||
|
||||
stopPayment(bookId: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequebooks/${bookId}`, payload, {
|
||||
params: new HttpParams().set('command', 'stoppayment')
|
||||
});
|
||||
}
|
||||
|
||||
revokeStopPayment(bookId: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequebooks/${bookId}`, payload, {
|
||||
params: new HttpParams().set('command', 'revokestoppayment')
|
||||
});
|
||||
}
|
||||
|
||||
reportBookLost(bookId: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequebooks/${bookId}`, payload, { params: new HttpParams().set('command', 'reportlost') });
|
||||
}
|
||||
|
||||
cancelLeaf(bookId: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequebooks/${bookId}`, payload, { params: new HttpParams().set('command', 'cancelleaf') });
|
||||
}
|
||||
|
||||
/** Manually record that leaves cleared, once the branch sees them on the bank/clearing statement. */
|
||||
markUsed(bookId: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequebooks/${bookId}`, payload, { params: new HttpParams().set('command', 'markused') });
|
||||
}
|
||||
|
||||
// ----- Pending requests (bespoke maker-checker for series/stock/book creation) -----
|
||||
|
||||
/**
|
||||
* A different user than the maker must approve/reject — enforced server-side, not just in this UI.
|
||||
* Server-side paginated and date-filtered, matching the clerk disbursement/withdrawal request queues.
|
||||
*/
|
||||
getPendingRequests(status?: string, fromDate?: string, toDate?: string, offset = 0, limit = 20): Observable<any> {
|
||||
let params = new HttpParams().set('offset', offset).set('limit', limit);
|
||||
if (status) params = params.set('status', status);
|
||||
if (fromDate) params = params.set('fromDate', fromDate);
|
||||
if (toDate) params = params.set('toDate', toDate);
|
||||
return this.http.get('/chequebook/pending-requests', { params });
|
||||
}
|
||||
|
||||
getPendingRequest(id: string | number): Observable<any> {
|
||||
return this.http.get(`/chequebook/pending-requests/${id}`);
|
||||
}
|
||||
|
||||
approvePendingRequest(id: string | number): Observable<any> {
|
||||
return this.http.post(
|
||||
`/chequebook/pending-requests/${id}`,
|
||||
{},
|
||||
{ params: new HttpParams().set('command', 'approve') }
|
||||
);
|
||||
}
|
||||
|
||||
rejectPendingRequest(id: string | number, payload: any): Observable<any> {
|
||||
return this.http.post(`/chequebook/pending-requests/${id}`, payload, {
|
||||
params: new HttpParams().set('command', 'reject')
|
||||
});
|
||||
}
|
||||
|
||||
// ----- Leaf lookup -----
|
||||
|
||||
lookupLeaf(serialNo: string | number): Observable<any> {
|
||||
return this.http.get('/chequeleaves/lookup', { params: new HttpParams().set('serialNo', serialNo) });
|
||||
}
|
||||
|
||||
// ----- Account lookup (by account number) -----
|
||||
|
||||
/** Search a Savings or Current account by its account number; returns a paginated list to match against. */
|
||||
searchAccountByNo(accountType: string, accountNo: string): Observable<any> {
|
||||
const base = accountType === 'CURRENT' ? '/currentaccounts' : '/savingsaccounts';
|
||||
const params = new HttpParams().set('accountNo', accountNo).set('offset', '0').set('limit', '20');
|
||||
return this.http.get(base, { params });
|
||||
}
|
||||
|
||||
// ----- Shared template data -----
|
||||
|
||||
getOffices(): Observable<any> {
|
||||
return this.http.get('/offices');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user