Skip to main content

Overview

The Inventory Management module provides complete control over product catalog, stock levels, pricing, and profit margins. It integrates seamlessly with the POS system for automatic stock deduction.

Key Features

Product Catalog

Manage products, parts, and services with detailed information and categorization

Barcode Integration

Assign and manage unique barcodes for quick scanning at point of sale

Stock Tracking

Real-time inventory levels with minimum stock alerts

Profit Margins

Automatic margin calculation between purchase and sale prices

Quick Lookup

“Modify by Code” feature for rapid product updates via barcode scanner

Active/Inactive

Control product visibility in POS without deleting records

Product Data Structure

interface Producto {
  id: string;
  nombre: string;
  codigo: string; // Barcode
  categoria: string;
  tipo: "producto" | "refaccion" | "servicio";
  precioCompra: number;
  precioVenta: number;
  stock: number;
  stockMinimo: number;
  compatible: string; // Compatible devices
  generaPuntos: boolean;
  activo: boolean;
  createdAt: Timestamp;
  updatedAt: Timestamp;
}

Field Descriptions

FieldRequiredDescription
nombreYesProduct name displayed in POS
codigoYesUnique barcode identifier
categoriaNoProduct category (e.g., “RAM”, “Discos”)
tipoYesProduct type: product, part, or service
precioCompraNoPurchase cost (for margin calculation)
precioVentaYesSelling price
stockYesCurrent inventory quantity
stockMinimoNoAlert threshold for low stock
compatibleNoDevice compatibility notes
generaPuntosYesWhether purchase earns loyalty points
activoYesProduct availability in POS

Product List Interface

The main inventory page shows all products in a comprehensive table:
// Component: src/pages/productos.jsx
const [productos, setProductos] = useState([]);

useEffect(() => {
  cargarProductos();
}, []);

const cargarProductos = async () => {
  const data = await obtenerProductos();
  setProductos(data);
};

Table Columns

ColumnDisplays
ProductoProduct name
CodigoBarcode number
CategoriaProduct category
CompraPurchase price
VentaSale price
MargenCalculated profit percentage
StockCurrent inventory
EstadoActive/Inactive badge
AccionesEdit and Delete buttons

Creating Products

Standard Creation Flow

  1. Click ”+ Nuevo Producto” button
  2. Fill in the product form:
const productoVacio = {
  nombre: "",
  codigo: "",
  categoria: "",
  tipo: "producto",
  precioCompra: "",
  precioVenta: "",
  stock: "",
  stockMinimo: "",
  compatible: "",
  generaPuntos: true,
  activo: true
};
  1. System validates required fields
  2. Duplicate code check is performed
  3. Product is saved to Firestore
The barcode field (codigo) must be unique across all products. The system will alert you if you try to use a duplicate code.

Product Form Fields

  • Nombre: Product display name
  • Codigo de barras: Scannable barcode
  • Categoria: Optional grouping (e.g., “Componentes”, “Periféricos”)
Three options available:
  • Producto: Finished goods for sale
  • Refaccion: Repair parts/components
  • Servicio: Service items (labor, diagnostics)
  • Precio compra: Your acquisition cost
  • Precio venta: Customer price (required)
  • Margin is auto-calculated on save
  • Stock: Current quantity on hand
  • Stock minimo: Reorder point threshold
  • System doesn’t auto-reorder, but highlights low stock
  • Compatible con: Free text for device compatibility
  • Example: “Laptop HP 15-dy2xxx, Dell Inspiron 15”

Barcode-Based Editing

The “Modificar por codigo” feature enables rapid updates:

Workflow

  1. Click “Modificar por codigo” button
  2. Modal appears with barcode input
  3. Scan or type barcode
  4. Press Enter or click “Buscar producto”
  5. System searches for matching product:
const buscarProductoPorCodigo = () => {
  const codigoNormalizado = normalizarCodigo(codigoBusqueda);
  
  if (!codigoNormalizado) {
    setErrorCodigoBusqueda("Ingresa un codigo para buscar.");
    return;
  }

  const producto = productos.find(
    p => normalizarCodigo(p.codigo) === codigoNormalizado
  );

  if (!producto) {
    setErrorCodigoBusqueda("No se encontro un producto con ese codigo.");
    return;
  }

  editarProducto(producto);
};
  1. Product form opens pre-filled
  2. Make changes and save
This feature is ideal for quickly updating prices or stock levels using a barcode scanner.

Duplicate Code Prevention

The system enforces unique barcodes:
const normalizarCodigo = (value) => 
  String(value ?? "").trim().toLowerCase();

const existeCodigoRegistrado = (codigo, idActual = null) => {
  const codigoNormalizado = normalizarCodigo(codigo);
  if (!codigoNormalizado) return false;

  return productos.some((p) => {
    const mismoCodigo = normalizarCodigo(p.codigo) === codigoNormalizado;
    const otroProducto = idActual ? p.id !== idActual : true;
    return mismoCodigo && otroProducto;
  });
};

const validarCodigoUnico = () => {
  if (!form.codigo) return;

  if (existeCodigoRegistrado(form.codigo, modoEdicion ? form.id : null)) {
    alert("Ese codigo ya esta dado de alta.");
  }
};
Validation occurs:
  • On blur of the codigo field
  • Before saving (create or update)

Profit Margin Calculation

Margins are automatically calculated and displayed:
const calcularMargen = (compra, venta) => {
  if (!compra || !venta) return 0;
  return (((venta - compra) / compra) * 100).toFixed(1);
};

// Example:
// precioCompra: 100
// precioVenta: 150
// Margen: ((150 - 100) / 100) * 100 = 50.0%
The margin column updates in real-time as you edit prices.

Stock Management

Manual Stock Updates

const actualizarStock = async (productoId, nuevoStock) => {
  await actualizarProducto(productoId, {
    stock: nuevoStock,
    updatedAt: serverTimestamp()
  });
};

Automatic Stock Deduction (POS)

When a sale is completed in POS:
// In POS.jsx after successful payment
for (const [productoId, cantidadVendida] of ventas.entries()) {
  const stockActual = productos.get(productoId).stock;
  const nuevoStock = Math.max(0, stockActual - cantidadVendida);
  await descontarStock(productoId, nuevoStock);
}

Stock Validation in POS

Before allowing a sale:
const validarStock = (producto, cantidadRequerida) => {
  if (cantidadRequerida > producto.stock) {
    alert("No hay más stock disponible");
    return false;
  }
  return true;
};

// Prevent adding to cart if insufficient stock
if (existe.cantidad >= producto.stock) {
  alert("No hay más stock disponible");
  return;
}
The POS system will block sales if combined cart items + service parts exceed available stock.

Low Stock Monitoring

Products with stock below stockMinimo should be visually highlighted:
const productosBajoStock = productos.filter(
  p => p.stock <= (p.stockMinimo || 0) && p.activo
);
While the current UI doesn’t automatically display low stock alerts, you can implement a dashboard widget to show products needing reorder.

Product Types and Usage

Producto (Product)

  • Finished goods for direct sale
  • Examples: Keyboards, mice, cables
  • Generates loyalty points by default
  • Full stock tracking

Refaccion (Part)

  • Components used in repairs
  • Examples: RAM modules, hard drives, screens
  • Often added to service boletas (parts lists)
  • Stock deducted when service is paid

Servicio (Service)

  • Service catalog items
  • Examples: OS installation, virus removal
  • Typically doesn’t affect physical inventory
  • Can be sold standalone in POS

Product Activation States

Active Products

activo: true
// - Visible in POS search
// - Scannable at checkout
// - Included in inventory reports

Inactive Products

activo: false
// - Hidden from POS
// - Retained in database for history
// - Can be reactivated later
Use inactive status instead of deleting products to preserve sales history and data integrity.

Editing Products

Edit Flow

  1. Click “Editar” button on product row
  2. Form opens with current values:
const editarProducto = (producto) => {
  setForm(producto);
  setModoEdicion(true);
  setMostrarModal(true);
};
  1. Modify fields
  2. Code uniqueness is validated (excluding current product)
  3. Save updates product in Firestore

Constraints

  • Codigo: Can be changed if new code is unique
  • Stock: Can be manually adjusted
  • Prices: No restrictions on changes
  • History: No audit trail in current implementation

Deleting Products

const eliminarProducto = async (id) => {
  if (!id) return;
  if (!confirm("Estas seguro de eliminar este producto?")) return;

  await eliminarProductoDB(id);
  cargarProductos();
};
Deleting products removes them permanently. Consider using inactive status instead to preserve historical data.

Firebase Operations

Create Product

export async function crearProducto(producto) {
  const docRef = await addDoc(collection(db, "productos"), {
    ...producto,
    createdAt: serverTimestamp(),
    updatedAt: serverTimestamp()
  });
  return docRef.id;
}

Update Product

export async function actualizarProducto(id, cambios) {
  await updateDoc(doc(db, "productos", id), {
    ...cambios,
    updatedAt: serverTimestamp()
  });
}

Load All Products

export async function obtenerProductos() {
  const snapshot = await getDocs(collection(db, "productos"));
  return snapshot.docs.map(doc => ({
    id: doc.id,
    ...doc.data()
  }));
}

Integration with Other Modules

POS Integration

  • Products loaded on POS mount
  • Real-time stock validation
  • Barcode scanning for quick lookup
  • Price display and cart addition

Service Boletas

  • Parts can be added to service boletas
  • Stock reserved when service marked “listo”
  • Stock deducted when service paid in POS

Reports

  • Sales by product
  • Profit margins by item
  • Stock movement history
  • Low stock alerts

Best Practices

Barcode Standards

Use standard barcode formats (UPC, EAN) for products. For custom items, create a consistent internal numbering system.

Stock Audits

Regularly verify physical stock matches system records. Update discrepancies immediately.

Pricing Strategy

Set purchase prices to accurately calculate margins. Review margins regularly to ensure profitability.

Categories

Establish consistent category names to simplify inventory organization and reporting.

Technical Implementation

The inventory system uses:
import { 
  obtenerProductos,
  crearProducto,
  actualizarProducto,
  eliminarProductoDB
} from "../js/services/POS_firebase";
Key Files:
  • src/pages/productos.jsx - Main interface
  • src/js/services/POS_firebase.js - Data layer
  • src/css/productos.css - Styling
Firebase Collection:
  • Collection: productos
  • Indexes: codigo, activo
  • Security: Write requires authentication

Build docs developers (and LLMs) love