752 lines
21 KiB
Plaintext
752 lines
21 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
enum SettlementType {
|
|
cash
|
|
check
|
|
}
|
|
|
|
enum CheckRegistrationMode {
|
|
full
|
|
brief
|
|
}
|
|
|
|
enum OrderStatus {
|
|
cart
|
|
awaiting_settlement
|
|
cash_in_progress
|
|
check_in_progress
|
|
submitted
|
|
awaiting_review
|
|
pricing
|
|
needs_correction
|
|
awaiting_settlement_fix
|
|
cash_invoice_issued
|
|
awaiting_cash_payment
|
|
cash_payment_submitted
|
|
awaiting_cash_confirmation
|
|
cash_confirmed
|
|
awaiting_check_review
|
|
check_needs_fix
|
|
check_rejected
|
|
checks_approved
|
|
final_approved
|
|
preparing
|
|
ready_to_ship
|
|
shipped
|
|
delivered
|
|
cancelled
|
|
rejected
|
|
expired
|
|
}
|
|
|
|
enum CheckStatus {
|
|
pending_review
|
|
approved
|
|
rejected
|
|
}
|
|
|
|
enum CashPaymentStatus {
|
|
pending_review
|
|
approved
|
|
rejected
|
|
expired
|
|
}
|
|
|
|
enum ShipmentStatus {
|
|
preparing
|
|
ready
|
|
shipped
|
|
delivered
|
|
}
|
|
|
|
enum CustomerType {
|
|
wholesale
|
|
retail
|
|
}
|
|
|
|
enum AppUserRole {
|
|
customer
|
|
agent
|
|
}
|
|
|
|
enum InteractionType {
|
|
in_person
|
|
call
|
|
message
|
|
note
|
|
}
|
|
|
|
enum ProductSeason {
|
|
summer
|
|
winter
|
|
all_season
|
|
}
|
|
|
|
enum ProductUsageCategory {
|
|
trousers
|
|
suit
|
|
shirt
|
|
other
|
|
}
|
|
|
|
enum AdminRole {
|
|
admin
|
|
sales
|
|
finance
|
|
warehouse
|
|
}
|
|
|
|
enum NotificationTone {
|
|
info
|
|
warning
|
|
success
|
|
danger
|
|
}
|
|
|
|
/// گردش کالیته فیزیکی (§۷ سند)
|
|
enum CatalogRequestStatus {
|
|
new
|
|
approved
|
|
ready_to_ship
|
|
delivered
|
|
returned
|
|
cancelled
|
|
}
|
|
|
|
enum CatalogItemHolderType {
|
|
warehouse
|
|
customer
|
|
agent
|
|
}
|
|
|
|
model Company {
|
|
id String @id @default(cuid())
|
|
name String
|
|
slug String @unique
|
|
description String?
|
|
logoUrl String?
|
|
isActive Boolean @default(true)
|
|
displayOrder Int @default(0)
|
|
/// خالی یعنی از تنظیمات سیستم ارث میبرد
|
|
maxMeteragePerInvoice Int?
|
|
cashInvoiceValidityHours Int @default(8)
|
|
cashInvoiceValidUntilHour Int?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
bankAccounts BankAccount[]
|
|
catalogs Catalog[]
|
|
products Product[]
|
|
orders Order[]
|
|
customers Customer[]
|
|
}
|
|
|
|
model BankAccount {
|
|
id String @id @default(cuid())
|
|
companyId String
|
|
bankName String
|
|
accountHolder String
|
|
accountNumber String
|
|
cardNumber String
|
|
iban String
|
|
notes String?
|
|
isActive Boolean @default(true)
|
|
displayOrder Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
|
|
cashReceipts CashPaymentReceipt[]
|
|
|
|
@@index([companyId])
|
|
}
|
|
|
|
model Catalog {
|
|
id String @id @default(cuid())
|
|
companyId String
|
|
name String
|
|
code String
|
|
description String?
|
|
displayOrder Int @default(0)
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
|
|
products Product[]
|
|
|
|
@@unique([companyId, code])
|
|
@@index([companyId])
|
|
}
|
|
|
|
model Product {
|
|
id String @id @default(cuid())
|
|
companyId String
|
|
catalogId String
|
|
name String
|
|
code String
|
|
color String
|
|
description String?
|
|
minMeterage Float @default(30)
|
|
orderMultiplier Float @default(10)
|
|
basePrice Float?
|
|
stock Float?
|
|
isActive Boolean @default(true)
|
|
/// فیلدهای نمایش کاتالوگ موکاپ (اپ کاربر)
|
|
usageCategory ProductUsageCategory @default(other)
|
|
season ProductSeason @default(all_season)
|
|
widthCm Float?
|
|
composition String?
|
|
weightGrams Float?
|
|
weaveType String?
|
|
colorsJson Json?
|
|
tagsJson Json?
|
|
imageUrlsJson Json?
|
|
subtitle String?
|
|
rollMeters Float @default(50)
|
|
isNew Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
|
|
catalog Catalog @relation(fields: [catalogId], references: [id], onDelete: Cascade)
|
|
cartItems CartItem[]
|
|
orderItems OrderItem[]
|
|
favorites ProductFavorite[]
|
|
catalogRequestItems CatalogRequestItem[]
|
|
stockNotifyRequests StockNotifyRequest[]
|
|
productSuggestions ProductSuggestion[]
|
|
|
|
@@unique([companyId, code])
|
|
@@index([catalogId])
|
|
@@index([companyId])
|
|
@@index([usageCategory])
|
|
@@index([season])
|
|
}
|
|
|
|
model Customer {
|
|
id String @id @default(cuid())
|
|
firstName String
|
|
lastName String
|
|
phone String @unique
|
|
nationalId String?
|
|
customerType CustomerType @default(wholesale)
|
|
/// نقش در اپ کاربر: مشتری عادی یا واسطه
|
|
appRole AppUserRole @default(customer)
|
|
agentCode String?
|
|
businessName String?
|
|
levelLabel String?
|
|
nextLevelLabel String?
|
|
pointsToNext Int?
|
|
levelProgress Int? @default(0)
|
|
assignedAgentId String?
|
|
avatarLetter String?
|
|
avatarColor String?
|
|
avatarUrl String?
|
|
companyId String?
|
|
levelDefId String?
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull)
|
|
levelDef CustomerLevelDef? @relation(fields: [levelDefId], references: [id], onDelete: SetNull)
|
|
assignedAgent Customer? @relation("AgentCustomers", fields: [assignedAgentId], references: [id], onDelete: SetNull)
|
|
agentCustomers Customer[] @relation("AgentCustomers")
|
|
carts Cart[]
|
|
orders Order[]
|
|
notifications CustomerNotification[]
|
|
meetings MeetingInteraction[] @relation("MeetingCustomer")
|
|
meetingsAsAgent MeetingInteraction[] @relation("MeetingAgent")
|
|
reminders Reminder[] @relation("ReminderOwner")
|
|
remindersAbout Reminder[] @relation("ReminderCustomer")
|
|
favorites ProductFavorite[]
|
|
catalogRequests CatalogRequest[]
|
|
stockNotifyRequests StockNotifyRequest[]
|
|
productSuggestions ProductSuggestion[]
|
|
customerBrands CustomerBrand[]
|
|
|
|
@@index([companyId])
|
|
@@index([appRole])
|
|
@@index([assignedAgentId])
|
|
@@index([agentCode])
|
|
@@index([levelDefId])
|
|
}
|
|
|
|
model AdminUser {
|
|
id String @id @default(cuid())
|
|
fullName String
|
|
phone String @unique
|
|
email String @unique
|
|
passwordHash String
|
|
role AdminRole @default(admin)
|
|
isActive Boolean @default(true)
|
|
lastLoginAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model OtpChallenge {
|
|
id String @id @default(cuid())
|
|
phone String
|
|
code String
|
|
firstName String?
|
|
lastName String?
|
|
isRegister Boolean @default(false)
|
|
attempts Int @default(0)
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([phone])
|
|
}
|
|
|
|
model Cart {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
companyId String?
|
|
notes String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user Customer @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
items CartItem[]
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
model CartItem {
|
|
id String @id @default(cuid())
|
|
cartId String
|
|
productId String
|
|
catalogCode String
|
|
productName String
|
|
productCode String
|
|
color String
|
|
meterage Float
|
|
unitPrice Float?
|
|
notes String?
|
|
companyId String
|
|
companyName String
|
|
|
|
cart Cart @relation(fields: [cartId], references: [id], onDelete: Cascade)
|
|
product Product @relation(fields: [productId], references: [id], onDelete: Restrict)
|
|
|
|
@@unique([cartId, productId])
|
|
@@index([cartId])
|
|
}
|
|
|
|
model Order {
|
|
id String @id @default(cuid())
|
|
orderNumber String @unique
|
|
userId String
|
|
companyId String
|
|
companyName String
|
|
// روش تسویه بعد از قیمتگذاری ادمین توسط مشتری انتخاب میشود
|
|
settlementType SettlementType?
|
|
checkMode CheckRegistrationMode?
|
|
status OrderStatus @default(awaiting_review)
|
|
totalMeterage Float @default(0)
|
|
totalAmount Float?
|
|
shippingCost Float?
|
|
totalDiscount Float?
|
|
finalAmount Float?
|
|
notes String?
|
|
checkRassDays Int?
|
|
checkRassDate DateTime?
|
|
cashInvoiceExpiresAt DateTime?
|
|
/// زمان ارسال یادآوری پایان مهلت پرداخت تا دوباره ارسال نشود
|
|
cashReminderSentAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user Customer @relation(fields: [userId], references: [id], onDelete: Restrict)
|
|
company Company @relation(fields: [companyId], references: [id], onDelete: Restrict)
|
|
items OrderItem[]
|
|
checks Check[]
|
|
cashReceipt CashPaymentReceipt?
|
|
shipment Shipment?
|
|
statusHistory OrderStatusHistory[]
|
|
notifications CustomerNotification[]
|
|
|
|
@@index([userId])
|
|
@@index([companyId])
|
|
@@index([status])
|
|
@@index([createdAt])
|
|
@@index([status, cashInvoiceExpiresAt])
|
|
}
|
|
|
|
model OrderItem {
|
|
id String @id @default(cuid())
|
|
orderId String
|
|
productId String?
|
|
catalogCode String
|
|
productName String
|
|
productCode String?
|
|
color String
|
|
requestedMeterage Float
|
|
confirmedMeterage Float?
|
|
unitPrice Float?
|
|
unitDiscount Float? @default(0)
|
|
lineTotal Float?
|
|
notes String?
|
|
companyId String
|
|
companyName String
|
|
|
|
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
|
|
product Product? @relation(fields: [productId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([orderId])
|
|
@@index([companyId])
|
|
}
|
|
|
|
model OrderStatusHistory {
|
|
id String @id @default(cuid())
|
|
orderId String
|
|
fromStatus OrderStatus?
|
|
toStatus OrderStatus
|
|
changedBy String
|
|
note String?
|
|
reason String?
|
|
changedAt DateTime @default(now())
|
|
|
|
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([orderId])
|
|
}
|
|
|
|
model Check {
|
|
id String @id @default(cuid())
|
|
orderId String
|
|
row Int
|
|
amount Float
|
|
dueDate DateTime
|
|
sayadId String?
|
|
checkNumber String?
|
|
ownerNationalId String?
|
|
ownerFirstName String?
|
|
ownerLastName String?
|
|
bankName String?
|
|
branchName String?
|
|
frontImage String?
|
|
backImage String?
|
|
notes String?
|
|
mode CheckRegistrationMode @default(full)
|
|
status CheckStatus @default(pending_review)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([orderId])
|
|
@@index([dueDate])
|
|
}
|
|
|
|
model CashPaymentReceipt {
|
|
id String @id @default(cuid())
|
|
orderId String @unique
|
|
amount Float
|
|
paidAmount Float?
|
|
transferDate DateTime?
|
|
transferTime String?
|
|
trackingNumber String?
|
|
destinationAccountId String?
|
|
receiptImage String?
|
|
notes String?
|
|
expiresAt DateTime?
|
|
status CashPaymentStatus @default(pending_review)
|
|
submittedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
|
|
destinationAccount BankAccount? @relation(fields: [destinationAccountId], references: [id], onDelete: SetNull)
|
|
}
|
|
|
|
model Shipment {
|
|
id String @id @default(cuid())
|
|
orderId String @unique
|
|
carrier String
|
|
trackingNumber String?
|
|
meterage Float
|
|
status ShipmentStatus @default(preparing)
|
|
shippedAt DateTime?
|
|
deliveredAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model SystemSettings {
|
|
id String @id @default("default")
|
|
maxMeteragePerInvoice Int @default(5000)
|
|
cashInvoiceValidityHours Int @default(8)
|
|
defaultSettlementCheck Boolean @default(true)
|
|
defaultSettlementCash Boolean @default(true)
|
|
/// حداقل سفارش: تعداد طاقه از هر محصول (§۱۹)
|
|
minOrderRollsPerProduct Int @default(1)
|
|
/// نمایش قیمت عمومی در کاتالوگ (پیشفرض خاموش)
|
|
showCatalogPublicPrice Boolean @default(false)
|
|
/// اجازه قیمتگذاری توسط واسطه (§۱۹)
|
|
allowAgentPricing Boolean @default(false)
|
|
/// کف قیمت واحد برای قیمتگذاری واسطه (ریال) — اختیاری
|
|
agentMinUnitPrice Int?
|
|
smsOrderRegistered Boolean @default(true)
|
|
smsPricingReady Boolean @default(true)
|
|
smsPaymentReminder Boolean @default(true)
|
|
smsCheckReview Boolean @default(true)
|
|
smsShippingUpdate Boolean @default(true)
|
|
supportPhone String?
|
|
supportEmail String?
|
|
/// تصاویر دستههای کاربرد/فصل اپ کاربر: { trousers: dataUrl, summer: dataUrl, ... }
|
|
categoryImagesJson Json?
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
/// بنرهای قابل مدیریت بخشهای اپ کاربر
|
|
model AppBanner {
|
|
id String @id @default(cuid())
|
|
slot String
|
|
title String
|
|
subtitle String?
|
|
ctaLabel String?
|
|
ctaLink String?
|
|
imageUrl String?
|
|
displayOrder Int @default(0)
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([slot, isActive, displayOrder])
|
|
}
|
|
|
|
model AdminNotification {
|
|
id String @id @default(cuid())
|
|
title String
|
|
body String
|
|
tone NotificationTone? @default(info)
|
|
link String?
|
|
isRead Boolean @default(false)
|
|
sentAt DateTime @default(now())
|
|
}
|
|
|
|
model CustomerNotification {
|
|
id String @id @default(cuid())
|
|
customerId String
|
|
orderId String?
|
|
event String
|
|
title String
|
|
body String
|
|
tone NotificationTone? @default(info)
|
|
link String?
|
|
isRead Boolean @default(false)
|
|
smsSent Boolean @default(false)
|
|
sentAt DateTime @default(now())
|
|
|
|
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
|
order Order? @relation(fields: [orderId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([customerId, isRead])
|
|
@@index([sentAt])
|
|
}
|
|
|
|
model InternalMessage {
|
|
id String @id @default(cuid())
|
|
senderName String
|
|
subject String
|
|
preview String
|
|
isRead Boolean @default(false)
|
|
sentAt DateTime @default(now())
|
|
}
|
|
|
|
model MeetingInteraction {
|
|
id String @id @default(cuid())
|
|
customerId String
|
|
agentId String
|
|
type InteractionType @default(in_person)
|
|
title String
|
|
description String?
|
|
subject String?
|
|
content String?
|
|
date String
|
|
time String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
customer Customer @relation("MeetingCustomer", fields: [customerId], references: [id], onDelete: Cascade)
|
|
agent Customer @relation("MeetingAgent", fields: [agentId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([customerId])
|
|
@@index([agentId])
|
|
@@index([date])
|
|
}
|
|
|
|
model Reminder {
|
|
id String @id @default(cuid())
|
|
ownerId String
|
|
customerId String
|
|
title String
|
|
description String?
|
|
note String?
|
|
date String
|
|
time String
|
|
interactionType InteractionType @default(note)
|
|
completed Boolean @default(false)
|
|
completedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
owner Customer @relation("ReminderOwner", fields: [ownerId], references: [id], onDelete: Cascade)
|
|
customer Customer @relation("ReminderCustomer", fields: [customerId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([ownerId, completed])
|
|
@@index([customerId])
|
|
@@index([date])
|
|
}
|
|
|
|
model ProductFavorite {
|
|
id String @id @default(cuid())
|
|
customerId String
|
|
productId String
|
|
createdAt DateTime @default(now())
|
|
|
|
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
|
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([customerId, productId])
|
|
@@index([customerId])
|
|
}
|
|
|
|
model Brand {
|
|
id String @id @default(cuid())
|
|
name String
|
|
slug String? @unique
|
|
description String?
|
|
isActive Boolean @default(true)
|
|
displayOrder Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
customers CustomerBrand[]
|
|
}
|
|
|
|
model CustomerBrand {
|
|
customerId String
|
|
brandId String
|
|
createdAt DateTime @default(now())
|
|
|
|
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
|
brand Brand @relation(fields: [brandId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([customerId, brandId])
|
|
@@index([brandId])
|
|
}
|
|
|
|
model CustomerLevelDef {
|
|
id String @id @default(cuid())
|
|
code String @unique
|
|
label String
|
|
sortOrder Int @default(0)
|
|
criteriaJson Json?
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
benefits LevelBenefit[]
|
|
customers Customer[]
|
|
}
|
|
|
|
model LevelBenefit {
|
|
id String @id @default(cuid())
|
|
levelId String
|
|
title String
|
|
description String?
|
|
|
|
level CustomerLevelDef @relation(fields: [levelId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([levelId])
|
|
}
|
|
|
|
model CatalogRequest {
|
|
id String @id @default(cuid())
|
|
customerId String
|
|
status CatalogRequestStatus @default(new)
|
|
note String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
|
items CatalogRequestItem[]
|
|
|
|
@@index([customerId])
|
|
@@index([status])
|
|
}
|
|
|
|
model CatalogRequestItem {
|
|
id String @id @default(cuid())
|
|
requestId String
|
|
productId String
|
|
quantity Int @default(1)
|
|
holderType CatalogItemHolderType @default(warehouse)
|
|
holderCustomerId String?
|
|
|
|
request CatalogRequest @relation(fields: [requestId], references: [id], onDelete: Cascade)
|
|
product Product @relation(fields: [productId], references: [id], onDelete: Restrict)
|
|
movements CatalogItemMovement[]
|
|
|
|
@@index([requestId])
|
|
@@index([productId])
|
|
}
|
|
|
|
model CatalogItemMovement {
|
|
id String @id @default(cuid())
|
|
itemId String
|
|
fromHolder String
|
|
toHolder String
|
|
note String?
|
|
movedAt DateTime @default(now())
|
|
|
|
item CatalogRequestItem @relation(fields: [itemId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([itemId])
|
|
}
|
|
|
|
model StockNotifyRequest {
|
|
id String @id @default(cuid())
|
|
customerId String
|
|
productId String
|
|
active Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
notifiedAt DateTime?
|
|
|
|
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
|
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([customerId, productId])
|
|
@@index([productId, active])
|
|
}
|
|
|
|
model ProductSuggestion {
|
|
id String @id @default(cuid())
|
|
customerId String
|
|
productId String
|
|
note String?
|
|
createdByAdminId String?
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
|
|
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
|
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([customerId, isActive])
|
|
@@index([productId])
|
|
}
|