// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init generator client { provider = "prisma-client" output = "../generated/prisma" } datasource db { provider = "postgresql" } model Session { id String @id @default(uuid()) sessionKey String @unique @default(cuid()) userId String expires DateTime refreshTokenGenerated Boolean @default(false) refreshedAt DateTime? invalidatedAt DateTime? user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model User { id String @id @default(cuid()) roles Role[] permissions String? login String @unique name String? email String @unique emailVerified DateTime? image String? cwIdentifier String? userId String @unique token String? sessions Session[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt generatedQuotes GeneratedQuotes[] } model Role { id String @id @default(uuid()) title String moniker String @unique // e.g. admin, super_admin, moderator permissions String users User[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model UnifiSite { id String @id @default(cuid()) name String siteId String @unique companyId String? company Company? @relation(fields: [companyId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Company { id String @id @default(cuid()) name String cw_CompanyId Int @unique cw_Identifier String @unique credentials Credential[] unifiSites UnifiSite[] opportunities Opportunity[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model CatalogItem { id String @id @default(cuid()) cwCatalogId Int @unique identifier String? @unique name String description String? customerDescription String? internalNotes String? linkedItems CatalogItem[] @relation("LinkedItems") linkedTo CatalogItem[] @relation("LinkedItems") category String? categoryCwId Int? subcategory String? subcategoryCwId Int? manufacturer String? manufactureCwId Int? partNumber String? vendorName String? vendorSku String? vendorCwId Int? price Float cost Float inactive Boolean @default(false) salesTaxable Boolean @default(true) onHand Int @default(0) cwLastUpdated DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Opportunity { id String @id @default(cuid()) cwOpportunityId Int @unique name String notes String? generatedQuotes GeneratedQuotes[] // Stage / status / priority / type / rating stored as JSON references // so we don't need separate lookup tables for CW enums typeName String? typeCwId Int? stageName String? stageCwId Int? statusName String? statusCwId Int? priorityName String? priorityCwId Int? ratingName String? ratingCwId Int? source String? campaignName String? campaignCwId Int? // Sales rep references primarySalesRepName String? primarySalesRepIdentifier String? primarySalesRepCwId Int? secondarySalesRepName String? secondarySalesRepIdentifier String? secondarySalesRepCwId Int? // Company / contact / site companyCwId Int? companyName String? contactCwId Int? contactName String? siteCwId Int? siteName String? customerPO String? // Financials totalSalesTax Float @default(0) probability Float @default(0) // Location / department locationName String? locationCwId Int? departmentName String? departmentCwId Int? // Dates expectedCloseDate DateTime? pipelineChangeDate DateTime? dateBecameLead DateTime? closedDate DateTime? closedFlag Boolean @default(false) closedByName String? closedByCwId Int? // Internal relation to Company (optional, linked by cwCompanyId) companyId String? company Company? @relation(fields: [companyId], references: [id]) // Local product sequence — array of CW forecast item IDs in display order. // When present, fetchProducts() uses this order instead of CW sequenceNumber. productSequence Int[] @default([]) cwLastUpdated DateTime? cwDateEntered DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model CredentialType { id String @id @default(cuid()) name String @unique permissionScope String icon String? fields Json credentials Credential[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model SecureValue { id String @id @default(cuid()) name String content String // Encrypted content hash String // Hash of the original content for integrity verification and Search credentialId String credential Credential @relation(fields: [credentialId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Credential { id String @id @default(cuid()) name String notes String? subCredentialOfId String? subCredentialOf Credential? @relation("SubCredentials", fields: [subCredentialOfId], references: [id], onDelete: Cascade) subCredentials Credential[] @relation("SubCredentials") typeId String type CredentialType @relation(fields: [typeId], references: [id], onDelete: Cascade) fields Json companyId String company Company @relation(fields: [companyId], references: [id], onDelete: Cascade) securevalues SecureValue[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model GeneratedQuotes { id String @id @default(uuid()) quoteRegenData Json @default("{}") // Store any additional data needed for quote regeneration, such as product details, pricing, etc. quoteRegenParams Json @default("{}") // Store parameters used for quote regeneration, such as template ID, formatting options, etc. quoteRegenHash String @unique @default("") downloads Json @default("[]") // Array of download records with timestamp and user info quoteFile Bytes quoteFileName String opportunityId String opportunity Opportunity @relation(fields: [opportunityId], references: [id], onDelete: Cascade) createdById String? createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model CwMember { id String @id @default(cuid()) cwMemberId Int @unique identifier String @unique firstName String lastName String officeEmail String? inactiveFlag Boolean @default(false) apiKey String? cwLastUpdated DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }