feat(investment): add Treasury Investment module (FD, Govt Sec/Non-SLR/Bond/MF, Share)
Adds full frontend-only Investment module mirroring the legacy AutoBank CBS Investment menu: Dashboard, FD/Bond/Share entry screens (purchase, interest, sell/redeem, write-off, amortize and more), 10 master screens, an Interest calculation/register screen, and ~21 reports. All figures are derived from a single holdings/transactions dataset so entry, interest, and reports can never drift out of sync. Wires the new "Investment" mega-menu into the shell and routes the previously inert Investment stub buttons.
This commit is contained in:
@@ -0,0 +1,557 @@
|
||||
<!--
|
||||
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 · MASTERS</div>
|
||||
<h1 class="pg-title">Investment Masters</h1>
|
||||
<p class="pg-sub">
|
||||
Bank/Party, Script, SubType, Broker, Control Parameter, LIEN Reason, Mutual Fund Product, SGL Bank and Script
|
||||
Market Detail masters.
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="cctr-tabs">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'party'"
|
||||
(click)="setTab('party')"
|
||||
>
|
||||
04 Bank / Party Master
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'script'"
|
||||
(click)="setTab('script')"
|
||||
>
|
||||
05 Script Master
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'subtype'"
|
||||
(click)="setTab('subtype')"
|
||||
>
|
||||
06 SubType Master
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'broker'"
|
||||
(click)="setTab('broker')"
|
||||
>
|
||||
07 Broker Master
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'control'"
|
||||
(click)="setTab('control')"
|
||||
>
|
||||
08 Control Parameter
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'dailyscript'"
|
||||
(click)="setTab('dailyscript')"
|
||||
>
|
||||
09 Daily Script Master
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'lien'"
|
||||
(click)="setTab('lien')"
|
||||
>
|
||||
10 LIEN Reason Master
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'mfproduct'"
|
||||
(click)="setTab('mfproduct')"
|
||||
>
|
||||
11 Mutual Fund Product Master
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'sglbank'"
|
||||
(click)="setTab('sglbank')"
|
||||
>
|
||||
12 SGL Bank Master
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--ghost"
|
||||
[class.pg-btn--active]="activeTab === 'marketdetail'"
|
||||
(click)="setTab('marketdetail')"
|
||||
>
|
||||
13 Script Market Detail
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- PARTY MASTER -->
|
||||
@if (activeTab === 'party') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add Bank / Party</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Party Name *</span><input type="text" [(ngModel)]="newParty.partyName"
|
||||
/></label>
|
||||
<label class="inv-form-field">
|
||||
<span>Party Type *</span>
|
||||
<select [(ngModel)]="newParty.partyType">
|
||||
<option [ngValue]="undefined">-- Select --</option>
|
||||
<option value="BANK">BANK</option>
|
||||
<option value="CORPORATE">CORPORATE</option>
|
||||
<option value="GOVT">GOVT</option>
|
||||
<option value="AMC">AMC</option>
|
||||
<option value="EXCHANGE">EXCHANGE</option>
|
||||
<option value="COOP_SOCIETY">COOP SOCIETY</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="inv-form-field"><span>PAN No</span><input type="text" [(ngModel)]="newParty.panNo" /></label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addParty()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Party Code</th>
|
||||
<th>Party Name</th>
|
||||
<th>Type</th>
|
||||
<th>PAN No</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (p of parties; track p.partyCode) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ p.partyCode }}</span>
|
||||
</td>
|
||||
<td>{{ p.partyName }}</td>
|
||||
<td>{{ p.partyType }}</td>
|
||||
<td>{{ p.panNo }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- SCRIPT MASTER -->
|
||||
@if (activeTab === 'script') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add Script</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Script Code</span><input type="text" [(ngModel)]="newScript.scriptCode"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Script Name *</span><input type="text" [(ngModel)]="newScript.scriptName"
|
||||
/></label>
|
||||
<label class="inv-form-field">
|
||||
<span>Instrument Type *</span>
|
||||
<select [(ngModel)]="newScript.instrumentType">
|
||||
<option [ngValue]="undefined">-- Select --</option>
|
||||
@for (t of instrumentTypes; track t) {
|
||||
<option [value]="t">{{ t }}</option>
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
<label class="inv-form-field"><span>ISIN</span><input type="text" [(ngModel)]="newScript.isin" /></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Face Value / Unit</span><input type="number" [(ngModel)]="newScript.faceValuePerUnit"
|
||||
/></label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addScript()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Script Code</th>
|
||||
<th>Script Name</th>
|
||||
<th>Instrument</th>
|
||||
<th>ISIN</th>
|
||||
<th class="num">Face Value/Unit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (s of scripts; track s.scriptCode) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ s.scriptCode }}</span>
|
||||
</td>
|
||||
<td>{{ s.scriptName }}</td>
|
||||
<td>{{ s.instrumentType }}</td>
|
||||
<td>{{ s.isin || '—' }}</td>
|
||||
<td class="num">{{ s.faceValuePerUnit }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- SUBTYPE MASTER -->
|
||||
@if (activeTab === 'subtype') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add Sub Type</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Sub Type Name *</span><input type="text" [(ngModel)]="newSubType.subTypeName"
|
||||
/></label>
|
||||
<label class="inv-form-field">
|
||||
<span>Instrument Type *</span>
|
||||
<select [(ngModel)]="newSubType.instrumentType">
|
||||
<option [ngValue]="undefined">-- Select --</option>
|
||||
@for (t of instrumentTypes; track t) {
|
||||
<option [value]="t">{{ t }}</option>
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addSubType()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Sub Type</th>
|
||||
<th>Instrument</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (s of subTypes; track s.subTypeCode + s.subTypeName) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ s.subTypeCode }}</span>
|
||||
</td>
|
||||
<td>{{ s.subTypeName }}</td>
|
||||
<td>{{ s.instrumentType }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- BROKER MASTER -->
|
||||
@if (activeTab === 'broker') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add Broker</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Broker Name *</span><input type="text" [(ngModel)]="newBroker.brokerName"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Contact No</span><input type="text" [(ngModel)]="newBroker.contactNo"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Empanelled On</span><input type="text" [(ngModel)]="newBroker.empanelledOn" placeholder="DD-MM-YYYY"
|
||||
/></label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addBroker()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Broker Code</th>
|
||||
<th>Broker Name</th>
|
||||
<th>Contact</th>
|
||||
<th>Empanelled On</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (b of brokers; track b.brokerCode) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ b.brokerCode }}</span>
|
||||
</td>
|
||||
<td>{{ b.brokerName }}</td>
|
||||
<td>{{ b.contactNo }}</td>
|
||||
<td>{{ b.empanelledOn }}</td>
|
||||
<td>
|
||||
<span class="inv-badge inv-badge--active">{{ b.status }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- CONTROL PARAMETER -->
|
||||
@if (activeTab === 'control') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add Control Parameter</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Parameter Name *</span><input type="text" [(ngModel)]="newControlParam.paramName"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Value *</span><input type="text" [(ngModel)]="newControlParam.paramValue"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Description</span><input type="text" [(ngModel)]="newControlParam.description"
|
||||
/></label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addControlParam()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (c of controlParams; track c.paramName) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ c.paramName }}</span>
|
||||
</td>
|
||||
<td class="num">{{ c.paramValue }}</td>
|
||||
<td>{{ c.description }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- DAILY SCRIPT MASTER -->
|
||||
@if (activeTab === 'dailyscript') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add Daily Script Rate</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Rate Date *</span><input type="text" [(ngModel)]="newDailyRate.rateDate" placeholder="DD-MM-YYYY"
|
||||
/></label>
|
||||
<label class="inv-form-field">
|
||||
<span>Script Code *</span>
|
||||
<select [(ngModel)]="newDailyRate.scriptCode">
|
||||
<option [ngValue]="undefined">-- Select --</option>
|
||||
@for (s of scripts; track s.scriptCode) {
|
||||
<option [value]="s.scriptCode">{{ s.scriptCode }} — {{ s.scriptName }}</option>
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
<label class="inv-form-field"
|
||||
><span>Open Rate</span><input type="number" [(ngModel)]="newDailyRate.openRate" step="0.01"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Close Rate *</span><input type="number" [(ngModel)]="newDailyRate.closeRate" step="0.01"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>High Rate</span><input type="number" [(ngModel)]="newDailyRate.highRate" step="0.01"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Low Rate</span><input type="number" [(ngModel)]="newDailyRate.lowRate" step="0.01"
|
||||
/></label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addDailyRate()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rate Date</th>
|
||||
<th>Script Code</th>
|
||||
<th>Script Name</th>
|
||||
<th class="num">Open</th>
|
||||
<th class="num">High</th>
|
||||
<th class="num">Low</th>
|
||||
<th class="num">Close</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (r of dailyScriptRates; track r.rateDate + r.scriptCode) {
|
||||
<tr>
|
||||
<td>{{ r.rateDate }}</td>
|
||||
<td>
|
||||
<span class="pg-chip">{{ r.scriptCode }}</span>
|
||||
</td>
|
||||
<td>{{ r.scriptName }}</td>
|
||||
<td class="num">{{ r.openRate | number: '1.2-2' }}</td>
|
||||
<td class="num">{{ r.highRate | number: '1.2-2' }}</td>
|
||||
<td class="num">{{ r.lowRate | number: '1.2-2' }}</td>
|
||||
<td class="num">{{ r.closeRate | number: '1.2-2' }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- LIEN REASON MASTER -->
|
||||
@if (activeTab === 'lien') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add LIEN Reason</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Reason Description *</span><input type="text" [(ngModel)]="newLienReason.reasonDescription"
|
||||
/></label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addLienReason()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Reason Code</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (l of lienReasons; track l.reasonCode) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ l.reasonCode }}</span>
|
||||
</td>
|
||||
<td>{{ l.reasonDescription }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- MUTUAL FUND PRODUCT MASTER -->
|
||||
@if (activeTab === 'mfproduct') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add Mutual Fund Product</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Product Name *</span><input type="text" [(ngModel)]="newMfProduct.productName"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>AMC Name *</span><input type="text" [(ngModel)]="newMfProduct.amcName"
|
||||
/></label>
|
||||
<label class="inv-form-field"
|
||||
><span>Category</span><input type="text" [(ngModel)]="newMfProduct.category"
|
||||
/></label>
|
||||
<label class="inv-form-field">
|
||||
<span>Risk Level</span>
|
||||
<select [(ngModel)]="newMfProduct.riskLevel">
|
||||
<option value="LOW">LOW</option>
|
||||
<option value="MODERATE">MODERATE</option>
|
||||
<option value="HIGH">HIGH</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addMfProduct()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product Code</th>
|
||||
<th>Product Name</th>
|
||||
<th>AMC</th>
|
||||
<th>Category</th>
|
||||
<th>Risk</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (m of mfProducts; track m.productCode) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ m.productCode }}</span>
|
||||
</td>
|
||||
<td>{{ m.productName }}</td>
|
||||
<td>{{ m.amcName }}</td>
|
||||
<td>{{ m.category }}</td>
|
||||
<td>{{ m.riskLevel }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- SGL BANK MASTER -->
|
||||
@if (activeTab === 'sglbank') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Add SGL Bank</div>
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field"
|
||||
><span>Bank Name *</span><input type="text" [(ngModel)]="newSglBank.bankName"
|
||||
/></label>
|
||||
<label class="inv-form-field"><span>Branch</span><input type="text" [(ngModel)]="newSglBank.branch" /></label>
|
||||
</div>
|
||||
<div class="inv-form-actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="addSglBank()">Save</button>
|
||||
</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>SGL Code</th>
|
||||
<th>Bank Name</th>
|
||||
<th>Branch</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (s of sglBanks; track s.sglCode) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ s.sglCode }}</span>
|
||||
</td>
|
||||
<td>{{ s.bankName }}</td>
|
||||
<td>{{ s.branch }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- SCRIPT MARKET DETAIL (also covers "09 Daily Script Master") -->
|
||||
@if (activeTab === 'marketdetail') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Script Market Detail (Daily Script Rates)</div>
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Script Code</th>
|
||||
<th class="num">Market Price</th>
|
||||
<th>As On</th>
|
||||
<th class="num">YTM (%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (m of marketDetails; track m.scriptCode) {
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pg-chip">{{ m.scriptCode }}</span>
|
||||
</td>
|
||||
<td class="num">₹{{ m.marketPrice | number: '1.2-2' }}</td>
|
||||
<td>{{ m.asOnDate }}</td>
|
||||
<td class="num">{{ m.ytm }}%</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="inv-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
|
||||
Reference in New Issue
Block a user