generator client { provider = "prisma-client-js" binaryTargets = ["native", "debian-openssl-3.0.x"] } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model User { id String @id @default(cuid()) email String @unique passwordHash String firstName String lastName String isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt userRoles UserRole[] authSessions AuthSession[] @relation("AuthSessionUser") revokedAuthSessions AuthSession[] @relation("AuthSessionRevokedBy") contactEntries CrmContactEntry[] inventoryTransactions InventoryTransaction[] purchaseReceipts PurchaseReceipt[] ownedProjects Project[] @relation("ProjectOwner") workOrderMaterialIssues WorkOrderMaterialIssue[] workOrderCompletions WorkOrderCompletion[] approvedSalesQuotes SalesQuote[] @relation("SalesQuoteApprovedBy") approvedSalesOrders SalesOrder[] @relation("SalesOrderApprovedBy") salesQuoteRevisionsCreated SalesQuoteRevision[] @relation("SalesQuoteRevisionCreatedBy") salesOrderRevisionsCreated SalesOrderRevision[] @relation("SalesOrderRevisionCreatedBy") purchaseOrderRevisionsCreated PurchaseOrderRevision[] inventoryTransfersCreated InventoryTransfer[] @relation("InventoryTransferCreatedBy") auditEvents AuditEvent[] } model Role { id String @id @default(cuid()) name String @unique description String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt userRoles UserRole[] rolePermissions RolePermission[] } model Permission { id String @id @default(cuid()) key String @unique description String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt rolePermissions RolePermission[] } model UserRole { userId String roleId String assignedAt DateTime @default(now()) assignedBy String? role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@id([userId, roleId]) } model RolePermission { roleId String permissionId String grantedAt DateTime @default(now()) permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) @@id([roleId, permissionId]) } model AuthSession { id String @id @default(cuid()) userId String expiresAt DateTime lastSeenAt DateTime @default(now()) ipAddress String? userAgent String? revokedAt DateTime? revokedById String? revokedReason String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation("AuthSessionUser", fields: [userId], references: [id], onDelete: Cascade) revokedBy User? @relation("AuthSessionRevokedBy", fields: [revokedById], references: [id], onDelete: SetNull) @@index([userId, createdAt]) @@index([expiresAt]) @@index([revokedAt]) } model CompanyProfile { id String @id @default(cuid()) companyName String legalName String email String phone String website String taxId String addressLine1 String addressLine2 String city String state String postalCode String country String primaryColor String @default("#185ADB") accentColor String @default("#00A6A6") surfaceColor String @default("#F4F7FB") fontFamily String @default("Manrope") logoFileId String? @unique isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt logoFile FileAttachment? @relation("CompanyLogo", fields: [logoFileId], references: [id], onDelete: SetNull) } model FileAttachment { id String @id @default(cuid()) originalName String storedName String mimeType String sizeBytes Int relativePath String ownerType String ownerId String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt companyLogoFor CompanyProfile? @relation("CompanyLogo") } model InventoryItem { id String @id @default(cuid()) sku String @unique skuFamilyId String? skuNodeId String? skuSequenceNumber Int? name String description String type String status String unitOfMeasure String isSellable Boolean @default(true) isPurchasable Boolean @default(true) preferredVendorId String? defaultCost Float? defaultPrice Float? notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt bomLines InventoryBomLine[] @relation("InventoryBomParent") usedInBomLines InventoryBomLine[] @relation("InventoryBomComponent") inventoryTransactions InventoryTransaction[] salesQuoteLines SalesQuoteLine[] salesOrderLines SalesOrderLine[] purchaseOrderLines PurchaseOrderLine[] workOrders WorkOrder[] workOrderMaterialIssues WorkOrderMaterialIssue[] operations InventoryItemOperation[] reservations InventoryReservation[] transfers InventoryTransfer[] preferredVendor Vendor? @relation(fields: [preferredVendorId], references: [id], onDelete: SetNull) skuFamily InventorySkuFamily? @relation(fields: [skuFamilyId], references: [id], onDelete: SetNull) skuNode InventorySkuNode? @relation(fields: [skuNodeId], references: [id], onDelete: SetNull) @@index([preferredVendorId]) @@index([skuFamilyId]) @@index([skuNodeId]) } model InventorySkuFamily { id String @id @default(cuid()) code String @unique sequenceCode String @unique name String description String nextSequenceNumber Int @default(1) isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt nodes InventorySkuNode[] items InventoryItem[] } model InventorySkuNode { id String @id @default(cuid()) familyId String parentNodeId String? code String label String description String path String level Int sortOrder Int @default(0) isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt family InventorySkuFamily @relation(fields: [familyId], references: [id], onDelete: Cascade) parentNode InventorySkuNode? @relation("InventorySkuNodeTree", fields: [parentNodeId], references: [id], onDelete: Cascade) childNodes InventorySkuNode[] @relation("InventorySkuNodeTree") items InventoryItem[] @@unique([familyId, path]) @@index([familyId, parentNodeId, sortOrder]) } model Warehouse { id String @id @default(cuid()) code String @unique name String notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt locations WarehouseLocation[] inventoryTransactions InventoryTransaction[] purchaseReceipts PurchaseReceipt[] workOrders WorkOrder[] workOrderMaterialIssues WorkOrderMaterialIssue[] reservations InventoryReservation[] transferSources InventoryTransfer[] @relation("InventoryTransferFromWarehouse") transferDestinations InventoryTransfer[] @relation("InventoryTransferToWarehouse") } model Customer { id String @id @default(cuid()) name String email String phone String addressLine1 String addressLine2 String city String state String postalCode String country String status String @default("ACTIVE") lifecycleStage String @default("ACTIVE") isReseller Boolean @default(false) resellerDiscountPercent Float @default(0) parentCustomerId String? paymentTerms String? currencyCode String? @default("USD") taxExempt Boolean @default(false) creditHold Boolean @default(false) preferredAccount Boolean @default(false) strategicAccount Boolean @default(false) requiresApproval Boolean @default(false) blockedAccount Boolean @default(false) notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt contactEntries CrmContactEntry[] contacts CrmContact[] parentCustomer Customer? @relation("CustomerHierarchy", fields: [parentCustomerId], references: [id], onDelete: SetNull) childCustomers Customer[] @relation("CustomerHierarchy") salesQuotes SalesQuote[] salesOrders SalesOrder[] projects Project[] } model InventoryBomLine { id String @id @default(cuid()) parentItemId String componentItemId String quantity Float unitOfMeasure String notes String position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt parentItem InventoryItem @relation("InventoryBomParent", fields: [parentItemId], references: [id], onDelete: Cascade) componentItem InventoryItem @relation("InventoryBomComponent", fields: [componentItemId], references: [id], onDelete: Restrict) @@index([parentItemId, position]) @@index([componentItemId]) } model WarehouseLocation { id String @id @default(cuid()) warehouseId String code String name String notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt warehouse Warehouse @relation(fields: [warehouseId], references: [id], onDelete: Cascade) inventoryTransactions InventoryTransaction[] purchaseReceipts PurchaseReceipt[] workOrders WorkOrder[] workOrderMaterialIssues WorkOrderMaterialIssue[] reservations InventoryReservation[] transferSourceLocations InventoryTransfer[] @relation("InventoryTransferFromLocation") transferDestinationLocations InventoryTransfer[] @relation("InventoryTransferToLocation") @@unique([warehouseId, code]) @@index([warehouseId]) } model InventoryTransaction { id String @id @default(cuid()) itemId String warehouseId String locationId String transactionType String quantity Int reference String notes String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt item InventoryItem @relation(fields: [itemId], references: [id], onDelete: Cascade) warehouse Warehouse @relation(fields: [warehouseId], references: [id], onDelete: Restrict) location WarehouseLocation @relation(fields: [locationId], references: [id], onDelete: Restrict) createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) @@index([itemId, createdAt]) @@index([warehouseId, createdAt]) @@index([locationId, createdAt]) } model InventoryTransfer { id String @id @default(cuid()) itemId String fromWarehouseId String fromLocationId String toWarehouseId String toLocationId String quantity Int notes String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt item InventoryItem @relation(fields: [itemId], references: [id], onDelete: Cascade) fromWarehouse Warehouse @relation("InventoryTransferFromWarehouse", fields: [fromWarehouseId], references: [id], onDelete: Restrict) fromLocation WarehouseLocation @relation("InventoryTransferFromLocation", fields: [fromLocationId], references: [id], onDelete: Restrict) toWarehouse Warehouse @relation("InventoryTransferToWarehouse", fields: [toWarehouseId], references: [id], onDelete: Restrict) toLocation WarehouseLocation @relation("InventoryTransferToLocation", fields: [toLocationId], references: [id], onDelete: Restrict) createdBy User? @relation("InventoryTransferCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) @@index([itemId, createdAt]) } model InventoryReservation { id String @id @default(cuid()) itemId String warehouseId String? locationId String? workOrderId String? sourceType String sourceId String? quantity Int status String @default("ACTIVE") notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt item InventoryItem @relation(fields: [itemId], references: [id], onDelete: Cascade) warehouse Warehouse? @relation(fields: [warehouseId], references: [id], onDelete: SetNull) location WarehouseLocation? @relation(fields: [locationId], references: [id], onDelete: SetNull) workOrder WorkOrder? @relation(fields: [workOrderId], references: [id], onDelete: Cascade) @@index([itemId, status, createdAt]) @@index([warehouseId, locationId, status]) @@index([workOrderId, status]) } model Vendor { id String @id @default(cuid()) name String email String phone String addressLine1 String addressLine2 String city String state String postalCode String country String status String @default("ACTIVE") lifecycleStage String @default("ACTIVE") paymentTerms String? currencyCode String? @default("USD") taxExempt Boolean @default(false) creditHold Boolean @default(false) preferredAccount Boolean @default(false) strategicAccount Boolean @default(false) requiresApproval Boolean @default(false) blockedAccount Boolean @default(false) notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt contactEntries CrmContactEntry[] contacts CrmContact[] purchaseOrders PurchaseOrder[] preferredSupplyItems InventoryItem[] } model CrmContactEntry { id String @id @default(cuid()) type String @default("NOTE") summary String body String contactAt DateTime customerId String? vendorId String? createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt customer Customer? @relation(fields: [customerId], references: [id], onDelete: Cascade) vendor Vendor? @relation(fields: [vendorId], references: [id], onDelete: Cascade) createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) } model CrmContact { id String @id @default(cuid()) fullName String role String @default("OTHER") email String phone String isPrimary Boolean @default(false) customerId String? vendorId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt customer Customer? @relation(fields: [customerId], references: [id], onDelete: Cascade) vendor Vendor? @relation(fields: [vendorId], references: [id], onDelete: Cascade) } model SalesQuote { id String @id @default(cuid()) documentNumber String @unique customerId String status String issueDate DateTime expiresAt DateTime? approvedAt DateTime? approvedById String? discountPercent Float @default(0) taxPercent Float @default(0) freightAmount Float @default(0) notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt customer Customer @relation(fields: [customerId], references: [id], onDelete: Restrict) approvedBy User? @relation("SalesQuoteApprovedBy", fields: [approvedById], references: [id], onDelete: SetNull) lines SalesQuoteLine[] projects Project[] revisions SalesQuoteRevision[] } model SalesQuoteLine { id String @id @default(cuid()) quoteId String itemId String description String quantity Int unitOfMeasure String unitPrice Float position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt quote SalesQuote @relation(fields: [quoteId], references: [id], onDelete: Cascade) item InventoryItem @relation(fields: [itemId], references: [id], onDelete: Restrict) @@index([quoteId, position]) } model SalesOrder { id String @id @default(cuid()) documentNumber String @unique customerId String status String issueDate DateTime approvedAt DateTime? approvedById String? discountPercent Float @default(0) taxPercent Float @default(0) freightAmount Float @default(0) notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt customer Customer @relation(fields: [customerId], references: [id], onDelete: Restrict) approvedBy User? @relation("SalesOrderApprovedBy", fields: [approvedById], references: [id], onDelete: SetNull) lines SalesOrderLine[] shipments Shipment[] projects Project[] revisions SalesOrderRevision[] workOrders WorkOrder[] purchaseOrderLines PurchaseOrderLine[] } model SalesOrderLine { id String @id @default(cuid()) orderId String itemId String description String quantity Int unitOfMeasure String unitPrice Float position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt order SalesOrder @relation(fields: [orderId], references: [id], onDelete: Cascade) item InventoryItem @relation(fields: [itemId], references: [id], onDelete: Restrict) workOrders WorkOrder[] purchaseOrderLines PurchaseOrderLine[] @@index([orderId, position]) } model SalesQuoteRevision { id String @id @default(cuid()) quoteId String revisionNumber Int reason String snapshot String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt quote SalesQuote @relation(fields: [quoteId], references: [id], onDelete: Cascade) createdBy User? @relation("SalesQuoteRevisionCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) @@unique([quoteId, revisionNumber]) @@index([quoteId, createdAt]) } model SalesOrderRevision { id String @id @default(cuid()) orderId String revisionNumber Int reason String snapshot String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt order SalesOrder @relation(fields: [orderId], references: [id], onDelete: Cascade) createdBy User? @relation("SalesOrderRevisionCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) @@unique([orderId, revisionNumber]) @@index([orderId, createdAt]) } model Shipment { id String @id @default(cuid()) shipmentNumber String @unique salesOrderId String status String shipDate DateTime? carrier String serviceLevel String trackingNumber String packageCount Int @default(1) notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt salesOrder SalesOrder @relation(fields: [salesOrderId], references: [id], onDelete: Restrict) projects Project[] @@index([salesOrderId, createdAt]) } model Project { id String @id @default(cuid()) projectNumber String @unique name String status String priority String customerId String salesQuoteId String? salesOrderId String? shipmentId String? ownerId String? dueDate DateTime? notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt customer Customer @relation(fields: [customerId], references: [id], onDelete: Restrict) salesQuote SalesQuote? @relation(fields: [salesQuoteId], references: [id], onDelete: SetNull) salesOrder SalesOrder? @relation(fields: [salesOrderId], references: [id], onDelete: SetNull) shipment Shipment? @relation(fields: [shipmentId], references: [id], onDelete: SetNull) owner User? @relation("ProjectOwner", fields: [ownerId], references: [id], onDelete: SetNull) workOrders WorkOrder[] milestones ProjectMilestone[] @@index([customerId, createdAt]) @@index([ownerId, dueDate]) @@index([status, priority]) } model ProjectMilestone { id String @id @default(cuid()) projectId String title String status String dueDate DateTime? completedAt DateTime? notes String sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) @@index([projectId, sortOrder]) @@index([projectId, dueDate]) } model WorkOrder { id String @id @default(cuid()) workOrderNumber String @unique itemId String projectId String? salesOrderId String? salesOrderLineId String? warehouseId String locationId String status String quantity Int completedQuantity Int @default(0) dueDate DateTime? notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt item InventoryItem @relation(fields: [itemId], references: [id], onDelete: Restrict) project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull) salesOrder SalesOrder? @relation(fields: [salesOrderId], references: [id], onDelete: SetNull) salesOrderLine SalesOrderLine? @relation(fields: [salesOrderLineId], references: [id], onDelete: SetNull) warehouse Warehouse @relation(fields: [warehouseId], references: [id], onDelete: Restrict) location WarehouseLocation @relation(fields: [locationId], references: [id], onDelete: Restrict) operations WorkOrderOperation[] materialIssues WorkOrderMaterialIssue[] completions WorkOrderCompletion[] reservations InventoryReservation[] @@index([itemId, createdAt]) @@index([projectId, dueDate]) @@index([salesOrderId, dueDate]) @@index([salesOrderLineId, dueDate]) @@index([status, dueDate]) @@index([warehouseId, createdAt]) } model ManufacturingStation { id String @id @default(cuid()) code String @unique name String description String queueDays Int @default(0) dailyCapacityMinutes Int @default(480) parallelCapacity Int @default(1) workingDays String @default("1,2,3,4,5") isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt itemOperations InventoryItemOperation[] workOrderOperations WorkOrderOperation[] } model InventoryItemOperation { id String @id @default(cuid()) itemId String stationId String setupMinutes Int @default(0) runMinutesPerUnit Int @default(0) moveMinutes Int @default(0) notes String position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt item InventoryItem @relation(fields: [itemId], references: [id], onDelete: Cascade) station ManufacturingStation @relation(fields: [stationId], references: [id], onDelete: Restrict) @@index([itemId, position]) @@index([stationId]) } model WorkOrderOperation { id String @id @default(cuid()) workOrderId String stationId String sequence Int setupMinutes Int @default(0) runMinutesPerUnit Int @default(0) moveMinutes Int @default(0) plannedMinutes Int @default(0) plannedStart DateTime plannedEnd DateTime notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt workOrder WorkOrder @relation(fields: [workOrderId], references: [id], onDelete: Cascade) station ManufacturingStation @relation(fields: [stationId], references: [id], onDelete: Restrict) @@index([workOrderId, sequence]) @@index([stationId, plannedStart]) } model WorkOrderMaterialIssue { id String @id @default(cuid()) workOrderId String componentItemId String warehouseId String locationId String quantity Int notes String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt workOrder WorkOrder @relation(fields: [workOrderId], references: [id], onDelete: Cascade) componentItem InventoryItem @relation(fields: [componentItemId], references: [id], onDelete: Restrict) warehouse Warehouse @relation(fields: [warehouseId], references: [id], onDelete: Restrict) location WarehouseLocation @relation(fields: [locationId], references: [id], onDelete: Restrict) createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) @@index([workOrderId, createdAt]) @@index([componentItemId, createdAt]) } model WorkOrderCompletion { id String @id @default(cuid()) workOrderId String quantity Int notes String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt workOrder WorkOrder @relation(fields: [workOrderId], references: [id], onDelete: Cascade) createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) @@index([workOrderId, createdAt]) } model PurchaseOrder { id String @id @default(cuid()) documentNumber String @unique vendorId String status String issueDate DateTime taxPercent Float @default(0) freightAmount Float @default(0) notes String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt vendor Vendor @relation(fields: [vendorId], references: [id], onDelete: Restrict) lines PurchaseOrderLine[] receipts PurchaseReceipt[] revisions PurchaseOrderRevision[] } model PurchaseOrderLine { id String @id @default(cuid()) purchaseOrderId String itemId String salesOrderId String? salesOrderLineId String? description String quantity Int unitOfMeasure String unitCost Float position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt purchaseOrder PurchaseOrder @relation(fields: [purchaseOrderId], references: [id], onDelete: Cascade) item InventoryItem @relation(fields: [itemId], references: [id], onDelete: Restrict) salesOrder SalesOrder? @relation(fields: [salesOrderId], references: [id], onDelete: SetNull) salesOrderLine SalesOrderLine? @relation(fields: [salesOrderLineId], references: [id], onDelete: SetNull) receiptLines PurchaseReceiptLine[] @@index([purchaseOrderId, position]) @@index([salesOrderId, position]) @@index([salesOrderLineId, position]) } model PurchaseReceipt { id String @id @default(cuid()) receiptNumber String @unique purchaseOrderId String warehouseId String locationId String receivedAt DateTime notes String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt purchaseOrder PurchaseOrder @relation(fields: [purchaseOrderId], references: [id], onDelete: Cascade) warehouse Warehouse @relation(fields: [warehouseId], references: [id], onDelete: Restrict) location WarehouseLocation @relation(fields: [locationId], references: [id], onDelete: Restrict) createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) lines PurchaseReceiptLine[] @@index([purchaseOrderId, createdAt]) @@index([warehouseId, createdAt]) @@index([locationId, createdAt]) } model PurchaseReceiptLine { id String @id @default(cuid()) purchaseReceiptId String purchaseOrderLineId String quantity Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt purchaseReceipt PurchaseReceipt @relation(fields: [purchaseReceiptId], references: [id], onDelete: Cascade) purchaseOrderLine PurchaseOrderLine @relation(fields: [purchaseOrderLineId], references: [id], onDelete: Restrict) @@index([purchaseReceiptId]) @@index([purchaseOrderLineId]) } model PurchaseOrderRevision { id String @id @default(cuid()) purchaseOrderId String revisionNumber Int reason String snapshot String createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt purchaseOrder PurchaseOrder @relation(fields: [purchaseOrderId], references: [id], onDelete: Cascade) createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) @@unique([purchaseOrderId, revisionNumber]) @@index([purchaseOrderId, createdAt]) } model AuditEvent { id String @id @default(cuid()) actorId String? entityType String entityId String? action String summary String metadataJson String createdAt DateTime @default(now()) actor User? @relation(fields: [actorId], references: [id], onDelete: SetNull) @@index([createdAt]) @@index([entityType, entityId, createdAt]) @@index([actorId, createdAt]) }