Autosave: 20260212-161706

This commit is contained in:
Flatlogic Bot 2026-02-12 16:17:06 +00:00
parent 7ba3e3fac5
commit d37c7e72bd
12 changed files with 234 additions and 66 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -1 +1 @@
ALTER TABLE products ADD COLUMN sku VARCHAR(100) NULL UNIQUE; -- ALTER TABLE products ADD COLUMN sku VARCHAR(100) NULL UNIQUE;

View File

@ -2,6 +2,7 @@
$pageTitle = "Generador de Etiquetas"; $pageTitle = "Generador de Etiquetas";
include 'layout_header.php'; include 'layout_header.php';
include 'db/config.php'; include 'db/config.php';
require_once 'includes/barcode_generator.php'; // Added this line
// Fetch products for the dropdown // Fetch products for the dropdown
$products = []; $products = [];
@ -13,13 +14,32 @@ try {
$_SESSION['error_message'] = "Error al cargar los productos: " . $e->getMessage(); $_SESSION['error_message'] = "Error al cargar los productos: " . $e->getMessage();
} }
$generated_codes = [];
// Handle form submission // Handle form submission
$generated_codes = []; // Initialize array to hold codes for display
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$producto_id = $_POST['producto_id'] ?? null; $producto_id = $_POST['producto_id'] ?? null;
$cantidad = isset($_POST['cantidad']) ? (int)$_POST['cantidad'] : 0; $cantidad = isset($_POST['cantidad']) ? (int)$_POST['cantidad'] : 0;
if ($producto_id && $cantidad > 0 && $cantidad <= 1000) { if ($producto_id && $cantidad > 0 && $cantidad <= 1000) {
// Find product name to generate initials
$product_name = '';
foreach ($products as $p) {
if ($p['id'] == $producto_id) {
$product_name = $p['nombre'];
break;
}
}
$initials = '';
if (!empty($product_name)) {
$words = preg_split('/\s+/', trim($product_name));
foreach ($words as $w) {
if (isset($w[0])) {
$initials .= strtoupper($w[0]);
}
}
}
$db = db(); $db = db();
try { try {
$db->beginTransaction(); $db->beginTransaction();
@ -28,8 +48,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt = $db->prepare("INSERT INTO unidades_inventario (codigo_unico, producto_id) VALUES (?, ?)"); $stmt = $db->prepare("INSERT INTO unidades_inventario (codigo_unico, producto_id) VALUES (?, ?)");
for ($i = 0; $i < $cantidad; $i++) { for ($i = 0; $i < $cantidad; $i++) {
// Generate a more robust unique code // Generate a unique code with product initials
$unique_code = 'FL-' . $producto_id . '-' . strtoupper(uniqid()); $unique_part = strtoupper(uniqid());
$unique_code = ($initials ? $initials . '-' : '') . $producto_id . '-' . $unique_part;
$stmt->execute([$unique_code, $producto_id]); $stmt->execute([$unique_code, $producto_id]);
$generated_codes[] = $unique_code; $generated_codes[] = $unique_code;
@ -46,6 +67,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['error_message'] = 'Por favor, seleccione un producto y especifique una cantidad válida (entre 1 y 1000).'; $_SESSION['error_message'] = 'Por favor, seleccione un producto y especifique una cantidad válida (entre 1 y 1000).';
} }
} }
?> ?>
<div class="card"> <div class="card">
@ -53,6 +75,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<h3 class="card-title">Generar Nuevas Etiquetas de Inventario</h3> <h3 class="card-title">Generar Nuevas Etiquetas de Inventario</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<?php
// Display success or error messages
if (isset($_SESSION['success_message'])) {
echo '<div class="alert alert-success">' . $_SESSION['success_message'] . '</div>';
unset($_SESSION['success_message']); // Clear the message
}
if (isset($_SESSION['error_message'])) {
echo '<div class="alert alert-danger">' . $_SESSION['error_message'] . '</div>';
unset($_SESSION['error_message']); // Clear the message
}
?>
<form action="generar_etiquetas.php" method="POST"> <form action="generar_etiquetas.php" method="POST">
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
@ -78,20 +111,42 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div> </div>
</div> </div>
<?php if (!empty($generated_codes)): ?> <?php
if (!empty($generated_codes)) {
$generator = new BarcodeGenerator();
?>
<div class="card mt-4"> <div class="card mt-4">
<div class="card-header"> <div class="card-header d-flex justify-content-between align-items-center">
<h3 class="card-title">Códigos Generados</h3> <h3 class="card-title mb-0">Códigos Generados</h3>
<form action="imprimir_etiquetas.php" method="POST" target="_blank" class="m-0">
<?php foreach ($generated_codes as $code): ?>
<input type="hidden" name="codes[]" value="<?php echo htmlspecialchars($code); ?>">
<?php endforeach; ?>
<button type="submit" class="btn btn-success">
<i class="fas fa-print"></i> Imprimir Etiquetas
</button>
</form>
</div> </div>
<div class="card-body"> <div class="card-body">
<p>Guarda estos códigos para imprimirlos y pegarlos en tus paquetes.</p> <p>Estos son los códigos generados. Puedes escanearlos con una app en tu teléfono para probar que funcionan.</p>
<textarea class="form-control" rows="10" readonly><?php echo implode(" <div class="row mt-4">
", $generated_codes); ?></textarea> <?php foreach ($generated_codes as $code): ?>
<button class="btn btn-secondary mt-2" onclick="navigator.clipboard.writeText(this.previousElementSibling.value).then(() => alert('¡Códigos copiados!'));"> <div class="col-lg-4 col-md-6 text-center mb-4">
<i class="fas fa-copy"></i> Copiar Códigos <div class="barcode-container" style="min-height: 70px;">
</button> <?php
try {
echo $generator->getBarcode($code);
} catch (Exception $e) {
echo '<div class="alert alert-danger">Error al generar código de barras.</div>';
}
?>
</div>
<code><?php echo htmlspecialchars($code); ?></code>
</div>
<?php endforeach; ?>
</div>
</div> </div>
</div> </div>
<?php endif; ?> <?php } ?>
<?php include 'layout_footer.php'; ?> <?php include 'layout_footer.php'; ?>

View File

@ -1,89 +1,104 @@
<?php <?php
session_start(); require_once 'includes/barcode_generator.php';
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit;
}
require_once 'db/config.php';
$product_ids = isset($_POST['product_ids']) ? $_POST['product_ids'] : []; $codes = $_POST['codes'] ?? [];
if (empty($product_ids)) { if (empty($codes)) {
echo "No se han seleccionado productos para imprimir."; // For direct access, you can add a fallback or a message.
exit; // For now, we'll just show a blank page if no codes are provided.
} }
// Sanitize input $generator = new BarcodeGenerator();
$placeholders = implode(',', array_fill(0, count($product_ids), '?'));
$stmt = db()->prepare("SELECT id, nombre, sku FROM products WHERE id IN ($placeholders)");
foreach ($product_ids as $k => $id) {
$stmt->bindValue(($k + 1), $id, PDO::PARAM_INT);
}
$stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="es"> <html lang="es">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Imprimir Etiquetas de Códigos de Barras</title> <title>Imprimir Etiquetas</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style> <style>
@media print { @media print {
body { body {
-webkit-print-color-adjust: exact; margin: 0;
padding: 0;
} }
.no-print { .no-print {
display: none; display: none !important;
}
.label {
page-break-inside: avoid;
} }
} }
body { body {
font-family: Arial, sans-serif; font-family: sans-serif;
background-color: #f8f9fa;
} }
.label-grid { .label-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); grid-template-columns: 1fr 1fr; /* Two columns */
gap: 20px; gap: 0.5rem;
padding: 20px; padding: 0.5rem;
} }
.label { .label {
border: 1px solid #ccc; border: 1px dashed #ccc;
padding: 10px; padding: 0.5rem;
text-align: center; text-align: center;
page-break-inside: avoid; background-color: white;
} }
.label img { .barcode-svg svg {
max-width: 100%; max-width: 100%;
height: auto; height: 50px;
margin-bottom: 5px; display: block;
margin: 0 auto;
} }
.label .product-name { .code-text {
font-size: 14px; font-size: 0.7rem;
font-weight: bold; word-break: break-all;
margin-top: 0.25rem;
} }
.print-button-container { .print-button {
text-align: center; position: fixed;
margin: 20px; top: 20px;
right: 20px;
z-index: 1000;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="print-button-container no-print"> <div class="container-fluid">
<button onclick="window.print();">Imprimir</button> <div class="my-3 text-center no-print">
</div> <h1 class="h3">Vista Previa de Impresión</h1>
<p>Ajusta el tamaño y la escala en el diálogo de impresión de tu navegador si es necesario.</p>
<button onclick="window.print();" class="btn btn-primary">
<i class="fas fa-print"></i> Imprimir Ahora
</button>
</div>
<div class="label-grid"> <?php if (!empty($codes)): ?>
<?php foreach ($products as $product): ?> <div class="label-grid">
<div class="label"> <?php foreach ($codes as $code): ?>
<div class="product-name"><?php echo htmlspecialchars($product['nombre']); ?></div> <div class="label">
<?php if (!empty($product['sku'])): ?> <div class="barcode-svg">
<img src="https://barcode.tec-it.com/barcode.ashx?data=<?php echo urlencode($product['sku']); ?>&code=Code128" alt="Barcode for product SKU <?php echo htmlspecialchars($product['sku']); ?>"> <?php
<?php else: ?> try {
<p>Sin SKU</p> echo $generator->getBarcode($code, 2, 50);
<?php endif; ?> } catch (Exception $e) {
echo '<div class="alert alert-danger p-1">Error</div>';
}
?>
</div>
<div class="code-text">
<code><?php echo htmlspecialchars($code); ?></code>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="alert alert-warning text-center no-print">
No se han proporcionado códigos. Por favor, <a href="generar_etiquetas.php">vuelve al generador</a> para crear nuevas etiquetas.
</div> </div>
<?php endforeach; ?> <?php endif; ?>
</div> </div>
</body> </body>

View File

@ -0,0 +1,98 @@
<?php
// Simplified Barcode Generator for Code 128
// Generates SVG output.
class BarcodeGenerator
{
private $barcode_codes;
public function __construct()
{
$this->barcode_codes = $this->getCode128Map();
}
public function getBarcode($code, $widthFactor = 2, $height = 50, $foregroundColor = 'black')
{
$barcodeData = $this->getBarcodeData($code);
$bars = '';
$x = 0;
// Generate SVG bars
foreach (str_split($barcodeData['bars']) as $bar) {
$width = $widthFactor * (int)$bar;
if ($x % 2 == 0) { // 0, 2, 4... are black bars
$bars .= '<rect x="' . $x . '" y="0" width="' . $width . '" height="' . $height . '" style="fill:' . $foregroundColor . ';" />';
}
$x += $width;
}
$svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="' . $x . '" height="' . $height . '" viewBox="0 0 ' . $x . ' ' . $height . '">';
$svg .= $bars;
$svg .= '</svg>';
return $svg;
}
private function getBarcodeData($code)
{
// For simplicity, we'll stick to Code Set B
$codeSet = 'B';
$data = '';
$sum = 104; // Start code B
$mult = 1;
foreach (str_split($code) as $char) {
$value = $this->getCharValue($codeSet, $char);
if ($value === null) {
// Fallback for unsupported characters, or switch sets (more complex)
// For now, we'll just skip them to avoid errors
continue;
}
$data .= str_pad($value, 2, '0', STR_PAD_LEFT);
$sum += ($value * $mult);
$mult++;
}
$checksum = $sum % 103;
$data .= str_pad($checksum, 2, '0', STR_PAD_LEFT);
// Start, data, checksum, stop
$full_code = '104' . $data . '106';
$bars = '';
for ($i = 0; $i < strlen($full_code); $i += 2) {
$index = (int)substr($full_code, $i, 2);
$bars .= $this->barcode_codes['bars'][$index];
}
return ['bars' => $bars, 'code' => $full_code];
}
private function getCharValue($set, $char)
{
if (isset($this->barcode_codes[$set][$char])) {
return $this->barcode_codes[$set][$char];
}
return null; // Character not in set
}
private function getCode128Map()
{
return [
'B' => [
' ' => 0, '!' => 1, '"' => 2, '#' => 3, '$' => 4, '%' => 5, '&' => 6, '\'' => 7,
'(' => 8, ')' => 9, '*' => 10, '+' => 11, ',' => 12, '-' => 13, '.' => 14, '/' => 15,
'0' => 16, '1' => 17, '2' => 18, '3' => 19, '4' => 20, '5' => 21, '6' => 22, '7' => 23,
'8' => 24, '9' => 25, ':' => 26, ';' => 27, '<' => 28, '=' => 29, '>' => 30, '?' => 31,
'@' => 32, 'A' => 33, 'B' => 34, 'C' => 35, 'D' => 36, 'E' => 37, 'F' => 38, 'G' => 39,
'H' => 40, 'I' => 41, 'J' => 42, 'K' => 43, 'L' => 44, 'M' => 45, 'N' => 46, 'O' => 47,
'P' => 48, 'Q' => 49, 'R' => 50, 'S' => 51, 'T' => 52, 'U' => 53, 'V' => 54, 'W' => 55,
'X' => 56, 'Y' => 57, 'Z' => 58, '[' => 59, '\\' => 60, ']' => 61, '^' => 62, '_' => 63
],
'bars' => [
'212222', '222122', '222221', '121223', '121322', '131222', '122213', '122312', '132212', '221213', '221312', '231212', '112232', '122132', '122231', '113222', '123122', '123221', '223211', '221132', '221231', '213212', '223112', '312131', '311222', '321122', '321221', '312212', '322112', '322211', '212123', '212321', '232121', '111323', '131123', '131321', '112313', '132113', '132311', '211313', '231113', '231311', '112133', '112331', '132131', '113123', '113321', '133121', '313121', '211331', '231131', '213113', '213311', '213131', '311123', '311321', '331121', '312113', '312311', '332111', '314111', '221411', '413111', '111224', '111421', '121124', '121421', '141122', '141221', '112214', '112412', '122114', '122411', '142112', '142211', '241211', '221114', '411122', '411221', '421121', '421211', '212141', '214121', '412121', '111143', '111341', '131141', '114113', '114311', '411113', '411311', '113141', '114131', '311141', '411131', '211412', '211214', '211232', '2331112'
]
];
}
}
?>