/** * 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 { DateDdmmyyyyPickerComponent } from '../date-ddmmyyyy-picker/date-ddmmyyyy-picker.component'; 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'; import { exportWorkbook, ExportSheetSpec } from '../investment-export'; 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, DateDdmmyyyyPickerComponent ] }) 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' ]; private readonly masterLabels: Record = { party: 'Bank / Party Master', script: 'Script Master', subtype: 'SubType Master', broker: 'Broker Master', control: 'Control Parameter', dailyscript: 'Daily Script Master', lien: 'LIEN Reason Master', mfproduct: 'Mutual Fund Product Master', sglbank: 'SGL Bank Master', marketdetail: 'Script Market Detail' }; generating = false; toastMsg = ''; toastVisible = false; private toastTimer: ReturnType | undefined; newParty: Partial = {}; newScript: Partial = {}; newSubType: Partial = {}; newBroker: Partial = {}; newControlParam: Partial = {}; newLienReason: Partial = {}; newMfProduct: Partial = {}; newSglBank: Partial = {}; newDailyRate: Partial = {}; 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.'); } // -------------------- Export to Excel -------------------- private exportSheets(): ExportSheetSpec[] { const title = this.masterLabels[this.activeTab]; const one = (headers: string[], widths: number[], rows: (string | number | null)[][]): ExportSheetSpec[] => [ { sheetName: title, title, headers, widths, rows }]; switch (this.activeTab) { case 'party': return one( [ 'Party Code', 'Party Name', 'Type', 'PAN No' ], [ 12, 30, 16, 14 ], this.parties.map((p) => [ p.partyCode, p.partyName, p.partyType, p.panNo ]) ); case 'script': return one( [ 'Script Code', 'Script Name', 'Instrument', 'ISIN', 'Face Value/Unit' ], [ 16, 30, 16, 16, 16 ], this.scripts.map((s) => [ s.scriptCode, s.scriptName, s.instrumentType, s.isin, s.faceValuePerUnit ]) ); case 'subtype': return one( [ 'Code', 'Sub Type', 'Instrument' ], [ 10, 24, 16 ], this.subTypes.map((s) => [ s.subTypeCode, s.subTypeName, s.instrumentType ]) ); case 'broker': return one( [ 'Broker Code', 'Broker Name', 'Contact', 'Empanelled On', 'Status' ], [ 14, 30, 16, 16, 12 ], this.brokers.map((b) => [ b.brokerCode, b.brokerName, b.contactNo, b.empanelledOn, b.status ]) ); case 'control': return one( [ 'Parameter', 'Value', 'Description' ], [ 26, 14, 40 ], this.controlParams.map((c) => [ c.paramName, c.paramValue, c.description ]) ); case 'dailyscript': return one( [ 'Rate Date', 'Script Code', 'Script Name', 'Open', 'High', 'Low', 'Close' ], [ 14, 16, 26, 12, 12, 12, 12 ], this.dailyScriptRates.map((r) => [ r.rateDate, r.scriptCode, r.scriptName, r.openRate, r.highRate, r.lowRate, r.closeRate ]) ); case 'lien': return one( [ 'Reason Code', 'Description' ], [ 14, 40 ], this.lienReasons.map((l) => [ l.reasonCode, l.reasonDescription ]) ); case 'mfproduct': return one( [ 'Product Code', 'Product Name', 'AMC', 'Category', 'Risk' ], [ 14, 30, 24, 20, 12 ], this.mfProducts.map((m) => [ m.productCode, m.productName, m.amcName, m.category, m.riskLevel ]) ); case 'sglbank': return one( [ 'SGL Code', 'Bank Name', 'Branch' ], [ 14, 30, 24 ], this.sglBanks.map((s) => [ s.sglCode, s.bankName, s.branch ]) ); case 'marketdetail': return one( [ 'Script Code', 'Market Price', 'As On', 'YTM %' ], [ 16, 16, 14, 12 ], this.marketDetails.map((m) => [ m.scriptCode, m.marketPrice, m.asOnDate, m.ytm ]) ); default: return one([], [], []); } } async exportExcel(): Promise { this.generating = true; try { await exportWorkbook(this.exportSheets(), `Investment-${this.masterLabels[this.activeTab]}`); this.toast(`${this.masterLabels[this.activeTab]} exported.`); } catch (err) { console.error('Investment Masters export failed', err); this.toast('Could not generate the workbook — please retry.'); } finally { this.generating = false; } } private toast(msg: string): void { this.toastMsg = msg; this.toastVisible = true; if (this.toastTimer) { clearTimeout(this.toastTimer); } this.toastTimer = setTimeout(() => (this.toastVisible = false), 3200); } }