Adds a customer-account-scoped Cheque Book view under Clerk > Account Facilities (list + leaf detail, with stop-payment/report-lost/mark-used/ return actions), reachable via a new hover dropdown on the Account Facilities nav item. Also refreshes the Account Facilities page layout, and carries over a few chequebook service/detail-dialog fixes from the back-office module (used-leaf display, leaf-lookup dialog polish). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
227 lines
8.3 KiB
TypeScript
227 lines
8.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 { 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') });
|
|
}
|
|
|
|
/** Flag leaves as returned/dishonoured (insufficient funds, signature mismatch, etc.) — terminal, like reportlost. */
|
|
returnBook(bookId: string | number, payload: any): Observable<any> {
|
|
return this.http.post(`/chequebooks/${bookId}`, payload, { params: new HttpParams().set('command', 'return') });
|
|
}
|
|
|
|
// ----- 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');
|
|
}
|
|
}
|