Skip to main content

Overview

The Sales Tracking module enables you to register product sales, maintain a complete sales history, and automatically update inventory levels. All sales data is stored in the historial_ventas table.

Database Schema

Sales records are stored with the following fields:
  • id (INTEGER, PRIMARY KEY, AUTOINCREMENT) - Sale record ID
  • idProducto (INTEGER, FOREIGN KEY) - References productos(id)
  • nombre (TEXT) - Product name at time of sale
  • cantidad (INTEGER) - Quantity sold
  • fechaVenta (TEXT) - Sale date in yyyy-MM-dd format
CREATE TABLE IF NOT EXISTS historial_ventas (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    idProducto INTEGER,
    nombre TEXT,
    cantidad INTEGER,
    fechaVenta TEXT,
    FOREIGN KEY (idProducto) REFERENCES productos(id) ON DELETE CASCADE
)

Registering Sales

1

Open Sales View

Click the Ventas button in the main inventory view to open VentasView.
2

Add New Sale

Click Agregar Venta to open the registration dialog (RegistrarVentaView).
3

Enter Sale Details

Fill in the required fields:
  • ID del Medicamento: Product ID (must exist in inventory)
  • Nombre del Medicamento: Product name (must match registered name)
  • Cantidad Vendida: Quantity sold (spinner, minimum 1)
4

Save the Sale

Click Guardar. The system will validate inputs and update inventory automatically.

Method Signature

public boolean registrarVenta(int id, String nombre, int cantidad)
Controller: InventarioController.java:50-56
DAO Implementation: InventarioDAO.java:247-309

Validation Rules

The system verifies that the product ID exists in the inventory:
String selectQuery = "SELECT nombre, existencias FROM productos WHERE id = ?";
if (!rs.next()) {
    JOptionPane.showMessageDialog(null, "El producto con ID " + id + " no existe.", 
                                  "Error", JOptionPane.ERROR_MESSAGE);
    return false;
}
The entered name must match the registered product name (case-insensitive):
// InventarioDAO.java:267-270
if (!nombreReal.equalsIgnoreCase(nombreIngresado)) {
    JOptionPane.showMessageDialog(null, 
        "El nombre ingresado no coincide con el producto registrado para el ID " + id + ".", 
        "Error", JOptionPane.ERROR_MESSAGE);
    return false;
}
This validation prevents accidental sales to wrong products.
The system ensures sufficient stock before completing the sale:
// InventarioDAO.java:272-275
if (cantidadVendida > existenciasActuales) {
    JOptionPane.showMessageDialog(null, "No hay suficientes existencias.", 
                                  "Error", JOptionPane.ERROR_MESSAGE);
    return false;
}
  • Quantity must be greater than 0
  • Validated in RegistrarVentaView.java:119-122:
    if (cantidad <= 0) {
        JOptionPane.showMessageDialog(this, "La cantidad debe ser mayor a cero.", 
                                      "Error", JOptionPane.ERROR_MESSAGE);
        return false;
    }
    

Automatic Inventory Updates

Transaction-Based Updates

Sales registration uses database transactions to ensure data consistency:
// InventarioDAO.java:278-299
connection.setAutoCommit(false);

// Decrease inventory stock
String updateQuery = "UPDATE productos SET existencias = existencias - ? WHERE id = ?";
try (PreparedStatement updateStmt = connection.prepareStatement(updateQuery)) {
    updateStmt.setInt(1, cantidadVendida);
    updateStmt.setInt(2, id);
    updateStmt.executeUpdate();
}

// Register sale in history
String fechaVenta = LocalDate.now().toString();
String insertHistorial = "INSERT INTO historial_ventas (idProducto, nombre, cantidad, fechaVenta) VALUES (?, ?, ?, ?)";
try (PreparedStatement insertStmt = connection.prepareStatement(insertHistorial)) {
    insertStmt.setInt(1, id);
    insertStmt.setString(2, nombreReal);
    insertStmt.setInt(3, cantidadVendida);
    insertStmt.setString(4, fechaVenta);
    insertStmt.executeUpdate();
}

connection.commit();
If any step fails, the entire transaction is rolled back, preventing inventory inconsistencies.

Automatic Date Stamping

Sale dates are automatically set to the current date:
String fechaVenta = LocalDate.now().toString(); // Format: yyyy-MM-dd

Viewing Sales History

Sales Table Columns

The sales history table displays:
  1. ID Venta - Sale record ID
  2. ID Producto - Product ID reference
  3. Nombre del Medicamento - Product name
  4. Cantidad Vendida - Quantity sold
  5. Fecha de Venta - Sale date

Method to Retrieve Sales

public List<Object[]> obtenerHistorialVentas()
Controller: InventarioController.java:79-81
DAO Implementation: InventarioDAO.java:426-451
SELECT id, idProducto, nombre, cantidad, fechaVenta 
FROM historial_ventas 
ORDER BY id ASC

Editing Sales Records

1

Select a Sale

Click on a sale record in the sales history table.
2

Open Edit Dialog

Click Editar Venta to open EditarVentaView.
3

Modify Details

Update:
  • Product name (must still match the original product)
  • Quantity sold
  • Sale date
4

Save Changes

Click Guardar. The system will recalculate inventory based on the quantity difference.

Method Signature

public boolean editarVenta(Object id, String nuevoNombre, String nuevaCantidad, String nuevaFecha)
Controller: InventarioController.java:62-68
DAO Implementation: InventarioDAO.java:316-407

Stock Adjustment Logic

When editing a sale, the system calculates the quantity difference and adjusts inventory:
// InventarioDAO.java:358
int diff = nuevaCant - oldQuantity;

// If increasing the sale quantity, verify sufficient stock
if (diff > 0) {
    String selectStock = "SELECT existencias FROM productos WHERE id = ?";
    try (PreparedStatement psStock = connection.prepareStatement(selectStock)) {
        psStock.setInt(1, idProducto);
        try (ResultSet rs = psStock.executeQuery()) {
            if (rs.next()) {
                int stock = rs.getInt("existencias");
                if (stock < diff) {
                    JOptionPane.showMessageDialog(null, 
                        "No hay suficientes existencias para aumentar la venta.", 
                        "Error", JOptionPane.ERROR_MESSAGE);
                    return false;
                }
            }
        }
    }
}

// Update stock
String updateStock = "UPDATE productos SET existencias = existencias - ? WHERE id = ?";
The product name must still match the original product associated with the sale. This prevents accidentally reassigning sales to different products.

Deleting Sales Records

1

Select Sale(s)

Click on one or more sales records. Multiple selection is supported.
2

Click Delete

Click the Eliminar button (red color).
3

Confirm Deletion

Confirm the deletion in the dialog box.

Method Signature

public boolean eliminarVenta(Object id)
Controller: InventarioController.java:71-73
DAO Implementation: InventarioDAO.java:412-424
Important: Deleting a sale record does NOT restore the inventory stock. You must manually adjust inventory if needed.

Filtering Sales Data

Advanced Filtering

The sales view includes a powerful filtering system:
1

Select Filter Column

Choose from the dropdown:
  • ID Venta
  • ID Producto
  • Nombre
  • Cantidad
  • Fecha
2

Enter Filter Text

Type in the filter field. Results update in real-time.
// VentasView.java:193-201
private void aplicarFiltroAvanzado() {
    String texto = txtFiltrar.getText().trim();
    int columnIndex = cmbFiltro.getSelectedIndex();
    if (texto.isEmpty()) {
        rowSorter.setRowFilter(null);
    } else {
        rowSorter.setRowFilter(RowFilter.regexFilter("(?i)" + texto, columnIndex));
    }
}
Use regex patterns for advanced filtering. For example, ^2025 to find all sales from 2025.

Exporting Sales Reports

CSV Export

Export the complete sales history to CSV format:
1

Click Export

Click Exportar Reporte in the sales view.
2

Choose Location

Select a save location. Default filename: reporte_ventas.csv
3

Confirm Export

The file will be created with all visible sales records (respects current filters).

CSV Format

ID Venta, ID Producto, Nombre del Medicamento, Cantidad Vendida, Fecha de Venta
1, 5, Amoxicilina 500mg, 10, 2025-03-01
2, 12, Meloxicam 15mg, 5, 2025-03-02
Method: VentasView.java:284-307
The export includes all rows currently displayed in the table, making it easy to export filtered subsets of data.

Refreshing Sales Data

Click the Refrescar Tabla button to reload sales data from the database:
// VentasView.java:309-311
public void actualizarTabla() {
    cargarVentas();
}
This is useful after:
  • Registering new sales from another window
  • Making manual database changes
  • Ensuring you have the latest data

Sales Analytics

Viewing Sales Patterns

Use the date filter combined with CSV export to analyze:
  • Monthly sales volumes
  • Best-selling products
  • Seasonal trends
  • Inventory turnover rates

Product Performance

Filter by product name to see all sales history for a specific product, helping identify:
  • Total units sold
  • Sales frequency
  • Reorder points

Best Practices

Double-Check Product IDs: Always verify the product ID before registering a sale to prevent errors.
Regular Backups: Export sales data to CSV weekly as a backup and for offline analysis.
Never delete sales records unless absolutely necessary. They provide valuable historical data for business analysis.
Audit Trail: The sales history provides a complete audit trail. Review it regularly to catch data entry errors.

Build docs developers (and LLMs) love