Files
cbsnexora/src/app/deposits/dds-approvals/dds-approval-review/dds-approval-review.component.html
T
niteshandClaude Opus 5.5 1f87ad475c feat(dds): agent-first Party Master, account actions menu, FD-style statuses
- Party Master opens on agents (paged, from the server summary), then one
  agent's dashboard: accounts, maturity, money and collection cards, each
  with a View that filters the accounts table. The page title follows the
  view, and Back from an account returns to its agent.
- DDS account page: an actions menu with Edit, Premature Closure, and
  Maturity Payout & Close. The manual Close Account buttons are gone, since
  payout and premature closure close the account themselves.
- Statuses Active / Matured / Closed / Premature Closed are shown and
  filtered on every DDS screen through a shared dds-status helper.
- Premature closure dialog redesigned: term progress, numbered steps, and a
  payout calculation for each choice using the server's formulas.
- Agent & Slab Approval tab: slab approvals moved in beside agents, with
  toggle buttons and labels named for the action; richer slab review.
- Batch processing: calculation runs are reported as applied, and an empty
  run is reported as nothing to do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:33:15 +05:30

928 lines
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
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/.
-->
<!-- Laid out as Disbursement & Transfers reviews its requests, and styled by the same sheet. -->
<div class="dri dri--dialog">
<nav class="cashier-nav cashier-nav--clerk">
<span class="dri-header-title">DDS {{ entry.type }} Review</span>
<span class="cashier-nav__esc-hint" (click)="close()">✕ Close</span>
</nav>
@if (loading) {
<div class="dri-loading">
<mat-icon class="dri-loading__icon">hourglass_empty</mat-icon>
<span>Loading entry…</span>
</div>
} @else {
<div class="dri-body" [class.ddr-body]="isProductReview || isAgentReview || isPartyReview || isSlabReview">
@if (isProductReview) {
<!--
A DDS product request reads as the product it creates or changes: who and what at the top,
the four terms a checker looks at first, then every term grouped as the Account Type screen
asks for them. An edit shows each changed term as old → new.
-->
<section class="ddr-hero">
<div class="ddr-hero__icon">
<mat-icon>{{ isEdit ? 'edit_note' : 'savings' }}</mat-icon>
</div>
<div class="ddr-hero__main">
<div class="ddr-hero__eyebrow">
<span class="ddr-chip" [class.ddr-chip--edit]="isEdit">{{
isEdit ? 'Edit request' : 'New product'
}}</span>
<span class="dri-status" [ngClass]="statusClass(entry.status)">{{ entry.status }}</span>
</div>
<h2 class="ddr-hero__title">{{ valueOf('accountTypeDescription') || entry.details || 'DDS Product' }}</h2>
<div class="ddr-hero__code">
<span class="ddr-mono">{{ valueOf('accountTypeCode') || '—' }}</span>
@if (valueOf('accountTypeGroup')) {
<span class="ddr-dot"></span>
<span>{{ valueOf('accountTypeGroup') }} group</span>
}
</div>
</div>
<div class="ddr-hero__meta">
<span class="ddr-meta">
<mat-icon>person</mat-icon>
<span>
<b>{{ entry.makerName || entry.maker }}</b>
<small>{{ entry.maker }}</small>
</span>
</span>
<span class="ddr-meta">
<mat-icon>schedule</mat-icon>
<span>
<b>{{ formatDateTime(entry.madeOn) }}</b>
<small>Submitted</small>
</span>
</span>
@if (entry.status !== 'PENDING' && entry.checker) {
<span class="ddr-meta">
<mat-icon>verified_user</mat-icon>
<span>
<b>{{ entry.checkerName || entry.checker }}</b>
<small>{{ entry.status === 'APPROVED' ? 'Approved by' : 'Rejected by' }}</small>
</span>
</span>
}
</div>
</section>
<!-- The terms a checker reads first. -->
<section class="ddr-stats">
<div class="ddr-stat ddr-stat--accent">
<span class="ddr-stat__label"><mat-icon>percent</mat-icon>Interest Rate</span>
<span class="ddr-stat__value">{{ valueOf('interestRate') || '—' }}</span>
<span class="ddr-stat__hint">p.a., worked out daily</span>
</div>
<div class="ddr-stat">
<span class="ddr-stat__label"><mat-icon>event_repeat</mat-icon>Interest Posting</span>
<span class="ddr-stat__value">
{{ valueOf('autoInterestPost') === 'No' ? 'Manual' : valueOf('autoInterestPostWhen') || 'Monthly' }}
</span>
<span class="ddr-stat__hint">
{{ valueOf('autoInterestPost') === 'No' ? 'Posted from the batch screen' : 'Automatic, at period end' }}
</span>
</div>
<div class="ddr-stat">
<span class="ddr-stat__label"><mat-icon>lock_clock</mat-icon>Lock-in</span>
<span class="ddr-stat__value">{{ valueOf('prematureLockinMonths') || '—' }}</span>
<span class="ddr-stat__hint">before early closure</span>
</div>
<div class="ddr-stat">
<span class="ddr-stat__label"><mat-icon>event_available</mat-icon>Maturity</span>
<span class="ddr-stat__value">{{
valueOf('maturityAutoClose') === 'Yes' ? 'Auto close' : 'Manual payout'
}}</span>
<span class="ddr-stat__hint">on the maturity date</span>
</div>
</section>
@if (compareFields) {
<div class="ddr-banner" [class.ddr-banner--none]="!changedCount">
<mat-icon>{{ changedCount ? 'compare_arrows' : 'info' }}</mat-icon>
@if (changedCount) {
<span>
<b>{{ changedCount }} term{{ changedCount !== 1 ? 's' : '' }}</b> will change on approval - highlighted
below as current → proposed.
</span>
} @else {
<span>This edit changes nothing - every term matches the product as it stands.</span>
}
</div>
}
@for (group of fieldGroups; track group.name) {
<section class="ddr-section">
<header class="ddr-section__head">
<span class="ddr-section__icon"
><mat-icon>{{ group.icon }}</mat-icon></span
>
<span class="ddr-section__title">{{ group.name }}</span>
@if (group.changed) {
<span class="ddr-chip ddr-chip--edit">{{ group.changed }} changed</span>
}
</header>
@if (group.name === 'Accounting') {
<!--
The ledger accounts the product posts through, laid out as the product's own Accounting
tab lays them out: the kind of account, what it is for, and the account by name and code.
-->
<div class="ddr-acct">
@for (field of group.fields; track field.key) {
<div class="ddr-acct__row" [class.ddr-acct__row--changed]="field.changed">
<div class="ddr-acct__meta">
<span class="ddr-acct__kind" [ngClass]="'ddr-acct__kind--' + glKind(field.key).toLowerCase()">{{
glKind(field.key)
}}</span>
<span class="ddr-acct__label">
{{ field.label }}
@if (field.changed) {
<span class="ddr-tile__flag">Changed</span>
}
</span>
</div>
<div class="ddr-acct__value">
@if (field.changed) {
<span class="ddr-acct__gl ddr-acct__gl--old">
{{ glName(field.current) }}
@if (glCode(field.current)) {
<span class="ddr-acct__code">{{ glCode(field.current) }}</span>
}
</span>
<mat-icon class="ddr-acct__arrow">arrow_forward</mat-icon>
}
<span class="ddr-acct__gl" [class.ddr-acct__gl--none]="!field.value">
{{ glName(field.value) }}
@if (glCode(field.value)) {
<span class="ddr-acct__code">{{ glCode(field.value) }}</span>
}
</span>
</div>
</div>
}
</div>
} @else {
<div class="ddr-grid">
@for (field of group.fields; track field.key) {
<div class="ddr-tile" [class.ddr-tile--changed]="field.changed">
<span class="ddr-tile__label">
{{ field.label }}
@if (field.changed) {
<span class="ddr-tile__flag">Changed</span>
}
</span>
@if (field.changed) {
<span class="ddr-tile__diff">
<span class="ddr-tile__old">{{ field.current || '—' }}</span>
<mat-icon>arrow_forward</mat-icon>
<ng-container *ngTemplateOutlet="valueTpl; context: { field: field }"></ng-container>
</span>
} @else {
<ng-container *ngTemplateOutlet="valueTpl; context: { field: field }"></ng-container>
}
</div>
}
</div>
}
</section>
}
<ng-template #valueTpl let-field="field">
@if (field.format === 'yesno' && field.value) {
<span class="ddr-pill" [class.ddr-pill--yes]="field.value === 'Yes'">
<mat-icon>{{ field.value === 'Yes' ? 'check_circle' : 'remove_circle_outline' }}</mat-icon>
{{ field.value }}
</span>
} @else {
<span class="ddr-tile__value" [class.ddr-mono]="field.key === 'accountTypeCode'">{{
field.value || '—'
}}</span>
}
</ng-template>
} @else if (isSlabReview) {
<!--
A commission slab request reads as the slab: the volume band and the rate it pays, what an
agent earns at the band's start, the change for an edit as old → new, and the whole chart of
that type with this slab in place - so the checker sees it still gives one rate per volume.
-->
<section class="ddr-hero">
<div class="ddr-hero__icon">
<mat-icon>{{ isEdit ? 'edit_note' : 'stacked_bar_chart' }}</mat-icon>
</div>
<div class="ddr-hero__main">
<div class="ddr-hero__eyebrow">
<span class="ddr-chip" [class.ddr-chip--edit]="isEdit">{{ isEdit ? 'Edit request' : 'New slab' }}</span>
<span class="dri-status" [ngClass]="statusClass(entry.status)">{{ entry.status }}</span>
</div>
<h2 class="ddr-hero__title">{{ valueOf('slabFrom') || '—' }} – {{ valueOf('slabTo') || '—' }}</h2>
<div class="ddr-hero__code">
<span>{{ valueOf('slabType') || 'Commission' }} slab</span>
@if (entry.resourceId) {
<span class="ddr-dot"></span>
<span class="ddr-mono">#{{ entry.resourceId }}</span>
}
</div>
</div>
<div class="ddr-hero__meta">
<span class="ddr-meta">
<mat-icon>person</mat-icon>
<span>
<b>{{ entry.makerName || entry.maker }}</b>
<small>{{ entry.maker }}</small>
</span>
</span>
<span class="ddr-meta">
<mat-icon>schedule</mat-icon>
<span>
<b>{{ formatDateTime(entry.madeOn) }}</b>
<small>Submitted</small>
</span>
</span>
@if (entry.status !== 'PENDING' && entry.checker) {
<span class="ddr-meta">
<mat-icon>verified_user</mat-icon>
<span>
<b>{{ entry.checkerName || entry.checker }}</b>
<small>{{ entry.status === 'APPROVED' ? 'Approved by' : 'Rejected by' }}</small>
</span>
</span>
}
</div>
</section>
<section class="ddr-stats ddr-stats--three">
<div class="ddr-stat ddr-stat--accent">
<span class="ddr-stat__label"><mat-icon>percent</mat-icon>Commission</span>
<span class="ddr-stat__value">{{ valueOf('commissionPercent') || '—' }}</span>
<span class="ddr-stat__hint">of the month's collection</span>
</div>
<div class="ddr-stat">
<span class="ddr-stat__label"><mat-icon>straighten</mat-icon>Collection band</span>
<span class="ddr-stat__value ddr-stat__value--small"
>{{ valueOf('slabFrom') || '—' }} – {{ valueOf('slabTo') || '—' }}</span
>
<span class="ddr-stat__hint">agent's collection volume</span>
</div>
<div class="ddr-stat">
<span class="ddr-stat__label"><mat-icon>calculate</mat-icon>Example</span>
<span class="ddr-stat__value ddr-stat__value--small">{{ slabExample || '—' }}</span>
<span class="ddr-stat__hint">commission at the band's start</span>
</div>
</section>
@if (compareFields) {
<div class="ddr-banner" [class.ddr-banner--none]="!changedCount">
<mat-icon>{{ changedCount ? 'compare_arrows' : 'info' }}</mat-icon>
@if (changedCount) {
<span>
<b>{{ changedCount }} term{{ changedCount !== 1 ? 's' : '' }}</b> will change on approval - shown below
as current → proposed.
</span>
} @else {
<span>This edit changes nothing - every term matches the slab as it stands.</span>
}
</div>
}
@for (group of fieldGroups; track group.name) {
<section class="ddr-section">
<header class="ddr-section__head">
<span class="ddr-section__icon"
><mat-icon>{{ group.icon }}</mat-icon></span
>
<span class="ddr-section__title">{{ group.name }}</span>
@if (group.changed) {
<span class="ddr-chip ddr-chip--edit">{{ group.changed }} changed</span>
}
</header>
<div class="ddr-grid">
@for (field of group.fields; track field.key) {
<div class="ddr-tile" [class.ddr-tile--changed]="field.changed">
<span class="ddr-tile__label">
{{ field.label }}
@if (field.changed) {
<span class="ddr-tile__flag">Changed</span>
}
</span>
@if (field.changed) {
<span class="ddr-tile__diff">
<span class="ddr-tile__old">{{ field.current || '—' }}</span>
<mat-icon>arrow_forward</mat-icon>
<span class="ddr-tile__value">{{ field.value || '—' }}</span>
</span>
} @else {
<span class="ddr-tile__value">{{ field.value || '—' }}</span>
}
</div>
}
</div>
</section>
}
@if (slabChart.length) {
<section class="ddr-section">
<header class="ddr-section__head">
<span class="ddr-section__icon"><mat-icon>table_rows</mat-icon></span>
<span class="ddr-section__title">{{ valueOf('slabType') || 'Commission' }} slab chart</span>
<span class="ddr-slabs__note">{{
entry.status === 'PENDING' ? 'as it will read once approved' : 'as it reads now'
}}</span>
</header>
@if (slabOverlaps) {
<div class="ddr-banner ddr-banner--danger">
<mat-icon>warning</mat-icon>
<span>
This slab's band overlaps
<b>{{ slabOverlaps }} other slab{{ slabOverlaps !== 1 ? 's' : '' }}</b> - a collection in the overlap
would match more than one rate.
</span>
</div>
}
<div class="ddr-slabs">
<div class="ddr-slabs__row ddr-slabs__row--head">
<span>#</span>
<span>Collection from</span>
<span>Collection to</span>
<span class="ddr-slabs__num">Commission</span>
</div>
@for (slab of slabChart; track $index) {
<div
class="ddr-slabs__row"
[class.ddr-slabs__row--requested]="slab.requested"
[class.ddr-slabs__row--overlap]="slab.overlaps"
>
<span class="ddr-mono">
{{ slab.id ? slab.id : 'New' }}
@if (slab.requested) {
<span class="ddr-tile__flag">{{ isEdit ? 'Edited' : 'New' }}</span>
}
@if (slab.overlaps) {
<span class="ddr-slabs__flag">Overlaps</span>
}
</span>
<span>
@if (slab.was && !same(slab.was.from, slab.from)) {
<span class="ddr-tile__old">{{ rupees(slab.was.from) }}</span>
}
{{ rupees(slab.from) }}
</span>
<span>
@if (slab.was && !same(slab.was.to, slab.to)) {
<span class="ddr-tile__old">{{ rupees(slab.was.to) }}</span>
}
{{ rupees(slab.to) }}
</span>
<span class="ddr-slabs__num">
@if (slab.was && !same(slab.was.rate, slab.rate)) {
<span class="ddr-tile__old">{{ percent(slab.was.rate) }}</span>
}
<b>{{ percent(slab.rate) }}</b>
</span>
</div>
}
</div>
</section>
}
} @else if (isAgentReview) {
<!--
An agent request reads as the agent it opens or changes: who and when at the top, then the
client, address, branch and accounts - the branch, the ledger account and the client by name,
not number - and the guarantors named with it. An edit lists only what it changes.
-->
<section class="ddr-hero">
<div class="ddr-hero__icon">
<mat-icon>{{ isEdit ? 'manage_accounts' : 'person_add' }}</mat-icon>
</div>
<div class="ddr-hero__main">
<div class="ddr-hero__eyebrow">
<span class="ddr-chip" [class.ddr-chip--edit]="isEdit">{{ isEdit ? 'Edit request' : 'New agent' }}</span>
<span class="dri-status" [ngClass]="statusClass(entry.status)">{{ entry.status }}</span>
</div>
<h2 class="ddr-hero__title">{{ valueOf('agentNameId') || entry.details || 'DDS Agent' }}</h2>
<div class="ddr-hero__code">
@if (valueOf('clientId')) {
<span>Client {{ valueOf('clientId') }}</span>
}
@if (valueOf('agentSavingAccountNo')) {
<span class="ddr-dot"></span>
<span class="ddr-mono">{{ valueOf('agentSavingAccountNo') }}</span>
}
</div>
</div>
<div class="ddr-hero__meta">
<span class="ddr-meta">
<mat-icon>person</mat-icon>
<span>
<b>{{ entry.makerName || entry.maker }}</b>
<small>{{ entry.maker }}</small>
</span>
</span>
<span class="ddr-meta">
<mat-icon>schedule</mat-icon>
<span>
<b>{{ formatDateTime(entry.madeOn) }}</b>
<small>Submitted</small>
</span>
</span>
@if (entry.status !== 'PENDING' && entry.checker) {
<span class="ddr-meta">
<mat-icon>verified_user</mat-icon>
<span>
<b>{{ entry.checkerName || entry.checker }}</b>
<small>{{ entry.status === 'APPROVED' ? 'Approved by' : 'Rejected by' }}</small>
</span>
</span>
}
</div>
</section>
@for (group of fieldGroups; track group.name) {
<section class="ddr-section">
<header class="ddr-section__head">
<span class="ddr-section__icon"
><mat-icon>{{ group.icon }}</mat-icon></span
>
<span class="ddr-section__title">{{ group.name }}</span>
</header>
<div class="ddr-grid">
@for (field of group.fields; track field.key) {
<div class="ddr-tile">
<span class="ddr-tile__label">{{ field.label }}</span>
<span class="ddr-tile__value" [class.ddr-mono]="field.key === 'agentSavingAccountNo'">{{
field.value || '—'
}}</span>
</div>
}
</div>
</section>
}
<section class="ddr-section">
<header class="ddr-section__head">
<span class="ddr-section__icon"><mat-icon>shield_person</mat-icon></span>
<span class="ddr-section__title">Guarantors</span>
<span class="ddr-chip ddr-chip--edit">{{ guarantors.length }}</span>
</header>
@if (guarantors.length) {
<table class="dri-cr-table">
<thead>
<tr>
<th>#</th>
<th>Client</th>
<th>Name</th>
<th>Telephone</th>
<th>Address</th>
</tr>
</thead>
<tbody>
@for (g of guarantors; track $index; let i = $index) {
<tr>
<td class="dri-cr-idx">{{ i + 1 }}</td>
<td>{{ g.client || '—' }}</td>
<td>{{ g.name || '—' }}</td>
<td class="cell-mono">{{ g.telephone || '—' }}</td>
<td>{{ g.address || '—' }}</td>
</tr>
}
</tbody>
</table>
} @else {
<div class="ddr-tile">
<span class="ddr-tile__value">No guarantors named in this request.</span>
</div>
}
</section>
} @else if (isPartyReview) {
<!--
A DDS account request reads as the account it opens or changes: who it is for and when, the
terms a checker looks at first, then every detail in the sections the Party Master Entry screen
has - the client, scheme, agent and branch by name, not number - and the nominees named with it.
An edit lists only what it changes.
-->
<section class="ddr-hero">
<div class="ddr-hero__icon">
<mat-icon>{{ isEdit ? 'edit_note' : 'account_balance_wallet' }}</mat-icon>
</div>
<div class="ddr-hero__main">
<div class="ddr-hero__eyebrow">
<span class="ddr-chip" [class.ddr-chip--edit]="isEdit">{{
isEdit ? 'Edit request' : 'New DDS account'
}}</span>
<span class="dri-status" [ngClass]="statusClass(entry.status)">{{ entry.status }}</span>
</div>
<h2 class="ddr-hero__title">{{ valueOf('partyName') || entry.details || 'DDS Account' }}</h2>
<div class="ddr-hero__code">
@if (valueOf('subType')) {
<span>{{ valueOf('subType') }}</span>
}
@if (valueOf('clientId')) {
<span class="ddr-dot"></span>
<span>Client {{ valueOf('clientId') }}</span>
}
</div>
</div>
<div class="ddr-hero__meta">
<span class="ddr-meta">
<mat-icon>person</mat-icon>
<span>
<b>{{ entry.makerName || entry.maker }}</b>
<small>{{ entry.maker }}</small>
</span>
</span>
<span class="ddr-meta">
<mat-icon>schedule</mat-icon>
<span>
<b>{{ formatDateTime(entry.madeOn) }}</b>
<small>Submitted</small>
</span>
</span>
@if (entry.status !== 'PENDING' && entry.checker) {
<span class="ddr-meta">
<mat-icon>verified_user</mat-icon>
<span>
<b>{{ entry.checkerName || entry.checker }}</b>
<small>{{ entry.status === 'APPROVED' ? 'Approved by' : 'Rejected by' }}</small>
</span>
</span>
}
</div>
</section>
<!-- The terms a checker reads first. -->
<section class="ddr-stats">
<div class="ddr-stat ddr-stat--accent">
<span class="ddr-stat__label"><mat-icon>payments</mat-icon>Daily Deposit</span>
<span class="ddr-stat__value">{{ valueOf('dailyDepositAmount') || '—' }}</span>
<span class="ddr-stat__hint">collected every day</span>
</div>
<div class="ddr-stat">
<span class="ddr-stat__label"><mat-icon>date_range</mat-icon>Term</span>
<span class="ddr-stat__value">{{ valueOf('days') ? valueOf('days') + ' days' : '—' }}</span>
<span class="ddr-stat__hint">{{ valueOf('month') ? valueOf('month') + ' months' : '' }}</span>
</div>
<div class="ddr-stat">
<span class="ddr-stat__label"><mat-icon>event</mat-icon>Opening Date</span>
<span class="ddr-stat__value">{{ valueOf('openingDate') || '—' }}</span>
<span class="ddr-stat__hint">{{
valueOf('maturityDate') ? 'Matures ' + valueOf('maturityDate') : 'account opens'
}}</span>
</div>
<div class="ddr-stat">
<span class="ddr-stat__label"><mat-icon>support_agent</mat-icon>Agent</span>
<span class="ddr-stat__value ddr-stat__value--small">{{ valueOf('agentId') || '—' }}</span>
<span class="ddr-stat__hint">{{ valueOf('officeId') || '' }}</span>
</div>
</section>
@for (group of fieldGroups; track group.name) {
<section class="ddr-section">
<header class="ddr-section__head">
<span class="ddr-section__icon"
><mat-icon>{{ group.icon }}</mat-icon></span
>
<span class="ddr-section__title">{{ group.name }}</span>
</header>
<div class="ddr-grid">
@for (field of group.fields; track field.key) {
<div class="ddr-tile">
<span class="ddr-tile__label">{{ field.label }}</span>
@if (field.format === 'yesno') {
<span class="ddr-pill" [class.ddr-pill--yes]="field.value === 'Yes'">
<mat-icon>{{ field.value === 'Yes' ? 'check_circle' : 'remove_circle_outline' }}</mat-icon>
{{ field.value }}
</span>
} @else {
<span class="ddr-tile__value">{{ field.value || '—' }}</span>
}
</div>
}
</div>
</section>
}
<section class="ddr-section">
<header class="ddr-section__head">
<span class="ddr-section__icon"><mat-icon>family_restroom</mat-icon></span>
<span class="ddr-section__title">Nominees</span>
@if (detail?.nomineeSplit && nominees.length) {
<span class="ddr-chip ddr-chip--edit">Split by {{ detail.nomineeSplit }}</span>
}
<span class="ddr-chip ddr-chip--edit">{{ nominees.length }}</span>
</header>
@if (nominees.length) {
<table class="dri-cr-table">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Relation</th>
<th>Gender</th>
<th>Mobile</th>
<th>Birth Date</th>
<th>Guardian</th>
<th class="num">Share</th>
</tr>
</thead>
<tbody>
@for (n of nominees; track $index; let i = $index) {
<tr>
<td class="dri-cr-idx">{{ i + 1 }}</td>
<td>{{ n.name || '—' }}</td>
<td>{{ n.relation || '—' }}</td>
<td>{{ n.gender || '—' }}</td>
<td class="cell-mono">{{ n.mobileNo || '—' }}</td>
<td>{{ n.birthDate || '—' }}</td>
<td>{{ n.guardianName || '—' }}</td>
<td class="num">{{ n.share || '—' }}</td>
</tr>
}
</tbody>
</table>
} @else {
<div class="ddr-tile">
<span class="ddr-tile__value">No nominee named in this request.</span>
</div>
}
</section>
} @else {
<!-- Header card: the voucher it moves under, its status, who made it and when -->
<div class="dri-card dri-card--header">
<div class="dri-card-header">
<div class="dri-card-header__left">
<!-- No voucher slot for a change that moves no money - it would only ever read "—". -->
@if (voucherNo || movesMoney) {
<span class="dri-voucher">{{ voucherNo || '—' }}</span>
}
<span class="dri-status" [ngClass]="statusClass(entry.status)">
{{ entry.status }}{{ entry.voucherCancelled ? ' · VOUCHER CANCELLED' : '' }}
</span>
</div>
<div class="dri-card-header__meta">
<span class="dri-meta-item">
<mat-icon>person</mat-icon>
{{ entry.makerName || entry.maker }} ({{ entry.maker }})
</span>
<span class="dri-meta-item">
<mat-icon>schedule</mat-icon>
{{ formatDateTime(entry.madeOn) }}
</span>
@if (entry.status !== 'PENDING' && entry.checker) {
<span class="dri-meta-item">
<mat-icon>check_circle</mat-icon>
Actioned by: {{ entry.checkerName || entry.checker }}
</span>
}
</div>
</div>
</div>
<!-- Summary -->
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>swap_horiz</mat-icon>
{{ entry.type }} Summary
</div>
<div class="dri-detail-grid">
<!-- Amount and sides only for an entry that moves money; a product or agent change has neither. -->
@if (movesMoney) {
<div class="dri-detail-item">
<span class="dri-detail-label">Total Amount</span>
<span class="dri-detail-value dri-amount">
{{
entry.amount !== null && entry.amount !== undefined ? '₹' + (entry.amount | number: '1.2-2') : '—'
}}
</span>
</div>
@if (entriesProjected) {
<div class="dri-detail-item dri-detail-item--full">
<span class="dri-detail-label">Ledger</span>
<span class="dri-detail-value"
>No journal entry is made when this is approved - it records what has been earned. The entries below
are what the matching Posting run will book.</span
>
</div>
}
<div class="dri-detail-item">
<span class="dri-detail-label">{{
entriesProjected ? 'DR Accounts (when posted)' : 'DR Accounts'
}}</span>
<span class="dri-detail-value"
>{{ debitLines.length }} account{{ debitLines.length !== 1 ? 's' : '' }}</span
>
</div>
<div class="dri-detail-item">
<span class="dri-detail-label">{{
entriesProjected ? 'CR Accounts (when posted)' : 'CR Accounts'
}}</span>
<span class="dri-detail-value"
>{{ creditLines.length }} account{{ creditLines.length !== 1 ? 's' : '' }}</span
>
</div>
}
<div class="dri-detail-item dri-detail-item--full">
<span class="dri-detail-label">Details</span>
<span class="dri-detail-value">{{ entry.details || '—' }}</span>
</div>
@if (detail?.description) {
<div class="dri-detail-item dri-detail-item--full">
<span class="dri-detail-label">Description</span>
<span class="dri-detail-value">{{ detail.description }}</span>
</div>
}
</div>
</div>
}
<!-- Debit entries -->
@if (debitLines.length) {
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>arrow_upward</mat-icon>
{{ entriesProjected ? 'Debit entries when posted' : 'Debit Entries' }} ({{ debitLines.length }} account{{
debitLines.length !== 1 ? 's' : ''
}}
— funds out)
</div>
<table class="dri-cr-table">
<thead>
<tr>
<th>#</th>
<th>Account No</th>
<th>Account Name</th>
<th>Type</th>
<th class="num">Amount (DR)</th>
</tr>
</thead>
<tbody>
@for (line of debitLines; track $index; let i = $index) {
<tr>
<td class="dri-cr-idx">{{ i + 1 }}</td>
<td class="cell-mono">{{ line.accountNo || '—' }}</td>
<td>{{ line.accountName || '—' }}</td>
<td>
<span class="dri-type-badge">{{ line.type || '—' }}</span>
</td>
<td class="num dri-cr-amount">₹{{ line.amount | number: '1.2-2' }}</td>
</tr>
}
<tr class="dri-cr-total-row">
<td colspan="4" class="dri-cr-total-label">Total Debit</td>
<td class="num dri-cr-total-amount">₹{{ total(debitLines) | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
}
<!-- Credit entries -->
@if (creditLines.length) {
<div class="dri-card">
<div class="dri-section-title">
<mat-icon>arrow_downward</mat-icon>
{{ entriesProjected ? 'Credit entries when posted' : 'Credit Entries' }} ({{ creditLines.length }} account{{
creditLines.length !== 1 ? 's' : ''
}}
— funds in)
</div>
<table class="dri-cr-table">
<thead>
<tr>
<th>#</th>
<th>Account No</th>
<th>Account Name</th>
<th>Type</th>
<th class="num">Amount (CR)</th>
</tr>
</thead>
<tbody>
@for (line of creditLines; track $index; let i = $index) {
<tr>
<td class="dri-cr-idx">{{ i + 1 }}</td>
<td class="cell-mono">{{ line.accountNo || '—' }}</td>
<td>{{ line.accountName || '—' }}</td>
<td>
<span class="dri-type-badge">{{ line.type || '—' }}</span>
</td>
<td class="num dri-cr-amount">₹{{ line.amount | number: '1.2-2' }}</td>
</tr>
}
<tr class="dri-cr-total-row">
<td colspan="4" class="dri-cr-total-label">Total Credit</td>
<td class="num dri-cr-total-amount">₹{{ total(creditLines) | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
}
@if (entry.status === 'PENDING' && isMine && !done) {
<div class="dri-card dri-card--processing">
<div class="dri-section-title">
<mat-icon>info</mat-icon>
Your own entry
</div>
<p class="dri-processing-text">
You made this entry, so another user with checker rights has to approve or reject it.
</p>
</div>
}
@if (entry.status === 'REJECTED') {
<div class="dri-card dri-card--rejected">
<div class="dri-section-title">
<mat-icon>cancel</mat-icon>
Rejected
</div>
<p class="dri-rejection-text">
Nothing was done{{ voucherNo ? '. Voucher ' + voucherNo + ' is cancelled and will not be used' : '' }}.
</p>
@if (entry.checker) {
<div class="dri-meta-item" style="margin-top: 8px">
<mat-icon>manage_accounts</mat-icon>
{{ entry.checker }}
</div>
}
</div>
}
@if (entry.status === 'APPROVED') {
<div class="dri-card dri-card--approved">
<div class="dri-section-title">
<mat-icon>check_circle</mat-icon>
Approved and carried out
</div>
<p class="dri-txn-refs cell-mono">
{{
voucherNo
? 'Voucher ' + voucherNo
: entry.entity === 'DDSBATCH'
? 'Posting is running - refresh for its voucher'
: '—'
}}
</p>
<div class="dri-meta-item" style="margin-top: 8px">
<mat-icon>manage_accounts</mat-icon>
Approved by: {{ entry.checker || '—' }}
</div>
</div>
}
@if (error) {
<div class="dri-card dri-card--failed">
<div class="dri-section-title">
<mat-icon>error</mat-icon>
Could not complete
</div>
<p class="dri-rejection-text">{{ error }}</p>
</div>
}
@if (entry.status === 'PENDING' && !done) {
<div
class="dri-actions"
[class.ddr-actions]="isProductReview || isAgentReview || isPartyReview || isSlabReview"
>
@if (!confirmReject) {
<button type="button" class="dri-btn dri-btn--approve" [disabled]="!canAct" (click)="act('approve')">
<mat-icon>check_circle</mat-icon>
{{ working ? 'Processing…' : 'Approve ' + entry.type }}
</button>
<button type="button" class="dri-btn dri-btn--reject" [disabled]="!canAct" (click)="confirmReject = true">
<mat-icon>cancel</mat-icon>
Reject
</button>
} @else {
<div class="dri-reject-form">
<label class="dri-reject-label">
Reject this entry? Nothing will be done{{
voucherNo ? ' and voucher ' + voucherNo + ' will be cancelled' : ''
}}.
</label>
<div class="dri-reject-actions">
<button type="button" class="dri-btn dri-btn--reject" [disabled]="!canAct" (click)="act('reject')">
<mat-icon>cancel</mat-icon>
{{ working ? 'Processing…' : 'Confirm Rejection' }}
</button>
<button
type="button"
class="dri-btn dri-btn--ghost"
[disabled]="working"
(click)="confirmReject = false"
>
Cancel
</button>
</div>
</div>
}
</div>
}
</div>
}
</div>