@mazaventures/valentine
Version:
Toolkit for Venture Capital firms to run their back office efficiently and openly
152 lines • 5.37 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PortfolioService = void 0;
const types_1 = require("../models/types");
const decimal_js_1 = __importDefault(require("decimal.js"));
/**
* Service for managing portfolio companies and investments.
* Handles company information, investment tracking, and ownership calculations.
*
* @example
* ```typescript
* const portfolio = new PortfolioService();
*
* // Add a company
* const company = await portfolio.addCompany({
* name: "TechCo",
* sector: "SaaS",
* stage: "SERIES_A",
* founded: new Date("2024-01-01")
* });
* ```
*/
class PortfolioService {
/**
* Initializes a new instance of the PortfolioService class.
*
* @description Creates a new PortfolioService instance with empty company and investment maps.
*/
constructor() {
this.companies = new Map();
this.investments = new Map();
}
/**
* Adds a new company to the portfolio.
*
* @param company - Company information without ID
* @returns Newly created company with generated ID
* @throws {Error} If company data validation fails
*/
async addCompany(company) {
const id = crypto.randomUUID();
const newCompany = types_1.CompanySchema.parse({ ...company, id });
this.companies.set(id, newCompany);
return newCompany;
}
/**
* Retrieves a company by its ID.
*
* @param id - Company ID
* @returns Company information or undefined if not found
*/
async getCompany(id) {
return this.companies.get(id);
}
/**
* Updates an existing company's information.
*
* @param id - Company ID
* @param updates - Partial company information to update
* @returns Updated company information
* @throws {Error} If company not found or validation fails
*/
async updateCompany(id, updates) {
const existing = this.companies.get(id);
if (!existing) {
throw new Error(`Company with id ${id} not found`);
}
const updated = types_1.CompanySchema.parse({ ...existing, ...updates });
this.companies.set(id, updated);
return updated;
}
/**
* Lists all companies in the portfolio.
*
* @returns Array of all companies
*/
async listCompanies() {
return Array.from(this.companies.values());
}
/**
* Records a new investment in a portfolio company.
*
* @param investment - Investment information without ID
* @returns Newly created investment with generated ID
* @throws {Error} If company not found or validation fails
*/
async addInvestment(investment) {
const id = crypto.randomUUID();
const newInvestment = types_1.InvestmentSchema.parse({ ...investment, id });
if (!this.companies.has(newInvestment.companyId)) {
throw new Error(`Company with id ${newInvestment.companyId} not found`);
}
this.investments.set(id, newInvestment);
return newInvestment;
}
/**
* Retrieves an investment by its ID.
*
* @param id - Investment ID
* @returns Investment information or undefined if not found
*/
async getInvestment(id) {
return this.investments.get(id);
}
/**
* Lists investments, optionally filtered by company.
*
* @param companyId - Optional company ID to filter investments
* @returns Array of investments
*/
async listInvestments(companyId) {
const investments = Array.from(this.investments.values());
return companyId
? investments.filter(i => i.companyId === companyId)
: investments;
}
/**
* Calculates total invested amount, optionally for a specific company.
*
* @param companyId - Optional company ID to calculate total for
* @returns Object containing total amount and currency
*/
async getTotalInvested(companyId) {
const investments = await this.listInvestments(companyId);
const byCurrency = new Map();
for (const inv of investments) {
const current = byCurrency.get(inv.currency) || new decimal_js_1.default(0);
byCurrency.set(inv.currency, current.plus(inv.amount));
}
// For now, return the first currency group. In practice, you'd want to handle
// multiple currencies and possibly convert them to a base currency
const [firstCurrency] = byCurrency.entries();
return firstCurrency
? { amount: firstCurrency[1], currency: firstCurrency[0] }
: { amount: new decimal_js_1.default(0), currency: 'USD' };
}
/**
* Calculates total ownership percentage in a company.
*
* @param companyId - Company ID to calculate ownership for
* @returns Total ownership percentage as a decimal
*/
async getOwnership(companyId) {
const investments = await this.listInvestments(companyId);
return investments.reduce((total, inv) => total.plus(inv.ownership), new decimal_js_1.default(0));
}
}
exports.PortfolioService = PortfolioService;
//# sourceMappingURL=portfolio.js.map