34786-vm/registrar_entrada.php
2026-02-12 03:10:11 +00:00

252 lines
10 KiB
PHP

<?php
require_once 'includes/header.php';
// Fetch products and cities for the dropdowns
try {
$pdo = db();
// Fetch products
$stmt_productos = $pdo->query("SELECT id, nombre, sku, codigo_barras FROM productos WHERE activo = 1 ORDER BY nombre ASC");
$productos = $stmt_productos->fetchAll(PDO::FETCH_ASSOC);
// Fetch cities
$stmt_ciudades = $pdo->query("SELECT id, nombre FROM ciudades ORDER BY nombre ASC");
$ciudades = $stmt_ciudades->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo '<div class="alert alert-danger" role="alert">Error al conectar con la base de datos: ' . htmlspecialchars($e->getMessage()) . '</div>';
$productos = [];
$ciudades = [];
}
?>
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow-sm">
<div class="card-body p-4">
<h2 class="h3 mb-3 text-center">Registrar Entrada</h2>
<p class="text-muted text-center mb-4">Añade nuevas unidades al inventario.</p>
<?php if (isset($_SESSION['error'])): ?>
<div class="alert alert-danger" role="alert">
<?php echo htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['success'])): ?>
<div class="alert alert-success" role="alert">
<?php echo htmlspecialchars($_SESSION['success']); unset($_SESSION['success']); ?>
</div>
<?php endif; ?>
<form action="handle_entrada.php" method="POST">
<div class="mb-4">
<label for="scan_barcode" class="form-label"><strong>Escanear Código</strong></label>
<div class="d-grid gap-2">
<input type="text" class="form-control" id="scan_barcode" placeholder="Haz clic y escanea..." autofocus>
<button class="btn btn-outline-success" type="button" id="btn-scan-camera">
<i class="fas fa-camera"></i> Escanear con Cámara
</button>
</div>
<div id="reader" style="width: 100%; display:none; margin-top: 10px;"></div>
</div>
<div class="mb-4">
<label for="producto_id" class="form-label">Producto</label>
<select class="form-select" id="producto_id" name="producto_id" required>
<option value="">Selecciona un producto</option>
<?php foreach ($productos as $producto): ?>
<option value="<?php echo htmlspecialchars($producto['id']); ?>" data-barcode="<?php echo htmlspecialchars($producto['codigo_barras'] ?? ''); ?>" data-sku="<?php echo htmlspecialchars($producto['sku']); ?>">
<?php echo htmlspecialchars($producto['nombre']) . ' (SKU: ' . htmlspecialchars($producto['sku']) . ')'; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-4">
<label for="ciudad_id" class="form-label">Ciudad de Destino</label>
<select class="form-select" id="ciudad_id" name="ciudad_id" required>
<option value="">Selecciona una ciudad</option>
<?php foreach ($ciudades as $ciudad): ?>
<option value="<?php echo htmlspecialchars($ciudad['id']); ?>">
<?php echo htmlspecialchars($ciudad['nombre']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-4">
<label for="cantidad" class="form-label">Cantidad</label>
<input type="number" class="form-control" id="cantidad" name="cantidad" min="1" required>
</div>
<div class="mb-4">
<label for="fecha" class="form-label">Fecha de Entrada</label>
<input type="date" class="form-control" id="fecha" name="fecha" required>
</div>
<div class="mb-4">
<label for="observacion" class="form-label">Observación (Opcional)</label>
<textarea class="form-control" id="observacion" name="observacion" rows="3" placeholder="Ej: Compra, ajuste..."></textarea>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-success btn-lg">Registrar Entrada</button>
<a href="productos.php" class="btn btn-outline-secondary">Cancelar</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Include html5-qrcode library -->
<script src="https://unpkg.com/html5-qrcode" type="text/javascript"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const barcodeInput = document.getElementById('scan_barcode');
const productSelect = document.getElementById('producto_id');
const quantityInput = document.getElementById('cantidad');
const btnScan = document.getElementById('btn-scan-camera');
const readerDiv = document.getElementById('reader');
let html5QrCode = null;
let isScanning = false;
let audioCtx = null;
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
}
// Function to search and select product
function searchProduct(code) {
if (code) {
let found = false;
for (let i = 0; i < productSelect.options.length; i++) {
const option = productSelect.options[i];
const barcode = option.getAttribute('data-barcode');
const sku = option.getAttribute('data-sku');
if (barcode === code || sku === code) {
productSelect.selectedIndex = i;
found = true;
barcodeInput.value = ''; // Clear input
quantityInput.focus(); // Move to quantity
break;
}
}
if (!found) {
alert('Producto no encontrado con el código: ' + code);
barcodeInput.value = '';
}
}
}
// Manual input listener
barcodeInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault(); // Prevent form submission
searchProduct(barcodeInput.value.trim());
}
});
// Camera scan listener
btnScan.addEventListener('click', function() {
initAudio();
if (isScanning) {
stopScanning();
} else {
startScanning();
}
});
function startScanning() {
readerDiv.style.display = 'block';
html5QrCode = new Html5Qrcode("reader");
const config = { fps: 10, qrbox: { width: 250, height: 250 } };
// Prefer back camera
html5QrCode.start({ facingMode: "environment" }, config, onScanSuccess, onScanFailure)
.then(() => {
isScanning = true;
btnScan.innerHTML = '<i class="fas fa-stop"></i> Detener Cámara';
btnScan.classList.remove('btn-outline-success');
btnScan.classList.add('btn-danger');
})
.catch(err => {
console.error("Error starting scanner", err);
alert("No se pudo iniciar la cámara. Por favor, verifica los permisos y que estés usando HTTPS.");
readerDiv.style.display = 'none';
});
}
function stopScanning() {
if (html5QrCode) {
html5QrCode.stop().then(() => {
readerDiv.style.display = 'none';
isScanning = false;
btnScan.innerHTML = '<i class="fas fa-camera"></i> Escanear con Cámara';
btnScan.classList.remove('btn-danger');
btnScan.classList.add('btn-outline-success');
html5QrCode.clear();
}).catch(err => {
console.error("Failed to stop scanner", err);
});
}
}
// Professional scanner sound using Web Audio API
function playScanSound() {
if (!audioCtx) initAudio();
const oscillator = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(1200, audioCtx.currentTime); // 1200Hz beep
// Smooth envelope
gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
gainNode.gain.linearRampToValueAtTime(0.1, audioCtx.currentTime + 0.01);
gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.15);
oscillator.start();
oscillator.stop(audioCtx.currentTime + 0.15);
}
function onScanSuccess(decodedText, decodedResult) {
// Handle the scanned code
console.log(`Code matched = ${decodedText}`, decodedResult);
// Play sound
playScanSound();
// Stop scanning after successful read
stopScanning();
// Fill input and search
barcodeInput.value = decodedText;
searchProduct(decodedText);
}
function onScanFailure(error) {
// handle scan failure, usually better to ignore and keep scanning.
// console.warn(`Code scan error = ${error}`);
}
});
</script>
<?php
require_once 'includes/footer.php';
?>