- Transaction entry: detect collection mode (N DR savings → 1 CR GL) and disbursement mode (1 DR GL → N CR savings); build dbCr-tagged payload for backend - Disbursement review: show 'GL Credit (Target)' / 'Debit Entries' labels for collection; fix double-count by summing only DR entries for collection totals; dynamic approve button text - My disbursement requests: add DR/CR badge column in expanded credit-entries preview - My requests / withdrawal-requests: minor consistency updates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
207 lines
7.3 KiB
HTML
207 lines
7.3 KiB
HTML
<!--
|
|
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>
|
|
<button type="button" class="mdr-credit-badge mdr-credit-badge--btn" (click)="toggleExpand(r.id)">
|
|
{{ creditCount(r) }} account{{ creditCount(r) !== 1 ? 's' : '' }}
|
|
<mat-icon class="mdr-credit-chevron" [class.mdr-credit-chevron--open]="expandedIds.has(r.id)">
|
|
expand_more
|
|
</mat-icon>
|
|
</button>
|
|
</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 (expandedIds.has(r.id)) {
|
|
<tr class="mdr-expand-row">
|
|
<td colspan="8" class="mdr-expand-cell">
|
|
<div class="mdr-credit-preview">
|
|
<table class="mdr-credit-table">
|
|
<thead>
|
|
<tr>
|
|
<th>DR/CR</th>
|
|
<th>Account No</th>
|
|
<th>Account Name</th>
|
|
<th>Type</th>
|
|
<th class="num">Amount (₹)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (ce of getCreditEntries(r); track ce.accountNo) {
|
|
<tr>
|
|
<td>
|
|
<span
|
|
class="mdr-actype"
|
|
[style.background]="ce.dbCr === 'Dr' ? '#fee2e2' : '#dcfce7'"
|
|
[style.color]="ce.dbCr === 'Dr' ? '#991b1b' : '#166534'"
|
|
>
|
|
{{ ce.dbCr || 'CR' }}
|
|
</span>
|
|
</td>
|
|
<td class="cell-mono">{{ ce.accountNo || '—' }}</td>
|
|
<td>{{ ce.accountName || '—' }}</td>
|
|
<td>
|
|
<span class="mdr-actype">{{ ce.accountType || '—' }}</span>
|
|
</td>
|
|
<td class="num mdr-ce-amount">{{ ce.amount | number: '1.2-2' }}</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</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>
|