Clerk submits a governed share redemption request (shares, Board Resolution No., Meeting Date, settlement Payment Type); Bank Manager reviews with a live backend-validated checklist (account/GL state, not just submit-time snapshot) before approving, which redeems the shares and settles the linked savings account in one transaction. Adds a Passbook tab to the Share Account view, a reusable search-select control, and a Savings Product "Map Payment Types to Specific Funds" fund-source override (only Loan Products had this before) so settlement can be GL-mapped correctly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
113 lines
3.0 KiB
TypeScript
113 lines
3.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/.
|
|
*/
|
|
|
|
import { Component, OnInit, inject } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { Router, RouterModule } from '@angular/router';
|
|
import { catchError } from 'rxjs/operators';
|
|
import { of } from 'rxjs';
|
|
|
|
import { ClerkShareWithdrawalService } from '../../cashier/clerk-share-withdrawal.service';
|
|
|
|
/** Clerk Dashboard "My Share Requests" — lists the share withdrawal requests this clerk has
|
|
* submitted and their approval status. Mirrors MyRequestsComponent/MyDisbursementRequestsComponent. */
|
|
@Component({
|
|
selector: 'mifosx-my-share-withdrawal-requests',
|
|
templateUrl: './my-share-withdrawal-requests.component.html',
|
|
styleUrls: [
|
|
'../../cashier/cashier-shared.scss',
|
|
'../clerk-shared.scss'
|
|
],
|
|
standalone: true,
|
|
imports: [
|
|
CommonModule,
|
|
FormsModule,
|
|
RouterModule
|
|
]
|
|
})
|
|
export class MyShareWithdrawalRequestsComponent implements OnInit {
|
|
private service = inject(ClerkShareWithdrawalService);
|
|
private router = inject(Router);
|
|
|
|
requests: any[] = [];
|
|
totalRecords = 0;
|
|
loading = false;
|
|
loaded = false;
|
|
|
|
filterStatus = '';
|
|
|
|
get statusOptions(): { value: string; label: string }[] {
|
|
return [
|
|
{ value: '', label: 'All' },
|
|
{ value: 'PENDING', label: 'Pending' },
|
|
{ value: 'APPROVED', label: 'Approved' },
|
|
{ value: 'REJECTED', label: 'Rejected' }
|
|
];
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
this.load();
|
|
}
|
|
|
|
load(): void {
|
|
this.loading = true;
|
|
this.service
|
|
.getMyRequests(this.filterStatus || undefined, 0, 50)
|
|
.pipe(catchError(() => of({ pageItems: [], totalFilteredRecords: 0 })))
|
|
.subscribe((res: any) => {
|
|
this.requests = res?.pageItems ?? [];
|
|
this.totalRecords = Number(res?.totalFilteredRecords ?? 0);
|
|
this.loading = false;
|
|
this.loaded = true;
|
|
});
|
|
}
|
|
|
|
onFilterChange(): void {
|
|
this.load();
|
|
}
|
|
|
|
newRequest(): void {
|
|
this.router.navigate(['/clerk/share-withdrawal-request']);
|
|
}
|
|
|
|
statusClass(status: string): string {
|
|
const map: Record<string, string> = {
|
|
PENDING: 'inv-badge--active',
|
|
APPROVED: 'inv-badge--matured',
|
|
REJECTED: 'inv-badge--written_off'
|
|
};
|
|
return `inv-badge ${map[status] ?? ''}`;
|
|
}
|
|
|
|
statusLabel(status: string): string {
|
|
const map: Record<string, string> = {
|
|
PENDING: 'Pending Approval',
|
|
APPROVED: 'Approved',
|
|
REJECTED: 'Rejected'
|
|
};
|
|
return map[status] ?? status;
|
|
}
|
|
|
|
formatDateTime(val: any): string {
|
|
if (!val) return '—';
|
|
try {
|
|
const d = new Date(val);
|
|
return d.toLocaleString('en-IN', {
|
|
day: '2-digit',
|
|
month: 'short',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
} catch {
|
|
return String(val);
|
|
}
|
|
}
|
|
}
|