import { db } from "../../db/index"; import { eq, and, desc, ilike, or } from "drizzle-orm"; import { valuationProjects, valuationInputs, valuationAssumptions, valuationCalculations, valuationMaturityScores, valuationCapTable, valuationTransactions, valuationDocuments, valuationDocumentLogs, valuationCanvas, valuationAgentInsights, valuationChecklistCategories, valuationChecklistItems, valuationChecklistProgress, valuationChecklistAttachments, valuationCategoryWeights, valuationSectorBenchmarks, valuationSectorScores, valuationCanvasBlocks, valuationCanvasSnapshots, valuationGovernance, valuationPdca, valuationSwot, valuationResults, valuationAssets, valuationReports, valuationAiLog, crmClients, InsertValuationProject, InsertValuationInput, InsertValuationAssumption, InsertValuationCalculation, InsertValuationMaturityScore, InsertValuationCapTableEntry, InsertValuationTransaction, InsertValuationDocument, InsertValuationDocumentLog, InsertValuationCanvasBlock, InsertValuationAgentInsight, InsertValuationChecklistProgress, InsertValuationChecklistAttachment, InsertValuationCategoryWeight, InsertValuationSectorBenchmark, InsertValuationSectorScore, InsertValuationCanvasSnapshot, InsertValuationGovernance, InsertValuationPdca, InsertValuationSwot, InsertValuationResult, InsertValuationAsset, InsertValuationReport, InsertValuationAiLog, } from "@shared/schema"; export const valuationStorage = { // ========== PROJECTS ========== async getProjects(tenantId: number) { return await db .select() .from(valuationProjects) .where(eq(valuationProjects.tenantId, tenantId)) .orderBy(desc(valuationProjects.createdAt)); }, async getProject(id: number, tenantId: number) { const [project] = await db .select() .from(valuationProjects) .where(and(eq(valuationProjects.id, id), eq(valuationProjects.tenantId, tenantId))); return project; }, async createProject(data: InsertValuationProject) { const [project] = await db.insert(valuationProjects).values(data).returning(); return project; }, async updateProject(id: number, tenantId: number, data: Partial) { const [project] = await db .update(valuationProjects) .set({ ...data, updatedAt: new Date() }) .where(and(eq(valuationProjects.id, id), eq(valuationProjects.tenantId, tenantId))) .returning(); return project; }, async deleteProject(id: number, tenantId: number) { const result = await db .delete(valuationProjects) .where(and(eq(valuationProjects.id, id), eq(valuationProjects.tenantId, tenantId))) .returning(); return result.length > 0; }, // ========== INPUTS ========== async getInputs(projectId: number) { return await db .select() .from(valuationInputs) .where(eq(valuationInputs.projectId, projectId)) .orderBy(valuationInputs.year); }, async getInput(id: number, projectId: number) { const [input] = await db .select() .from(valuationInputs) .where(and(eq(valuationInputs.id, id), eq(valuationInputs.projectId, projectId))); return input; }, async createInput(data: InsertValuationInput) { const [input] = await db.insert(valuationInputs).values(data).returning(); return input; }, async updateInput(id: number, projectId: number, data: Partial) { const [input] = await db .update(valuationInputs) .set({ ...data, updatedAt: new Date() }) .where(and(eq(valuationInputs.id, id), eq(valuationInputs.projectId, projectId))) .returning(); return input; }, async deleteInput(id: number, projectId: number) { const result = await db .delete(valuationInputs) .where(and(eq(valuationInputs.id, id), eq(valuationInputs.projectId, projectId))) .returning(); return result.length > 0; }, // ========== ASSUMPTIONS ========== async getAssumptions(projectId: number) { return await db .select() .from(valuationAssumptions) .where(eq(valuationAssumptions.projectId, projectId)); }, async createAssumption(data: InsertValuationAssumption) { const [assumption] = await db.insert(valuationAssumptions).values(data).returning(); return assumption; }, async updateAssumption(id: number, projectId: number, data: Partial) { const [assumption] = await db .update(valuationAssumptions) .set(data) .where(and(eq(valuationAssumptions.id, id), eq(valuationAssumptions.projectId, projectId))) .returning(); return assumption; }, async deleteAssumption(id: number, projectId: number) { const result = await db .delete(valuationAssumptions) .where(and(eq(valuationAssumptions.id, id), eq(valuationAssumptions.projectId, projectId))) .returning(); return result.length > 0; }, // ========== CALCULATIONS ========== async getCalculations(projectId: number) { return await db .select() .from(valuationCalculations) .where(eq(valuationCalculations.projectId, projectId)) .orderBy(desc(valuationCalculations.calculatedAt)); }, async getCalculation(id: number, projectId: number) { const [calc] = await db .select() .from(valuationCalculations) .where(and(eq(valuationCalculations.id, id), eq(valuationCalculations.projectId, projectId))); return calc; }, async createCalculation(data: InsertValuationCalculation) { const [calc] = await db.insert(valuationCalculations).values(data).returning(); return calc; }, async updateCalculation(id: number, projectId: number, data: Partial) { const [calc] = await db .update(valuationCalculations) .set(data) .where(and(eq(valuationCalculations.id, id), eq(valuationCalculations.projectId, projectId))) .returning(); return calc; }, async deleteCalculation(id: number, projectId: number) { const result = await db .delete(valuationCalculations) .where(and(eq(valuationCalculations.id, id), eq(valuationCalculations.projectId, projectId))) .returning(); return result.length > 0; }, // ========== MATURITY SCORES ========== async getMaturityScores(projectId: number) { return await db .select() .from(valuationMaturityScores) .where(eq(valuationMaturityScores.projectId, projectId)); }, async createMaturityScore(data: InsertValuationMaturityScore) { const [score] = await db.insert(valuationMaturityScores).values(data).returning(); return score; }, async updateMaturityScore(id: number, projectId: number, data: Partial) { const [score] = await db .update(valuationMaturityScores) .set(data) .where(and(eq(valuationMaturityScores.id, id), eq(valuationMaturityScores.projectId, projectId))) .returning(); return score; }, async deleteMaturityScore(id: number, projectId: number) { const result = await db .delete(valuationMaturityScores) .where(and(eq(valuationMaturityScores.id, id), eq(valuationMaturityScores.projectId, projectId))) .returning(); return result.length > 0; }, // ========== CAP TABLE ========== async getCapTable(projectId: number) { return await db .select() .from(valuationCapTable) .where(eq(valuationCapTable.projectId, projectId)); }, async createCapTableEntry(data: InsertValuationCapTableEntry) { const [entry] = await db.insert(valuationCapTable).values(data).returning(); return entry; }, async updateCapTableEntry(id: number, projectId: number, data: Partial) { const [entry] = await db .update(valuationCapTable) .set(data) .where(and(eq(valuationCapTable.id, id), eq(valuationCapTable.projectId, projectId))) .returning(); return entry; }, async deleteCapTableEntry(id: number, projectId: number) { const result = await db .delete(valuationCapTable) .where(and(eq(valuationCapTable.id, id), eq(valuationCapTable.projectId, projectId))) .returning(); return result.length > 0; }, // ========== TRANSACTIONS ========== async getTransactions(projectId: number) { return await db .select() .from(valuationTransactions) .where(eq(valuationTransactions.projectId, projectId)) .orderBy(desc(valuationTransactions.createdAt)); }, async getTransaction(id: number, projectId: number) { const [transaction] = await db .select() .from(valuationTransactions) .where(and(eq(valuationTransactions.id, id), eq(valuationTransactions.projectId, projectId))); return transaction; }, async createTransaction(data: InsertValuationTransaction) { const [transaction] = await db.insert(valuationTransactions).values(data).returning(); return transaction; }, async updateTransaction(id: number, projectId: number, data: Partial) { const [transaction] = await db .update(valuationTransactions) .set({ ...data, updatedAt: new Date() }) .where(and(eq(valuationTransactions.id, id), eq(valuationTransactions.projectId, projectId))) .returning(); return transaction; }, async deleteTransaction(id: number, projectId: number) { const result = await db .delete(valuationTransactions) .where(and(eq(valuationTransactions.id, id), eq(valuationTransactions.projectId, projectId))) .returning(); return result.length > 0; }, // ========== DOCUMENTS ========== async getDocuments(projectId: number) { return await db .select() .from(valuationDocuments) .where(eq(valuationDocuments.projectId, projectId)) .orderBy(desc(valuationDocuments.createdAt)); }, async getDocument(id: number, projectId: number) { const [doc] = await db .select() .from(valuationDocuments) .where(and(eq(valuationDocuments.id, id), eq(valuationDocuments.projectId, projectId))); return doc; }, async createDocument(data: InsertValuationDocument) { const [doc] = await db.insert(valuationDocuments).values(data).returning(); return doc; }, async updateDocument(id: number, projectId: number, data: Partial) { const [doc] = await db .update(valuationDocuments) .set(data) .where(and(eq(valuationDocuments.id, id), eq(valuationDocuments.projectId, projectId))) .returning(); return doc; }, async deleteDocument(id: number, projectId: number) { const result = await db .delete(valuationDocuments) .where(and(eq(valuationDocuments.id, id), eq(valuationDocuments.projectId, projectId))) .returning(); return result.length > 0; }, async logDocumentAccess(data: InsertValuationDocumentLog) { const [log] = await db.insert(valuationDocumentLogs).values(data).returning(); return log; }, async getDocumentLogs(documentId: number) { return await db .select() .from(valuationDocumentLogs) .where(eq(valuationDocumentLogs.documentId, documentId)) .orderBy(desc(valuationDocumentLogs.createdAt)); }, // ========== BUSINESS CANVAS ========== async getCanvas(projectId: number) { return await db .select() .from(valuationCanvas) .where(eq(valuationCanvas.projectId, projectId)) .orderBy(valuationCanvas.orderIndex); }, async createCanvasBlock(data: InsertValuationCanvasBlock) { const [block] = await db.insert(valuationCanvas).values(data).returning(); return block; }, async updateCanvasBlock(id: number, projectId: number, data: Partial) { const [block] = await db .update(valuationCanvas) .set({ ...data, updatedAt: new Date() }) .where(and(eq(valuationCanvas.id, id), eq(valuationCanvas.projectId, projectId))) .returning(); return block; }, async deleteCanvasBlock(id: number, projectId: number) { const result = await db .delete(valuationCanvas) .where(and(eq(valuationCanvas.id, id), eq(valuationCanvas.projectId, projectId))) .returning(); return result.length > 0; }, // ========== AGENT INSIGHTS ========== async getAgentInsights(projectId: number) { return await db .select() .from(valuationAgentInsights) .where(eq(valuationAgentInsights.projectId, projectId)) .orderBy(desc(valuationAgentInsights.createdAt)); }, async createAgentInsight(data: InsertValuationAgentInsight) { const [insight] = await db.insert(valuationAgentInsights).values(data).returning(); return insight; }, async updateAgentInsight(id: number, projectId: number, data: Partial) { const [insight] = await db .update(valuationAgentInsights) .set(data) .where(and(eq(valuationAgentInsights.id, id), eq(valuationAgentInsights.projectId, projectId))) .returning(); return insight; }, async deleteAgentInsight(id: number, projectId: number) { const result = await db .delete(valuationAgentInsights) .where(and(eq(valuationAgentInsights.id, id), eq(valuationAgentInsights.projectId, projectId))) .returning(); return result.length > 0; }, // ========== CRM CLIENTS ========== async getCrmClients(tenantId: number, search?: string) { const { isNull } = await import("drizzle-orm"); const tenantFilter = or(eq(crmClients.tenantId, tenantId), isNull(crmClients.tenantId)); if (search) { return await db .select() .from(crmClients) .where(and( tenantFilter, or( ilike(crmClients.name, `%${search}%`), ilike(crmClients.tradeName, `%${search}%`), ilike(crmClients.cnpj, `%${search}%`) ) )) .orderBy(crmClients.name) .limit(50); } return await db .select() .from(crmClients) .where(tenantFilter) .orderBy(crmClients.name) .limit(50); }, async getCrmClient(id: number, tenantId: number) { const { isNull } = await import("drizzle-orm"); const [client] = await db .select() .from(crmClients) .where(and(eq(crmClients.id, id), or(eq(crmClients.tenantId, tenantId), isNull(crmClients.tenantId)))); return client; }, // ========== CHECKLIST ========== async getChecklistCategories(segment?: string) { const { isNull } = await import("drizzle-orm"); const categories = await db .select() .from(valuationChecklistCategories) .orderBy(valuationChecklistCategories.orderIndex); if (segment) { return categories.filter(c => !c.segmentFilter || c.segmentFilter.split(',').includes(segment) ); } return categories.filter(c => !c.segmentFilter); }, async getChecklistItems(categoryId?: number, segment?: string) { let query = db.select().from(valuationChecklistItems); if (categoryId) { query = query.where(eq(valuationChecklistItems.categoryId, categoryId)) as typeof query; } const items = await query.orderBy(valuationChecklistItems.orderIndex); if (segment) { return items.filter(i => !i.segmentFilter || i.segmentFilter.split(',').includes(segment) ); } return items; }, async getChecklistProgress(projectId: number) { return await db .select() .from(valuationChecklistProgress) .where(eq(valuationChecklistProgress.projectId, projectId)); }, async getChecklistProgressItem(projectId: number, itemId: number) { const [progress] = await db .select() .from(valuationChecklistProgress) .where(and( eq(valuationChecklistProgress.projectId, projectId), eq(valuationChecklistProgress.itemId, itemId) )); return progress; }, async upsertChecklistProgress(data: InsertValuationChecklistProgress) { const existing = await this.getChecklistProgressItem(data.projectId, data.itemId); if (existing) { const [updated] = await db .update(valuationChecklistProgress) .set({ ...data, updatedAt: new Date() }) .where(eq(valuationChecklistProgress.id, existing.id)) .returning(); return updated; } const [created] = await db .insert(valuationChecklistProgress) .values(data) .returning(); return created; }, async initializeProjectChecklist(projectId: number, segment: string) { const categories = await this.getChecklistCategories(segment); const allItems: any[] = []; for (const cat of categories) { const items = await this.getChecklistItems(cat.id, segment); allItems.push(...items); } const segmentCategories = await db .select() .from(valuationChecklistCategories) .where(eq(valuationChecklistCategories.segmentFilter, segment)); for (const cat of segmentCategories) { const items = await this.getChecklistItems(cat.id, segment); allItems.push(...items); } for (const item of allItems) { const existing = await this.getChecklistProgressItem(projectId, item.id); if (!existing) { await db.insert(valuationChecklistProgress).values({ projectId, itemId: item.id, status: "pending", }); } } return this.getChecklistProgress(projectId); }, // ========== CHECKLIST ATTACHMENTS ========== async getChecklistAttachments(progressId: number) { return await db .select() .from(valuationChecklistAttachments) .where(eq(valuationChecklistAttachments.progressId, progressId)) .orderBy(desc(valuationChecklistAttachments.createdAt)); }, async createChecklistAttachment(data: InsertValuationChecklistAttachment) { const [attachment] = await db .insert(valuationChecklistAttachments) .values(data) .returning(); return attachment; }, async deleteChecklistAttachment(id: number) { const [deleted] = await db .delete(valuationChecklistAttachments) .where(eq(valuationChecklistAttachments.id, id)) .returning(); return deleted; }, async getChecklistAttachment(id: number) { const [attachment] = await db .select() .from(valuationChecklistAttachments) .where(eq(valuationChecklistAttachments.id, id)); return attachment; }, // ========== SECTOR ANALYSIS ========== async getCategoryWeights(tenantId: number | null, segment: string) { if (tenantId) { const tenantWeights = await db .select() .from(valuationCategoryWeights) .where(and(eq(valuationCategoryWeights.tenantId, tenantId), eq(valuationCategoryWeights.segment, segment))); if (tenantWeights.length > 0) return tenantWeights; } return await db .select() .from(valuationCategoryWeights) .where(and(eq(valuationCategoryWeights.segment, segment))); }, async upsertCategoryWeight(data: InsertValuationCategoryWeight) { const existing = await db .select() .from(valuationCategoryWeights) .where(and( eq(valuationCategoryWeights.segment, data.segment), eq(valuationCategoryWeights.categoryCode, data.categoryCode), data.tenantId ? eq(valuationCategoryWeights.tenantId, data.tenantId) : undefined )); if (existing.length > 0) { const [updated] = await db .update(valuationCategoryWeights) .set({ ...data, updatedAt: new Date() }) .where(eq(valuationCategoryWeights.id, existing[0].id)) .returning(); return updated; } const [created] = await db.insert(valuationCategoryWeights).values(data).returning(); return created; }, async getSectorBenchmarks(segment: string) { return await db .select() .from(valuationSectorBenchmarks) .where(eq(valuationSectorBenchmarks.segment, segment)) .orderBy(valuationSectorBenchmarks.indicatorCode); }, async createSectorBenchmark(data: InsertValuationSectorBenchmark) { const [benchmark] = await db.insert(valuationSectorBenchmarks).values(data).returning(); return benchmark; }, async getSectorScores(projectId: number) { return await db .select() .from(valuationSectorScores) .where(eq(valuationSectorScores.projectId, projectId)) .orderBy(desc(valuationSectorScores.calculatedAt)); }, async getLatestSectorScore(projectId: number) { const [score] = await db .select() .from(valuationSectorScores) .where(eq(valuationSectorScores.projectId, projectId)) .orderBy(desc(valuationSectorScores.calculatedAt)) .limit(1); return score; }, async createSectorScore(data: InsertValuationSectorScore) { const [score] = await db.insert(valuationSectorScores).values(data).returning(); return score; }, // ========== CANVAS BLOCKS ========== async getCanvasBlocks(projectId: number) { return await db .select() .from(valuationCanvasBlocks) .where(eq(valuationCanvasBlocks.projectId, projectId)) .orderBy(valuationCanvasBlocks.orderIndex); }, async getCanvasBlock(projectId: number, blockType: string) { const [block] = await db .select() .from(valuationCanvasBlocks) .where(and(eq(valuationCanvasBlocks.projectId, projectId), eq(valuationCanvasBlocks.blockType, blockType))); return block; }, async upsertCanvasBlock(projectId: number, blockType: string, data: Partial) { const existing = await this.getCanvasBlock(projectId, blockType); if (existing) { const [updated] = await db .update(valuationCanvasBlocks) .set({ ...data, updatedAt: new Date() }) .where(eq(valuationCanvasBlocks.id, existing.id)) .returning(); return updated; } const [created] = await db .insert(valuationCanvasBlocks) .values({ projectId, blockType, ...data }) .returning(); return created; }, // ========== CANVAS SNAPSHOTS ========== async getCanvasSnapshots(projectId: number) { return await db .select() .from(valuationCanvasSnapshots) .where(eq(valuationCanvasSnapshots.projectId, projectId)) .orderBy(desc(valuationCanvasSnapshots.createdAt)); }, async createCanvasSnapshot(data: InsertValuationCanvasSnapshot) { const [snapshot] = await db.insert(valuationCanvasSnapshots).values(data).returning(); return snapshot; }, async getCanvasSnapshot(id: number) { const [snapshot] = await db .select() .from(valuationCanvasSnapshots) .where(eq(valuationCanvasSnapshots.id, id)); return snapshot; }, // ========== GOVERNANCE ========== async getGovernanceCriteria(projectId: number) { return await db .select() .from(valuationGovernance) .where(eq(valuationGovernance.projectId, projectId)) .orderBy(valuationGovernance.criterionCode); }, async getGovernanceCriterion(id: number, projectId: number) { const [c] = await db .select() .from(valuationGovernance) .where(and(eq(valuationGovernance.id, id), eq(valuationGovernance.projectId, projectId))); return c; }, async createGovernanceCriterion(data: InsertValuationGovernance) { const [c] = await db.insert(valuationGovernance).values(data).returning(); return c; }, async updateGovernanceCriterion(id: number, projectId: number, data: Partial) { const [c] = await db .update(valuationGovernance) .set(data) .where(and(eq(valuationGovernance.id, id), eq(valuationGovernance.projectId, projectId))) .returning(); return c; }, async deleteGovernanceCriterion(id: number, projectId: number) { const r = await db .delete(valuationGovernance) .where(and(eq(valuationGovernance.id, id), eq(valuationGovernance.projectId, projectId))) .returning(); return r.length > 0; }, async initializeGovernance(projectId: number, criteria: InsertValuationGovernance[]) { const existing = await this.getGovernanceCriteria(projectId); if (existing.length > 0) return existing; const results = []; for (const c of criteria) { const [created] = await db.insert(valuationGovernance).values({ ...c, projectId }).returning(); results.push(created); } return results; }, // ========== PDCA ========== async getPdcaItems(projectId: number) { return await db .select() .from(valuationPdca) .where(eq(valuationPdca.projectId, projectId)) .orderBy(desc(valuationPdca.createdAt)); }, async getPdcaItem(id: number, projectId: number) { const [item] = await db .select() .from(valuationPdca) .where(and(eq(valuationPdca.id, id), eq(valuationPdca.projectId, projectId))); return item; }, async createPdcaItem(data: InsertValuationPdca) { const [item] = await db.insert(valuationPdca).values(data).returning(); return item; }, async updatePdcaItem(id: number, projectId: number, data: Partial) { const [item] = await db .update(valuationPdca) .set({ ...data, updatedAt: new Date() }) .where(and(eq(valuationPdca.id, id), eq(valuationPdca.projectId, projectId))) .returning(); return item; }, async deletePdcaItem(id: number, projectId: number) { const r = await db .delete(valuationPdca) .where(and(eq(valuationPdca.id, id), eq(valuationPdca.projectId, projectId))) .returning(); return r.length > 0; }, // ========== SWOT ========== async getSwotItems(projectId: number) { return await db .select() .from(valuationSwot) .where(eq(valuationSwot.projectId, projectId)) .orderBy(valuationSwot.orderIndex); }, async getSwotItem(id: number, projectId: number) { const [item] = await db .select() .from(valuationSwot) .where(and(eq(valuationSwot.id, id), eq(valuationSwot.projectId, projectId))); return item; }, async createSwotItem(data: InsertValuationSwot) { const [item] = await db.insert(valuationSwot).values(data).returning(); return item; }, async updateSwotItem(id: number, projectId: number, data: Partial) { const [item] = await db .update(valuationSwot) .set(data) .where(and(eq(valuationSwot.id, id), eq(valuationSwot.projectId, projectId))) .returning(); return item; }, async deleteSwotItem(id: number, projectId: number) { const r = await db .delete(valuationSwot) .where(and(eq(valuationSwot.id, id), eq(valuationSwot.projectId, projectId))) .returning(); return r.length > 0; }, // ========== RESULTS ========== async getResults(projectId: number) { return await db .select() .from(valuationResults) .where(eq(valuationResults.projectId, projectId)) .orderBy(desc(valuationResults.calculatedAt)); }, async createResult(data: InsertValuationResult) { const [r] = await db.insert(valuationResults).values(data).returning(); return r; }, async deleteResults(projectId: number) { await db.delete(valuationResults).where(eq(valuationResults.projectId, projectId)); }, // ========== ASSETS ========== async getAssets(projectId: number) { return await db .select() .from(valuationAssets) .where(eq(valuationAssets.projectId, projectId)) .orderBy(valuationAssets.name); }, async getAsset(id: number, projectId: number) { const [a] = await db .select() .from(valuationAssets) .where(and(eq(valuationAssets.id, id), eq(valuationAssets.projectId, projectId))); return a; }, async createAsset(data: InsertValuationAsset) { const [a] = await db.insert(valuationAssets).values(data).returning(); return a; }, async updateAsset(id: number, projectId: number, data: Partial) { const [a] = await db .update(valuationAssets) .set(data) .where(and(eq(valuationAssets.id, id), eq(valuationAssets.projectId, projectId))) .returning(); return a; }, async deleteAsset(id: number, projectId: number) { const r = await db .delete(valuationAssets) .where(and(eq(valuationAssets.id, id), eq(valuationAssets.projectId, projectId))) .returning(); return r.length > 0; }, // ========== REPORTS ========== async getReports(projectId: number) { return await db .select() .from(valuationReports) .where(eq(valuationReports.projectId, projectId)) .orderBy(desc(valuationReports.generatedAt)); }, async createReport(data: InsertValuationReport) { const [r] = await db.insert(valuationReports).values(data).returning(); return r; }, async deleteReport(id: number, projectId: number) { const r = await db .delete(valuationReports) .where(and(eq(valuationReports.id, id), eq(valuationReports.projectId, projectId))) .returning(); return r.length > 0; }, // ========== AI LOG ========== async getAiLogs(projectId: number, limit: number = 20) { return await db .select() .from(valuationAiLog) .where(eq(valuationAiLog.projectId, projectId)) .orderBy(desc(valuationAiLog.createdAt)) .limit(limit); }, async createAiLog(data: InsertValuationAiLog) { const [log] = await db.insert(valuationAiLog).values(data).returning(); return log; }, };