469 lines
22 KiB
HTML
469 lines
22 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="dte-container">
|
|
<div class="dte-header">
|
|
<button mat-stroked-button [routerLink]="['../']" class="dte-back">
|
|
<i class="fas fa-arrow-left"></i> Deposits
|
|
</button>
|
|
<h2 class="dte-header__title">Daily Transaction Detail</h2>
|
|
</div>
|
|
|
|
<div class="dte-card">
|
|
<form [formGroup]="entryForm">
|
|
<div class="dte-info-panels">
|
|
<div class="dte-info-panel">
|
|
<div class="dte-info-panel__title">Agent Information</div>
|
|
<dl>
|
|
<dt>Name</dt>
|
|
<dd>{{ agentInfo.name || '—' }}</dd>
|
|
<dt>Address</dt>
|
|
<dd>{{ agentInfo.address || '—' }}</dd>
|
|
<dt>City</dt>
|
|
<dd>{{ agentInfo.city || '—' }}</dd>
|
|
<dt>A/C</dt>
|
|
<dd>{{ agentInfo.accountNo || '—' }}</dd>
|
|
<dt>Guarantor</dt>
|
|
<dd>{{ agentInfo.guarantor1 || '—' }}</dd>
|
|
<dt>Guarantor</dt>
|
|
<dd>{{ agentInfo.guarantor2 || '—' }}</dd>
|
|
<dt>Curr Bal</dt>
|
|
<dd>{{ agentInfo.currentBalance | number: '1.2-2' }}</dd>
|
|
</dl>
|
|
</div>
|
|
|
|
<div class="dte-info-panel">
|
|
<div class="dte-info-panel__title">Party Information</div>
|
|
@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 dte-field--agent">
|
|
<mat-label>Agent</mat-label>
|
|
<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>
|
|
|
|
<mat-form-field appearance="outline" class="dte-field">
|
|
<mat-label>Date</mat-label>
|
|
<!-- 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(); loadAgentMoney()"
|
|
/>
|
|
<mat-datepicker-toggle matIconSuffix [for]="txnDatePicker"></mat-datepicker-toggle>
|
|
<mat-datepicker #txnDatePicker></mat-datepicker>
|
|
</mat-form-field>
|
|
|
|
<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.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>
|
|
|
|
<!--
|
|
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>Collections up to {{ entryForm.get('transactionDate')?.value | dateFormat }}</h3>
|
|
<button mat-stroked-button type="button" class="dte-add-row" (click)="loadPendingEntries()">
|
|
<i class="fas fa-sync"></i> Refresh
|
|
</button>
|
|
</div>
|
|
|
|
@if (pendingLoading) {
|
|
<p class="dte-empty">Reading this agent's entries...</p>
|
|
} @else if (pendingEntries.length === 0) {
|
|
<p class="dte-empty">{{ agentInfo.name }} has nothing outstanding up to this date.</p>
|
|
} @else {
|
|
@for (entry of pendingEntries; track entry.id) {
|
|
<div class="dte-pending-row">
|
|
<!-- The day the agent took it, on the line itself. All the lines carry the date
|
|
chosen above, but a row that says so can be read, printed or shown to the agent
|
|
on its own without the header to explain it. -->
|
|
<span class="dte-pending-row__date">{{ entry.entryDate | dateFormat }}</span>
|
|
<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 }}
|
|
@if (entry.advanceDays) {
|
|
· {{ entry.advanceDays }}d advance
|
|
}
|
|
@if (entry.arrearsDays) {
|
|
· {{ entry.arrearsDays }}d previous
|
|
}
|
|
</span>
|
|
<span class="dte-pending-row__amount">{{ entry.amount | number: '1.2-2' }}</span>
|
|
</div>
|
|
}
|
|
|
|
<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.
|
|
|
|
It says what the agent wrote down and stops there. The money side - what is owed, what the
|
|
cashier has taken in, and applying it - is Agent Tran, so each type has one job.
|
|
-->
|
|
<p class="dte-expected dte-expected--note">
|
|
Everything {{ agentInfo.name }} wrote down and has not handed in yet, up to this date, customer by
|
|
customer. The cash and its receipts are under Agent Tran.
|
|
</p>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
<!--
|
|
The agent's money: what they owe on everything written down, what the cashier has taken in
|
|
against it, and what is left.
|
|
|
|
This used to be a form the clerk typed an amount into to take the agent's cash in here. The
|
|
cash is taken at the cashier's counter now, into the agent's own GL account, and one way to take
|
|
the same cash in is the only safe number of ways - so there is nothing to type. What is done here
|
|
is applying the cashier's receipts to the customers, in Apply DDS Receipt.
|
|
-->
|
|
@if (isAgentTran && agentInfo.name) {
|
|
<div class="dte-section">
|
|
<div class="dte-section__header">
|
|
<i class="fas fa-hand-holding-usd"></i>
|
|
<h3>Agent's cash</h3>
|
|
<button mat-stroked-button type="button" class="dte-add-row" (click)="loadAgentMoney()">
|
|
<i class="fas fa-sync"></i> Refresh
|
|
</button>
|
|
<button
|
|
mat-raised-button
|
|
color="primary"
|
|
type="button"
|
|
class="dte-apply-receipt"
|
|
(click)="openApplyReceipt()"
|
|
>
|
|
<i class="fas fa-receipt"></i> Apply DDS Receipt
|
|
</button>
|
|
</div>
|
|
|
|
@if (!agentGlCode) {
|
|
<p class="dte-expected dte-expected--note">
|
|
{{ agentInfo.name }} has no GL account, so the cashier has nowhere to take their cash. Set one on Agent
|
|
Master.
|
|
</p>
|
|
} @else {
|
|
<!-- The GL the cashier takes this agent's cash into - the code to key at Transaction Entry. -->
|
|
<p class="dte-expected">
|
|
Agent GL: <strong>{{ agentGlCode }}{{ agentGlName ? ' · ' + agentGlName : '' }}</strong> — the cashier
|
|
takes {{ agentInfo.name }}'s cash into this GL (Entry Type DDS).
|
|
</p>
|
|
}
|
|
|
|
<div class="dte-money">
|
|
<div class="dte-money__box">
|
|
<span class="dte-money__label">To take</span>
|
|
<strong>{{ agentOwed | number: '1.2-2' }}</strong>
|
|
</div>
|
|
<div class="dte-money__box">
|
|
<span class="dte-money__label">Received (approved)</span>
|
|
<strong>{{ agentReceived | number: '1.2-2' }}</strong>
|
|
</div>
|
|
<div class="dte-money__box" [class.dte-money__box--due]="agentBalance > 0">
|
|
<span class="dte-money__label">Still to take</span>
|
|
<strong>{{ agentBalance | number: '1.2-2' }}</strong>
|
|
</div>
|
|
</div>
|
|
@if (agentReceivedBeyondDay > 0) {
|
|
<p class="dte-expected dte-expected--note">
|
|
{{ agentReceivedBeyondDay | number: '1.2-2' }} received at the counter is more than everything outstanding
|
|
up to {{ entryForm.get('transactionDate')?.value | dateFormat }}. Check the voucher was taken for the
|
|
right agent.
|
|
</p>
|
|
}
|
|
@if (agentWaiting > 0) {
|
|
<p class="dte-expected dte-expected--note">
|
|
{{ agentWaiting | number: '1.2-2' }} more is at the counter awaiting the manager's approval.
|
|
</p>
|
|
}
|
|
|
|
<h4 class="dte-subhead">
|
|
Outstanding entries up to {{ entryForm.get('transactionDate')?.value | dateFormat }}
|
|
</h4>
|
|
@if (agentMoneyLoading) {
|
|
<p class="dte-empty">Reading this agent's entries...</p>
|
|
} @else if (agentDays.length === 0) {
|
|
<p class="dte-empty">{{ agentInfo.name }} has nothing outstanding.</p>
|
|
} @else {
|
|
@for (day of agentDays; track day.date) {
|
|
<div class="dte-pending-row dte-pending-row--day" (click)="toggleAgentDay(day.date)">
|
|
<span class="dte-pending-row__date">{{ day.date | dateFormat }}</span>
|
|
<span class="dte-pending-row__name">
|
|
{{ day.entries.length }} {{ day.entries.length === 1 ? 'entry' : 'entries' }}
|
|
</span>
|
|
<span class="dte-pending-row__amount">{{ day.total | number: '1.2-2' }}</span>
|
|
<i [class]="agentOpenDay === day.date ? 'fas fa-chevron-up' : 'fas fa-chevron-down'"></i>
|
|
</div>
|
|
@if (agentOpenDay === day.date) {
|
|
<div class="dte-day-lines">
|
|
@for (entry of day.entries; 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 }}
|
|
@if (entry.advanceDays) {
|
|
· {{ entry.advanceDays }}d advance
|
|
}
|
|
@if (entry.arrearsDays) {
|
|
· {{ entry.arrearsDays }}d previous
|
|
}
|
|
</span>
|
|
<span class="dte-pending-row__amount">{{ entry.amount | number: '1.2-2' }}</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
}
|
|
}
|
|
|
|
<h4 class="dte-subhead">Cashier receipts</h4>
|
|
@if (agentReceipts.length === 0) {
|
|
<p class="dte-empty">
|
|
No DDS receipt on {{ agentInfo.name }}'s GL account{{ agentGlCode ? ' ' + agentGlCode : '' }} waiting to
|
|
be applied.
|
|
</p>
|
|
} @else {
|
|
@for (receipt of agentReceipts; track receipt.id) {
|
|
<div class="dte-pending-row">
|
|
<span class="dte-pending-row__code">{{ receipt.voucherNo }}</span>
|
|
<span class="dte-pending-row__date">{{ receipt.txnDate | dateFormat }}</span>
|
|
<span class="dte-pending-row__name">{{ receipt.cashierName || '—' }}</span>
|
|
<span class="dte-status" [class.dte-status--ok]="receipt.status === 'APPROVED'">
|
|
{{ receipt.status === 'APPROVED' ? 'Approved' : 'Awaiting manager' }}
|
|
</span>
|
|
<span class="dte-pending-row__amount">{{ receipt.amount | number: '1.2-2' }}</span>
|
|
</div>
|
|
}
|
|
}
|
|
</div>
|
|
}
|
|
|
|
@if (isWithdrawDeposite) {
|
|
<div class="dte-section">
|
|
<div class="dte-section__header">
|
|
<i class="fas fa-exchange-alt"></i>
|
|
<h3>Withdraw Deposite</h3>
|
|
</div>
|
|
<div class="dte-grid">
|
|
<mat-form-field appearance="outline" class="dte-field">
|
|
<mat-label>Creditor Party Code</mat-label>
|
|
<input
|
|
matInput
|
|
formControlName="creditorPartyCode"
|
|
[matAutocomplete]="creditorAuto"
|
|
#creditorTrigger="matAutocompleteTrigger"
|
|
(click)="creditorTrigger.openPanel()"
|
|
(focus)="creditorTrigger.openPanel()"
|
|
(blur)="settleTransferParty('creditor')"
|
|
placeholder="Search by code or name…"
|
|
autocomplete="off"
|
|
/>
|
|
<mat-icon matSuffix class="dte-list-arrow" (click)="$event.stopPropagation(); creditorTrigger.openPanel()"
|
|
>arrow_drop_down</mat-icon
|
|
>
|
|
<mat-autocomplete #creditorAuto="matAutocomplete" panelWidth="min(30rem, 92vw)">
|
|
@for (party of transferPartyOptions('creditor'); track party.id) {
|
|
<mat-option [value]="party.partyCode">
|
|
{{ party.partyCode }} · {{ party.partyName }} · ₹{{ party.currentBalance | number: '1.2-2' }}
|
|
</mat-option>
|
|
}
|
|
@if (transferPartyOptions('creditor').length === 0) {
|
|
<mat-option disabled>No open DDS account matches</mat-option>
|
|
}
|
|
</mat-autocomplete>
|
|
<mat-hint>Account the money goes into</mat-hint>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="dte-field">
|
|
<mat-label>Creditor Party Name</mat-label>
|
|
<input matInput formControlName="creditorPartyName" readonly />
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="dte-field">
|
|
<mat-label>Creditor Curr Bal</mat-label>
|
|
<input matInput formControlName="creditorPartyBalance" readonly />
|
|
</mat-form-field>
|
|
|
|
<mat-form-field appearance="outline" class="dte-field">
|
|
<mat-label>Debit Party Code</mat-label>
|
|
<input
|
|
matInput
|
|
formControlName="debitPartyCode"
|
|
[matAutocomplete]="debitAuto"
|
|
#debitTrigger="matAutocompleteTrigger"
|
|
(click)="debitTrigger.openPanel()"
|
|
(focus)="debitTrigger.openPanel()"
|
|
(blur)="settleTransferParty('debit')"
|
|
placeholder="Search by code or name…"
|
|
autocomplete="off"
|
|
/>
|
|
<mat-icon matSuffix class="dte-list-arrow" (click)="$event.stopPropagation(); debitTrigger.openPanel()"
|
|
>arrow_drop_down</mat-icon
|
|
>
|
|
<mat-autocomplete #debitAuto="matAutocomplete" panelWidth="min(30rem, 92vw)">
|
|
@for (party of transferPartyOptions('debit'); track party.id) {
|
|
<mat-option [value]="party.partyCode">
|
|
{{ party.partyCode }} · {{ party.partyName }} · ₹{{ party.currentBalance | number: '1.2-2' }}
|
|
</mat-option>
|
|
}
|
|
@if (transferPartyOptions('debit').length === 0) {
|
|
<mat-option disabled>No open DDS account matches</mat-option>
|
|
}
|
|
</mat-autocomplete>
|
|
@if (transferToItself) {
|
|
<mat-hint class="dte-same-party">Pick a different account - it cannot transfer to itself</mat-hint>
|
|
} @else {
|
|
<mat-hint>Account the money is taken from</mat-hint>
|
|
}
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="dte-field">
|
|
<mat-label>Debit Party Name</mat-label>
|
|
<input matInput formControlName="debitPartyName" readonly />
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="dte-field">
|
|
<mat-label>Debit Curr Bal</mat-label>
|
|
<input matInput formControlName="debitPartyBalance" readonly />
|
|
</mat-form-field>
|
|
|
|
<mat-form-field appearance="outline" class="dte-field">
|
|
<mat-label>Amount</mat-label>
|
|
<input matInput type="number" formControlName="withdrawAmount" />
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="dte-field dte-field--wide">
|
|
<mat-label>Description</mat-label>
|
|
<input matInput formControlName="withdrawDescription" />
|
|
</mat-form-field>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (!isPartyTran && !isWithdrawDeposite && !isAgentTran) {
|
|
<div class="dte-section">
|
|
<p class="dte-empty">
|
|
"{{ entryForm.get('transactionType').value }}" entry screen — same shape as Party Tran, to be filled in once
|
|
the backend distinguishes these transaction types.
|
|
</p>
|
|
</div>
|
|
}
|
|
|
|
<div class="dte-action-bar">
|
|
<button mat-raised-button type="button" (click)="exit()">Exit</button>
|
|
<button mat-stroked-button type="button" (click)="cancel()">Cancel</button>
|
|
<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>
|
|
<!-- Party Tran and Agent Tran are read here, never saved: the agent writes the round down on
|
|
their own screen and the cash is taken in at the cashier's counter. Agent Tran applies the
|
|
cashier's receipts from its own Apply DDS Receipt. Only Withdraw Deposite still saves. -->
|
|
@if (!isPartyTran && !isAgentTran) {
|
|
<button
|
|
mat-raised-button
|
|
color="primary"
|
|
type="button"
|
|
[disabled]="entryForm.invalid || transferToItself"
|
|
(click)="save()"
|
|
>
|
|
Save
|
|
</button>
|
|
}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|