generator client { provider = "prisma-client-js" } // Конфигурация для автоматического seeding generator seed { provider = "prisma-client-js" output = "./generated/client" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(cuid()) phone String @unique avatar String? managerName String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organizationId String? sentMessages Message[] @relation("SentMessages") smsCodes SmsCode[] organization Organization? @relation(fields: [organizationId], references: [id]) @@map("users") } model Admin { id String @id @default(cuid()) username String @unique password String // Хеш пароля email String? @unique isActive Boolean @default(true) lastLogin DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("admins") } model SmsCode { id String @id @default(cuid()) code String phone String expiresAt DateTime isUsed Boolean @default(false) attempts Int @default(0) maxAttempts Int @default(3) createdAt DateTime @default(now()) userId String? user User? @relation(fields: [userId], references: [id]) @@map("sms_codes") } model Organization { id String @id @default(cuid()) inn String @unique kpp String? name String? fullName String? ogrn String? ogrnDate DateTime? type OrganizationType createdAt DateTime @default(now()) updatedAt DateTime @updatedAt address String? addressFull String? status String? actualityDate DateTime? registrationDate DateTime? liquidationDate DateTime? managementName String? managementPost String? opfCode String? opfFull String? opfShort String? okato String? oktmo String? okpo String? okved String? phones Json? emails Json? employeeCount Int? revenue BigInt? taxSystem String? dadataData Json? apiKeys ApiKey[] carts Cart? counterpartyOf Counterparty[] @relation("CounterpartyOf") organizationCounterparties Counterparty[] @relation("OrganizationCounterparties") receivedRequests CounterpartyRequest[] @relation("ReceivedRequests") sentRequests CounterpartyRequest[] @relation("SentRequests") employees Employee[] favorites Favorites[] receivedMessages Message[] @relation("ReceivedMessages") sentMessages Message[] @relation("SentMessages") products Product[] services Service[] supplies Supply[] users User[] logistics Logistics[] supplyOrders SupplyOrder[] partnerSupplyOrders SupplyOrder[] @relation("SupplyOrderPartner") fulfillmentSupplyOrders SupplyOrder[] @relation("SupplyOrderFulfillmentCenter") logisticsSupplyOrders SupplyOrder[] @relation("SupplyOrderLogistics") wildberriesSupplies WildberriesSupply[] supplySuppliers SupplySupplier[] @relation("SupplySuppliers") externalAds ExternalAd[] @relation("ExternalAds") wbWarehouseCaches WBWarehouseCache[] @relation("WBWarehouseCaches") sellerStatsCaches SellerStatsCache[] @relation("SellerStatsCaches") sellerSupplies Supply[] @relation("SellerSupplies") @@map("organizations") } model ApiKey { id String @id @default(cuid()) marketplace MarketplaceType apiKey String isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt validationData Json? organizationId String organization Organization @relation(fields: [organizationId], references: [id]) @@unique([organizationId, marketplace]) @@map("api_keys") } model CounterpartyRequest { id String @id @default(cuid()) status CounterpartyRequestStatus @default(PENDING) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt senderId String receiverId String message String? receiver Organization @relation("ReceivedRequests", fields: [receiverId], references: [id]) sender Organization @relation("SentRequests", fields: [senderId], references: [id]) @@unique([senderId, receiverId]) @@map("counterparty_requests") } model Counterparty { id String @id @default(cuid()) createdAt DateTime @default(now()) organizationId String counterpartyId String counterparty Organization @relation("CounterpartyOf", fields: [counterpartyId], references: [id]) organization Organization @relation("OrganizationCounterparties", fields: [organizationId], references: [id]) @@unique([organizationId, counterpartyId]) @@map("counterparties") } model Message { id String @id @default(cuid()) content String? type MessageType @default(TEXT) voiceUrl String? voiceDuration Int? fileUrl String? fileName String? fileSize Int? fileType String? isRead Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt senderId String senderOrganizationId String receiverOrganizationId String receiverOrganization Organization @relation("ReceivedMessages", fields: [receiverOrganizationId], references: [id]) sender User @relation("SentMessages", fields: [senderId], references: [id]) senderOrganization Organization @relation("SentMessages", fields: [senderOrganizationId], references: [id]) @@index([senderOrganizationId, receiverOrganizationId, createdAt]) @@index([receiverOrganizationId, isRead]) @@map("messages") } model Service { id String @id @default(cuid()) name String description String? price Decimal @db.Decimal(10, 2) imageUrl String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) @@map("services") } model Supply { id String @id @default(cuid()) name String description String? price Decimal @db.Decimal(10, 2) quantity Int @default(0) unit String @default("шт") category String @default("Расходники") status String @default("planned") // planned, in-transit, delivered, in-stock date DateTime @default(now()) supplier String @default("Не указан") minStock Int @default(0) currentStock Int @default(0) usedStock Int @default(0) // Количество использованных расходников imageUrl String? type SupplyType @default(FULFILLMENT_CONSUMABLES) // Тип расходников sellerOwnerId String? // ID селлера-владельца (для расходников селлеров) sellerOwner Organization? @relation("SellerSupplies", fields: [sellerOwnerId], references: [id], onDelete: SetNull) shopLocation String? // Местоположение в магазине фулфилмента createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) @@map("supplies") } model Category { id String @id @default(cuid()) name String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt products Product[] @@map("categories") } model Product { id String @id @default(cuid()) name String article String description String? price Decimal @db.Decimal(12, 2) pricePerSet Decimal? @db.Decimal(12, 2) quantity Int @default(0) setQuantity Int? ordered Int? inTransit Int? stock Int? sold Int? type ProductType @default(PRODUCT) categoryId String? brand String? color String? size String? weight Decimal? @db.Decimal(8, 3) dimensions String? material String? images Json @default("[]") mainImage String? isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organizationId String cartItems CartItem[] favorites Favorites[] supplyOrderItems SupplyOrderItem[] category Category? @relation(fields: [categoryId], references: [id]) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) @@unique([organizationId, article]) @@map("products") } model Cart { id String @id @default(cuid()) organizationId String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt items CartItem[] organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) @@map("carts") } model CartItem { id String @id @default(cuid()) cartId String productId String quantity Int @default(1) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt cart Cart @relation(fields: [cartId], references: [id], onDelete: Cascade) product Product @relation(fields: [productId], references: [id], onDelete: Cascade) @@unique([cartId, productId]) @@map("cart_items") } model Favorites { id String @id @default(cuid()) organizationId String productId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) product Product @relation(fields: [productId], references: [id], onDelete: Cascade) @@unique([organizationId, productId]) @@map("favorites") } model Employee { id String @id @default(cuid()) firstName String lastName String middleName String? birthDate DateTime? avatar String? passportPhoto String? passportSeries String? passportNumber String? passportIssued String? passportDate DateTime? address String? position String department String? hireDate DateTime salary Float? status EmployeeStatus @default(ACTIVE) phone String email String? telegram String? whatsapp String? emergencyContact String? emergencyPhone String? organizationId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt scheduleRecords EmployeeSchedule[] organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) @@map("employees") } model EmployeeSchedule { id String @id @default(cuid()) date DateTime status ScheduleStatus hoursWorked Float? overtimeHours Float? notes String? employeeId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade) @@unique([employeeId, date]) @@map("employee_schedules") } model WildberriesSupply { id String @id @default(cuid()) organizationId String deliveryDate DateTime? status WildberriesSupplyStatus @default(DRAFT) totalAmount Decimal @db.Decimal(12, 2) totalItems Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) cards WildberriesSupplyCard[] @@map("wildberries_supplies") } model WildberriesSupplyCard { id String @id @default(cuid()) supplyId String nmId String vendorCode String title String brand String? price Decimal @db.Decimal(12, 2) discountedPrice Decimal? @db.Decimal(12, 2) quantity Int selectedQuantity Int selectedMarket String? selectedPlace String? sellerName String? sellerPhone String? deliveryDate DateTime? mediaFiles Json @default("[]") selectedServices Json @default("[]") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt supply WildberriesSupply @relation(fields: [supplyId], references: [id], onDelete: Cascade) @@map("wildberries_supply_cards") } enum OrganizationType { FULFILLMENT SELLER LOGIST WHOLESALE } enum MarketplaceType { WILDBERRIES OZON } enum CounterpartyRequestStatus { PENDING ACCEPTED REJECTED CANCELLED } enum MessageType { TEXT VOICE IMAGE FILE } enum EmployeeStatus { ACTIVE VACATION SICK FIRED } enum ScheduleStatus { WORK WEEKEND VACATION SICK ABSENT } enum SupplyOrderStatus { PENDING CONFIRMED IN_TRANSIT SUPPLIER_APPROVED LOGISTICS_CONFIRMED SHIPPED DELIVERED CANCELLED } enum WildberriesSupplyStatus { DRAFT CREATED IN_PROGRESS DELIVERED CANCELLED } enum ProductType { PRODUCT CONSUMABLE } enum SupplyType { FULFILLMENT_CONSUMABLES // Расходники фулфилмента (купленные фулфилментом для себя) SELLER_CONSUMABLES // Расходники селлеров (принятые от селлеров для хранения) } model Logistics { id String @id @default(cuid()) fromLocation String toLocation String priceUnder1m3 Float priceOver1m3 Float description String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organizationId String organization Organization @relation(fields: [organizationId], references: [id]) @@map("logistics") } model SupplyOrder { id String @id @default(cuid()) partnerId String deliveryDate DateTime status SupplyOrderStatus @default(PENDING) totalAmount Decimal @db.Decimal(12, 2) totalItems Int fulfillmentCenterId String? logisticsPartnerId String? // Опциональная логистика - может назначить фулфилмент consumableType String? // Классификация расходников: FULFILLMENT_CONSUMABLES, SELLER_CONSUMABLES createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organizationId String items SupplyOrderItem[] organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) partner Organization @relation("SupplyOrderPartner", fields: [partnerId], references: [id]) fulfillmentCenter Organization? @relation("SupplyOrderFulfillmentCenter", fields: [fulfillmentCenterId], references: [id]) logisticsPartner Organization? @relation("SupplyOrderLogistics", fields: [logisticsPartnerId], references: [id]) @@map("supply_orders") } model SupplyOrderItem { id String @id @default(cuid()) supplyOrderId String productId String quantity Int price Decimal @db.Decimal(12, 2) totalPrice Decimal @db.Decimal(12, 2) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt supplyOrder SupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade) product Product @relation(fields: [productId], references: [id]) @@unique([supplyOrderId, productId]) @@map("supply_order_items") } model SupplySupplier { id String @id @default(cuid()) name String contactName String phone String market String? address String? place String? telegram String? organizationId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organization Organization @relation("SupplySuppliers", fields: [organizationId], references: [id], onDelete: Cascade) @@map("supply_suppliers") } model ExternalAd { id String @id @default(cuid()) name String // Название рекламы url String // URL рекламы cost Decimal @db.Decimal(12, 2) // Стоимость date DateTime // Дата рекламы nmId String // ID товара Wildberries clicks Int @default(0) // Количество кликов organizationId String // ID организации createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organization Organization @relation("ExternalAds", fields: [organizationId], references: [id], onDelete: Cascade) @@index([organizationId, date]) @@map("external_ads") } model WBWarehouseCache { id String @id @default(cuid()) organizationId String // ID организации cacheDate DateTime // Дата кеширования (только дата, без времени) data Json // Кешированные данные склада WB totalProducts Int @default(0) // Общее количество товаров totalStocks Int @default(0) // Общее количество остатков totalReserved Int @default(0) // Общее количество в резерве createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organization Organization @relation("WBWarehouseCaches", fields: [organizationId], references: [id], onDelete: Cascade) @@unique([organizationId, cacheDate]) @@index([organizationId, cacheDate]) @@map("wb_warehouse_caches") } model SellerStatsCache { id String @id @default(cuid()) organizationId String // ID организации cacheDate DateTime // Дата кеширования (только дата, без времени) period String // Период статистики (week, month, quarter, custom) dateFrom DateTime? // Дата начала периода (для custom) dateTo DateTime? // Дата окончания периода (для custom) // Данные товаров productsData Json? // Кешированные данные товаров productsTotalSales Decimal? @db.Decimal(15, 2) // Общая сумма продаж товаров productsTotalOrders Int? // Общее количество заказов товаров productsCount Int? // Количество товаров // Данные рекламы advertisingData Json? // Кешированные данные рекламы advertisingTotalCost Decimal? @db.Decimal(15, 2) // Общие расходы на рекламу advertisingTotalViews Int? // Общие показы рекламы advertisingTotalClicks Int? // Общие клики рекламы // Метаданные expiresAt DateTime // Время истечения кеша (24 часа) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organization Organization @relation("SellerStatsCaches", fields: [organizationId], references: [id], onDelete: Cascade) @@unique([organizationId, cacheDate, period, dateFrom, dateTo]) @@index([organizationId, cacheDate]) @@index([expiresAt]) @@map("seller_stats_caches") }