// 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" } enum PhoneType { DIRECT MOBILE HOME COMPANY // Main company line, not direct to contact SITE // Main site line, not direct to contact } enum FaxType { FAX // Direct fax line to contact COMPANY // Main company fax line, not direct to contact SITE // Main site fax line, not direct to contact } // By human nature, there are only two genders. enum GenderType { MALE FEMALE } enum USState { AL AK AZ AR CA CO CT DE FL GA HI ID IL IN IA KS KY LA ME MD MA MI MN MS MO MT NE NV NH NJ NM NY NC ND OH OK OR PA RI SC SD TN TX UT VT VA WA WV WI WY } enum Country { US } enum OpportunityInterest { HOT WARM COLD } 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? active Boolean @default(true) hidden Boolean @default(false) cwIdentifier String? @unique userId String @unique token String? sessions Session[] companiesDeleted Company[] @relation("DeletedBy") companiesEntered Company[] @relation("EnteredBy") opportunities Opportunity[] @relation("PrimarySalesRep") opportunitiesSecondary Opportunity[] @relation("SecondarySalesRep") generatedQuotes GeneratedQuotes[] companyAddresses CompanyAddress[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } 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 CorporateLocation { id Int @unique uid String @id @default(uuid()) name String description String? updatedById String? addressLine1 String? addressLine2 String? city String? state USState? zipCode String? country Country? inactiveFlag Boolean @default(false) // Optima Only field, not synced to CW opportunities Opportunity[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model InternalDepartment { id Int @unique uid String @id @default(uuid()) name String description String? // Optima Only field, not synced to CW createdById String? updatedById String? opportunities Opportunity[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model UnifiSite { id String @id @default(cuid()) name String siteId String @unique companyId Int? company Company? @relation(fields: [companyId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Company { id Int @unique uid String @id @default(uuid()) name String phone String? website String? deleteFlag Boolean @default(false) dateDeleted DateTime? taxId String? taxExempt Boolean @default(false) // Optima Only field, not synced to CW enteredById String? deletedById String? contacts Contact[] companyAddresses CompanyAddress[] credentials Credential[] unifiSites UnifiSite[] opportunities Opportunity[] deletedBy User? @relation("DeletedBy", fields: [deletedById], references: [cwIdentifier]) enteredBy User? @relation("EnteredBy", fields: [enteredById], references: [cwIdentifier]) enteredAt DateTime @default(now()) deletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model CompanyAddress { id Int @unique uid String @id @default(uuid()) name String description String? addressLine1 String? addressLine2 String? city String? state USState? zipCode String? country Country? phone String? fax String? inactiveFlag Boolean @default(false) defaultFlag Boolean @default(false) defaultMailFlag Boolean @default(false) defaultBillFlag Boolean @default(false) defaultShipFlag Boolean @default(false) updatedById String? updatedBy User? @relation(fields: [updatedById], references: [cwIdentifier]) companyId Int company Company @relation(fields: [companyId], references: [id]) contacts Contact[] oppportunities Opportunity[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Contact { id Int @unique uid String @id @default(uuid()) active Boolean @default(true) default Boolean @default(false) firstName String lastName String nickname String? title String? gender GenderType? birthday DateTime? email String? phone String? phoneExtension String? phoneType PhoneType? companyAddressId Int? companyAddress CompanyAddress? @relation(fields: [companyAddressId], references: [id]) memberId Int? companyId Int? company Company? @relation(fields: [companyId], references: [id]) 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 OpportunityType { id Int @unique uid String @id @default(uuid()) name String description String? // Optima Only field, not synced to CW inactiveFlag Boolean @default(false) opportunities Opportunity[] updatedById String? createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model OpportunityStatus { id Int @unique uid String @id @default(uuid()) name String description String? // Optima Only field, not synced to CW inactiveFlag Boolean @default(false) defaultFlag Boolean @default(false) wonFlag Boolean @default(false) lostFlag Boolean @default(false) closeFlag Boolean @default(false) updatedById String? createdById String? opportunities Opportunity[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Opportunity { id Int @unique uid String @id @default(uuid()) name String notes String? generatedQuotes GeneratedQuotes[] typeId Int type OpportunityType @relation(fields: [typeId], references: [id]) stageName String? stageCwId Int? statusId Int? status OpportunityStatus? @relation(fields: [statusId], references: [id]) interest OpportunityInterest? probability Float @default(0) source String? // Sales rep references primarySalesRepId String? primarySalesRep User? @relation("PrimarySalesRep", fields: [primarySalesRepId], references: [cwIdentifier]) secondarySalesRepId String? secondarySalesRep User? @relation("SecondarySalesRep", fields: [secondarySalesRepId], references: [cwIdentifier]) // Company / contact / site companyId Int? company Company? @relation(fields: [companyId], references: [id]) contactId Int? contact Contact? @relation(fields: [contactId], references: [id]) siteId Int? site CompanyAddress? @relation(fields: [siteId], references: [id]) customerPO String? // Location / department locationId Int? location CorporateLocation? @relation(fields: [locationId], references: [id]) departmentId Int? department InternalDepartment? @relation(fields: [departmentId], references: [id]) // Dates expectedCloseDate DateTime? pipelineChangeDate DateTime? dateBecameLead DateTime? closedDate DateTime? closedFlag Boolean @default(false) closedById String? // 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([]) 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: [uid], 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: [uid], 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 }