371 lines
14 KiB
HTML
371 lines
14 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="fd-root">
|
||
<!-- HEADER -->
|
||
<div class="fd-head">
|
||
<div class="fd-head-left">
|
||
<fa-icon icon="file-signature" class="fd-head-ic"></fa-icon>
|
||
<div>
|
||
<h2 class="fd-head-t">Form 97 / 98 Transaction Report</h2>
|
||
<div class="fd-head-s">
|
||
Every transaction put through on a declaration under Rule 114B, for customers who have no PAN. Form 97 is the
|
||
general declaration; Form 98 is for a person whose only income is agricultural.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button class="fd-btn fd-btn--primary" [disabled]="generating" (click)="exportExcel()">
|
||
<fa-icon icon="file-excel"></fa-icon> {{ generating ? 'Generating…' : 'Download Excel' }}
|
||
</button>
|
||
</div>
|
||
|
||
<!-- FILTERS -->
|
||
<div class="fd-filters">
|
||
<div class="fd-filter-row">
|
||
<div class="fd-modes">
|
||
@for (m of periodModes; track m.key) {
|
||
<button class="fd-chip" [class.active]="periodMode === m.key" (click)="periodMode = m.key">
|
||
{{ m.label }}
|
||
</button>
|
||
}
|
||
</div>
|
||
|
||
@switch (periodMode) {
|
||
@case ('day') {
|
||
<label class="fd-field">
|
||
<span>Date</span>
|
||
<input type="date" [(ngModel)]="filterDay" [min]="period.fyStart" [max]="period.fyEnd" />
|
||
</label>
|
||
}
|
||
@case ('week') {
|
||
<label class="fd-field">
|
||
<span>Any date in week</span>
|
||
<input type="date" [(ngModel)]="filterWeek" [min]="period.fyStart" [max]="period.fyEnd" />
|
||
</label>
|
||
<span class="fd-resolved">Week: {{ rangeLabel }}</span>
|
||
}
|
||
@case ('month') {
|
||
<label class="fd-field">
|
||
<span>Month</span>
|
||
<input type="month" [(ngModel)]="filterMonth" min="2026-04" max="2026-07" />
|
||
</label>
|
||
}
|
||
@default {
|
||
<label class="fd-field">
|
||
<span>From</span>
|
||
<input type="date" [(ngModel)]="filterFrom" [min]="period.fyStart" [max]="period.fyEnd" />
|
||
</label>
|
||
<label class="fd-field">
|
||
<span>To</span>
|
||
<input type="date" [(ngModel)]="filterTo" [min]="period.fyStart" [max]="period.fyEnd" />
|
||
</label>
|
||
}
|
||
}
|
||
</div>
|
||
|
||
<div class="fd-filter-row">
|
||
<label class="fd-field">
|
||
<span>Branch</span>
|
||
<select [(ngModel)]="branchCode" [disabled]="!canSelectBranch">
|
||
@if (canSelectBranch) {
|
||
<option value="">All Branches</option>
|
||
}
|
||
@for (b of branches; track b.code) {
|
||
<option [value]="b.code">{{ b.code }} — {{ b.name }}</option>
|
||
}
|
||
</select>
|
||
</label>
|
||
|
||
<label class="fd-field">
|
||
<span>Declaration</span>
|
||
<select [(ngModel)]="formType">
|
||
<option value="">Both</option>
|
||
<option value="FORM_60">Form 97 — no PAN</option>
|
||
<option value="FORM_61">Form 98 — agricultural income</option>
|
||
</select>
|
||
</label>
|
||
|
||
<label class="fd-field">
|
||
<span>Transaction type</span>
|
||
<select [(ngModel)]="group">
|
||
<option value="">All types</option>
|
||
@for (g of txnGroups; track g) {
|
||
<option [value]="g">{{ g }}</option>
|
||
}
|
||
</select>
|
||
</label>
|
||
|
||
<label class="fd-field fd-field--grow">
|
||
<span>Search</span>
|
||
<input type="text" [(ngModel)]="search" placeholder="Name, account number or acknowledgement no…" />
|
||
</label>
|
||
|
||
<label class="fd-check">
|
||
<input type="checkbox" [(ngModel)]="flaggedOnly" />
|
||
Only CTR reportable
|
||
</label>
|
||
|
||
<button class="fd-btn" (click)="resetFilters()"><fa-icon icon="undo"></fa-icon> Reset</button>
|
||
</div>
|
||
|
||
@if (!canSelectBranch) {
|
||
<div class="fd-scope-note">
|
||
<fa-icon icon="lock"></fa-icon>
|
||
Showing {{ branchLabel }} only. Head Office logins can select any branch.
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<!-- SUMMARY -->
|
||
<div class="fd-kpis">
|
||
<div class="fd-kpi">
|
||
<div class="fd-kpi-n">{{ summary.persons }}</div>
|
||
<div class="fd-kpi-l">Declarants</div>
|
||
<div class="fd-kpi-sub">{{ summary.form60Persons }} on Form 97 · {{ summary.form61Persons }} on Form 98</div>
|
||
</div>
|
||
<div class="fd-kpi fd-kpi--blue">
|
||
<div class="fd-kpi-n">{{ summary.txnCount }}</div>
|
||
<div class="fd-kpi-l">Transactions — all types</div>
|
||
<div class="fd-kpi-sub">{{ summary.declarationRequired }} in a Rule 114B category</div>
|
||
</div>
|
||
<div class="fd-kpi fd-kpi--amber">
|
||
<div class="fd-kpi-n">{{ summary.cashTotal | number: '1.0-0' }}</div>
|
||
<div class="fd-kpi-l">Cash turnover (₹)</div>
|
||
<div class="fd-kpi-sub">{{ summary.cashCount }} cash transactions</div>
|
||
</div>
|
||
<div class="fd-kpi" [class.fd-kpi--red]="summary.ctrReportable > 0">
|
||
<div class="fd-kpi-n">{{ summary.ctrReportable }}</div>
|
||
<div class="fd-kpi-l">CTR reportable</div>
|
||
<div class="fd-kpi-sub">cash at or above ₹10,00,000</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CATEGORY MIX — what these customers actually do, beyond the declared rows -->
|
||
@if (summary.byGroup.length) {
|
||
<div class="fd-mix">
|
||
<span class="fd-mix-t">Transaction mix</span>
|
||
@for (g of summary.byGroup; track g.group) {
|
||
<button
|
||
class="fd-mix-item"
|
||
[class.active]="group === g.group"
|
||
(click)="group = group === g.group ? '' : g.group"
|
||
>
|
||
{{ g.group }} <strong>{{ g.count }}</strong>
|
||
</button>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
<!-- VIEW SWITCH -->
|
||
<div class="fd-views">
|
||
<button class="fd-chip" [class.active]="view === 'person'" (click)="view = 'person'">
|
||
<fa-icon icon="users"></fa-icon> Person wise
|
||
</button>
|
||
<button class="fd-chip" [class.active]="view === 'transaction'" (click)="view = 'transaction'">
|
||
<fa-icon icon="list"></fa-icon> Transaction wise
|
||
</button>
|
||
<span class="fd-spacer"></span>
|
||
<span class="fd-count">
|
||
{{ view === 'person' ? personRows.length + ' declarants' : txnRows.length + ' transactions' }}
|
||
</span>
|
||
</div>
|
||
|
||
<!-- PERSON WISE -->
|
||
@if (view === 'person') {
|
||
<div class="fd-table-wrap">
|
||
<table class="fd-table">
|
||
<thead>
|
||
<tr>
|
||
<th></th>
|
||
<th>Acknowledgement</th>
|
||
<th>Form</th>
|
||
<th>Name of declarant</th>
|
||
<th>Account No</th>
|
||
<th>Branch</th>
|
||
<th class="num">Txns</th>
|
||
<th class="num">Rule 114B</th>
|
||
<th class="num">Cash turnover</th>
|
||
<th class="num">Credit</th>
|
||
<th class="num">Debit</th>
|
||
<th class="num">Declared income</th>
|
||
<th class="num">Cash ÷ income</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (p of personRows; track p.declarant.ackNo) {
|
||
<tr class="fd-person" [class.open]="expanded === p.declarant.ackNo" (click)="toggle(p.declarant.ackNo)">
|
||
<td>
|
||
<fa-icon [icon]="expanded === p.declarant.ackNo ? 'caret-down' : 'caret-right'"></fa-icon>
|
||
</td>
|
||
<td class="mono">{{ p.declarant.ackNo }}</td>
|
||
<td>
|
||
<span class="fd-form" [class.fd-form--61]="p.declarant.formType === 'FORM_61'">
|
||
{{ formLabel[p.declarant.formType] }}
|
||
</span>
|
||
</td>
|
||
<td>
|
||
<strong>{{ p.declarant.name }}</strong>
|
||
@if (p.ctrCount > 0) {
|
||
<span class="fd-tag fd-tag--ctr">{{ p.ctrCount }} CTR</span>
|
||
}
|
||
</td>
|
||
<td class="mono">{{ p.declarant.accountNo }}</td>
|
||
<td>{{ p.declarant.branchCode }}</td>
|
||
<td class="num">{{ p.txnCount }}</td>
|
||
<td class="num">{{ p.declaredCount }}</td>
|
||
<td class="num strong">{{ p.cashTotal | number: '1.0-0' }}</td>
|
||
<td class="num">{{ p.totalCredit | number: '1.0-0' }}</td>
|
||
<td class="num">{{ p.totalDebit | number: '1.0-0' }}</td>
|
||
<td class="num">{{ p.declarant.estimatedIncome | number: '1.0-0' }}</td>
|
||
<td class="num">
|
||
<span [class]="ratioClass(p.incomeRatio)">{{ p.incomeRatio | number: '1.1-1' }}×</span>
|
||
</td>
|
||
</tr>
|
||
|
||
@if (expanded === p.declarant.ackNo) {
|
||
<tr class="fd-detail">
|
||
<td colspan="13">
|
||
<div class="fd-decl">
|
||
<div>
|
||
<label>Father's name</label><span>{{ p.declarant.fatherName }}</span>
|
||
</div>
|
||
<div>
|
||
<label>Date of birth</label><span>{{ p.declarant.dateOfBirth }}</span>
|
||
</div>
|
||
<div>
|
||
<label>Aadhaar</label><span class="mono">XXXX XXXX {{ p.declarant.aadhaarLast4 }}</span>
|
||
</div>
|
||
<div>
|
||
<label>Declared on</label><span>{{ p.declarant.declaredOn }}</span>
|
||
</div>
|
||
<div class="wide">
|
||
<label>Address</label><span>{{ p.declarant.address }}</span>
|
||
</div>
|
||
<div class="wide">
|
||
<label>Reason for no PAN</label><span>{{ p.declarant.reasonForNoPan }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<table class="fd-subtable">
|
||
<thead>
|
||
<tr>
|
||
<th>Date</th>
|
||
<th>Txn ID</th>
|
||
<th>Mode</th>
|
||
<th>Dr/Cr</th>
|
||
<th class="num">Amount</th>
|
||
<th>Nature of transaction (Rule 114B)</th>
|
||
<th>Voucher</th>
|
||
<th>Flag</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (t of p.txns; track t.txnId) {
|
||
<tr>
|
||
<td class="mono">{{ t.date }}</td>
|
||
<td class="mono">{{ t.txnId }}</td>
|
||
<td>
|
||
<span class="fd-mode">{{ t.mode }}</span>
|
||
</td>
|
||
<td [class.cr]="t.drCr === 'CR'" [class.dr]="t.drCr === 'DR'">{{ t.drCr }}</td>
|
||
<td class="num strong">{{ t.amount | number: '1.0-0' }}</td>
|
||
<td>{{ t.nature }}</td>
|
||
<td class="mono">{{ t.voucherNo }}</td>
|
||
<td>
|
||
@if (t.ctrReportable) {
|
||
<span class="fd-tag fd-tag--ctr">CTR</span>
|
||
} @else if (t.declarationRequired) {
|
||
<span class="fd-tag">Rule 114B</span>
|
||
}
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
}
|
||
} @empty {
|
||
<tr>
|
||
<td colspan="13" class="fd-empty">
|
||
No Form 97 or Form 98 transactions for {{ rangeLabel }} at {{ branchLabel }}.
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
}
|
||
|
||
<!-- TRANSACTION WISE -->
|
||
@if (view === 'transaction') {
|
||
<div class="fd-table-wrap">
|
||
<table class="fd-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Date</th>
|
||
<th>Txn ID</th>
|
||
<th>Acknowledgement</th>
|
||
<th>Form</th>
|
||
<th>Name of declarant</th>
|
||
<th>Account No</th>
|
||
<th>Branch</th>
|
||
<th>Category</th>
|
||
<th>Mode</th>
|
||
<th>Dr/Cr</th>
|
||
<th class="num">Amount</th>
|
||
<th>Nature of transaction</th>
|
||
<th>Voucher</th>
|
||
<th>Flag</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (t of txnRows; track t.txnId) {
|
||
<tr [class.fd-row--ctr]="t.ctrReportable">
|
||
<td class="mono">{{ t.date }}</td>
|
||
<td class="mono">{{ t.txnId }}</td>
|
||
<td class="mono">{{ t.ackNo }}</td>
|
||
<td>
|
||
<span class="fd-form" [class.fd-form--61]="t.formType === 'FORM_61'">{{ formLabel[t.formType] }}</span>
|
||
</td>
|
||
<td>{{ t.declarantName }}</td>
|
||
<td class="mono">{{ t.accountNo }}</td>
|
||
<td>{{ t.branchCode }}</td>
|
||
<td>
|
||
<span class="fd-group">{{ t.group }}</span>
|
||
</td>
|
||
<td>
|
||
<span class="fd-mode">{{ t.mode }}</span>
|
||
</td>
|
||
<td [class.cr]="t.drCr === 'CR'" [class.dr]="t.drCr === 'DR'">{{ t.drCr }}</td>
|
||
<td class="num strong">{{ t.amount | number: '1.0-0' }}</td>
|
||
<td>{{ t.nature }}</td>
|
||
<td class="mono">{{ t.voucherNo }}</td>
|
||
<td>
|
||
@if (t.ctrReportable) {
|
||
<span class="fd-tag fd-tag--ctr">CTR</span>
|
||
} @else if (t.declarationRequired) {
|
||
<span class="fd-tag">Rule 114B</span>
|
||
}
|
||
</td>
|
||
</tr>
|
||
} @empty {
|
||
<tr>
|
||
<td colspan="14" class="fd-empty">
|
||
No Form 97 or Form 98 transactions for {{ rangeLabel }} at {{ branchLabel }}.
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
}
|
||
|
||
<div class="fd-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
|
||
</div>
|