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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
57a61b3057
commit
e2eff9914f
@@ -39,21 +39,90 @@
|
||||
|
||||
<div class="dte-info-panel">
|
||||
<div class="dte-info-panel__title">Party Information</div>
|
||||
<p class="dte-info-panel__hint">Populated once a party is selected in a collection row below.</p>
|
||||
@if (partyInfo) {
|
||||
<dl>
|
||||
<dt>Client Id</dt>
|
||||
<dd>{{ partyInfo.clientId }}</dd>
|
||||
<dt>Name</dt>
|
||||
<dd>{{ partyInfo.partyName }}</dd>
|
||||
<dt>Address</dt>
|
||||
<dd>{{ partyInfo.address1 || '—' }}</dd>
|
||||
<dt>City</dt>
|
||||
<dd>{{ partyInfo.city || '—' }}</dd>
|
||||
<dt>Op. Date</dt>
|
||||
<dd>{{ partyInfo.openingDate ? (partyInfo.openingDate | dateFormat) : '—' }}</dd>
|
||||
<dt>Maturity</dt>
|
||||
<dd>{{ partyInfo.maturityDate ? (partyInfo.maturityDate | dateFormat) : '—' }}</dd>
|
||||
<dt>Daily Depo</dt>
|
||||
<dd>{{ partyInfo.dailyDepositAmount | number: '1.2-2' }}</dd>
|
||||
<dt>Ledger</dt>
|
||||
<dd>{{ partyInfo.ledgerNo || '—' }}</dd>
|
||||
<dt>Aadhar No</dt>
|
||||
<dd>{{ partyInfo.aadharNo || '—' }}</dd>
|
||||
<dt>Curr Bal</dt>
|
||||
<dd>{{ partyInfo.currentBalance | number: '1.2-2' }}</dd>
|
||||
<!--
|
||||
What should have been collected by today against what has been. Beside the balance
|
||||
rather than instead of it: the balance is what the customer holds, this is whether
|
||||
they have kept to the scheme.
|
||||
-->
|
||||
<dt>Due So Far</dt>
|
||||
<dd>{{ partyInfo.expectedToDate | number: '1.2-2' }}</dd>
|
||||
<dt>Collected</dt>
|
||||
<dd>{{ partyInfo.collectedToDate | number: '1.2-2' }}</dd>
|
||||
@if (schedule(partyInfo); as sched) {
|
||||
<dt [class]="'dte-sched dte-sched--' + sched.state">Schedule</dt>
|
||||
<dd [class]="'dte-sched dte-sched--' + sched.state">{{ sched.text }}</dd>
|
||||
}
|
||||
<dt class="dte-after">Bal. After This Tr</dt>
|
||||
<dd class="dte-after">{{ partyBalanceAfter | number: '1.2-2' }}</dd>
|
||||
</dl>
|
||||
} @else {
|
||||
<p class="dte-info-panel__hint">
|
||||
Pick a party in a collection row below — this panel shows whose account it is and what
|
||||
the balance becomes.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dte-controls">
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-form-field appearance="outline" class="dte-field dte-field--agent">
|
||||
<mat-label>Agent</mat-label>
|
||||
<input matInput required formControlName="agentId" />
|
||||
<mat-select required formControlName="agentId" (selectionChange)="loadAgent()">
|
||||
@for (agent of selectableAgents; track agent.id) {
|
||||
<mat-option [value]="agent.id">
|
||||
{{ agent.agentCode }} · {{ agent.agentNameId }}
|
||||
@if (!agent.active) {
|
||||
<span class="dte-agent-off">— not collecting</span>
|
||||
}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
@if (selectableAgents.length === 0) {
|
||||
<mat-hint class="dte-hint--missing">No active agent on file</mat-hint>
|
||||
} @else if (pickedAgentIsInactive) {
|
||||
<!-- Only reachable on a settlement: this agent no longer collects, but the cash they
|
||||
already hold still has to be able to reach a branch. -->
|
||||
<mat-hint class="dte-hint--missing">This agent no longer collects — settling held cash only</mat-hint>
|
||||
} @else if (agentInfo.name) {
|
||||
<mat-hint class="dte-hint--found">
|
||||
{{ agentParties.length }} {{ agentParties.length === 1 ? 'party' : 'parties' }} to collect from
|
||||
</mat-hint>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-stroked-button type="button" (click)="loadAgent()"><i class="fas fa-search"></i> Load</button>
|
||||
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>Date</mat-label>
|
||||
<input matInput required [matDatepicker]="txnDatePicker" formControlName="transactionDate" />
|
||||
<!-- The date picks out a round, not just a posting date: a Party Tran settles whatever
|
||||
the agent wrote down for that day, so changing it has to fetch that day's list. -->
|
||||
<input
|
||||
matInput
|
||||
required
|
||||
[matDatepicker]="txnDatePicker"
|
||||
formControlName="transactionDate"
|
||||
(dateChange)="loadPendingEntries()"
|
||||
/>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="txnDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #txnDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
@@ -61,75 +130,61 @@
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>Tran. Type</mat-label>
|
||||
<mat-select required formControlName="transactionType">
|
||||
@for (type of transactionTypes; track type) {
|
||||
<mat-option [value]="type">{{ type }}</mat-option>
|
||||
@for (type of transactionTypes; track type.label) {
|
||||
<mat-option [value]="type.label" [disabled]="!type.supported">
|
||||
{{ type.label }}@if (!type.supported) {<span class="dte-soon"> — not available yet</span>}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@if (isPartyTran) {
|
||||
<!--
|
||||
What the agent wrote down on their round, waiting to be counted.
|
||||
|
||||
This used to be an empty grid the clerk typed the round into, and typing it posted it: a
|
||||
customer was credited the moment a line was keyed, whether or not the cash behind it ever
|
||||
reached the branch. The agent records their own collections now, and what the clerk does here
|
||||
is agree with them - so there is nothing to type, only a list to check against a bag of cash.
|
||||
-->
|
||||
@if (isPartyTran && agentInfo.name) {
|
||||
<div class="dte-section">
|
||||
<div class="dte-section__header">
|
||||
<i class="fas fa-list"></i>
|
||||
<h3>Today's Collections</h3>
|
||||
<button mat-stroked-button type="button" class="dte-add-row" (click)="addCollectionRow()">
|
||||
<i class="fas fa-plus"></i> Add Row
|
||||
<h3>Collections to settle</h3>
|
||||
<button mat-stroked-button type="button" class="dte-add-row" (click)="loadPendingEntries()">
|
||||
<i class="fas fa-sync"></i> Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (collections.length === 0) {
|
||||
<p class="dte-empty">No collection rows added yet.</p>
|
||||
}
|
||||
@if (pendingLoading) {
|
||||
<p class="dte-empty">Reading this agent's entries...</p>
|
||||
} @else if (pendingEntries.length === 0) {
|
||||
<p class="dte-empty">
|
||||
{{ agentInfo.name }} has written nothing down for this date. Nothing to settle.
|
||||
</p>
|
||||
} @else {
|
||||
@for (entry of pendingEntries; track entry.id) {
|
||||
<div class="dte-pending-row">
|
||||
<span class="dte-pending-row__code">{{ entry.partyCode }}</span>
|
||||
<span class="dte-pending-row__name">{{ entry.partyName }}</span>
|
||||
<span class="dte-pending-row__mode">{{ entry.transactionMode }}</span>
|
||||
<span class="dte-pending-row__amount">{{ entry.amount | number: '1.2-2' }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@for (row of collections.controls; track row; let i = $index) {
|
||||
<div class="dte-collection-row" [formGroup]="$any(row)">
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>Party Code</mat-label>
|
||||
<input matInput required formControlName="partyCode" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>TRTP</mat-label>
|
||||
<mat-select required formControlName="transactionMode">
|
||||
@for (mode of transactionModes; track mode) {
|
||||
<mat-option [value]="mode">{{ mode }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>AC Type</mat-label>
|
||||
<input matInput formControlName="accountType" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>A/C No.</mat-label>
|
||||
<input matInput required formControlName="accountNo" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>Amount</mat-label>
|
||||
<input matInput required type="number" formControlName="amount" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>Cheque No</mat-label>
|
||||
<input matInput formControlName="chequeNo" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="dte-field">
|
||||
<mat-label>Token No</mat-label>
|
||||
<input matInput formControlName="tokenNo" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="dte-field dte-field--wide">
|
||||
<mat-label>Description</mat-label>
|
||||
<input matInput formControlName="description" />
|
||||
</mat-form-field>
|
||||
<button
|
||||
mat-icon-button
|
||||
type="button"
|
||||
class="dte-remove-row"
|
||||
(click)="removeCollectionRow(i)"
|
||||
matTooltip="Remove row"
|
||||
>
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<div class="dte-total">
|
||||
<span>{{ pendingEntries.length }} {{ pendingEntries.length === 1 ? 'entry' : 'entries' }}</span>
|
||||
<strong>{{ pendingTotal | number: '1.2-2' }}</strong>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
The figure the clerk counts against, said in words rather than left as a column total -
|
||||
it is the one number they are acting on, and the rest of the panel is working out.
|
||||
-->
|
||||
<p class="dte-expected">
|
||||
Take <strong>{{ pendingTotal | number: '1.2-2' }}</strong> from {{ agentInfo.name }}, then post.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -232,8 +287,16 @@
|
||||
<button mat-stroked-button type="button" (click)="retrieve()">Retrieve</button>
|
||||
<button mat-stroked-button type="button" (click)="viewLedger()">View Ledger</button>
|
||||
<button mat-stroked-button type="button" (click)="signature()">Signature</button>
|
||||
<button mat-raised-button color="primary" type="button" [disabled]="entryForm.invalid" (click)="save()">
|
||||
Save
|
||||
<!-- On a Party Tran there is nothing to save until the agent has written something down, so
|
||||
the button says what it will do and stays out of reach until it can do it. -->
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
type="button"
|
||||
[disabled]="entryForm.invalid || (isPartyTran && pendingEntries.length === 0)"
|
||||
(click)="save()"
|
||||
>
|
||||
{{ isPartyTran ? 'Confirm & Post' : 'Save' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user