# MODELO DE DOMINIO: Inventario y Compras **Módulos:** MGN-005 (Inventario), MGN-006 (Compras) **Fecha:** 2025-11-24 **Referencia Odoo:** stock, purchase **Referencia Gamilit:** inventory_management, purchasing_management --- ## Diagrama de Entidades (Texto UML) ``` [Product] - id: UUID (PK) - tenant_id: UUID (FK) - name: String - code: String - barcode: String - product_type: ENUM (storable, consumable, service) - category_id: UUID (FK) - uom_id: UUID (FK) - cost_price: Decimal - sale_price: Decimal 1 <----> * [StockMove] 1 <----> * [PurchaseOrderLine] [Warehouse] - id: UUID (PK) - tenant_id: UUID (FK) - company_id: UUID (FK) - name: String - code: String 1 <----> * [Location] [Location] - id: UUID (PK) - warehouse_id: UUID (FK) - name: String - parent_id: UUID (FK self) - location_type: ENUM (physical, virtual, supplier, customer) 1 <----> * [StockQuant] 1 <----> * [StockMove] (origen/destino) [StockQuant] - id: UUID (PK) - product_id: UUID (FK) - location_id: UUID (FK) - quantity: Decimal - lot_id: UUID (FK) - cost: Decimal [StockMove] - id: UUID (PK) - tenant_id: UUID (FK) - product_id: UUID (FK) - location_src_id: UUID (FK) - location_dest_id: UUID (FK) - quantity: Decimal - uom_id: UUID (FK) - status: ENUM (draft, confirmed, done, cancelled) - picking_id: UUID (FK) [Picking] - id: UUID (PK) - tenant_id: UUID (FK) - company_id: UUID (FK) - name: String - picking_type: ENUM (incoming, outgoing, internal) - partner_id: UUID (FK) - date: Date - status: ENUM (draft, ready, done, cancelled) 1 <----> * [StockMove] [Lot] - id: UUID (PK) - product_id: UUID (FK) - name: String - lot_date: Date - expiration_date: Date [PurchaseOrder] - id: UUID (PK) - tenant_id: UUID (FK) - company_id: UUID (FK) - partner_id: UUID (FK) - name: String - order_date: Date - expected_date: Date - status: ENUM (draft, confirmed, received, billed, cancelled) - amount_total: Decimal 1 <----> * [PurchaseOrderLine] 1 <----> * [Picking] [PurchaseOrderLine] - id: UUID (PK) - order_id: UUID (FK) - product_id: UUID (FK) - description: String - quantity: Decimal - price_unit: Decimal - tax_ids: UUID[] - subtotal: Decimal - total: Decimal - analytic_account_id: UUID (FK) ``` ## Entidades Principales ### 1. Product (Producto) **Descripción:** Producto almacenable, consumible o servicio. **Atributos:** - `id`: UUID - `name`: Nombre del producto - `code`: Código interno - `barcode`: Código de barras - `product_type`: storable, consumable, service - `category_id`: Categoría - `uom_id`: Unidad de medida - `cost_price`: Precio de costo - `sale_price`: Precio de venta **Relaciones:** - 1 Product → N StockMoves - 1 Product → N PurchaseOrderLines - 1 Product → N StockQuants **Patrón Odoo:** product.product + product.template **Tipos:** - storable: Productos físicos con inventario - consumable: Productos que no se trackean (ej: tornillos) - service: Servicios (no tienen stock) ### 2. Warehouse (Almacén) **Descripción:** Almacén físico de la empresa. **Atributos:** - `id`: UUID - `company_id`: Empresa propietaria - `name`: Nombre del almacén - `code`: Código (ej: "WH-MEX") **Relaciones:** - 1 Warehouse → N Locations **Patrón Odoo:** stock.warehouse **Almacenes típicos:** - Almacén Principal - Almacén de Tránsito - Almacén de Obra (construcción) ### 3. Location (Ubicación) **Descripción:** Ubicación física o virtual dentro de almacén. **Atributos:** - `id`: UUID - `warehouse_id`: Almacén propietario - `name`: Nombre de ubicación - `parent_id`: Ubicación padre (jerarquía) - `location_type`: physical, virtual, supplier, customer **Relaciones:** - 1 Location → N StockQuants - 1 Location → N StockMoves (origen/destino) **Patrón Odoo:** stock.location **Tipos:** - physical: Ubicaciones físicas - virtual: Ubicaciones virtuales (proveedores, clientes, producción) - supplier: Stock de proveedores - customer: Stock de clientes ### 4. StockQuant (Cantidad en Stock) **Descripción:** Cantidad de producto en ubicación específica. **Atributos:** - `id`: UUID - `product_id`: Producto - `location_id`: Ubicación - `quantity`: Cantidad disponible - `lot_id`: Lote (opcional) - `cost`: Costo unitario **Relaciones:** - N StockQuants → 1 Product - N StockQuants → 1 Location **Patrón Odoo:** stock.quant **Nota:** Se actualiza automáticamente con cada movimiento ### 5. StockMove (Movimiento de Inventario) **Descripción:** Movimiento de stock de una ubicación a otra. **Atributos:** - `id`: UUID - `product_id`: Producto movido - `location_src_id`: Ubicación origen - `location_dest_id`: Ubicación destino - `quantity`: Cantidad movida - `status`: draft, confirmed, done, cancelled - `picking_id`: Picking asociado **Relaciones:** - N StockMoves → 1 Product - N StockMoves → 1 Picking **Patrón Odoo:** stock.move **Flujo:** draft → confirmed → done ### 6. Picking (Albarán) **Descripción:** Agrupación de movimientos de stock. **Atributos:** - `id`: UUID - `name`: Número de albarán - `picking_type`: incoming (recepción), outgoing (entrega), internal (transferencia) - `partner_id`: Partner asociado - `date`: Fecha - `status`: draft, ready, done, cancelled **Relaciones:** - 1 Picking → N StockMoves **Patrón Odoo:** stock.picking **Tipos:** - incoming: Recepción de proveedores - outgoing: Entrega a clientes - internal: Transferencias internas ### 7. Lot (Lote) **Descripción:** Lote de producción o número de serie. **Atributos:** - `id`: UUID - `product_id`: Producto - `name`: Número de lote/serie - `lot_date`: Fecha de producción - `expiration_date`: Fecha de caducidad **Relaciones:** - N Lots → 1 Product **Patrón Odoo:** stock.production.lot **Uso:** Trazabilidad completa ### 8. PurchaseOrder (Orden de Compra) **Descripción:** Orden de compra a proveedor. **Atributos:** - `id`: UUID - `partner_id`: Proveedor - `name`: Número de orden - `order_date`: Fecha de orden - `expected_date`: Fecha esperada de entrega - `status`: draft, confirmed, received, billed, cancelled - `amount_total`: Monto total **Relaciones:** - 1 PurchaseOrder → N PurchaseOrderLines - 1 PurchaseOrder → N Pickings (recepciones) **Patrón Odoo:** purchase.order **Flujo:** draft → confirmed → received → billed ### 9. PurchaseOrderLine (Línea de Orden de Compra) **Descripción:** Línea de producto en orden de compra. **Atributos:** - `id`: UUID - `order_id`: Orden propietaria - `product_id`: Producto - `description`: Descripción - `quantity`: Cantidad ordenada - `price_unit`: Precio unitario - `tax_ids`: Impuestos - `analytic_account_id`: Cuenta analítica (opcional) **Relaciones:** - N PurchaseOrderLines → 1 PurchaseOrder **Patrón Odoo:** purchase.order.line ## Reglas de Negocio ### RN-INV-001: Movimientos Atómicos - TODO movimiento de stock debe tener origen y destino - Cantidad debe ser > 0 - Producto debe ser tipo 'storable' para afectar stock ### RN-INV-002: Actualización Automática de Quants - Al confirmar StockMove (status=done), actualizar StockQuants - Decrementar en location_src - Incrementar en location_dest ### RN-INV-003: Valoración de Inventario - Método FIFO por defecto - Calcular costo promedio automáticamente - Generar asientos contables si valoración automática activa ### RN-INV-004: Trazabilidad - Productos con tracking='lot' requieren lot_id en movimientos - Productos con tracking='serial' requieren quantity=1 por movimiento ### RN-INV-005: Integración Compras-Inventario - Al confirmar PO, generar Picking automáticamente - Al validar Picking, marcar PO como 'received' ### RN-INV-006: 3-Way Match - Validar: PO quantity = Picking quantity = Invoice quantity - Permitir tolerancias configurables (ej: +/- 5%) ## Casos de Uso Principales 1. **UC-INV-001:** Usuario crea producto almacenable 2. **UC-INV-002:** Usuario crea almacén con ubicaciones 3. **UC-INV-003:** Usuario realiza transferencia interna 4. **UC-INV-004:** Usuario crea orden de compra 5. **UC-INV-005:** Sistema genera picking al confirmar PO 6. **UC-INV-006:** Usuario valida recepción de productos 7. **UC-INV-007:** Sistema actualiza stock automáticamente 8. **UC-INV-008:** Usuario consulta inventario por ubicación ## Validaciones y Constraints ```sql -- Product code único por tenant UNIQUE (tenant_id, code) -- Cantidad > 0 en movimientos CHECK (quantity > 0) -- Ubicaciones no pueden ser su propio padre CHECK (parent_id != id) -- Status transitions válidos -- (state machine) ``` ## Índices Requeridos ```sql CREATE INDEX idx_products_code ON inventory.products(code); CREATE INDEX idx_products_barcode ON inventory.products(barcode); CREATE INDEX idx_stock_quants_product_id ON inventory.stock_quants(product_id); CREATE INDEX idx_stock_quants_location_id ON inventory.stock_quants(location_id); CREATE INDEX idx_stock_moves_product_id ON inventory.stock_moves(product_id); CREATE INDEX idx_stock_moves_picking_id ON inventory.stock_moves(picking_id); CREATE INDEX idx_purchase_orders_partner_id ON purchase.purchase_orders(partner_id); CREATE INDEX idx_purchase_orders_status ON purchase.purchase_orders(status); ``` ## Referencias - [ALCANCE-POR-MODULO.md - MGN-005](../../01-definicion-modulos/ALCANCE-POR-MODULO.md#mgn-005) - [ALCANCE-POR-MODULO.md - MGN-006](../../01-definicion-modulos/ALCANCE-POR-MODULO.md#mgn-006) - [ADR-007: Database Design](../../adr/ADR-007-database-design.md)