Volver al curso

Python Desde Cero

leccion
17 / 21
beginner
20 horas
Programacion Orientada a Objetos

Proyecto OOP: Sistema de Inventario en Python

Lectura
30 min~4 min lectura

Proyecto OOP: Sistema de Inventario en Python

En esta leccion aplicaremos todos los conceptos de OOP para construir un sistema de inventario completo. Es el tipo de proyecto que podrias mostrar en un portfolio o entrevista.

Diseno del sistema
  • Producto: clase base abstracta
  • ProductoPerecible y ProductoElectronico: clases especializadas
  • Inventario: gestiona la coleccion
  • Reporte: genera informes
Clase base: Producto
from datetime import date
from abc import ABC, abstractmethod

class Producto(ABC):
    _contador_ids = 0
    
    def __init__(self, nombre, precio, cantidad, categoria):
        Producto._contador_ids += 1
        self.id = Producto._contador_ids
        self.nombre = nombre
        self._precio = precio
        self.cantidad = cantidad
        self.categoria = categoria
        self.fecha_ingreso = date.today()
    
    @property
    def precio(self):
        return self._precio
    
    @precio.setter
    def precio(self, nuevo):
        if nuevo < 0:
            raise ValueError('El precio no puede ser negativo')
        self._precio = nuevo
    
    @property
    def valor_total(self):
        return self._precio * self.cantidad
    
    @abstractmethod
    def es_vendible(self):
        pass
    
    @abstractmethod
    def info_adicional(self):
        pass
    
    def __str__(self):
        estado = 'Disponible' if self.es_vendible() else 'No disponible'
        return f'[{self.id}] {self.nombre} | {self._precio} | Stock: {self.cantidad} | {estado}'
    
    def __lt__(self, other):
        return self._precio < other._precio
Clases especializadas
class ProductoPerecible(Producto):
    def __init__(self, nombre, precio, cantidad, categoria, fecha_vencimiento):
        super().__init__(nombre, precio, cantidad, categoria)
        self.fecha_vencimiento = fecha_vencimiento
    
    @property
    def dias_para_vencer(self):
        return (self.fecha_vencimiento - date.today()).days
    
    def es_vendible(self):
        return self.cantidad > 0 and self.dias_para_vencer > 0
    
    def info_adicional(self):
        dias = self.dias_para_vencer
        if dias < 0:
            return f'VENCIDO hace {abs(dias)} dias'
        elif dias <= 7:
            return f'URGENTE: Vence en {dias} dias'
        return f'Vence en {dias} dias ({self.fecha_vencimiento})'


class ProductoElectronico(Producto):
    def __init__(self, nombre, precio, cantidad, categoria, marca, garantia_meses):
        super().__init__(nombre, precio, cantidad, categoria)
        self.marca = marca
        self.garantia_meses = garantia_meses
    
    def es_vendible(self):
        return self.cantidad > 0
    
    def info_adicional(self):
        return f'Marca: {self.marca} | Garantia: {self.garantia_meses} meses'


class ProductoGeneral(Producto):
    def es_vendible(self):
        return self.cantidad > 0
    
    def info_adicional(self):
        return f'Categoria: {self.categoria}'
La clase Inventario
class Inventario:
    def __init__(self, nombre):
        self.nombre = nombre
        self._productos = {}
    
    def agregar(self, producto):
        if producto.id in self._productos:
            raise ValueError(f'ID {producto.id} ya existe')
        self._productos[producto.id] = producto
        print(f'Agregado: {producto.nombre}')
    
    def buscar_id(self, id_producto):
        return self._productos.get(id_producto)
    
    def buscar_nombre(self, nombre):
        return [p for p in self._productos.values() if nombre.lower() in p.nombre.lower()]
    
    def vender(self, id_producto, cantidad):
        p = self.buscar_id(id_producto)
        if not p:
            raise ValueError('Producto no encontrado')
        if not p.es_vendible():
            raise ValueError(f'{p.nombre} no disponible')
        if cantidad > p.cantidad:
            raise ValueError(f'Stock insuficiente: {p.cantidad}')
        p.cantidad -= cantidad
        total = p.precio * cantidad
        print(f'Venta: {cantidad}x {p.nombre} = {total}')
        return total
    
    def reponer(self, id_producto, cantidad):
        p = self.buscar_id(id_producto)
        if not p:
            raise ValueError('Producto no encontrado')
        p.cantidad += cantidad
        print(f'Reposicion: +{cantidad} de {p.nombre}')
    
    def bajo_stock(self, minimo=5):
        return [p for p in self._productos.values() if p.cantidad <= minimo]
    
    def proximos_vencer(self, dias=7):
        return [
            p for p in self._productos.values()
            if isinstance(p, ProductoPerecible) and 0 < p.dias_para_vencer <= dias
        ]
    
    def valor_total(self):
        return sum(p.valor_total for p in self._productos.values())
    
    def __len__(self):
        return len(self._productos)
    
    def __iter__(self):
        return iter(self._productos.values())
Clase Reporte
class Reporte:
    @staticmethod
    def resumen(inventario):
        print(f'REPORTE: {inventario.nombre}')
        print('=' * 50)
        print(f'Total productos: {len(inventario)}')
        print(f'Valor total: {inventario.valor_total():.2f}')
        
        bajos = inventario.bajo_stock()
        if bajos:
            print(f'ALERTA bajo stock ({len(bajos)}): ')
            for p in bajos:
                print(f'  - {p.nombre}: {p.cantidad}')
        
        vencen = inventario.proximos_vencer()
        if vencen:
            print(f'ALERTA proximos a vencer ({len(vencen)}): ')
            for p in vencen:
                print(f'  - {p.nombre}: {p.info_adicional()}')
        
        print('Todos los productos:')
        for producto in sorted(inventario):
            print(f'  {producto}')
            print(f'    {producto.info_adicional()}')
Usando el sistema
from datetime import timedelta

tienda = Inventario('TechStore')

tienda.agregar(ProductoElectronico('Laptop Gaming', 1200, 5, 'PC', 'Asus', 24))
tienda.agregar(ProductoElectronico('Mouse', 35, 50, 'Accesorios', 'Logitech', 12))
tienda.agregar(ProductoElectronico('Monitor 4K', 450, 3, 'Monitores', 'Samsung', 36))
tienda.agregar(ProductoPerecible('Bateria Alcalina', 5.99, 100, 'Accesorios', date.today() + timedelta(365)))
tienda.agregar(ProductoPerecible('Bateria Recargable', 15.99, 4, 'Accesorios', date.today() + timedelta(5)))

print('--- Procesando ventas ---')
tienda.vender(1, 2)
tienda.vender(2, 10)

try:
    tienda.vender(3, 10)  # Solo hay 3
except ValueError as e:
    print(f'Error: {e}')

tienda.reponer(3, 5)

Reporte.resumen(tienda)

resultados = tienda.buscar_nombre('bateria')
print('Busqueda bateria:')
for p in resultados:
    print(f'  {p}')
💡 Concepto Clave

Revisemos los puntos más importantes de esta lección antes de continuar.

Resumen del modulo OOP

Aprendiste:

  1. Clases y objetos: plantillas e instancias
  2. Herencia: reutilizar y extender codigo
  3. Polimorfismo: mismo metodo, diferentes comportamientos
  4. Metodos especiales: integracion con Python
  5. Encapsulacion: proteger datos
  6. Clases abstractas: contratos para subclases
  7. Propiedades: control sobre atributos

En el proximo y ultimo modulo conectaremos Python con el mundo real: archivos, APIs e internet.

🧠 Pon a prueba tu conocimiento
¿Cuál es el aspecto más importante que aprendiste en esta lección?
  • Comprendo el concepto principal y puedo explicarlo con mis palabras
  • Entiendo cómo aplicarlo en mi situación específica
  • Necesito repasar algunas partes antes de continuar
  • Quiero ver más ejemplos prácticos del tema
✅ ¡Excelente! Continúa con la siguiente lección para profundizar más.