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,262 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import {
|
||||
SCRIPT_MASTER,
|
||||
BROKER_MASTER,
|
||||
PARTY_MASTER,
|
||||
SUBTYPE_MASTER,
|
||||
CONTROL_PARAMETERS,
|
||||
MUTUAL_FUND_PRODUCTS,
|
||||
SGL_BANK_MASTER,
|
||||
SCRIPT_MARKET_DETAIL,
|
||||
DAILY_SCRIPT_RATES,
|
||||
LIEN_REASON_MASTER,
|
||||
ScriptMaster,
|
||||
BrokerMaster,
|
||||
PartyMaster,
|
||||
SubTypeMaster,
|
||||
ControlParameter,
|
||||
MutualFundProductMaster,
|
||||
SglBankMaster,
|
||||
ScriptMarketDetail,
|
||||
DailyScriptRate,
|
||||
LienReason,
|
||||
InstrumentType
|
||||
} from '../investment-data';
|
||||
|
||||
type MasterTab =
|
||||
| 'party'
|
||||
| 'script'
|
||||
| 'subtype'
|
||||
| 'broker'
|
||||
| 'control'
|
||||
| 'dailyscript'
|
||||
| 'lien'
|
||||
| 'mfproduct'
|
||||
| 'sglbank'
|
||||
| 'marketdetail';
|
||||
|
||||
/**
|
||||
* Investment master-data screens (legacy "04-13" Entry submenu masters) — 9 simple dummy
|
||||
* lists, each with an inline Add-row form so the demo stays interactive without a backend.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-investment-masters',
|
||||
templateUrl: './investment-masters.component.html',
|
||||
styleUrls: [
|
||||
'../../cashier/cashier-shared.scss',
|
||||
'../investment-shared.scss'
|
||||
],
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule
|
||||
]
|
||||
})
|
||||
export class InvestmentMastersComponent {
|
||||
activeTab: MasterTab = 'party';
|
||||
|
||||
scripts: ScriptMaster[] = [...SCRIPT_MASTER];
|
||||
brokers: BrokerMaster[] = [...BROKER_MASTER];
|
||||
parties: PartyMaster[] = [...PARTY_MASTER];
|
||||
subTypes: SubTypeMaster[] = [...SUBTYPE_MASTER];
|
||||
controlParams: ControlParameter[] = [...CONTROL_PARAMETERS];
|
||||
mfProducts: MutualFundProductMaster[] = [...MUTUAL_FUND_PRODUCTS];
|
||||
sglBanks: SglBankMaster[] = [...SGL_BANK_MASTER];
|
||||
marketDetails: ScriptMarketDetail[] = [...SCRIPT_MARKET_DETAIL];
|
||||
dailyScriptRates: DailyScriptRate[] = [...DAILY_SCRIPT_RATES];
|
||||
lienReasons: LienReason[] = [...LIEN_REASON_MASTER];
|
||||
|
||||
readonly instrumentTypes: InstrumentType[] = [
|
||||
'FD',
|
||||
'GOVT_SECURITY',
|
||||
'NON_SLR',
|
||||
'BOND',
|
||||
'MUTUAL_FUND',
|
||||
'SHARE'
|
||||
];
|
||||
|
||||
toastMsg = '';
|
||||
toastVisible = false;
|
||||
private toastTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
newParty: Partial<PartyMaster> = {};
|
||||
newScript: Partial<ScriptMaster> = {};
|
||||
newSubType: Partial<SubTypeMaster> = {};
|
||||
newBroker: Partial<BrokerMaster> = {};
|
||||
newControlParam: Partial<ControlParameter> = {};
|
||||
newLienReason: Partial<LienReason> = {};
|
||||
newMfProduct: Partial<MutualFundProductMaster> = {};
|
||||
newSglBank: Partial<SglBankMaster> = {};
|
||||
newDailyRate: Partial<DailyScriptRate> = {};
|
||||
|
||||
setTab(tab: MasterTab): void {
|
||||
this.activeTab = tab;
|
||||
}
|
||||
|
||||
addParty(): void {
|
||||
if (!this.newParty.partyName || !this.newParty.partyType) {
|
||||
this.toast('Enter Party Name and Type.');
|
||||
return;
|
||||
}
|
||||
this.parties = [
|
||||
{
|
||||
partyCode: `PTY-${String(this.parties.length + 1).padStart(3, '0')}`,
|
||||
panNo: '',
|
||||
...this.newParty
|
||||
} as PartyMaster,
|
||||
...this.parties
|
||||
];
|
||||
this.newParty = {};
|
||||
this.toast('Party/Bank master added.');
|
||||
}
|
||||
|
||||
addScript(): void {
|
||||
if (!this.newScript.scriptName || !this.newScript.instrumentType) {
|
||||
this.toast('Enter Script Name and Instrument Type.');
|
||||
return;
|
||||
}
|
||||
this.scripts = [
|
||||
{
|
||||
scriptCode: this.newScript.scriptCode || `SCR-${this.scripts.length + 1}`,
|
||||
isin: '',
|
||||
faceValuePerUnit: 100,
|
||||
...this.newScript
|
||||
} as ScriptMaster,
|
||||
...this.scripts
|
||||
];
|
||||
this.newScript = {};
|
||||
this.toast('Script master added.');
|
||||
}
|
||||
|
||||
addSubType(): void {
|
||||
if (!this.newSubType.subTypeName || !this.newSubType.instrumentType) {
|
||||
this.toast('Enter Sub Type Name and Instrument Type.');
|
||||
return;
|
||||
}
|
||||
this.subTypes = [
|
||||
{ subTypeCode: String(this.subTypes.length + 1), ...this.newSubType } as SubTypeMaster,
|
||||
...this.subTypes
|
||||
];
|
||||
this.newSubType = {};
|
||||
this.toast('Sub Type master added.');
|
||||
}
|
||||
|
||||
addBroker(): void {
|
||||
if (!this.newBroker.brokerName) {
|
||||
this.toast('Enter Broker Name.');
|
||||
return;
|
||||
}
|
||||
this.brokers = [
|
||||
{
|
||||
brokerCode: `BRK-${String(this.brokers.length + 1).padStart(3, '0')}`,
|
||||
contactNo: '',
|
||||
empanelledOn: '',
|
||||
status: 'ACTIVE',
|
||||
...this.newBroker
|
||||
} as BrokerMaster,
|
||||
...this.brokers
|
||||
];
|
||||
this.newBroker = {};
|
||||
this.toast('Broker master added.');
|
||||
}
|
||||
|
||||
addControlParam(): void {
|
||||
if (!this.newControlParam.paramName || !this.newControlParam.paramValue) {
|
||||
this.toast('Enter Parameter Name and Value.');
|
||||
return;
|
||||
}
|
||||
this.controlParams = [
|
||||
{ description: '', ...this.newControlParam } as ControlParameter,
|
||||
...this.controlParams
|
||||
];
|
||||
this.newControlParam = {};
|
||||
this.toast('Control parameter added.');
|
||||
}
|
||||
|
||||
addLienReason(): void {
|
||||
if (!this.newLienReason.reasonDescription) {
|
||||
this.toast('Enter Reason Description.');
|
||||
return;
|
||||
}
|
||||
this.lienReasons = [
|
||||
{ reasonCode: `LR-${String(this.lienReasons.length + 1).padStart(2, '0')}`, ...this.newLienReason } as LienReason,
|
||||
...this.lienReasons
|
||||
];
|
||||
this.newLienReason = {};
|
||||
this.toast('LIEN reason added.');
|
||||
}
|
||||
|
||||
addMfProduct(): void {
|
||||
if (!this.newMfProduct.productName || !this.newMfProduct.amcName) {
|
||||
this.toast('Enter Product Name and AMC Name.');
|
||||
return;
|
||||
}
|
||||
this.mfProducts = [
|
||||
{
|
||||
productCode: `MFP-${String(this.mfProducts.length + 1).padStart(2, '0')}`,
|
||||
category: '',
|
||||
riskLevel: 'MODERATE',
|
||||
...this.newMfProduct
|
||||
} as MutualFundProductMaster,
|
||||
...this.mfProducts
|
||||
];
|
||||
this.newMfProduct = {};
|
||||
this.toast('Mutual Fund product added.');
|
||||
}
|
||||
|
||||
addSglBank(): void {
|
||||
if (!this.newSglBank.bankName) {
|
||||
this.toast('Enter Bank Name.');
|
||||
return;
|
||||
}
|
||||
this.sglBanks = [
|
||||
{
|
||||
sglCode: `SGL-${String(this.sglBanks.length + 1).padStart(2, '0')}`,
|
||||
branch: '',
|
||||
...this.newSglBank
|
||||
} as SglBankMaster,
|
||||
...this.sglBanks
|
||||
];
|
||||
this.newSglBank = {};
|
||||
this.toast('SGL Bank master added.');
|
||||
}
|
||||
|
||||
addDailyRate(): void {
|
||||
if (!this.newDailyRate.scriptCode || !this.newDailyRate.rateDate || this.newDailyRate.closeRate == null) {
|
||||
this.toast('Enter Rate Date, Script Code and Close Rate.');
|
||||
return;
|
||||
}
|
||||
const script = this.scripts.find((s) => s.scriptCode === this.newDailyRate.scriptCode);
|
||||
this.dailyScriptRates = [
|
||||
{
|
||||
scriptName: script?.scriptName || '',
|
||||
openRate: this.newDailyRate.closeRate,
|
||||
highRate: this.newDailyRate.closeRate,
|
||||
lowRate: this.newDailyRate.closeRate,
|
||||
...this.newDailyRate
|
||||
} as DailyScriptRate,
|
||||
...this.dailyScriptRates
|
||||
];
|
||||
this.newDailyRate = {};
|
||||
this.toast('Daily script rate added.');
|
||||
}
|
||||
|
||||
private toast(msg: string): void {
|
||||
this.toastMsg = msg;
|
||||
this.toastVisible = true;
|
||||
if (this.toastTimer) {
|
||||
clearTimeout(this.toastTimer);
|
||||
}
|
||||
this.toastTimer = setTimeout(() => (this.toastVisible = false), 3200);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user