Files
dinggadingga-backend/prisma/schema.prisma
MastersGame 9e6e5162ad feat(sync): synchronize customer addresses
Mirror the addresses embedded in GET /customer/🆔 per customer, delete
the ones missing from Hiboutik, then upsert each of them (failures logged
and skipped). CustomerAddress gains its own recipient (firstName,
lastName, company, phone) plus state and other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:37:00 +01:00

224 lines
6.4 KiB
Plaintext

// 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. Its recipient (name, company, phone) can differ from the customer.
model CustomerAddress {
id String @id @default(uuid())
updatedAt DateTime @updatedAt
address String
city String
company String?
country String
firstName String
hiboutikId Int @unique
isDefault Boolean @default(false)
lastName String
/// Free complement (building, floor, door code...)
other String?
phone String?
state String?
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")
}