feat(db): add Prisma schema and global PrismaModule

This commit is contained in:
MastersGame
2026-11-05 23:00:46 +01:00
parent 0d7e7b7f87
commit e6e39b4f80
7 changed files with 271 additions and 4 deletions

216
prisma/schema.prisma Normal file
View File

@@ -0,0 +1,216 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Get a free hosted Postgres database in seconds: `npx create-db`
generator client {
provider = "prisma-client"
output = "./generated/"
}
datasource db {
provider = "postgresql"
}
/// Product category, mirrored from Hiboutik. Categories form a tree (parent / children).
model Category {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
/// Enabled in the Hiboutik POS (category_enabled)
enabled Boolean @default(true)
hiboutikId Int @unique
name String
/// Display order among its siblings (category_position)
position Int @default(0)
/// Displayed on the website (category_enabled_www)
webEnabled Boolean @default(true)
/// Null for a root category
parent Category? @relation("CategoryTree", fields: [parentId], references: [id], onDelete: SetNull)
parentId String?
children Category[] @relation("CategoryTree")
products Product[]
@@map("categories")
}
/// Product brand, mirrored from Hiboutik.
model Brand {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
enabled Boolean @default(true)
hiboutikId Int @unique
name String
products Product[]
@@map("brands")
}
/// Product supplier, mirrored from Hiboutik.
model Supplier {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
enabled Boolean @default(true)
hiboutikId Int @unique
name String
products Product[]
@@map("suppliers")
}
/// Product mirrored from Hiboutik. What is actually sold is one of its versions (sizes).
model Product {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
/// Archived in Hiboutik (product_arch)
archived Boolean @default(false)
barcode String?
description String
/// Null when the product has no discount
discountPrice Decimal? @db.Decimal(10, 2)
/// Displayed on the website (product_display_www)
enabled Boolean @default(true)
/// Sum of the available stock of all its versions
globalStock Int @default(0)
hiboutikId Int @unique
/// Last update in Hiboutik
hiboutikUpdatedAt DateTime?
name String
price Decimal @db.Decimal(10, 2)
brand Brand? @relation(fields: [brandId], references: [id], onDelete: SetNull)
brandId String?
category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
categoryId String?
supplier Supplier? @relation(fields: [supplierId], references: [id], onDelete: SetNull)
supplierId String?
versions ProductVersion[]
@@map("products")
}
/// Sellable version of a product: one per Hiboutik size, or a single one (hiboutikSizeId 0) for a product without sizes.
model ProductVersion {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
/// Not unique: Hiboutik is the source of truth and may hold the same barcode on several products
barcode String?
/// Size-specific discount, null when none
discountPrice Decimal? @db.Decimal(10, 2)
/// Hiboutik size id, 0 for a product without sizes
hiboutikSizeId Int
/// Size-specific price, null to use the product price (copy of the product price for a product without sizes)
price Decimal? @db.Decimal(10, 2)
stock Int @default(0)
/// Size name, or the product name for a product without sizes
versionName String
product Product @relation(fields: [productId], references: [id], onDelete: Cascade, onUpdate: Restrict)
productId String
orderLines OrderLine[]
@@unique([productId, hiboutikSizeId])
@@index([barcode])
@@map("productVersions")
}
/// Customer mirrored from Hiboutik.
model Customer {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
dateOfBirth DateTime?
/// Kept when removed in Hiboutik (an empty Hiboutik email does not erase it)
email String?
firstName String
hiboutikId Int @unique
/// Last update in Hiboutik
hiboutikUpdatedAt DateTime?
lastName String
phone String?
addresses CustomerAddress[]
orders Order[]
@@index([email])
@@map("customers")
}
/// Address of a customer, mirrored from Hiboutik (sync not implemented yet).
model CustomerAddress {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
address String
city String
country String
hiboutikId Int @unique
isDefault Boolean @default(false)
zipCode String
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
customerId String
@@map("customer_addresses")
}
/// Where an order was placed.
enum OrderSource {
STORE // hiboutik
WEB // site
}
/// Order placed in store (Hiboutik sale) or on the website.
model Order {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
/// Null for a website order not (yet) sent to Hiboutik
hiboutikSaleId Int? @unique
orderedAt DateTime
source OrderSource
status String
total Decimal @db.Decimal(10, 2)
/// Null for an anonymous sale or a deleted customer
customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
customerId String?
lines OrderLine[]
@@map("orders")
}
/// Line of an order.
model OrderLine {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
// Snapshot of the product at order time: the catalog can change or be deleted afterwards
barcode String?
hiboutikProductId Int
hiboutikSizeId Int
productName String
quantity Int
unitPrice Decimal @db.Decimal(10, 2)
versionName String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
orderId String
// Link to the current catalog, set to null when the version is deleted
productVersion ProductVersion? @relation(fields: [productVersionId], references: [id], onDelete: SetNull)
productVersionId String?
@@map("order_lines")
}