- Correct Investment portfolio allocation and Loan Origination product-mix targets against RBI's UCB SLR/Non-SLR framework and PSL circulars, with a Target Basis column distinguishing RBI-mandated vs board-policy rows (Excel export included). - Expand Investment holdings dataset with more near-term maturities so the Due-Date Diary reflects a realistic spread across FD/Govt Security/Non-SLR/Bond. - Add From/To period-based Interest Calculation & Post (with GL selection for both posting and receiving) across the Investment Interest module, FD Entry and Bond Entry screens; fix a bug where a posted period kept re-appearing as due since accrual wasn't tracked per-holding. - Add a reusable DD-MM-YYYY date-picker component and roll it out across all Investment date fields. - NPA Summary Report: drop SMA-0/1/2 from the stage-wise table (kept in Day-wise Overdue only). - NPA Recovery Report: add Account Type / NPA Category / Target-vs-Actual columns, a shared Account Type filter driving both the account picker and the register, and a searchable (fixed-position, not clipped by card overflow) account select for long lists. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
496 lines
19 KiB
HTML
496 lines
19 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="pg">
|
|
<header class="pg-head">
|
|
<div>
|
|
<div class="pg-eyebrow">TREASURY · INVESTMENT · FIXED DEPOSIT</div>
|
|
<h1 class="pg-title">FD Investment</h1>
|
|
<p class="pg-sub">
|
|
Bank's own Fixed Deposit investments with other banks — Purchase, Interest, Sell/Premature, Write-off and
|
|
Part-Payment entries.
|
|
</p>
|
|
</div>
|
|
@if (activeTab === 'holdings') {
|
|
<div class="pg-toolbar">
|
|
<button type="button" class="pg-btn pg-btn--ghost" [disabled]="generating" (click)="exportExcel()">
|
|
{{ generating ? 'Exporting…' : 'Export to Excel' }}
|
|
</button>
|
|
</div>
|
|
}
|
|
</header>
|
|
|
|
<div class="cctr-tabs">
|
|
<button
|
|
type="button"
|
|
class="pg-btn pg-btn--ghost"
|
|
[class.pg-btn--active]="activeTab === 'holdings'"
|
|
(click)="setTab('holdings')"
|
|
>
|
|
Holdings
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pg-btn pg-btn--ghost"
|
|
[class.pg-btn--active]="activeTab === 'purchase'"
|
|
(click)="setTab('purchase')"
|
|
>
|
|
01 Purchase Entry
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pg-btn pg-btn--ghost"
|
|
[class.pg-btn--active]="activeTab === 'interest'"
|
|
(click)="setTab('interest')"
|
|
>
|
|
02 Interest Entry
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pg-btn pg-btn--ghost"
|
|
[class.pg-btn--active]="activeTab === 'sell'"
|
|
(click)="setTab('sell')"
|
|
>
|
|
03 Sell / Premature Entry
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pg-btn pg-btn--ghost"
|
|
[class.pg-btn--active]="activeTab === 'writeoff'"
|
|
(click)="setTab('writeoff')"
|
|
>
|
|
04 Write Off Entry
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pg-btn pg-btn--ghost"
|
|
[class.pg-btn--active]="activeTab === 'partpayment'"
|
|
(click)="setTab('partpayment')"
|
|
>
|
|
05 FD Part Payment Entry
|
|
</button>
|
|
</div>
|
|
|
|
<!-- HOLDINGS -->
|
|
@if (activeTab === 'holdings') {
|
|
<div class="pg-card">
|
|
<div class="pos-section-label">FD Holdings Register</div>
|
|
<table class="pg-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Cert No</th>
|
|
<th>Party</th>
|
|
<th>Type</th>
|
|
<th class="num">Face Value</th>
|
|
<th class="num">Rate</th>
|
|
<th>Purchase Date</th>
|
|
<th>Maturity Date</th>
|
|
<th class="num col-in">Accrued Interest</th>
|
|
<th class="num col-net">Maturity Value</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (h of holdings; track h.certNo) {
|
|
<tr>
|
|
<td>
|
|
<span class="pg-chip">{{ h.certNo }}</span>
|
|
</td>
|
|
<td>{{ h.party }}</td>
|
|
<td>{{ h.interestFreq === 'CUMULATIVE' ? 'Cumulative' : 'Payout' }}</td>
|
|
<td class="num">₹{{ h.faceValue | number: '1.2-2' }}</td>
|
|
<td class="num">{{ h.interestRate }}%</td>
|
|
<td>{{ h.purchaseDate }}</td>
|
|
<td>{{ h.maturityDate }}</td>
|
|
<td class="num col-in">₹{{ accrued(h) | number: '1.2-2' }}</td>
|
|
<td class="num col-net">₹{{ maturity(h) | number: '1.2-2' }}</td>
|
|
<td>
|
|
<span [class]="statusClass(h)">{{ h.status }}</span>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
|
|
<!-- PURCHASE ENTRY -->
|
|
@if (activeTab === 'purchase') {
|
|
<div class="pg-card">
|
|
<div class="pos-section-label">Investment - FD Purchase Entry</div>
|
|
<div class="inv-form-grid">
|
|
<label class="inv-form-field">
|
|
<span>Tran. Type</span>
|
|
<select [(ngModel)]="purchaseForm.tranType">
|
|
<option value="FRESH">FRESH</option>
|
|
<option value="RENEWAL">RENEWAL</option>
|
|
</select>
|
|
</label>
|
|
@if (purchaseForm.tranType === 'RENEWAL') {
|
|
<label class="inv-form-field"
|
|
><span>OLD CertNo</span><input type="text" [(ngModel)]="purchaseForm.oldCertNo"
|
|
/></label>
|
|
}
|
|
<label class="inv-form-field">
|
|
<span>FD Type</span>
|
|
<select [(ngModel)]="purchaseForm.fdType">
|
|
@for (t of fdTypeOptions; track t) {
|
|
<option [value]="t">{{ t }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field">
|
|
<span>Mature Amt.calc.Type</span>
|
|
<select [(ngModel)]="purchaseForm.matureAmtCalcType">
|
|
<option value="GENERAL">GENERAL</option>
|
|
<option value="DATEWISE">DATEWISE</option>
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field">
|
|
<span>Party *</span>
|
|
<select [(ngModel)]="purchaseForm.party">
|
|
<option value="">-- Select --</option>
|
|
@for (p of partyOptions; track p.partyCode) {
|
|
<option [value]="p.partyName">{{ p.partyName }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field">
|
|
<span>Sub Party</span>
|
|
<select [(ngModel)]="purchaseForm.subParty">
|
|
<option value="">-- Select --</option>
|
|
@for (p of partyOptions; track p.partyCode) {
|
|
<option [value]="p.partyName">{{ p.partyName }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field"
|
|
><span>GL ACNO</span><input type="text" [(ngModel)]="purchaseForm.glAcNo"
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>GL A/c Name</span><input type="text" [(ngModel)]="purchaseForm.glAcName" readonly
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Certificate No.</span><input type="text" [(ngModel)]="purchaseForm.certNo"
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Certificate Date</span><mifosx-date-ddmmyyyy [(ngModel)]="purchaseForm.certDate"></mifosx-date-ddmmyyyy
|
|
></label>
|
|
<label class="inv-form-field">
|
|
<span>Sub Type</span>
|
|
<select [(ngModel)]="purchaseForm.subType">
|
|
@for (s of subTypeOptions; track s.subTypeCode + s.subTypeName) {
|
|
<option [value]="s.subTypeName">{{ s.subTypeCode }} - {{ s.subTypeName }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field">
|
|
<span>Reciept/Advice</span>
|
|
<select [(ngModel)]="purchaseForm.receiptAdvice">
|
|
<option value="RECEIPT">RECEIPT</option>
|
|
<option value="ADVICE">ADVICE</option>
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field"
|
|
><span>Transaction Date</span
|
|
><mifosx-date-ddmmyyyy [(ngModel)]="purchaseForm.transactionDate"></mifosx-date-ddmmyyyy
|
|
></label>
|
|
<label class="inv-form-field"
|
|
><span>Deposit Date *</span
|
|
><mifosx-date-ddmmyyyy [(ngModel)]="purchaseForm.depositDate"></mifosx-date-ddmmyyyy
|
|
></label>
|
|
<label class="inv-form-field"
|
|
><span>Deposit Amount *</span><input type="number" [(ngModel)]="purchaseForm.depositAmount" min="0"
|
|
/></label>
|
|
<label class="inv-form-field">
|
|
<span>Interest[M/Q/H/Y]</span>
|
|
<select [(ngModel)]="purchaseForm.interestFreq">
|
|
@for (i of intTypeOptions; track i.value) {
|
|
<option [value]="i.value">{{ i.label }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field"
|
|
><span>Interest Date</span
|
|
><mifosx-date-ddmmyyyy [(ngModel)]="purchaseForm.interestDate"></mifosx-date-ddmmyyyy
|
|
></label>
|
|
<label class="inv-form-field"
|
|
><span>Interest Rate *</span><input type="number" [(ngModel)]="purchaseForm.interestRate" min="0" step="0.01"
|
|
/></label>
|
|
@if (purchaseForm.matureAmtCalcType === 'GENERAL') {
|
|
<label class="inv-form-field"
|
|
><span>Period Month</span><input type="number" [(ngModel)]="purchaseForm.periodMonth" min="0" max="11"
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Year</span><input type="number" [(ngModel)]="purchaseForm.periodYear" min="0"
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Days</span><input type="number" [(ngModel)]="purchaseForm.periodDays" min="0"
|
|
/></label>
|
|
} @else {
|
|
<label class="inv-form-field"
|
|
><span>Maturity Date (manual) *</span
|
|
><mifosx-date-ddmmyyyy [(ngModel)]="purchaseForm.manualMaturityDate"></mifosx-date-ddmmyyyy
|
|
></label>
|
|
}
|
|
<label class="inv-form-field"
|
|
><span>Resolution No.</span><input type="text" [(ngModel)]="purchaseForm.resolutionNo"
|
|
/></label>
|
|
<label class="inv-form-field">
|
|
<span>CR. Branch Code</span>
|
|
<select [(ngModel)]="purchaseForm.crBranchCode">
|
|
@for (b of branchOptions; track b) {
|
|
<option [value]="b">{{ b }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field"
|
|
><span>CR. To A/c</span><input type="text" [(ngModel)]="purchaseForm.crToAc"
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>CR. A/c Name</span><input type="text" [(ngModel)]="purchaseForm.crToAcName" readonly
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Int.Receivable Code</span><input type="text" [(ngModel)]="purchaseForm.intReceivableCode"
|
|
/></label>
|
|
<label class="inv-form-field">
|
|
<span>LIEN Y/N</span>
|
|
<select [(ngModel)]="purchaseForm.lien">
|
|
<option value="NO">NO</option>
|
|
<option value="YES">YES</option>
|
|
</select>
|
|
</label>
|
|
@if (purchaseForm.lien === 'YES') {
|
|
<label class="inv-form-field"
|
|
><span>LIEN AMT.</span><input type="number" [(ngModel)]="purchaseForm.lienAmt"
|
|
/></label>
|
|
<label class="inv-form-field">
|
|
<span>LIEN Reason</span>
|
|
<select [(ngModel)]="purchaseForm.lienReason">
|
|
<option value="">-- Select --</option>
|
|
@for (l of lienReasonOptions; track l.reasonCode) {
|
|
<option [value]="l.reasonDescription">{{ l.reasonDescription }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
}
|
|
<label class="inv-form-field">
|
|
<span>Classification</span>
|
|
<select [(ngModel)]="purchaseForm.classification">
|
|
<option value="">-- Select --</option>
|
|
<option value="STANDARD">STANDARD</option>
|
|
<option value="SUB-STANDARD">SUB-STANDARD</option>
|
|
<option value="DOUBTFUL">DOUBTFUL</option>
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field">
|
|
<span>Collable</span>
|
|
<select [(ngModel)]="purchaseForm.collable">
|
|
<option value="NO">NO</option>
|
|
<option value="YES">YES</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
<div class="pos-section-label">Mature / Premature Details</div>
|
|
<div class="inv-form-grid">
|
|
<label class="inv-form-field"
|
|
><span>* Maturity Date</span><input type="text" [value]="purchaseMaturityDate" readonly
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>* Maturity Amt</span><input type="text" [value]="purchaseMaturityAmount | number: '1.0-0'" readonly
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Search by Cert No</span><input type="text" [(ngModel)]="searchCertNo" placeholder="Enter Cert No"
|
|
/></label>
|
|
</div>
|
|
<div class="inv-form-actions">
|
|
<button type="button" class="pg-btn pg-btn--primary" (click)="savePurchase()">Save</button>
|
|
<button type="button" class="pg-btn pg-btn--ghost" (click)="cancelPurchase()">Cancel</button>
|
|
<button type="button" class="pg-btn pg-btn--ghost" (click)="searchPurchase()">Search</button>
|
|
<button type="button" class="pg-btn pg-btn--ghost" (click)="dropPurchase()">Drop</button>
|
|
<button type="button" class="pg-btn pg-btn--ghost" (click)="exitPurchase()">Exit</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- INTEREST ENTRY -->
|
|
@if (activeTab === 'interest') {
|
|
<div class="pg-card">
|
|
<div class="pos-section-label">Investment - FD Interest Entry / Calculation</div>
|
|
<div class="inv-form-grid">
|
|
<label class="inv-form-field">
|
|
<span>Certificate No *</span>
|
|
<select [(ngModel)]="interestCertNo">
|
|
<option value="">-- Select --</option>
|
|
@for (h of interestEligibleHoldings; track h.certNo) {
|
|
<option [value]="h.certNo">{{ h.certNo }} — {{ h.party }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
<label class="inv-form-field"
|
|
><span>From Date</span><mifosx-date-ddmmyyyy [(ngModel)]="interestFromDate"></mifosx-date-ddmmyyyy
|
|
></label>
|
|
<label class="inv-form-field"
|
|
><span>To Date</span><mifosx-date-ddmmyyyy [(ngModel)]="interestToDate"></mifosx-date-ddmmyyyy
|
|
></label>
|
|
<label class="inv-form-field">
|
|
<span>Post To (Interest Income GL)</span>
|
|
<select [(ngModel)]="interestGlCode">
|
|
@for (g of interestIncomeGlOptions; track g.glCode) {
|
|
<option [value]="g.glCode">{{ g.glCode }} — {{ g.glName }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
@if (interestHolding; as h) {
|
|
<label class="inv-form-field"><span>Party</span><input type="text" [value]="h.party" readonly /></label>
|
|
<label class="inv-form-field"
|
|
><span>Interest Rate</span><input type="text" [value]="h.interestRate + '%'" readonly
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Frequency</span><input type="text" [value]="h.interestFreq" readonly
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Interest for Period</span
|
|
><input type="text" [value]="'₹' + (interestAccrued(h) | number: '1.2-2')" readonly
|
|
/></label>
|
|
}
|
|
</div>
|
|
<div class="inv-form-actions">
|
|
<button type="button" class="pg-btn pg-btn--primary" (click)="postInterest()">Process / Post Interest</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- SELL / PREMATURE ENTRY -->
|
|
@if (activeTab === 'sell') {
|
|
<div class="pg-card">
|
|
<div class="pos-section-label">Investment - FD Sell / Premature Closure Entry</div>
|
|
<div class="inv-form-grid">
|
|
<label class="inv-form-field">
|
|
<span>Certificate No *</span>
|
|
<select [(ngModel)]="sellCertNo">
|
|
<option value="">-- Select --</option>
|
|
@for (h of activeHoldings; track h.certNo) {
|
|
<option [value]="h.certNo">{{ h.certNo }} — {{ h.party }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
@if (sellHolding; as h) {
|
|
<label class="inv-form-field"
|
|
><span>Face Value</span><input type="text" [value]="'₹' + (h.faceValue | number: '1.2-2')" readonly
|
|
/></label>
|
|
<label class="inv-form-field"
|
|
><span>Accrued Interest till Sell Date</span
|
|
><input type="text" [value]="'₹' + (accrued(h) | number: '1.2-2')" readonly
|
|
/></label>
|
|
}
|
|
<label class="inv-form-field"
|
|
><span>Sell / Premature Date *</span><mifosx-date-ddmmyyyy [(ngModel)]="sellDate"></mifosx-date-ddmmyyyy
|
|
></label>
|
|
<label class="inv-form-field"><span>Remarks</span><input type="text" [(ngModel)]="sellRemark" /></label>
|
|
</div>
|
|
<div class="inv-form-actions">
|
|
<button type="button" class="pg-btn pg-btn--primary" (click)="saveSell()">Save</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- WRITE OFF ENTRY -->
|
|
@if (activeTab === 'writeoff') {
|
|
<div class="pg-card">
|
|
<div class="pos-section-label">Investment - FD Write Off Entry</div>
|
|
<div class="inv-form-grid">
|
|
<label class="inv-form-field">
|
|
<span>Certificate No *</span>
|
|
<select [(ngModel)]="writeoffCertNo">
|
|
<option value="">-- Select --</option>
|
|
@for (h of activeHoldings; track h.certNo) {
|
|
<option [value]="h.certNo">{{ h.certNo }} — {{ h.party }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
@if (writeoffHolding; as h) {
|
|
<label class="inv-form-field"
|
|
><span>Face Value to be Written Off</span
|
|
><input type="text" [value]="'₹' + (h.faceValue | number: '1.2-2')" readonly
|
|
/></label>
|
|
}
|
|
<label class="inv-form-field"><span>Reason *</span><input type="text" [(ngModel)]="writeoffReason" /></label>
|
|
</div>
|
|
<div class="inv-form-actions">
|
|
<button type="button" class="pg-btn pg-btn--primary" (click)="saveWriteoff()">Save</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- PART PAYMENT ENTRY -->
|
|
@if (activeTab === 'partpayment') {
|
|
<div class="pg-card">
|
|
<div class="pos-section-label">Investment - FD Part Payment Entry</div>
|
|
<div class="inv-form-grid">
|
|
<label class="inv-form-field">
|
|
<span>Certificate No *</span>
|
|
<select [(ngModel)]="partPayCertNo">
|
|
<option value="">-- Select --</option>
|
|
@for (h of activeHoldings; track h.certNo) {
|
|
<option [value]="h.certNo">{{ h.certNo }} — {{ h.party }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
@if (partPayHolding; as h) {
|
|
<label class="inv-form-field"
|
|
><span>Current Face Value</span><input type="text" [value]="'₹' + (h.faceValue | number: '1.2-2')" readonly
|
|
/></label>
|
|
}
|
|
<label class="inv-form-field"
|
|
><span>Part Payment Amount *</span><input type="number" [(ngModel)]="partPayAmount" min="0"
|
|
/></label>
|
|
</div>
|
|
<div class="inv-form-actions">
|
|
<button type="button" class="pg-btn pg-btn--primary" (click)="savePartPayment()">Save</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (activeTab !== 'holdings') {
|
|
<div class="pg-card">
|
|
<div class="pos-section-label">FD Transaction Log</div>
|
|
<table class="pg-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Voucher No</th>
|
|
<th>Cert No</th>
|
|
<th>Type</th>
|
|
<th>Particulars</th>
|
|
<th>GL Posted</th>
|
|
<th class="num">Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (t of txns; track t.voucherNo) {
|
|
<tr>
|
|
<td>{{ t.txnDate }}</td>
|
|
<td>
|
|
<span class="pg-chip">{{ t.voucherNo }}</span>
|
|
</td>
|
|
<td>{{ t.certNo }}</td>
|
|
<td>{{ t.txnType }}</td>
|
|
<td>{{ t.particulars }}</td>
|
|
<td>{{ t.glCode ? t.glCode + ' — ' + t.glName : '—' }}</td>
|
|
<td class="num">₹{{ t.amount | number: '1.2-2' }}</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<div class="inv-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
|