import type { MedusaRequest, MedusaResponse } from "@medusajs/framework"; import { ContainerRegistrationKeys } from "@medusajs/framework/utils"; import { createInventoryItemsWorkflow, updateInventoryLevelsWorkflow, updateProductVariantsWorkflow, } from "@medusajs/medusa/core-flows"; import { attachInventoryItemToVariantWorkflow } from "../../../workflows/attach-inventory-item-to-variant"; // Panel simplificado de inventario (pensado para un usuario no tecnico, // no reemplaza la vista nativa de Medusa Admin -- ver ADR/memoria del // bug de Firefox Android, tema aparte). Un producto = una variante en // este catalogo real (confirmado en vivo contra /store/products antes // de asumirlo), asi que la tabla trabaja a nivel de producto. const PRODUCT_FIELDS = [ "id", "title", "thumbnail", "status", "variants.id", "variants.sku", "variants.manage_inventory", "variants.inventory_items.inventory_item_id", "variants.inventory_items.inventory.location_levels.id", "variants.inventory_items.inventory.location_levels.location_id", "variants.inventory_items.inventory.location_levels.stocked_quantity", ]; async function defaultLocationId(req: MedusaRequest): Promise { const query = req.scope.resolve(ContainerRegistrationKeys.QUERY); const { data: locations = [] } = await query.graph({ entity: "stock_location", fields: ["id"], }); return locations[0]?.id ?? null; } export async function GET(req: MedusaRequest, res: MedusaResponse) { const query = req.scope.resolve(ContainerRegistrationKeys.QUERY); const locationId = await defaultLocationId(req); const { data: products = [] } = await query.graph({ entity: "product", fields: PRODUCT_FIELDS, }); const rows = products.map((product) => { const variant = product.variants?.[0]; const inventoryItem = variant?.inventory_items?.[0]; const level = inventoryItem?.inventory?.location_levels?.find( (candidate) => candidate?.location_id === locationId, ); return { productId: product.id, variantId: variant?.id ?? null, title: product.title, thumbnail: product.thumbnail, status: product.status, sku: variant?.sku ?? null, managed: variant?.manage_inventory ?? false, quantity: level?.stocked_quantity ?? 0, }; }); res.json({ rows, locationId }); } type PanelUpdateBody = { productId: string; variantId: string; managed?: boolean; quantity?: number; }; export async function POST(req: MedusaRequest, res: MedusaResponse) { const { productId, variantId, managed, quantity } = req.body; if (!productId || !variantId) { res.status(400).json({ error: "productId y variantId son obligatorios" }); return; } const query = req.scope.resolve(ContainerRegistrationKeys.QUERY); const locationId = await defaultLocationId(req); if (!locationId) { res.status(400).json({ error: "No hay una bodega (stock location) configurada" }); return; } const { data: products = [] } = await query.graph({ entity: "product", fields: PRODUCT_FIELDS, filters: { id: productId }, }); const variant = products[0]?.variants?.find((v: { id: string }) => v.id === variantId); const inventoryItem = variant?.inventory_items?.[0]; let inventoryItemId: string | undefined = inventoryItem?.inventory_item_id; if (managed === true && !inventoryItemId) { // Primera vez que se activa el seguimiento para esta variante -- no // trae InventoryItem todavia (confirmado en vivo: togglear // manage_inventory=true en una variante existente NO crea uno solo, // a diferencia de crear un producto nuevo con manage_inventory // desde el inicio). Se crea con el nivel de stock inicial y se // vincula aparte. const { result: items } = await createInventoryItemsWorkflow(req.scope).run({ input: { items: [ { title: variant?.sku ? undefined : products[0]?.title, sku: variant?.sku ?? undefined, location_levels: [{ location_id: locationId, stocked_quantity: quantity ?? 0 }], }, ], }, }); inventoryItemId = items[0]?.id; if (inventoryItemId) { await attachInventoryItemToVariantWorkflow(req.scope).run({ input: { inventoryItemId, variantId }, }); } await updateProductVariantsWorkflow(req.scope).run({ input: { product_variants: [{ id: variantId, manage_inventory: true }] }, }); } else { if (typeof managed === "boolean") { await updateProductVariantsWorkflow(req.scope).run({ input: { product_variants: [{ id: variantId, manage_inventory: managed }] }, }); } if (typeof quantity === "number" && inventoryItemId) { const level = inventoryItem?.inventory?.location_levels?.find( (candidate) => candidate?.location_id === locationId, ); await updateInventoryLevelsWorkflow(req.scope).run({ input: { updates: [ { id: level?.id, inventory_item_id: inventoryItemId, location_id: locationId, stocked_quantity: quantity, }, ], }, }); } } res.json({ ok: true }); }