feat(clerk): add GL disbursement maker-checker flow
- Add ClerkDisbursementService for submit/approve/reject HTTP calls - Add DisbursementRequestsComponent (manager inbox tab under checker tasks) - Add DisbursementReviewComponent (manager approve/reject detail page) - Add MyDisbursementRequestsComponent (clerk's own disbursement history) - Add 'My Disbursements' nav tab to clerk shell navigation header - Wire routes in cashier-routing and tasks-routing modules - Add 'My Disbursements' button to transaction-entry clerk footer - Include accountName in creditEntries payload for manager review display Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
89d1167145
commit
efb01d5441
@@ -0,0 +1,161 @@
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
<div class="mdr-page">
|
||||
<!-- ── HEADER ── -->
|
||||
<div class="mdr-header">
|
||||
<div class="mdr-header__left">
|
||||
<mat-icon class="mdr-header__icon">swap_horiz</mat-icon>
|
||||
<div>
|
||||
<div class="mdr-header__title">My Disbursement Requests</div>
|
||||
<div class="mdr-header__sub">Track your submitted GL disbursement requests.</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="mdr-btn mdr-btn--new" (click)="newDisbursement()">
|
||||
<mat-icon>add</mat-icon>
|
||||
New Disbursement
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ── FILTER BAR ── -->
|
||||
<div class="mdr-filter-bar">
|
||||
<select class="mdr-filter-select" [(ngModel)]="filterStatus" (change)="onFilterChange()">
|
||||
@for (opt of statusOptions; track opt.value) {
|
||||
<option [value]="opt.value">{{ opt.label }}</option>
|
||||
}
|
||||
</select>
|
||||
|
||||
<span class="mdr-fb-sep"></span>
|
||||
|
||||
<div class="mdr-presets">
|
||||
@for (p of datePresets; track p.key) {
|
||||
<button
|
||||
type="button"
|
||||
class="mdr-preset-chip"
|
||||
[class.mdr-preset-chip--active]="activePreset === p.key"
|
||||
(click)="applyPreset(p.key)"
|
||||
>
|
||||
{{ p.label }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<span class="mdr-fb-sep"></span>
|
||||
|
||||
<div class="mdr-date-range">
|
||||
<div class="mdr-date-box">
|
||||
<span class="mdr-date-box__lbl">From</span>
|
||||
<input
|
||||
class="mdr-date-box__input"
|
||||
[matDatepicker]="fromPicker"
|
||||
[(ngModel)]="fromDate"
|
||||
[max]="maxDate"
|
||||
(dateChange)="onFromDateChange()"
|
||||
readonly
|
||||
/>
|
||||
<button type="button" class="mdr-date-box__btn" (click)="fromPicker.open()">
|
||||
<mat-icon>event</mat-icon>
|
||||
</button>
|
||||
<mat-datepicker #fromPicker></mat-datepicker>
|
||||
</div>
|
||||
<span class="mdr-date-range__sep">—</span>
|
||||
<div class="mdr-date-box">
|
||||
<span class="mdr-date-box__lbl">To</span>
|
||||
<input
|
||||
class="mdr-date-box__input"
|
||||
[matDatepicker]="toPicker"
|
||||
[(ngModel)]="toDate"
|
||||
[min]="fromDate"
|
||||
[max]="maxDate"
|
||||
(dateChange)="onToDateChange()"
|
||||
readonly
|
||||
/>
|
||||
<button type="button" class="mdr-date-box__btn" (click)="toPicker.open()">
|
||||
<mat-icon>event</mat-icon>
|
||||
</button>
|
||||
<mat-datepicker #toPicker></mat-datepicker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="mdr-btn mdr-btn--refresh" (click)="load()" [disabled]="loading">
|
||||
<mat-icon>refresh</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ── TABLE ── -->
|
||||
@if (loading) {
|
||||
<div class="mdr-empty">
|
||||
<p class="mdr-empty__title">Loading…</p>
|
||||
</div>
|
||||
} @else if (loaded && requests.length === 0) {
|
||||
<div class="mdr-empty">
|
||||
<i class="fas fa-inbox mdr-empty__icon"></i>
|
||||
<p class="mdr-empty__title">No disbursement requests found</p>
|
||||
<p class="mdr-empty__text">You have not submitted any disbursement requests yet.</p>
|
||||
<button type="button" class="mdr-btn mdr-btn--new" style="margin-top: 12px" (click)="newDisbursement()">
|
||||
<mat-icon>add</mat-icon>
|
||||
New Disbursement
|
||||
</button>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="mdr-table-wrap">
|
||||
<table class="mdr-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Voucher No</th>
|
||||
<th>GL Account</th>
|
||||
<th class="num">DR Amount</th>
|
||||
<th>Credit Entries</th>
|
||||
<th>Payment Mode</th>
|
||||
<th>Reviewed By</th>
|
||||
<th>Submitted At</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (r of requests; track r.id) {
|
||||
<tr class="mdr-row">
|
||||
<td class="cell-mono mdr-voucher">{{ r.voucher_no || '—' }}</td>
|
||||
<td>
|
||||
<span class="cell-mono">{{ r.gl_account_no }}</span>
|
||||
<span class="mdr-gl-name">{{ r.gl_account_name }}</span>
|
||||
</td>
|
||||
<td class="num mdr-amount">₹{{ r.dr_amount | number: '1.2-2' }}</td>
|
||||
<td>
|
||||
<span class="mdr-credit-badge">{{ creditCount(r) }} account{{ creditCount(r) !== 1 ? 's' : '' }}</span>
|
||||
</td>
|
||||
<td>{{ r.payment_type_name || 'Transfer' }}</td>
|
||||
<td>{{ r.manager_name || r.manager_username || '—' }}</td>
|
||||
<td class="mdr-dt">{{ formatDateTime(r.submitted_at) }}</td>
|
||||
<td>
|
||||
<span class="mdr-status" [ngClass]="statusClass(r.status)">{{ statusLabel(r.status) }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
@if (r.status === 'REJECTED' && r.rejection_reason) {
|
||||
<tr class="mdr-reason-row">
|
||||
<td colspan="8" class="mdr-reason-cell">
|
||||
<mat-icon class="mdr-reason-icon">info</mat-icon>
|
||||
{{ r.rejection_reason }}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<mat-paginator
|
||||
[length]="totalRecords"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[pageIndex]="pageIndex"
|
||||
showFirstLastButtons
|
||||
(page)="onPageChange($event)"
|
||||
></mat-paginator>
|
||||
}
|
||||
</div>
|
||||
Reference in New Issue
Block a user