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:
@@ -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 })
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user