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

This commit is contained in:
Nils-Johan Gynther
2026-05-06 07:25:42 +02:00
parent 612fcddb47
commit e4f201ea36
9 changed files with 349 additions and 267 deletions
+25 -6
View File
@@ -12,20 +12,39 @@ import {
import { Type } from 'class-transformer';
class CreateRecipeIngredientDto {
@IsOptional()
@IsInt()
productId!: number;
@IsNumber()
@Min(0)
quantity!: number;
productId?: number;
@IsString()
unit!: string;
rawName!: string;
@IsOptional()
@IsString()
rawLine?: string;
@IsOptional()
@IsNumber()
@Min(0)
quantity?: number;
@IsOptional()
@IsString()
unit?: string;
@IsOptional()
@IsString()
note?: string;
@IsOptional()
@IsNumber()
@Min(0)
matchConfidence?: number;
@IsOptional()
@IsString()
matchSource?: string;
@IsOptional()
@IsArray()
@IsInt({ each: true })
+43 -8
View File
@@ -113,6 +113,24 @@ export class RecipesService {
const ingredientPreviews = await Promise.all(
recipe.ingredients.map(async (ingredient: any) => {
if (!ingredient.productId || !ingredient.product) {
return {
ingredientId: ingredient.id,
productId: null,
productName: ingredient.rawName || 'Okänd ingrediens',
requiredQuantity: Number(ingredient.quantity ?? 0),
requiredUnit: ingredient.unit || '',
note: ingredient.note,
availableQuantity: 0,
availableUnit: ingredient.unit || '',
matchingInventoryItems: [],
otherInventoryItems: [],
status: 'missing' as const,
fromPantry: false,
missingQuantity: Number(ingredient.quantity ?? 0),
};
}
// Täcks ingrediensen av pantry (inkl. alternativ)?
const coveredByPantry =
pantryProductIds.has(ingredient.productId) ||
@@ -313,7 +331,11 @@ export class RecipesService {
await this.assertAndClaimRecipeOwner(existingRecipe, userId);
// Validera att alla produkter är aktiva
await this.assertProductsActive(updateRecipeDto.ingredients.map((i) => i.productId));
await this.assertProductsActive(
updateRecipeDto.ingredients
.map((i) => i.productId)
.filter((id): id is number => typeof id === 'number'),
);
// Transaktionsblock: ta bort gamla + skapa nya ingredienser atomärt
const recipe = await this.prisma.$transaction(async (tx) => {
@@ -329,11 +351,15 @@ export class RecipesService {
...(updateRecipeDto.imageUrl !== undefined && { imageUrl: updateRecipeDto.imageUrl || null }),
ingredients: {
create: updateRecipeDto.ingredients.map((ingredient) => ({
productId: ingredient.productId,
quantity: ingredient.quantity,
unit: ingredient.unit,
productId: ingredient.productId ?? null,
rawName: ingredient.rawName,
rawLine: ingredient.rawLine ?? null,
quantity: ingredient.quantity ?? null,
unit: ingredient.unit?.trim() ? ingredient.unit : null,
note: ingredient.note || null,
alternativeProductIds: ingredient.alternativeProductIds ?? [],
matchConfidence: ingredient.matchConfidence ?? null,
matchSource: ingredient.matchSource ?? null,
})),
},
},
@@ -462,7 +488,11 @@ export class RecipesService {
async create(createRecipeDto: CreateRecipeDto, userId: number) {
// Validera att alla produkter är aktiva
await this.assertProductsActive(createRecipeDto.ingredients.map((i) => i.productId));
await this.assertProductsActive(
createRecipeDto.ingredients
.map((i) => i.productId)
.filter((id): id is number => typeof id === 'number'),
);
this.logger.log(
`[create] Incoming imageUrl from client: ${createRecipeDto.imageUrl ?? 'null'}`,
@@ -497,11 +527,15 @@ export class RecipesService {
isPublic: false,
ingredients: {
create: createRecipeDto.ingredients.map((ingredient) => ({
productId: ingredient.productId,
quantity: ingredient.quantity,
unit: ingredient.unit,
productId: ingredient.productId ?? null,
rawName: ingredient.rawName,
rawLine: ingredient.rawLine ?? null,
quantity: ingredient.quantity ?? null,
unit: ingredient.unit?.trim() ? ingredient.unit : null,
note: ingredient.note || null,
alternativeProductIds: ingredient.alternativeProductIds ?? [],
matchConfidence: ingredient.matchConfidence ?? null,
matchSource: ingredient.matchSource ?? null,
})),
},
},
@@ -745,6 +779,7 @@ Regler:
return {
rawName: ingredient.rawName,
rawLine: ingredient.rawName,
alternatives: ingredient.alternatives ?? [],
quantity: ingredient.quantity,
unit: ingredient.unit,