Добавлены модели и функциональность для управления сотрудниками, включая создание, обновление и удаление сотрудников через GraphQL. Обновлены компоненты для отображения списка сотрудников и их расписания, улучшен интерфейс взаимодействия с пользователем. Реализованы функции экспорта отчетов в CSV и TXT форматах, добавлены новые интерфейсы и типы данных для сотрудников.

This commit is contained in:
Bivekich
2025-07-17 23:55:11 +03:00
parent 3e2a03da8c
commit d361364716
13 changed files with 3444 additions and 428 deletions

View File

@ -1,6 +1,3 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
@ -10,238 +7,326 @@ datasource db {
url = env("DATABASE_URL")
}
// Модель пользователя
model User {
id String @id @default(cuid())
phone String @unique
avatar String? // URL аватара в S3
managerName String? // Имя управляющего
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Связь с организацией
organization Organization? @relation(fields: [organizationId], references: [id])
id String @id @default(cuid())
phone String @unique
avatar String?
managerName String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizationId String?
// SMS коды для авторизации
smsCodes SmsCode[]
// Отправленные сообщения
sentMessages Message[] @relation("SentMessages")
sentMessages Message[] @relation("SentMessages")
smsCodes SmsCode[]
organization Organization? @relation(fields: [organizationId], references: [id])
@@map("users")
}
// Модель для SMS кодов
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())
// Связь с пользователем
user User? @relation(fields: [userId], references: [id])
userId String?
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? // ACTIVE, LIQUIDATED и т.д.
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? // Массив email адресов
// Финансовые данные
employeeCount Int? // Численность сотрудников
revenue BigInt? // Выручка
taxSystem String? // Система налогообложения
// Полные данные из DaData (для полноты)
dadataData Json?
// Связи
users User[]
apiKeys ApiKey[]
// Связи контрагентов
sentRequests CounterpartyRequest[] @relation("SentRequests")
receivedRequests CounterpartyRequest[] @relation("ReceivedRequests")
organizationCounterparties Counterparty[] @relation("OrganizationCounterparties")
counterpartyOf Counterparty[] @relation("CounterpartyOf")
// Сообщения
sentMessages Message[] @relation("SentMessages")
receivedMessages Message[] @relation("ReceivedMessages")
// Услуги и расходники (только для фулфилмент центров)
services Service[]
supplies Supply[]
// Товары (только для оптовиков)
products Product[]
// Корзины
carts Cart[]
// Избранные товары
favorites Favorites[]
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[]
@@map("organizations")
}
// Модель для API ключей маркетплейсов
model ApiKey {
id String @id @default(cuid())
marketplace MarketplaceType
apiKey String
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Данные для валидации (например, информация о продавце)
id String @id @default(cuid())
marketplace MarketplaceType
apiKey String
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
validationData Json?
// Связь с организацией
organization Organization @relation(fields: [organizationId], references: [id])
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
@@unique([organizationId, marketplace])
@@map("api_keys")
}
// Тип организации
enum OrganizationType {
FULFILLMENT // Фулфилмент
SELLER // Селлер
LOGIST // Логистика
WHOLESALE // Оптовик
}
// Тип маркетплейса
enum MarketplaceType {
WILDBERRIES
OZON
}
// Модель для заявок на добавление в контрагенты
model CounterpartyRequest {
id String @id @default(cuid())
status CounterpartyRequestStatus @default(PENDING)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Кто отправил заявку
sender Organization @relation("SentRequests", fields: [senderId], references: [id])
senderId String
// Кому отправили заявку
receiver Organization @relation("ReceivedRequests", fields: [receiverId], references: [id])
receiverId String
// Комментарий к заявке
message String?
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())
// Основная организация
organization Organization @relation("OrganizationCounterparties", fields: [organizationId], references: [id])
organizationId String
// Контрагент
counterparty Organization @relation("CounterpartyOf", fields: [counterpartyId], references: [id])
counterpartyId String
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")
}
// Статус заявки на добавление в контрагенты
enum CounterpartyRequestStatus {
PENDING // Ожидает ответа
ACCEPTED // Принята
REJECTED // Отклонена
CANCELLED // Отменена отправителем
}
// Модель сообщений в мессенджере
model Message {
id String @id @default(cuid())
content String? // Текст сообщения (nullable для медиа)
type MessageType @default(TEXT)
voiceUrl String? // URL голосового файла в S3
voiceDuration Int? // Длительность голосового сообщения в секундах
fileUrl String? // URL файла/изображения в S3
fileName String? // Оригинальное имя файла
fileSize Int? // Размер файла в байтах
fileType String? // MIME тип файла
isRead Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Отправитель
sender User @relation("SentMessages", fields: [senderId], references: [id])
senderId String
senderOrganization Organization @relation("SentMessages", fields: [senderOrganizationId], references: [id])
senderOrganizationId String
// Получатель
receiverOrganization Organization @relation("ReceivedMessages", fields: [receiverOrganizationId], references: [id])
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)
imageUrl 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)
quantity Int @default(0)
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)
@@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?
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")
}
enum OrganizationType {
FULFILLMENT
SELLER
LOGIST
WHOLESALE
}
enum MarketplaceType {
WILDBERRIES
OZON
}
enum CounterpartyRequestStatus {
PENDING
ACCEPTED
REJECTED
CANCELLED
}
enum MessageType {
TEXT
VOICE
@ -249,164 +334,17 @@ enum MessageType {
FILE
}
// Модель услуг (для фулфилмент центров)
model Service {
id String @id @default(cuid())
name String
description String?
price Decimal @db.Decimal(10,2) // Цена за единицу
imageUrl String? // URL фотографии в S3
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Связь с организацией
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
organizationId String
@@map("services")
enum EmployeeStatus {
ACTIVE
VACATION
SICK
FIRED
}
// Модель расходников (для фулфилмент центров)
model Supply {
id String @id @default(cuid())
name String
description String?
price Decimal @db.Decimal(10,2) // Цена за единицу
quantity Int @default(0) // Количество в наличии
imageUrl String? // URL фотографии в S3
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Связь с организацией
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
organizationId String
@@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) // Цена за единицу
quantity Int @default(0) // Количество в наличии
// Основные характеристики
category Category? @relation(fields: [categoryId], references: [id])
categoryId String? // ID категории
brand String? // Бренд
// Дополнительные характеристики (необязательные)
color String? // Цвет
size String? // Размер
weight Decimal? @db.Decimal(8,3) // Вес в кг
dimensions String? // Габариты (ДxШxВ)
material String? // Материал
// Изображения (JSON массив URL-ов в S3)
images Json @default("[]") // Массив URL изображений
mainImage String? // URL главного изображения
// Статус товара
isActive Boolean @default(true) // Активен ли товар
// Временные метки
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Связь с организацией (только оптовики)
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
organizationId String
// Связь с элементами корзины
cartItems CartItem[]
// Избранные товары
favorites Favorites[]
// Уникальность артикула в рамках организации
@@unique([organizationId, article])
@@map("products")
}
// Модель корзины
model Cart {
id String @id @default(cuid())
// Связь с организацией (только покупатель может иметь корзину)
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
organizationId String @unique // У каждой организации может быть только одна корзина
// Элементы корзины
items CartItem[]
// Временные метки
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("carts")
}
// Модель элемента корзины
model CartItem {
id String @id @default(cuid())
// Связь с корзиной
cart Cart @relation(fields: [cartId], references: [id], onDelete: Cascade)
cartId String
// Связь с товаром
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
productId String
// Количество товара в корзине
quantity Int @default(1)
// Временные метки
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Уникальность: один товар может быть только один раз в корзине
@@unique([cartId, productId])
@@map("cart_items")
}
// Модель избранных товаров
model Favorites {
id String @id @default(cuid())
// Связь с организацией (пользователь может добавлять товары в избранное)
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
organizationId String
// Связь с товаром
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
productId String
// Временные метки
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Уникальность: один товар может быть только один раз в избранном у организации
@@unique([organizationId, productId])
@@map("favorites")
enum ScheduleStatus {
WORK
WEEKEND
VACATION
SICK
ABSENT
}