Files
Cibello-app/packages/validation/src/inventory.ts
T
Claude 3942890d6f fix(matlager): stada upp – flytta lager till egen vy, gruppera per plats, fixa limit-bugg
- BUGG: kitchen hamtade ?limit=500 men schemat tillat max 200 -> 400-fel nar man
  gick in i matlager / tryckte "+N till". Hojer inventoryQuerySchema.limit till 500.
- Hem: tar bort den roriga inline-listan (matlager lag kvar under Hemma). Ersatts
  med ett "Matlager"-kort med genvagsknappar per plats (Kyl/Frys/Skafferi) + "Allt".
- Matlager-skarmen: grupperad per plats med rubrik + antal, sorterad pa namn,
  plats-filter via chips ELLER djuplank (?loc=), sok, markera flera, massradering.
- Byter skarmtitel "Ditt kok" -> "Matlager".
- Fixar aven en latent hooks-ordningsbugg i home (useFocusEffect efter early return).
2026-08-18 16:02:57 +00:00

82 lines
3.5 KiB
TypeScript

import { z } from "zod";
import { DATE_KINDS, INVENTORY_SOURCES, INVENTORY_TRANSACTION_TYPES } from "@app/shared-types";
import { dateStringSchema, quantitySchema, unitSchema, uuidSchema } from "./common.js";
export const createInventoryItemInputSchema = z.object({
canonicalIngredientId: z.string().max(80).optional(),
productId: uuidSchema.optional(),
displayName: z.string().min(1).max(120).trim(),
brand: z.string().max(80).optional(),
quantity: quantitySchema,
unit: unitSchema,
storageLocationId: uuidSchema,
sublocation: z.string().max(60).optional(),
purchasedAt: dateStringSchema.optional(),
openedAt: dateStringSchema.optional(),
bestBeforeDate: dateStringSchema.optional(),
useByDate: dateStringSchema.optional(),
dateKind: z.enum(DATE_KINDS).optional(),
frozenAt: dateStringSchema.optional(),
priceMinor: z.number().int().min(0).max(10_000_000).optional(),
source: z.enum(INVENTORY_SOURCES).default("manual_search"),
});
export type CreateInventoryItemInput = z.infer<typeof createInventoryItemInputSchema>;
export const updateInventoryItemInputSchema = createInventoryItemInputSchema.partial().extend({
verifiedByUser: z.boolean().optional(),
});
export type UpdateInventoryItemInput = z.infer<typeof updateInventoryItemInputSchema>;
/** Manuell lagertransaktion, t.ex. "använde 300 g" eller "slängde resten". */
export const inventoryTransactionInputSchema = z.object({
type: z.enum(INVENTORY_TRANSACTION_TYPES),
quantityDelta: z.number().refine((v) => v !== 0, "Delta får inte vara 0"),
note: z.string().max(200).optional(),
});
export type InventoryTransactionInput = z.infer<typeof inventoryTransactionInputSchema>;
export const inventoryQuerySchema = z.object({
storageLocationId: uuidSchema.optional(),
expiryStatus: z.enum(["fresh", "use_soon", "expiring", "expired", "unknown"]).optional(),
search: z.string().max(80).optional(),
limit: z.coerce.number().int().min(1).max(500).default(100),
offset: z.coerce.number().int().min(0).default(0),
});
export type InventoryQuery = z.infer<typeof inventoryQuerySchema>;
/** Naturligt-språklig fritextsökning i hushållslagret (spec §8). */
export const inventoryNaturalSearchQuerySchema = z.object({
q: z.string().min(1).max(80).trim(),
languageTag: z.string().min(2).max(35).default("sv-SE"),
limit: z.coerce.number().int().min(1).max(20).default(5),
});
export type InventoryNaturalSearchQuery = z.infer<typeof inventoryNaturalSearchQuerySchema>;
export const reconciliationStartInputSchema = z.object({
maxItems: z.coerce.number().int().min(1).max(50).optional(),
});
export type ReconciliationStartInput = z.infer<typeof reconciliationStartInputSchema>;
export const reconciliationResolveInputSchema = z.object({
action: z.enum(["exists", "depleted", "uncertain"]),
quantity: z.number().min(0).optional(),
note: z.string().max(200).optional(),
});
export type ReconciliationResolveInput = z.infer<typeof reconciliationResolveInputSchema>;
export const scanDiffApplyInputSchema = z.object({
idempotencyKey: z.string().max(64).optional(),
rows: z.array(
z.object({
kind: z.enum(["new_item", "moved", "quantity_changed", "depleted"]),
previousItemId: z.string().uuid().optional(),
displayName: z.string().min(1).max(120),
unit: z.string().max(20),
newQuantity: z.number().min(0).optional(),
newLocationId: z.string().uuid().optional(),
confidence: z.number().min(0).max(1),
}),
),
});
export type ScanDiffApplyInput = z.infer<typeof scanDiffApplyInputSchema>;