refactor(api): started implementing all of the tables needed for full data synchronization

BREAKING CHANGE: refer to body
This commit is contained in:
2026-03-22 20:48:29 -05:00
parent 6b7eec67b8
commit 688a9096c2
65 changed files with 856 additions and 1463 deletions
+309 -58
View File
@@ -13,6 +13,89 @@ 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())
@@ -34,16 +117,27 @@ model User {
emailVerified DateTime?
image String?
cwIdentifier String?
active Boolean @default(true)
hidden Boolean @default(false)
cwIdentifier String? @unique
userId String @unique
token String?
sessions Session[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
generatedQuotes GeneratedQuotes[]
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 {
@@ -58,13 +152,53 @@ model Role {
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 String?
companyId Int?
company Company? @relation(fields: [companyId], references: [id])
createdAt DateTime @default(now())
@@ -72,16 +206,104 @@ model UnifiSite {
}
model Company {
id String @id @default(cuid())
id Int @unique
uid String @id @default(uuid())
name String
cw_CompanyId Int @unique
cw_Identifier String @unique
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
}
@@ -125,56 +347,93 @@ model CatalogItem {
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 String @id @default(cuid())
cwOpportunityId Int @unique
name String
notes String?
id Int @unique
uid String @id @default(uuid())
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?
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
primarySalesRepName String?
primarySalesRepIdentifier String?
primarySalesRepCwId Int?
secondarySalesRepName String?
secondarySalesRepIdentifier String?
secondarySalesRepCwId Int?
primarySalesRepId String?
primarySalesRep User? @relation("PrimarySalesRep", fields: [primarySalesRepId], references: [cwIdentifier])
secondarySalesRepId String?
secondarySalesRep User? @relation("SecondarySalesRep", fields: [secondarySalesRepId], references: [cwIdentifier])
// Company / contact / site
companyCwId Int?
companyName String?
contactCwId Int?
contactName String?
siteCwId Int?
siteName String?
customerPO String?
companyId Int?
company Company? @relation(fields: [companyId], references: [id])
// Financials
totalSalesTax Float @default(0)
probability Float @default(0)
contactId Int?
contact Contact? @relation(fields: [contactId], references: [id])
siteId Int?
site CompanyAddress? @relation(fields: [siteId], references: [id])
customerPO String?
// Location / department
locationName String?
locationCwId Int?
departmentName String?
departmentCwId Int?
locationId Int?
location CorporateLocation? @relation(fields: [locationId], references: [id])
departmentId Int?
department InternalDepartment? @relation(fields: [departmentId], references: [id])
// Dates
expectedCloseDate DateTime?
@@ -182,20 +441,12 @@ model Opportunity {
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])
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([])
cwLastUpdated DateTime?
cwDateEntered DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
@@ -242,7 +493,7 @@ model Credential {
fields Json
companyId String
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
company Company @relation(fields: [companyId], references: [uid], onDelete: Cascade)
securevalues SecureValue[]
@@ -263,7 +514,7 @@ model GeneratedQuotes {
quoteFileName String
opportunityId String
opportunity Opportunity @relation(fields: [opportunityId], references: [id], onDelete: Cascade)
opportunity Opportunity @relation(fields: [opportunityId], references: [uid], onDelete: Cascade)
createdById String?
createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)