← Volver al portfolio

Desarrollo web · Proyecto 02

Gestor de inventario

Aplicación web CRUD y responsive para controlar el material de una pequeña empresa: portátiles, monitores, periféricos y licencias. Búsqueda, filtros, avisos de bajo stock y exportación a CSV, sobre una API REST en PHP y una base de datos MySQL.

Rol
Desarrollo full-stack
Stack
PHP 8 · MySQL · JavaScript
Tipo
Aplicación CRUD

ⓘ Demo con datos ficticios. En el navegador usa localStorage; la carpeta incluye el backend real en PHP/MySQL.

Inventario de equipos

Oficina demo · datos ficticios

Total artículos– 
Valor del inventario–stock × precio
Bajo stock–en o bajo el mínimo
Categorías–con artículos
API GET api.php?recurso=articulos 200
Estado Acciones

Cómo funciona

1

Frontend responsive

HTML, CSS y JavaScript sin frameworks. La tabla se ordena y pagina, los formularios se validan en cliente y en móvil cada fila pasa a ser una tarjeta. Todas las acciones se hacen con fetch contra la API.

2

API REST en PHP

api.php expone GET, POST, PUT, PATCH y DELETE sobre artículos. Valida la entrada, usa PDO con sentencias preparadas y responde JSON con el código HTTP adecuado (201, 404, 409, 422…).

3

Base de datos MySQL

Dos tablas, categorias y articulos, con clave foránea, SKU único e índices para búsqueda y filtros. Los KPIs se calculan con una sola consulta agregada.

Código destacado

// POST: validar, insertar con sentencia preparada y devolver 201
case 'POST':
    [$d, $errores] = validarArticulo(leerJson());
    if ($errores) {
        error(422, 'Revisa los campos marcados.', $errores);
    }
    $st = $pdo->prepare(
        'INSERT INTO articulos (sku, nombre, categoria_id, estado, stock, stock_minimo, precio, ubicacion)
         VALUES (:sku, :nombre, :categoria_id, :estado, :stock, :stock_minimo, :precio, :ubicacion)'
    );
    $st->execute($d);
    responder(201, ['ok' => true, 'data' => buscarArticulo($pdo, (int) $pdo->lastInsertId())]);

// PATCH: ajuste de stock atómico, nunca por debajo de 0
case 'PATCH':
    $delta = filter_var(leerJson()['delta'] ?? null, FILTER_VALIDATE_INT);
    $st = $pdo->prepare(
        'UPDATE articulos SET stock = CAST(stock AS SIGNED) + :delta
          WHERE id = :id AND CAST(stock AS SIGNED) + :delta2 >= 0'
    );
    $st->execute(['delta' => $delta, 'delta2' => $delta, 'id' => $id]);
    if ($st->rowCount() === 0) {
        error(409, 'El stock no puede ser negativo.');
    }
CREATE TABLE articulos (
  id            INT UNSIGNED      NOT NULL AUTO_INCREMENT,
  sku           VARCHAR(20)       NOT NULL,
  nombre        VARCHAR(120)      NOT NULL,
  categoria_id  SMALLINT UNSIGNED NOT NULL,
  estado        ENUM('disponible','asignado','reparacion','baja') NOT NULL DEFAULT 'disponible',
  stock         INT UNSIGNED      NOT NULL DEFAULT 0,
  stock_minimo  INT UNSIGNED      NOT NULL DEFAULT 1,
  precio        DECIMAL(10,2)     NOT NULL DEFAULT 0.00,
  PRIMARY KEY (id),
  UNIQUE KEY uq_articulos_sku (sku),
  KEY idx_articulos_categoria (categoria_id),
  KEY idx_articulos_estado (estado),
  CONSTRAINT fk_articulos_categoria
    FOREIGN KEY (categoria_id) REFERENCES categorias (id)
    ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB;  -- (extracto)

En un hosting estático los archivos .php pueden descargarse en lugar de mostrarse; para ejecutarlos hace falta PHP 8.1+ y MySQL (ver README).