/** * 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 { SHARE_HOLDINGS, ALL_TXNS, InvestmentHolding, InvestmentTxn, todayDDMMYYYY } from '../investment-data'; type ShareTab = 'holdings' | 'purchase' | 'dividend' | 'sell' | 'writeoff'; interface SharePurchaseForm { certNo: string; scriptCode: string; scriptName: string; party: string; subType: string; units: number | null; purchaseRate: number | null; purchaseDate: string; brokerCode: string; crToAc: string; } /** * Share Entry treasury screen — Holdings register + Purchase / Dividend / Sell / Write-off * entry tabs (legacy "02 Share Entry" menu). Dummy in-memory data; Save appends locally. */ @Component({ selector: 'mifosx-share-investment', templateUrl: './share-investment.component.html', styleUrls: [ '../../cashier/cashier-shared.scss', '../investment-shared.scss' ], standalone: true, imports: [ CommonModule, FormsModule ] }) export class ShareInvestmentComponent { activeTab: ShareTab = 'holdings'; asOnDate = todayDDMMYYYY(); holdings: InvestmentHolding[] = [...SHARE_HOLDINGS]; txns: InvestmentTxn[] = ALL_TXNS.filter((t) => t.instrumentType === 'SHARE'); toastMsg = ''; toastVisible = false; private toastTimer: ReturnType | undefined; purchaseForm: SharePurchaseForm = this.blankPurchaseForm(); dividendCertNo = ''; dividendPerShare: number | null = null; sellCertNo = ''; sellUnits: number | null = null; sellRate: number | null = null; writeoffCertNo = ''; writeoffReason = ''; setTab(tab: ShareTab): void { this.activeTab = tab; } get activeHoldings(): InvestmentHolding[] { return this.holdings.filter((h) => h.status === 'ACTIVE'); } private blankPurchaseForm(): SharePurchaseForm { return { certNo: `SH-${6000 + this.holdings.length + 1}`, scriptCode: '', scriptName: '', party: 'NSE/BSE', subType: 'EQUITY', units: null, purchaseRate: null, purchaseDate: this.asOnDate, brokerCode: '', crToAc: '310070' }; } savePurchase(): void { const f = this.purchaseForm; if (!f.scriptName || !f.units || !f.purchaseRate) { this.toast('Please fill Script Name, No. of Units and Purchase Rate.'); return; } const faceValue = f.units * f.purchaseRate; const holding: InvestmentHolding = { certNo: f.certNo, instrumentType: 'SHARE', scriptCode: f.scriptCode || f.scriptName, scriptName: f.scriptName, party: f.party, subType: f.subType, faceValue, purchaseRate: f.purchaseRate, units: f.units, purchaseDate: f.purchaseDate, maturityDate: f.purchaseDate, interestRate: 0, interestFreq: 'YEARLY', glCode: '310060', glName: 'INVESTMENT IN SHARES', crToAc: f.crToAc, brokerCode: f.brokerCode, status: 'ACTIVE' }; this.holdings = [ holding, ...this.holdings ]; this.txns = [ { certNo: holding.certNo, instrumentType: 'SHARE', txnType: 'PURCHASE', voucherNo: `INV/PUR/${holding.certNo}`, txnDate: f.purchaseDate, amount: faceValue, particulars: `Purchase of ${f.units} shares - ${holding.scriptName}` }, ...this.txns ]; this.toast(`Share holding ${holding.certNo} saved successfully.`); this.purchaseForm = this.blankPurchaseForm(); this.activeTab = 'holdings'; } cancelPurchase(): void { this.purchaseForm = this.blankPurchaseForm(); } // -------------------- Dividend -------------------- get dividendHolding(): InvestmentHolding | undefined { return this.holdings.find((h) => h.certNo === this.dividendCertNo); } get dividendAmount(): number { const h = this.dividendHolding; if (!h || !this.dividendPerShare) { return 0; } return Math.round(h.units * this.dividendPerShare); } saveDividend(): void { const h = this.dividendHolding; if (!h || !this.dividendPerShare) { this.toast('Select a holding and enter dividend per share.'); return; } const amt = this.dividendAmount; this.txns = [ { certNo: h.certNo, instrumentType: 'SHARE', txnType: 'DIVIDEND', voucherNo: `INV/DIV/${h.certNo}-${this.txns.length + 1}`, txnDate: this.asOnDate, amount: amt, particulars: `Dividend received - ${h.scriptName} (₹${this.dividendPerShare}/share)` }, ...this.txns ]; this.toast(`Dividend of ₹${amt.toLocaleString('en-IN')} recorded for ${h.certNo}.`); this.dividendCertNo = ''; this.dividendPerShare = null; } // -------------------- Sell -------------------- get sellHolding(): InvestmentHolding | undefined { return this.holdings.find((h) => h.certNo === this.sellCertNo); } get sellConsideration(): number { if (!this.sellUnits || !this.sellRate) { return 0; } return Math.round(this.sellUnits * this.sellRate); } saveSell(): void { const h = this.sellHolding; if (!h || !this.sellUnits || !this.sellRate || this.sellUnits > h.units) { this.toast('Select a holding and enter valid units (≤ held) and sell rate.'); return; } const consideration = this.sellConsideration; h.units -= this.sellUnits; h.faceValue = Math.round(h.units * h.purchaseRate); if (h.units === 0) { h.status = 'SOLD'; } this.txns = [ { certNo: h.certNo, instrumentType: 'SHARE', txnType: 'SELL', voucherNo: `INV/SEL/${h.certNo}-${this.txns.length + 1}`, txnDate: this.asOnDate, amount: consideration, particulars: `Sold ${this.sellUnits} shares of ${h.scriptName} @ ${this.sellRate}` }, ...this.txns ]; this.toast(`Sold ${this.sellUnits} shares. Consideration ₹${consideration.toLocaleString('en-IN')}.`); this.sellCertNo = ''; this.sellUnits = null; this.sellRate = null; this.activeTab = 'holdings'; } // -------------------- Write-off -------------------- get writeoffHolding(): InvestmentHolding | undefined { return this.holdings.find((h) => h.certNo === this.writeoffCertNo); } saveWriteoff(): void { const h = this.writeoffHolding; if (!h || !this.writeoffReason) { this.toast('Select a holding and enter a write-off reason.'); return; } h.status = 'WRITTEN_OFF'; this.txns = [ { certNo: h.certNo, instrumentType: 'SHARE', txnType: 'WRITE_OFF', voucherNo: `INV/WOF/${h.certNo}`, txnDate: this.asOnDate, amount: h.faceValue, particulars: `Written off - ${this.writeoffReason}` }, ...this.txns ]; this.toast(`${h.certNo} written off.`); this.writeoffCertNo = ''; this.writeoffReason = ''; this.activeTab = 'holdings'; } statusClass(h: InvestmentHolding): string { return `inv-badge inv-badge--${h.status.toLowerCase()}`; } private toast(msg: string): void { this.toastMsg = msg; this.toastVisible = true; if (this.toastTimer) { clearTimeout(this.toastTimer); } this.toastTimer = setTimeout(() => (this.toastVisible = false), 3200); } }