710 lines
22 KiB
TypeScript
710 lines
22 KiB
TypeScript
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,
|
|
crmClients,
|
|
InsertValuationProject,
|
|
InsertValuationInput,
|
|
InsertValuationAssumption,
|
|
InsertValuationCalculation,
|
|
InsertValuationMaturityScore,
|
|
InsertValuationCapTableEntry,
|
|
InsertValuationTransaction,
|
|
InsertValuationDocument,
|
|
InsertValuationDocumentLog,
|
|
InsertValuationCanvasBlock,
|
|
InsertValuationAgentInsight,
|
|
InsertValuationChecklistProgress,
|
|
InsertValuationChecklistAttachment,
|
|
InsertValuationCategoryWeight,
|
|
InsertValuationSectorBenchmark,
|
|
InsertValuationSectorScore,
|
|
InsertValuationCanvasSnapshot,
|
|
} 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<InsertValuationProject>) {
|
|
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<InsertValuationInput>) {
|
|
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<InsertValuationAssumption>) {
|
|
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<InsertValuationCalculation>) {
|
|
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<InsertValuationMaturityScore>) {
|
|
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<InsertValuationCapTableEntry>) {
|
|
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<InsertValuationTransaction>) {
|
|
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<InsertValuationDocument>) {
|
|
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<InsertValuationCanvasBlock>) {
|
|
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<InsertValuationAgentInsight>) {
|
|
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<InsertValuationCanvasBlock>) {
|
|
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;
|
|
},
|
|
};
|