3c-fix: partial consumption now deducts eaten + leftovers
- consumptionPortions = min(planned, actualPortionsEaten + leftoverEstimatePortions). Meal boxes are a subset of leftovers; raw inventory is only retained for portions that were never cooked. - Added validation leftoverEstimatePortions >= mealBoxPortions with localized 400 error (cooked.leftoverLessThanBox) in server i18n + all 12 locales. - Removed unused COOKING_SESSION_STATUSES / MEAL_BOX_STATUSES imports from apps/api/src/lib/cooking.ts. - Hardened test cleanup to delete mealBoxes/meals/recipeCooks/cookingSessions by household before dropping storageLocations. - New regression tests: meal-box double-counting, eaten+leftover split, and leftover < box rejection. - Updated FAS3-COOKING-SESSIONS-AUDIT.md with corrected physics semantics.
This commit is contained in:
@@ -9,11 +9,7 @@ import {
|
||||
} from "@app/inventory-engine";
|
||||
import { scaleNutrition } from "@app/nutrition-engine";
|
||||
import type { Unit } from "@app/shared-types";
|
||||
import {
|
||||
COOKING_SESSION_STATUSES,
|
||||
MEAL_BOX_STATUSES,
|
||||
MEAL_TYPES,
|
||||
} from "@app/shared-types";
|
||||
import { MEAL_TYPES } from "@app/shared-types";
|
||||
import {
|
||||
cookingSessionStarted,
|
||||
cookingSessionCompleted,
|
||||
@@ -76,6 +72,11 @@ export async function completeCookingSession(
|
||||
throw errors.badRequest(t("cooked.portionsSumExceedsPlanned", languageTag));
|
||||
}
|
||||
|
||||
if (leftoverEstimatePortions < mealBoxPortions) {
|
||||
const languageTag = await userLanguageTag(app.db, userId);
|
||||
throw errors.badRequest(t("cooked.leftoverLessThanBox", languageTag));
|
||||
}
|
||||
|
||||
if (options.emitStartedEvent && session.status === "started") {
|
||||
await trackProductAnalytics(
|
||||
app.db,
|
||||
@@ -204,10 +205,15 @@ export async function completeCookingSessionCore(
|
||||
// 1. FEFO-avdrag
|
||||
const deductions: Array<{ itemId: string; quantity: number; unit: string; name: string }> = [];
|
||||
if (input.deductInventory !== false) {
|
||||
// Partiell förbrukning (Fas 3 §6.3): om actualPortionsEaten anges drar vi
|
||||
// endast råvaror för de faktiskt ätna portionerna. Rester/överskott stannar
|
||||
// kvar som råvara i lagret tills de förbrukas på annat sätt.
|
||||
const consumptionPortions = input.actualPortionsEaten ?? portionsCooked;
|
||||
// Partiell förbrukning (Fas 3 §6.3): råvaror dras för de tillagade
|
||||
// portionerna = ätna + rester (inklusive matlådor). Endast portioner som
|
||||
// aldrig tillagats stannar kvar som råvara i lagret.
|
||||
const actualPortionsEaten = input.actualPortionsEaten ?? portionsCooked;
|
||||
const leftoverEstimatePortions = input.leftoverEstimatePortions ?? 0;
|
||||
const consumptionPortions = Math.min(
|
||||
portionsCooked,
|
||||
actualPortionsEaten + leftoverEstimatePortions,
|
||||
);
|
||||
const factor = consumptionPortions / recipe.portions;
|
||||
const overrides = new Map(input.inventoryOverrides?.map((o) => [o.canonicalIngredientId, o]) ?? []);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user