Files
cbsnexora/src/app/organization/agent-master/agent-master.component.html
T
tarunmalviyaandClaude Opus 5 e2eff9914f DDS: agent collection entry screen, per-agent GL account, maturity auto close
The Daily Transaction Entry screen no longer composes a Party Tran. An agent records their own round
elsewhere; this screen reads back what they wrote down, shows what it adds up to, and settles the
day once a clerk has counted the cash against it. Nothing is typed here and nothing is edited - what
settles is what the entries say, to the rupee.

Agent Master now asks which ledger account the agent's collections pass through, one agent to an
account, and offers only the accounts that can carry them. The DDS scheme product is kept out of the
agent's own savings list for the same reason the server refuses it: debits are blocked on those
accounts, so commission paid into one would go in and never come out.

The DDS product carries a Maturity Auto Close setting, asked as Yes/No to match the Auto Interest
Post question directly above it, and Batch Processing offers the run that applies it.

Also: the report viewer stops printing two money columns ungrouped and stops totalling a column that
holds a word, and an agent's birth date cannot be picked later than eighteen years ago.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 11:54:49 +05:30

181 lines
6.9 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="pl-root">
<div class="pl-header">
<div class="pl-header-left">
<div class="pl-header-ic"><fa-icon icon="user-tie"></fa-icon></div>
<div class="pl-header-text">
<h2>Agent Master</h2>
<p>The DDS collection agents on file.</p>
</div>
</div>
<div class="pl-header-right">
<div class="pl-count-chip" matTooltip="Total agents">
<fa-icon icon="user-tie"></fa-icon>
<strong>{{ totalCount }}</strong>
<span>{{ totalCount === 1 ? 'agent' : 'agents' }}</span>
</div>
<button class="pl-create-btn" type="button" [routerLink]="['create']">
<fa-icon icon="plus"></fa-icon>
<span>Create Agent</span>
</button>
</div>
</div>
<div class="pl-kpis">
<div class="pl-kpi pl-kpi--blue">
<div class="pl-kpi-ic"><fa-icon icon="user-tie"></fa-icon></div>
<div>
<div class="pl-kpi-n">{{ totalCount | number }}</div>
<div class="pl-kpi-l">Total Agents</div>
</div>
</div>
<div class="pl-kpi pl-kpi--purple">
<div class="pl-kpi-ic"><fa-icon icon="toggle-on"></fa-icon></div>
<div>
<div class="pl-kpi-n">{{ activeCount | number }}</div>
<div class="pl-kpi-l">Active Agents</div>
</div>
</div>
<div class="pl-kpi pl-kpi--teal" matTooltip="Money collected from parties that agents have not yet handed over to a branch">
<div class="pl-kpi-ic"><fa-icon icon="wallet"></fa-icon></div>
<div>
<div class="pl-kpi-n">{{ totalCurrentBalance | number: '1.2-2' }}</div>
<div class="pl-kpi-l">Cash With Agents (Unsettled)</div>
</div>
</div>
</div>
<div class="pl-card">
<div class="pl-toolbar">
<div class="pl-search">
<fa-icon icon="search" class="pl-search-ic"></fa-icon>
<input type="text" (input)="applyFilter($any($event.target).value)" placeholder="Search by code or name…" />
</div>
</div>
@if (totalCount === 0) {
<div class="pl-empty">
<div class="pl-empty-ic"><fa-icon icon="user-tie"></fa-icon></div>
<h3>No agents yet</h3>
<p>Create one so daily collections can be recorded against it.</p>
<button class="pl-create-btn" type="button" [routerLink]="['create']">
<fa-icon icon="plus"></fa-icon>
<span>Create Agent</span>
</button>
</div>
} @else {
<div class="pl-table-wrap">
<table mat-table [dataSource]="dataSource" matSort class="pl-table">
<ng-container matColumnDef="agentCode">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Agent Code</th>
<td mat-cell *matCellDef="let a">
<div class="pl-product-cell">
<div class="pl-product-icon"><fa-icon icon="user-tie"></fa-icon></div>
<div class="pl-product-meta">
<div class="pl-product-name">{{ a.agentCode }}</div>
</div>
</div>
</td>
</ng-container>
<ng-container matColumnDef="clientName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Agent Name</th>
<td mat-cell *matCellDef="let a">{{ a.agentNameId }}</td>
</ng-container>
<!-- Which branch the agent's collections belong to. Its own column rather than a subtitle:
it decides which branch's batch run picks the agent up, so it has to be scannable. -->
<ng-container matColumnDef="homeBranch">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Branch</th>
<td mat-cell *matCellDef="let a">
@if (a.homeBranch) {
<span class="pl-tag">{{ a.homeBranch }}</span>
} @else {
<span class="am-no-branch">Not set</span>
}
</td>
</ng-container>
<ng-container matColumnDef="agentCity">
<th mat-header-cell *matHeaderCellDef mat-sort-header>City</th>
<td mat-cell *matCellDef="let a">{{ a.agentCity }}</td>
</ng-container>
<ng-container matColumnDef="agentSavingAccountNo">
<th mat-header-cell *matHeaderCellDef>Saving A/C No</th>
<td mat-cell *matCellDef="let a">
<span class="pl-tag">{{ a.agentSavingAccountNo }}</span>
</td>
</ng-container>
<ng-container matColumnDef="agentCurrentBalance">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="num">Current Balance</th>
<td mat-cell *matCellDef="let a" class="num">
<strong>{{ a.agentCurrentBalance | number: '1.2-2' }}</strong>
</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef>Status</th>
<td mat-cell *matCellDef="let a">
<span class="am-pill" [class.am-pill--off]="!a.active">
{{ a.active ? 'Active' : 'Inactive' }}
</span>
</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Created</th>
<td mat-cell *matCellDef="let a">
{{ a.createdDate ? (a.createdDate | dateFormat: 'DD MMM YYYY, hh:mm A') : '—' }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="num">Actions</th>
<td mat-cell *matCellDef="let a" class="num">
<!-- A closed agent is off the books: nothing can be changed on it, only read. -->
@if (a.closeDate) {
<span class="am-closed-on">Closed {{ a.closeDate | dateFormat }}</span>
} @else {
<button
type="button"
class="pl-action-btn pl-action-btn--edit"
[routerLink]="[a.id, 'edit']"
matTooltip="Edit this agent"
>
<fa-icon icon="edit"></fa-icon><span>Edit</span>
</button>
}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr
mat-row
*matRowDef="let row; columns: displayedColumns"
class="pl-row"
[class.am-row--inactive]="!row.active"
></tr>
</table>
@if (filteredCount === 0 && totalCount > 0) {
<div class="pl-no-match">
<fa-icon icon="search"></fa-icon>
<span>No agents match your filter.</span>
</div>
}
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" [pageSize]="10" showFirstLastButtons></mat-paginator>
</div>
}
</div>
</div>