feat: enhance recipe ingredient model; add raw fields and optional properties for better ingredient handling
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
ALTER TABLE `RecipeIngredient`
|
||||
MODIFY `productId` INTEGER NULL,
|
||||
MODIFY `quantity` DECIMAL(10, 2) NULL,
|
||||
MODIFY `unit` VARCHAR(191) NULL,
|
||||
ADD COLUMN `rawName` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
ADD COLUMN `rawLine` TEXT NULL,
|
||||
ADD COLUMN `matchConfidence` DOUBLE NULL,
|
||||
ADD COLUMN `matchSource` VARCHAR(191) NULL,
|
||||
ADD COLUMN `analysisStatus` VARCHAR(191) NULL;
|
||||
+242
-237
@@ -1,237 +1,242 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "mysql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique
|
||||
email String @unique
|
||||
firstName String?
|
||||
lastName String?
|
||||
passwordHash String
|
||||
role String @default("user")
|
||||
isPremium Boolean @default(false)
|
||||
canShareRecipes Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
userProducts UserProduct[]
|
||||
ownedRecipes Recipe[] @relation("RecipeOwner")
|
||||
sharedRecipes RecipeShare[]
|
||||
ownedProducts Product[]
|
||||
pantryItems PantryItem[]
|
||||
mealPlanEntries MealPlanEntry[]
|
||||
receiptAliases ReceiptAlias[]
|
||||
}
|
||||
|
||||
model Product {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
normalizedName String @unique
|
||||
category String?
|
||||
canonicalName String?
|
||||
isActive Boolean @default(true)
|
||||
status String @default("active")
|
||||
deletedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
inventoryItems InventoryItem[]
|
||||
recipeIngredients RecipeIngredient[]
|
||||
pantryItems PantryItem[]
|
||||
receiptAliases ReceiptAlias[]
|
||||
tags ProductTag[]
|
||||
nutrition Nutrition?
|
||||
ownerId Int
|
||||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
userProducts UserProduct[]
|
||||
categoryId Int?
|
||||
categoryRef Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
||||
isPrivate Boolean @default(false)
|
||||
}
|
||||
|
||||
model Category {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
parentId Int?
|
||||
parent Category? @relation("CategoryTree", fields: [parentId], references: [id], onDelete: SetNull)
|
||||
children Category[] @relation("CategoryTree")
|
||||
products Product[]
|
||||
|
||||
@@unique([name, parentId])
|
||||
@@index([parentId])
|
||||
}
|
||||
|
||||
model UserProduct {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
productId Int
|
||||
note String? @db.Text
|
||||
preferredBrand String?
|
||||
preferredStore String?
|
||||
isPrivate Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([userId, productId])
|
||||
@@index([userId])
|
||||
@@index([productId])
|
||||
}
|
||||
|
||||
model InventoryItem {
|
||||
id Int @id @default(autoincrement())
|
||||
productId Int
|
||||
quantity Decimal @db.Decimal(10, 2)
|
||||
unit String
|
||||
brand String?
|
||||
origin String?
|
||||
receiptName String?
|
||||
location String?
|
||||
purchaseDate DateTime?
|
||||
opened Boolean?
|
||||
suitableFor String?
|
||||
bestBeforeDate DateTime?
|
||||
comment String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
consumptions InventoryConsumption[]
|
||||
|
||||
@@index([productId])
|
||||
}
|
||||
|
||||
model InventoryConsumption {
|
||||
id Int @id @default(autoincrement())
|
||||
inventoryItem InventoryItem @relation(fields: [inventoryItemId], references: [id])
|
||||
inventoryItemId Int
|
||||
amountUsed Decimal @db.Decimal(10, 2)
|
||||
comment String?
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model Recipe {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
description String? @db.Text
|
||||
instructions String? @db.Text
|
||||
imageUrl String?
|
||||
servings Int?
|
||||
isPublic Boolean @default(false)
|
||||
ownerId Int?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
owner User? @relation("RecipeOwner", fields: [ownerId], references: [id], onDelete: SetNull)
|
||||
ingredients RecipeIngredient[]
|
||||
mealPlanEntries MealPlanEntry[]
|
||||
shares RecipeShare[]
|
||||
}
|
||||
|
||||
model RecipeShare {
|
||||
recipeId Int
|
||||
userId Int
|
||||
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([recipeId, userId])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model RecipeIngredient {
|
||||
id Int @id @default(autoincrement())
|
||||
recipe Recipe @relation(fields: [recipeId], references: [id])
|
||||
recipeId Int
|
||||
product Product @relation(fields: [productId], references: [id])
|
||||
productId Int
|
||||
quantity Decimal @db.Decimal(10, 2)
|
||||
unit String
|
||||
note String?
|
||||
alternativeProductIds Json? // [id, id, ...] — alternativa produkter (t.ex. "ris eller couscous")
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model PantryItem {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
productId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([userId, productId])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model ReceiptAlias {
|
||||
id Int @id @default(autoincrement())
|
||||
receiptName String // normaliserat kvittonamn (lowercase, trim)
|
||||
ownerId Int?
|
||||
owner User? @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
isGlobal Boolean @default(false)
|
||||
productId Int
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([receiptName, ownerId, isGlobal])
|
||||
@@index([ownerId])
|
||||
@@index([isGlobal])
|
||||
}
|
||||
|
||||
model MealPlanEntry {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
date DateTime @db.Date
|
||||
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
|
||||
recipeId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
servings Int?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Bara ett recept per dag och anvandare.
|
||||
@@unique([userId, date])
|
||||
@@index([userId])
|
||||
@@index([date])
|
||||
}
|
||||
|
||||
model Tag {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
products ProductTag[]
|
||||
}
|
||||
|
||||
model ProductTag {
|
||||
productId Int
|
||||
tagId Int
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([productId, tagId])
|
||||
@@index([tagId])
|
||||
}
|
||||
|
||||
model Nutrition {
|
||||
id Int @id @default(autoincrement())
|
||||
productId Int @unique
|
||||
calories Float?
|
||||
protein Float?
|
||||
fat Float?
|
||||
carbohydrates Float?
|
||||
salt Float?
|
||||
sugar Float?
|
||||
fiber Float?
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "mysql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique
|
||||
email String @unique
|
||||
firstName String?
|
||||
lastName String?
|
||||
passwordHash String
|
||||
role String @default("user")
|
||||
isPremium Boolean @default(false)
|
||||
canShareRecipes Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
userProducts UserProduct[]
|
||||
ownedRecipes Recipe[] @relation("RecipeOwner")
|
||||
sharedRecipes RecipeShare[]
|
||||
ownedProducts Product[]
|
||||
pantryItems PantryItem[]
|
||||
mealPlanEntries MealPlanEntry[]
|
||||
receiptAliases ReceiptAlias[]
|
||||
}
|
||||
|
||||
model Product {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
normalizedName String @unique
|
||||
category String?
|
||||
canonicalName String?
|
||||
isActive Boolean @default(true)
|
||||
status String @default("active")
|
||||
deletedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
inventoryItems InventoryItem[]
|
||||
recipeIngredients RecipeIngredient[]
|
||||
pantryItems PantryItem[]
|
||||
receiptAliases ReceiptAlias[]
|
||||
tags ProductTag[]
|
||||
nutrition Nutrition?
|
||||
ownerId Int
|
||||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
userProducts UserProduct[]
|
||||
categoryId Int?
|
||||
categoryRef Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
||||
isPrivate Boolean @default(false)
|
||||
}
|
||||
|
||||
model Category {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
parentId Int?
|
||||
parent Category? @relation("CategoryTree", fields: [parentId], references: [id], onDelete: SetNull)
|
||||
children Category[] @relation("CategoryTree")
|
||||
products Product[]
|
||||
|
||||
@@unique([name, parentId])
|
||||
@@index([parentId])
|
||||
}
|
||||
|
||||
model UserProduct {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
productId Int
|
||||
note String? @db.Text
|
||||
preferredBrand String?
|
||||
preferredStore String?
|
||||
isPrivate Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([userId, productId])
|
||||
@@index([userId])
|
||||
@@index([productId])
|
||||
}
|
||||
|
||||
model InventoryItem {
|
||||
id Int @id @default(autoincrement())
|
||||
productId Int
|
||||
quantity Decimal @db.Decimal(10, 2)
|
||||
unit String
|
||||
brand String?
|
||||
origin String?
|
||||
receiptName String?
|
||||
location String?
|
||||
purchaseDate DateTime?
|
||||
opened Boolean?
|
||||
suitableFor String?
|
||||
bestBeforeDate DateTime?
|
||||
comment String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
consumptions InventoryConsumption[]
|
||||
|
||||
@@index([productId])
|
||||
}
|
||||
|
||||
model InventoryConsumption {
|
||||
id Int @id @default(autoincrement())
|
||||
inventoryItem InventoryItem @relation(fields: [inventoryItemId], references: [id])
|
||||
inventoryItemId Int
|
||||
amountUsed Decimal @db.Decimal(10, 2)
|
||||
comment String?
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model Recipe {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
description String? @db.Text
|
||||
instructions String? @db.Text
|
||||
imageUrl String?
|
||||
servings Int?
|
||||
isPublic Boolean @default(false)
|
||||
ownerId Int?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
owner User? @relation("RecipeOwner", fields: [ownerId], references: [id], onDelete: SetNull)
|
||||
ingredients RecipeIngredient[]
|
||||
mealPlanEntries MealPlanEntry[]
|
||||
shares RecipeShare[]
|
||||
}
|
||||
|
||||
model RecipeShare {
|
||||
recipeId Int
|
||||
userId Int
|
||||
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([recipeId, userId])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model RecipeIngredient {
|
||||
id Int @id @default(autoincrement())
|
||||
recipe Recipe @relation(fields: [recipeId], references: [id])
|
||||
recipeId Int
|
||||
product Product? @relation(fields: [productId], references: [id])
|
||||
productId Int?
|
||||
rawName String @default("")
|
||||
rawLine String? @db.Text
|
||||
quantity Decimal? @db.Decimal(10, 2)
|
||||
unit String?
|
||||
note String?
|
||||
alternativeProductIds Json? // [id, id, ...] — alternativa produkter (t.ex. "ris eller couscous")
|
||||
matchConfidence Float?
|
||||
matchSource String?
|
||||
analysisStatus String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model PantryItem {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
productId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([userId, productId])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model ReceiptAlias {
|
||||
id Int @id @default(autoincrement())
|
||||
receiptName String // normaliserat kvittonamn (lowercase, trim)
|
||||
ownerId Int?
|
||||
owner User? @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
isGlobal Boolean @default(false)
|
||||
productId Int
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([receiptName, ownerId, isGlobal])
|
||||
@@index([ownerId])
|
||||
@@index([isGlobal])
|
||||
}
|
||||
|
||||
model MealPlanEntry {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
date DateTime @db.Date
|
||||
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
|
||||
recipeId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
servings Int?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Bara ett recept per dag och anvandare.
|
||||
@@unique([userId, date])
|
||||
@@index([userId])
|
||||
@@index([date])
|
||||
}
|
||||
|
||||
model Tag {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
products ProductTag[]
|
||||
}
|
||||
|
||||
model ProductTag {
|
||||
productId Int
|
||||
tagId Int
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([productId, tagId])
|
||||
@@index([tagId])
|
||||
}
|
||||
|
||||
model Nutrition {
|
||||
id Int @id @default(autoincrement())
|
||||
productId Int @unique
|
||||
calories Float?
|
||||
protein Float?
|
||||
fat Float?
|
||||
carbohydrates Float?
|
||||
salt Float?
|
||||
sugar Float?
|
||||
fiber Float?
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user