# US-MGN-005-003-001: Crear Movimiento de Stock **RF Asociado:** [RF-MGN-005-003](../../02-modelado/requerimientos-funcionales/mgn-005/RF-MGN-005-003-movimientos-de-stock.md) **Módulo:** MGN-005 - Inventario Básico **Epic:** Movimientos de Stock **Prioridad:** P0 **Story Points:** 5 **Sprint:** Sprint 10 **Estado:** Ready for Development **Fecha:** 2025-11-24 --- ## User Story **Como** usuario de inventario, **Quiero** crear movimientos de stock entre ubicaciones (origen → destino), **Para** registrar transferencias y actualizar el stock en cada ubicación. --- ## Descripción Detallada Un movimiento de stock (stock_move) registra el traslado de productos entre ubicaciones. Tiene tres estados: - **draft:** Borrador, puede editarse - **done:** Validado, actualiza stock en quants - **cancelled:** Cancelado Los movimientos se crean en draft y al validarlos actualizan las cantidades en inventory.stock_quants restando de location_from y sumando a location_to. --- ## Criterios de Aceptación ### Escenario 1: Crear movimiento de stock en draft (Camino Feliz) **Dado que** soy usuario con permiso inventory_user, **Cuando** creo movimiento: product_id=1, location_from_id=10, location_to_id=20, quantity=5, **Entonces** el sistema crea movimiento en estado draft sin actualizar stock. ### Escenario 2: Validar producto existe **Dado que** intento crear movimiento con product_id=999 (no existe), **Cuando** envío el request, **Entonces** el sistema retorna error 404 "Producto no encontrado". ### Escenario 3: Validar ubicaciones existen **Dado que** intento crear movimiento con location_from_id=888 (no existe), **Cuando** envío el request, **Entonces** el sistema retorna error 404 "Ubicación origen no encontrada". ### Escenario 4: Validar cantidad positiva **Dado que** intento crear movimiento con quantity=-5, **Cuando** envío el request, **Entonces** el sistema retorna error 400 "La cantidad debe ser positiva". ### Escenario 5: Crear movimiento con referencia **Dado que** creo movimiento con reference="Transferencia #123", **Cuando** envío el request, **Entonces** el sistema guarda la referencia para trazabilidad. --- ## Reglas de Negocio - **RN-1:** Movimientos se crean en estado draft por defecto. - **RN-2:** Cantidad debe ser mayor a 0. - **RN-3:** location_from y location_to deben ser diferentes. - **RN-4:** Stock se actualiza solo al validar el movimiento (state=done). - **RN-5:** Movimientos draft pueden editarse y eliminarse. - **RN-6:** RLS filtra movimientos por empresa. --- ## Tareas Técnicas ### Backend - [ ] Endpoint: POST /api/v1/inventory/stock-moves - [ ] Endpoint: GET /api/v1/inventory/stock-moves - [ ] Endpoint: GET /api/v1/inventory/stock-moves/:id - [ ] Service: StockMoveService.create(createStockMoveDto) - [ ] DTO: CreateStockMoveDto (product_id, location_from_id, location_to_id, quantity, reference) - [ ] Validar producto existe - [ ] Validar ubicaciones existen - [ ] Validar cantidad > 0 - [ ] Validar ubicaciones diferentes - [ ] Unit tests (>80% coverage) - [ ] Integration tests - [ ] Swagger docs ### Frontend - [ ] Componente: CreateStockMoveForm.tsx - [ ] Página: CreateStockMovePage.tsx (/inventory/stock-moves/create) - [ ] Selector de producto - [ ] Selector de ubicación origen/destino - [ ] Input cantidad con validación - [ ] API client: stockMoveApi.create(data) - [ ] State management: useStockMoveStore - [ ] Component tests - [ ] E2E test: "should create stock move in draft" ### Database - [ ] Tabla: inventory.stock_moves - [ ] Índices: idx_stock_moves_product_id, idx_stock_moves_location_from, idx_stock_moves_location_to, idx_stock_moves_state - [ ] RLS policy: company_isolation_stock_moves --- ## Mockups / Wireframes **Formulario Crear Movimiento:** ``` ┌─────────────────────────────────────────────┐ │ Crear Movimiento de Stock │ ├─────────────────────────────────────────────┤ │ Producto: [Select: Laptop HP ▼] * │ │ │ │ Ubicación Origen: [Select: Zona A ▼] * │ │ Ubicación Destino: [Select: Zona B ▼] * │ │ │ │ Cantidad: [Input: 5] unidades * │ │ Referencia: [Input: Opcional] │ │ │ │ Nota: El movimiento se creará en estado │ │ borrador. Deberá validarse para actualizar │ │ el stock. │ ├─────────────────────────────────────────────┤ │ [Guardar Borrador] [Cancelar] │ └─────────────────────────────────────────────┘ ``` --- ## Casos de Prueba ### Funcionales 1. **TC-001:** Crear movimiento en draft exitosamente 2. **TC-002:** Error por producto inexistente 3. **TC-003:** Error por ubicación inexistente 4. **TC-004:** Error por cantidad negativa o cero 5. **TC-005:** Error por ubicaciones iguales 6. **TC-006:** RLS filtra por empresa ### No Funcionales 1. **Performance:** < 300ms para crear movimiento 2. **Seguridad:** JWT + permiso inventory_user --- ## Dependencias - **US bloqueantes:** - US-MGN-005-001-001 (Crear Producto) - US-MGN-005-002-001 (Gestionar Almacenes/Ubicaciones) - **Módulos requeridos:** MGN-001, MGN-002 - **Datos maestros:** Productos, Ubicaciones --- ## Notas de Implementación - Estado draft no afecta stock_quants - Validación se hace en US separada (US-MGN-005-003-002) - Frontend: Mostrar stock disponible en ubicación origen - Considerar agregar campo date (fecha programada del movimiento) --- ## Estimación Detallada | Tarea | Horas | |-------|-------| | Backend | 2.5 | | Frontend | 2.5 | | Testing | 2 | | Code Review | 1 | | **TOTAL** | **8 horas = 5 SP** | --- ## Definition of Done - [ ] Código implementado según ET - [ ] Tests pasando (>80%) - [ ] Code review aprobado - [ ] Documentación actualizada - [ ] Swagger docs completo - [ ] RLS aplicado - [ ] QA validado - [ ] PO aprobado --- ## Referencias - [RF-MGN-005-003](../../02-modelado/requerimientos-funcionales/mgn-005/RF-MGN-005-003-movimientos-de-stock.md) - [ET Backend](../../02-modelado/especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-003-movimientos-de-stock.md) - [ET Frontend](../../02-modelado/especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-003-movimientos-de-stock.md) - [Traceability](../../02-modelado/trazabilidad/TRACEABILITY-MGN-005.yaml) - [Schema](../../02-modelado/database-design/schemas/inventory-schema-ddl.sql)