
Обновления: - prisma/schema.prisma - обновлена схема БД для V2 расходников фулфилмента - src/graphql/typedefs.ts - новые типы для V2 FulfillmentInventoryItem - src/graphql/resolvers.ts - обновлены resolvers mySupplies и counterpartySupplies для V2 - src/graphql/resolvers/index.ts - подключены новые V2 resolvers - src/graphql/queries.ts - обновлены queries - src/graphql/mutations.ts - добавлена V2 мутация updateFulfillmentInventoryPrice - обновлен компонент fulfillment-consumables-orders-tab для V2 ESLint warnings исправим в отдельном коммите. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
996 lines
38 KiB
Plaintext
996 lines
38 KiB
Plaintext
generator client {
|
||
provider = "prisma-client-js"
|
||
}
|
||
|
||
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])
|
||
|
||
// === НОВЫЕ СВЯЗИ С ПРИЕМКОЙ ПОСТАВОК V2 ===
|
||
fulfillmentSupplyOrdersReceived FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersReceiver")
|
||
sellerSupplyOrdersReceived SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersReceiver")
|
||
|
||
@@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
|
||
market String?
|
||
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?
|
||
referralCode String? @unique
|
||
referredById String?
|
||
referralPoints Int @default(0)
|
||
apiKeys ApiKey[]
|
||
carts Cart?
|
||
counterpartyOf Counterparty[] @relation("CounterpartyOf")
|
||
organizationCounterparties Counterparty[] @relation("OrganizationCounterparties")
|
||
receivedRequests CounterpartyRequest[] @relation("ReceivedRequests")
|
||
sentRequests CounterpartyRequest[] @relation("SentRequests")
|
||
employees Employee[]
|
||
externalAds ExternalAd[] @relation("ExternalAds")
|
||
favorites Favorites[]
|
||
logistics Logistics[]
|
||
receivedMessages Message[] @relation("ReceivedMessages")
|
||
sentMessages Message[] @relation("SentMessages")
|
||
referredBy Organization? @relation("ReferralRelation", fields: [referredById], references: [id])
|
||
referrals Organization[] @relation("ReferralRelation")
|
||
products Product[]
|
||
referralTransactions ReferralTransaction[] @relation("ReferralTransactions")
|
||
referrerTransactions ReferralTransaction[] @relation("ReferrerTransactions")
|
||
sellerStatsCaches SellerStatsCache[] @relation("SellerStatsCaches")
|
||
services Service[]
|
||
supplies Supply[]
|
||
sellerSupplies Supply[] @relation("SellerSupplies")
|
||
fulfillmentSupplyOrders SupplyOrder[] @relation("SupplyOrderFulfillmentCenter")
|
||
logisticsSupplyOrders SupplyOrder[] @relation("SupplyOrderLogistics")
|
||
supplyOrders SupplyOrder[]
|
||
partnerSupplyOrders SupplyOrder[] @relation("SupplyOrderPartner")
|
||
supplySuppliers SupplySupplier[] @relation("SupplySuppliers")
|
||
users User[]
|
||
wbWarehouseCaches WBWarehouseCache[] @relation("WBWarehouseCaches")
|
||
wildberriesSupplies WildberriesSupply[]
|
||
|
||
// === НОВЫЕ СВЯЗИ С ПОСТАВКАМИ V2 ===
|
||
// Поставки расходников ФФ
|
||
fulfillmentSupplyOrdersAsFulfillment FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersFulfillment")
|
||
fulfillmentSupplyOrdersAsSupplier FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersSupplier")
|
||
fulfillmentSupplyOrdersAsLogistics FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersLogistics")
|
||
|
||
// Поставки расходников селлера
|
||
sellerSupplyOrdersAsSeller SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersSeller")
|
||
sellerSupplyOrdersAsFulfillment SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersFulfillment")
|
||
sellerSupplyOrdersAsSupplier SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersSupplier")
|
||
|
||
// === НОВЫЕ СВЯЗИ СО СКЛАДСКИМИ ОСТАТКАМИ V2 ===
|
||
fulfillmentInventory FulfillmentConsumableInventory[] @relation("FFInventory")
|
||
sellerSupplyOrdersAsLogistics SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersLogistics")
|
||
|
||
@@index([referralCode])
|
||
@@index([referredById])
|
||
@@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
|
||
type CounterpartyType @default(MANUAL)
|
||
triggeredBy String?
|
||
triggerEntityId String?
|
||
counterparty Organization @relation("CounterpartyOf", fields: [counterpartyId], references: [id])
|
||
organization Organization @relation("OrganizationCounterparties", fields: [organizationId], references: [id])
|
||
|
||
@@unique([organizationId, counterpartyId])
|
||
@@index([type])
|
||
@@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
|
||
article String
|
||
description String?
|
||
price Decimal @db.Decimal(10, 2)
|
||
pricePerUnit Decimal? @db.Decimal(10, 2)
|
||
quantity Int @default(0)
|
||
unit String @default("шт")
|
||
category String @default("Расходники")
|
||
status String @default("planned")
|
||
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?
|
||
shopLocation String?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
organizationId String
|
||
actualQuantity Int?
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
||
sellerOwner Organization? @relation("SellerSupplies", fields: [sellerOwnerId], references: [id])
|
||
|
||
@@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[]
|
||
category Category? @relation(fields: [categoryId], references: [id])
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
||
supplyOrderItems SupplyOrderItem[]
|
||
|
||
// === НОВЫЕ СВЯЗИ С ПОСТАВКАМИ V2 ===
|
||
fulfillmentSupplyItems FulfillmentConsumableSupplyItem[] @relation("FFSupplyItems")
|
||
sellerSupplyItems SellerConsumableSupplyItem[] @relation("SellerSupplyItems")
|
||
|
||
// === НОВЫЕ СВЯЗИ СО СКЛАДСКИМИ ОСТАТКАМИ V2 ===
|
||
inventoryRecords FulfillmentConsumableInventory[] @relation("InventoryProducts")
|
||
|
||
@@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)
|
||
supplyOrders SupplyOrder[] @relation("SupplyOrderResponsible")
|
||
|
||
@@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")
|
||
}
|
||
|
||
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])
|
||
routes SupplyRoute[] @relation("SupplyRouteLogistics")
|
||
|
||
@@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?
|
||
// Новые поля для многоуровневой системы поставок
|
||
packagesCount Int? // Количество грузовых мест (от поставщика)
|
||
volume Float? // Объём товара в м³ (от поставщика)
|
||
responsibleEmployee String? // ID ответственного сотрудника ФФ
|
||
notes String? // Заметки и комментарии
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
organizationId String
|
||
items SupplyOrderItem[]
|
||
routes SupplyRoute[] // Связь с маршрутами поставки
|
||
fulfillmentCenter Organization? @relation("SupplyOrderFulfillmentCenter", fields: [fulfillmentCenterId], references: [id])
|
||
logisticsPartner Organization? @relation("SupplyOrderLogistics", fields: [logisticsPartnerId], references: [id])
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
||
partner Organization @relation("SupplyOrderPartner", fields: [partnerId], references: [id])
|
||
employee Employee? @relation("SupplyOrderResponsible", fields: [responsibleEmployee], references: [id])
|
||
|
||
@@map("supply_orders")
|
||
}
|
||
|
||
// Модель для маршрутов поставки (модульная архитектура)
|
||
model SupplyRoute {
|
||
id String @id @default(cuid())
|
||
supplyOrderId String
|
||
logisticsId String? // Ссылка на предустановленный маршрут из Logistics
|
||
fromLocation String // Точка забора (рынок/поставщик)
|
||
toLocation String // Точка доставки (фулфилмент)
|
||
fromAddress String? // Полный адрес точки забора
|
||
toAddress String? // Полный адрес точки доставки
|
||
distance Float? // Расстояние в км
|
||
estimatedTime Int? // Время доставки в часах
|
||
price Decimal? @db.Decimal(10, 2) // Стоимость логистики
|
||
status String? @default("pending") // Статус маршрута
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
createdDate DateTime @default(now()) // Дата создания маршрута (уровень 2)
|
||
supplyOrder SupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade)
|
||
logistics Logistics? @relation("SupplyRouteLogistics", fields: [logisticsId], references: [id])
|
||
|
||
@@map("supply_routes")
|
||
}
|
||
|
||
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)
|
||
services String[] @default([])
|
||
fulfillmentConsumables String[] @default([])
|
||
sellerConsumables String[] @default([])
|
||
marketplaceCardId String?
|
||
// ОТКАТ: recipe Json? // Полная рецептура в JSON формате - ЗАКОММЕНТИРОВАНО
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
product Product @relation(fields: [productId], references: [id])
|
||
supplyOrder SupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade)
|
||
|
||
@@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
|
||
cost Decimal @db.Decimal(12, 2)
|
||
date DateTime
|
||
nmId String
|
||
clicks Int @default(0)
|
||
organizationId String
|
||
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
|
||
cacheDate DateTime
|
||
data Json
|
||
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
|
||
cacheDate DateTime
|
||
period String
|
||
dateFrom DateTime?
|
||
dateTo DateTime?
|
||
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
|
||
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")
|
||
}
|
||
|
||
model ReferralTransaction {
|
||
id String @id @default(cuid())
|
||
referrerId String
|
||
referralId String
|
||
points Int
|
||
type ReferralTransactionType
|
||
description String?
|
||
createdAt DateTime @default(now())
|
||
referral Organization @relation("ReferralTransactions", fields: [referralId], references: [id])
|
||
referrer Organization @relation("ReferrerTransactions", fields: [referrerId], references: [id])
|
||
|
||
@@index([referrerId, createdAt])
|
||
@@index([referralId])
|
||
@@map("referral_transactions")
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
enum CounterpartyType {
|
||
MANUAL
|
||
REFERRAL
|
||
AUTO_BUSINESS
|
||
AUTO
|
||
}
|
||
|
||
enum ReferralTransactionType {
|
||
REGISTRATION
|
||
AUTO_PARTNERSHIP
|
||
FIRST_ORDER
|
||
MONTHLY_BONUS
|
||
}
|
||
|
||
model AuditLog {
|
||
id String @id @default(cuid())
|
||
userId String
|
||
organizationType OrganizationType
|
||
action String
|
||
resourceType String
|
||
resourceId String?
|
||
metadata Json @default("{}")
|
||
ipAddress String?
|
||
userAgent String?
|
||
timestamp DateTime @default(now())
|
||
|
||
@@index([userId])
|
||
@@index([timestamp])
|
||
@@index([action])
|
||
@@index([resourceType])
|
||
@@map("audit_logs")
|
||
}
|
||
|
||
model SecurityAlert {
|
||
id String @id @default(cuid())
|
||
type SecurityAlertType
|
||
severity SecurityAlertSeverity
|
||
userId String
|
||
message String
|
||
metadata Json @default("{}")
|
||
timestamp DateTime @default(now())
|
||
resolved Boolean @default(false)
|
||
|
||
@@index([userId])
|
||
@@index([timestamp])
|
||
@@index([resolved])
|
||
@@index([severity])
|
||
@@map("security_alerts")
|
||
}
|
||
|
||
enum SecurityAlertType {
|
||
EXCESSIVE_ACCESS
|
||
UNAUTHORIZED_ATTEMPT
|
||
DATA_LEAK_RISK
|
||
SUSPICIOUS_PATTERN
|
||
BULK_EXPORT_DETECTED
|
||
RULE_VIOLATION
|
||
}
|
||
|
||
enum SecurityAlertSeverity {
|
||
LOW
|
||
MEDIUM
|
||
HIGH
|
||
CRITICAL
|
||
}
|
||
|
||
// ===============================================
|
||
// НОВАЯ СИСТЕМА ПОСТАВОК V2.0
|
||
// ===============================================
|
||
|
||
// Новый enum для статусов поставок v2
|
||
enum SupplyOrderStatusV2 {
|
||
PENDING // Ожидает одобрения поставщика
|
||
SUPPLIER_APPROVED // Одобрено поставщиком
|
||
LOGISTICS_CONFIRMED // Логистика подтверждена
|
||
SHIPPED // Отгружено поставщиком
|
||
IN_TRANSIT // В пути
|
||
DELIVERED // Доставлено и принято
|
||
CANCELLED // Отменено
|
||
}
|
||
|
||
// 5-статусная система для поставок расходников селлера
|
||
enum SellerSupplyOrderStatus {
|
||
PENDING // Ожидает одобрения поставщика
|
||
APPROVED // Одобрено поставщиком
|
||
SHIPPED // Отгружено
|
||
DELIVERED // Доставлено
|
||
COMPLETED // Завершено
|
||
CANCELLED // Отменено
|
||
}
|
||
|
||
// Модель для поставок расходников фулфилмента
|
||
model FulfillmentConsumableSupplyOrder {
|
||
// === БАЗОВЫЕ ПОЛЯ ===
|
||
id String @id @default(cuid())
|
||
status SupplyOrderStatusV2 @default(PENDING)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
// === ДАННЫЕ ФФ (создатель) ===
|
||
fulfillmentCenterId String // кто заказывает (FK: Organization)
|
||
requestedDeliveryDate DateTime // когда нужно
|
||
resalePricePerUnit Decimal? @db.Decimal(10, 2) // цена продажи селлерам
|
||
minStockLevel Int? // минимальный остаток
|
||
notes String? // заметки ФФ
|
||
|
||
// === ДАННЫЕ ПОСТАВЩИКА ===
|
||
supplierId String? // кто поставляет (FK: Organization)
|
||
supplierApprovedAt DateTime? // когда одобрил
|
||
packagesCount Int? // количество грузомест
|
||
estimatedVolume Decimal? @db.Decimal(8, 3) // объем груза в м³
|
||
supplierContractId String? // номер договора
|
||
supplierNotes String? // заметки поставщика
|
||
|
||
// === ДАННЫЕ ЛОГИСТИКИ ===
|
||
logisticsPartnerId String? // кто везет (FK: Organization)
|
||
estimatedDeliveryDate DateTime? // план доставки
|
||
routeId String? // маршрут (FK: LogisticsRoute)
|
||
logisticsCost Decimal? @db.Decimal(10, 2) // стоимость доставки
|
||
logisticsNotes String? // заметки логистики
|
||
|
||
// === ДАННЫЕ ОТГРУЗКИ ===
|
||
shippedAt DateTime? // факт отгрузки
|
||
trackingNumber String? // номер отслеживания
|
||
|
||
// === ДАННЫЕ ПРИЕМКИ ===
|
||
receivedAt DateTime? // факт приемки
|
||
receivedById String? // кто принял (FK: User)
|
||
actualQuantity Int? // принято количество
|
||
defectQuantity Int? // брак
|
||
receiptNotes String? // заметки приемки
|
||
|
||
// === СВЯЗИ ===
|
||
fulfillmentCenter Organization @relation("FFSupplyOrdersFulfillment", fields: [fulfillmentCenterId], references: [id])
|
||
supplier Organization? @relation("FFSupplyOrdersSupplier", fields: [supplierId], references: [id])
|
||
logisticsPartner Organization? @relation("FFSupplyOrdersLogistics", fields: [logisticsPartnerId], references: [id])
|
||
receivedBy User? @relation("FFSupplyOrdersReceiver", fields: [receivedById], references: [id])
|
||
items FulfillmentConsumableSupplyItem[]
|
||
|
||
@@map("fulfillment_consumable_supply_orders")
|
||
}
|
||
|
||
model FulfillmentConsumableSupplyItem {
|
||
id String @id @default(cuid())
|
||
supplyOrderId String // связь с поставкой
|
||
productId String // какой расходник (FK: Product)
|
||
|
||
// === КОЛИЧЕСТВА ===
|
||
requestedQuantity Int // запросили
|
||
approvedQuantity Int? // поставщик одобрил
|
||
shippedQuantity Int? // отгрузили
|
||
receivedQuantity Int? // приняли
|
||
defectQuantity Int? @default(0) // брак
|
||
|
||
// === ЦЕНЫ ===
|
||
unitPrice Decimal @db.Decimal(10, 2) // цена за единицу от поставщика
|
||
totalPrice Decimal @db.Decimal(12, 2) // общая стоимость
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
// === СВЯЗИ ===
|
||
supplyOrder FulfillmentConsumableSupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade)
|
||
product Product @relation("FFSupplyItems", fields: [productId], references: [id])
|
||
|
||
@@unique([supplyOrderId, productId])
|
||
@@map("fulfillment_consumable_supply_items")
|
||
}
|
||
|
||
// =============================================================================
|
||
// 📦 СИСТЕМА ПОСТАВОК РАСХОДНИКОВ СЕЛЛЕРА
|
||
// =============================================================================
|
||
|
||
// Модель для поставок расходников селлера
|
||
model SellerConsumableSupplyOrder {
|
||
// === БАЗОВЫЕ ПОЛЯ ===
|
||
id String @id @default(cuid())
|
||
status SellerSupplyOrderStatus @default(PENDING)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
// === ДАННЫЕ СЕЛЛЕРА (создатель) ===
|
||
sellerId String // кто заказывает (FK: Organization SELLER)
|
||
fulfillmentCenterId String // куда доставлять (FK: Organization FULFILLMENT)
|
||
requestedDeliveryDate DateTime // когда нужно
|
||
notes String? // заметки селлера
|
||
|
||
// === ДАННЫЕ ПОСТАВЩИКА ===
|
||
supplierId String? // кто поставляет (FK: Organization WHOLESALE)
|
||
supplierApprovedAt DateTime? // когда одобрил
|
||
packagesCount Int? // количество грузомест
|
||
estimatedVolume Decimal? @db.Decimal(8, 3) // объем груза в м³
|
||
supplierContractId String? // номер договора
|
||
supplierNotes String? // заметки поставщика
|
||
|
||
// === ДАННЫЕ ЛОГИСТИКИ ===
|
||
logisticsPartnerId String? // кто везет (FK: Organization LOGIST)
|
||
estimatedDeliveryDate DateTime? // план доставки
|
||
routeId String? // маршрут (FK: LogisticsRoute)
|
||
logisticsCost Decimal? @db.Decimal(10, 2) // стоимость доставки
|
||
logisticsNotes String? // заметки логистики
|
||
|
||
// === ДАННЫЕ ОТГРУЗКИ ===
|
||
shippedAt DateTime? // факт отгрузки
|
||
trackingNumber String? // номер отслеживания
|
||
|
||
// === ДАННЫЕ ПРИЕМКИ ===
|
||
deliveredAt DateTime? // факт доставки в ФФ
|
||
receivedById String? // кто принял в ФФ (FK: User)
|
||
actualQuantity Int? // принято количество
|
||
defectQuantity Int? // брак
|
||
receiptNotes String? // заметки приемки
|
||
|
||
// === ЭКОНОМИКА (для будущего раздела экономики) ===
|
||
totalCostWithDelivery Decimal? @db.Decimal(12, 2) // общая стоимость с доставкой
|
||
estimatedStorageCost Decimal? @db.Decimal(10, 2) // оценочная стоимость хранения
|
||
|
||
// === СВЯЗИ ===
|
||
seller Organization @relation("SellerSupplyOrdersSeller", fields: [sellerId], references: [id])
|
||
fulfillmentCenter Organization @relation("SellerSupplyOrdersFulfillment", fields: [fulfillmentCenterId], references: [id])
|
||
supplier Organization? @relation("SellerSupplyOrdersSupplier", fields: [supplierId], references: [id])
|
||
logisticsPartner Organization? @relation("SellerSupplyOrdersLogistics", fields: [logisticsPartnerId], references: [id])
|
||
receivedBy User? @relation("SellerSupplyOrdersReceiver", fields: [receivedById], references: [id])
|
||
items SellerConsumableSupplyItem[]
|
||
|
||
@@map("seller_consumable_supply_orders")
|
||
}
|
||
|
||
// Позиции в поставке расходников селлера
|
||
model SellerConsumableSupplyItem {
|
||
id String @id @default(cuid())
|
||
supplyOrderId String // связь с поставкой
|
||
productId String // какой расходник (FK: Product)
|
||
|
||
// === КОЛИЧЕСТВА ===
|
||
requestedQuantity Int // запросили
|
||
approvedQuantity Int? // поставщик одобрил
|
||
shippedQuantity Int? // отгрузили
|
||
receivedQuantity Int? // приняли в ФФ
|
||
defectQuantity Int? @default(0) // брак
|
||
|
||
// === ЦЕНЫ ===
|
||
unitPrice Decimal @db.Decimal(10, 2) // цена за единицу от поставщика
|
||
totalPrice Decimal @db.Decimal(12, 2) // общая стоимость
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
// === СВЯЗИ ===
|
||
supplyOrder SellerConsumableSupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade)
|
||
product Product @relation("SellerSupplyItems", fields: [productId], references: [id])
|
||
|
||
@@unique([supplyOrderId, productId])
|
||
@@map("seller_consumable_supply_items")
|
||
}
|
||
|
||
// ===============================================
|
||
// INVENTORY SYSTEM V2.0 - СКЛАДСКИЕ ОСТАТКИ
|
||
// ===============================================
|
||
|
||
// Складские остатки расходников фулфилмента
|
||
model FulfillmentConsumableInventory {
|
||
// === ИДЕНТИФИКАЦИЯ ===
|
||
id String @id @default(cuid())
|
||
|
||
// === СВЯЗИ ===
|
||
fulfillmentCenterId String // где хранится (FK: Organization)
|
||
productId String // что хранится (FK: Product)
|
||
|
||
// === СКЛАДСКИЕ ДАННЫЕ ===
|
||
currentStock Int @default(0) // текущий остаток на складе
|
||
minStock Int @default(0) // минимальный порог для заказа
|
||
maxStock Int? // максимальный порог (опционально)
|
||
reservedStock Int @default(0) // зарезервировано для отгрузок
|
||
totalReceived Int @default(0) // всего получено с момента создания
|
||
totalShipped Int @default(0) // всего отгружено селлерам
|
||
|
||
// === ЦЕНЫ ===
|
||
averageCost Decimal @default(0) @db.Decimal(10, 2) // средняя себестоимость
|
||
resalePrice Decimal? @db.Decimal(10, 2) // цена продажи селлерам
|
||
|
||
// === МЕТАДАННЫЕ ===
|
||
lastSupplyDate DateTime? // последняя поставка
|
||
lastUsageDate DateTime? // последнее использование/отгрузка
|
||
notes String? // заметки по складскому учету
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
// === СВЯЗИ ===
|
||
fulfillmentCenter Organization @relation("FFInventory", fields: [fulfillmentCenterId], references: [id])
|
||
product Product @relation("InventoryProducts", fields: [productId], references: [id])
|
||
|
||
// === ИНДЕКСЫ ===
|
||
@@unique([fulfillmentCenterId, productId]) // один товар = одна запись на фулфилмент
|
||
@@index([fulfillmentCenterId, currentStock])
|
||
@@index([currentStock, minStock]) // для поиска "заканчивающихся"
|
||
@@index([fulfillmentCenterId, lastSupplyDate])
|
||
@@map("fulfillment_consumable_inventory")
|
||
}
|