Autosave: 20260521-170036

This commit is contained in:
Flatlogic Bot 2026-05-21 17:00:39 +00:00
parent 4bc4e0b695
commit 29814f9aa8
46 changed files with 271 additions and 116 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

View File

@ -0,0 +1,2 @@
-- Migration: Add nota_adicional column to pedidos table
ALTER TABLE pedidos ADD COLUMN nota_adicional TEXT NULL AFTER clave;

View File

@ -15,69 +15,82 @@ try {
}
// Handle form submission
$generated_codes = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$producto_id = $_POST['producto_id'] ?? null;
$cantidad = isset($_POST['cantidad']) ? (int)$_POST['cantidad'] : 0;
$generated_data = []; // Store both code and product name
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['productos'])) {
$productos_input = $_POST['productos'];
$cantidades_input = $_POST['cantidades'];
if ($producto_id && $cantidad > 0 && $cantidad <= 1000) {
$db = db();
try {
$db->beginTransaction();
$db = db();
try {
$db->beginTransaction();
$total_generated = 0;
// 1. Lock the product row and get current counter and code base
$stmt = $db->prepare("SELECT codigo_base, sku, contador_etiquetas FROM products WHERE id = ? FOR UPDATE");
$stmt->execute([$producto_id]);
$product_data = $stmt->fetch(PDO::FETCH_ASSOC);
foreach ($productos_input as $index => $producto_id) {
$cantidad = isset($cantidades_input[$index]) ? (int)$cantidades_input[$index] : 0;
if (!$product_data) {
throw new Exception("Producto no encontrado.");
}
if ($producto_id && $cantidad > 0 && $cantidad <= 1000) {
// 1. Lock the product row and get current counter and code base
$stmt = $db->prepare("SELECT nombre, codigo_base, sku, contador_etiquetas FROM products WHERE id = ? FOR UPDATE");
$stmt->execute([$producto_id]);
$product_data = $stmt->fetch(PDO::FETCH_ASSOC);
$codigo_base = $product_data['codigo_base'];
$contador_actual = (int)$product_data['contador_etiquetas'];
if (!$product_data) {
continue; // Skip if product not found
}
// If codigo_base is missing, try to use SKU
if (empty($codigo_base)) {
$codigo_base = $product_data['sku'];
// If SKU is also missing, abort
$product_name = $product_data['nombre'];
$codigo_base = $product_data['codigo_base'];
$contador_actual = (int)$product_data['contador_etiquetas'];
// If codigo_base is missing, try to use SKU
if (empty($codigo_base)) {
throw new Exception("El producto no tiene un SKU o Código Base definido. Por favor, edite el producto y añada un SKU.");
$codigo_base = $product_data['sku'];
// If SKU is also missing, skip or throw error
if (empty($codigo_base)) {
throw new Exception("El producto '$product_name' no tiene un SKU o Código Base definido.");
}
// Persist the SKU as the codigo_base for future use
$update_base_stmt = $db->prepare("UPDATE products SET codigo_base = ? WHERE id = ?");
$update_base_stmt->execute([$codigo_base, $producto_id]);
}
// Prepare statement for insertion into unidades_inventario
$insert_stmt = $db->prepare("INSERT INTO unidades_inventario (codigo_unico, producto_id) VALUES (?, ?)");
// 2. Generate new codes
for ($i = 1; $i <= $cantidad; $i++) {
$nuevo_contador = $contador_actual + $i;
$numero_formateado = str_pad($nuevo_contador, 4, '0', STR_PAD_LEFT);
$unique_code = $codigo_base . '-' . $numero_formateado;
$insert_stmt->execute([$unique_code, $producto_id]);
$generated_data[] = [
'code' => $unique_code,
'product_name' => $product_name
];
$total_generated++;
}
// Persist the SKU as the codigo_base for future use
$update_base_stmt = $db->prepare("UPDATE products SET codigo_base = ? WHERE id = ?");
$update_base_stmt->execute([$codigo_base, $producto_id]);
// 3. Update the counter in the products table
$nuevo_total_contador = $contador_actual + $cantidad;
$update_stmt = $db->prepare("UPDATE products SET contador_etiquetas = ? WHERE id = ?");
$update_stmt->execute([$nuevo_total_contador, $producto_id]);
}
// Prepare statement for insertion into unidades_inventario
$insert_stmt = $db->prepare("INSERT INTO unidades_inventario (codigo_unico, producto_id) VALUES (?, ?)");
// 2. Generate new codes
for ($i = 1; $i <= $cantidad; $i++) {
$nuevo_contador = $contador_actual + $i;
$numero_formateado = str_pad($nuevo_contador, 4, '0', STR_PAD_LEFT);
$unique_code = $codigo_base . '-' . $numero_formateado;
$insert_stmt->execute([$unique_code, $producto_id]);
$generated_codes[] = $unique_code;
}
// 3. Update the counter in the products table
$nuevo_total_contador = $contador_actual + $cantidad;
$update_stmt = $db->prepare("UPDATE products SET contador_etiquetas = ? WHERE id = ?");
$update_stmt->execute([$nuevo_total_contador, $producto_id]);
$db->commit();
$_SESSION['success_message'] = 'Se generaron ' . count($generated_codes) . ' códigos exitosamente.';
} catch (Exception $e) {
$db->rollBack();
$_SESSION['error_message'] = 'Error al generar los códigos: ' . $e->getMessage();
}
} else {
$_SESSION['error_message'] = 'Por favor, seleccione un producto y especifique una cantidad válida (entre 1 y 1000).';
$db->commit();
if ($total_generated > 0) {
$_SESSION['success_message'] = 'Se generaron ' . $total_generated . ' códigos exitosamente.';
} else {
$_SESSION['error_message'] = 'No se generó ningún código. Verifique los datos ingresados.';
}
} catch (Exception $e) {
if ($db->inTransaction()) $db->rollBack();
$_SESSION['error_message'] = 'Error al generar los códigos: ' . $e->getMessage();
}
}
@ -99,51 +112,91 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
unset($_SESSION['error_message']); // Clear the message
}
?>
<form action="generar_etiquetas.php" method="POST">
<div class="row">
<div class="col-md-6 mb-3">
<label for="producto_id" class="form-label">Producto</label>
<select class="form-select" id="producto_id" name="producto_id" required>
<option value="">Seleccione un producto</option>
<?php foreach ($products as $product): ?>
<option value="<?php echo htmlspecialchars($product['id']); ?>">
<?php echo htmlspecialchars($product['nombre']); ?>
</option>
<?php endforeach; ?>
</select>
<form action="generar_etiquetas.php" method="POST" id="labelsForm">
<div id="productsContainer">
<div class="row product-row mb-3">
<div class="col-md-6">
<label class="form-label">Producto</label>
<select class="form-select" name="productos[]" required>
<option value="">Seleccione un producto</option>
<?php foreach ($products as $product): ?>
<option value="<?php echo htmlspecialchars($product['id']); ?>">
<?php echo htmlspecialchars($product['nombre']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Cantidad de Etiquetas</label>
<input type="number" class="form-control" name="cantidades[]" min="1" max="1000" required>
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="button" class="btn btn-danger w-100 remove-product" style="display: none;">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="col-md-4 mb-3">
<label for="cantidad" class="form-label">Cantidad de Etiquetas</label>
<input type="number" class="form-control" id="cantidad" name="cantidad" min="1" max="1000" required>
</div>
<div class="row mt-3">
<div class="col-md-6">
<button type="button" id="addProduct" class="btn btn-secondary">
<i class="fas fa-plus"></i> Agregar otro producto
</button>
</div>
<div class="col-md-2 mb-3 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Generar</button>
<div class="col-md-6 text-end">
<button type="submit" class="btn btn-primary px-5">Generar Todas</button>
</div>
</div>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const container = document.getElementById('productsContainer');
const addButton = document.getElementById('addProduct');
addButton.addEventListener('click', function() {
const firstRow = container.querySelector('.product-row');
const newRow = firstRow.cloneNode(true);
// Clear inputs
newRow.querySelector('select').value = '';
newRow.querySelector('input').value = '';
// Show remove button
const removeBtn = newRow.querySelector('.remove-product');
removeBtn.style.display = 'block';
removeBtn.addEventListener('click', function() {
newRow.remove();
});
container.appendChild(newRow);
});
// Initial remove button logic for the first row (if needed, though hidden by default)
container.querySelectorAll('.remove-product').forEach(btn => {
btn.addEventListener('click', function() {
if (container.querySelectorAll('.product-row').length > 1) {
btn.closest('.product-row').remove();
}
});
});
});
</script>
<?php
if (!empty($generated_codes)) {
if (!empty($generated_data)) {
?>
<div class="card mt-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h3 class="card-title mb-0">Códigos Generados</h3>
<form action="imprimir_etiquetas.php" method="POST" target="_blank" class="m-0">
<?php
// Get product name to pass to the export script
$product_name = '';
foreach ($products as $p) {
if ($p['id'] == $producto_id) {
$product_name = $p['nombre'];
break;
}
}
?>
<input type="hidden" name="product_name" value="<?php echo htmlspecialchars($product_name); ?>">
<?php foreach ($generated_codes as $code): ?>
<input type="hidden" name="codes[]" value="<?php echo htmlspecialchars($code); ?>">
<?php foreach ($generated_data as $data): ?>
<input type="hidden" name="codes[]" value="<?php echo htmlspecialchars($data['code']); ?>">
<input type="hidden" name="product_names[]" value="<?php echo htmlspecialchars($data['product_name']); ?>">
<?php endforeach; ?>
<button type="submit" class="btn btn-success">
<i class="fas fa-file-excel"></i> Exportar a Excel
@ -153,12 +206,13 @@ if (!empty($generated_codes)) {
<div class="card-body">
<p>Estos son los códigos generados. Puedes escanearlos con una app en tu teléfono para probar que funcionan.</p>
<div class="row mt-4">
<?php foreach ($generated_codes as $code): ?>
<?php foreach ($generated_data as $data): ?>
<div class="col-lg-4 col-md-6 text-center mb-4">
<div class="barcode-container" style="min-height: 70px;">
<img src="https://barcode.tec-it.com/barcode.ashx?data=<?php echo urlencode($code); ?>&code=Code128" alt="Barcode for <?php echo htmlspecialchars($code); ?>" style="max-height: 50px;">
<img src="https://barcode.tec-it.com/barcode.ashx?data=<?php echo urlencode($data['code']); ?>&code=Code128" alt="Barcode for <?php echo htmlspecialchars($data['code']); ?>" style="max-height: 50px;">
</div>
<code><?php echo htmlspecialchars($code); ?></code>
<code><?php echo htmlspecialchars($data['code']); ?></code><br>
<small class="text-muted"><?php echo htmlspecialchars($data['product_name']); ?></small>
</div>
<?php endforeach; ?>
</div>

View File

@ -1,38 +1,69 @@
<?php
// Receive data from the form
// Limpiar cualquier salida previa para evitar corrupción del archivo binario
if (ob_get_level()) ob_end_clean();
// Recibir datos del formulario
$codes = $_POST['codes'] ?? [];
$product_name = $_POST['product_name'] ?? 'Producto Desconocido';
$product_names = $_POST['product_names'] ?? [];
$single_product_name = $_POST['product_name'] ?? 'Varios_Productos';
if (empty($codes)) {
die("No se proporcionaron códigos para exportar.");
}
// 1. Set HTTP headers to trigger file download
$filename = "etiquetas_" . str_replace(' ', '_', $product_name) . "_" . date("Y-m-d") . ".csv";
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
$display_name = count($product_names) > 0 ? "etiquetas_multiples" : str_replace(' ', '_', $single_product_name);
$filename = $display_name . "_" . date("Y-m-d") . ".xls";
// 2. Create a file pointer connected to the output stream
$output = fopen('php://output', 'w');
// 3. Add a UTF-8 BOM to ensure Excel opens it correctly
fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));
// 4. Write the header row
$header = ['Nombre del producto', 'Código único', 'Código de barra (usar fuente)'];
fputcsv($output, $header, ';');
// 5. Write the data rows
foreach ($codes as $code) {
$row = [
$product_name,
$code,
$code // The same code, to be formatted as a barcode in Excel
];
fputcsv($output, $row, ';');
// Funciones para generar el formato binario BIFF5 (Excel 97-2003)
function xlsBOF() {
echo pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
}
// 6. Close the file pointer
fclose($output);
function xlsEOF() {
echo pack("ss", 0x0A, 0x00);
}
function xlsWriteLabel($Row, $Col, $Value) {
$L = strlen($Value);
echo pack("ssssss", 0x204, 8 + $L, $Row, $Col, 0x0, $L);
echo $Value;
}
// Añadir registro de Codepage para que Excel reconozca el juego de caracteres Windows-1252
function xlsCodepage() {
echo pack("sss", 0x0042, 0x0002, 0x04E4); // 1252 (Windows Latin 1)
}
// Configurar cabeceras para descarga de archivo binario XLS (Excel 97-2003)
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: max-age=0');
header('Pragma: public');
header('Content-Transfer-Encoding: binary');
// Iniciar el archivo Excel
xlsBOF();
xlsCodepage();
// Escribir cabeceras de columnas
xlsWriteLabel(0, 0, mb_convert_encoding("Nombre del producto", 'Windows-1252', 'UTF-8'));
xlsWriteLabel(0, 1, mb_convert_encoding("Codigo unico", 'Windows-1252', 'UTF-8'));
xlsWriteLabel(0, 2, mb_convert_encoding("Codigo de barra (usar fuente)", 'Windows-1252', 'UTF-8'));
// Escribir los datos
$row = 1;
foreach ($codes as $index => $code) {
$p_name = isset($product_names[$index]) ? $product_names[$index] : $single_product_name;
// Convertir a Windows-1252 para compatibilidad con el formato BIFF antiguo
$p_name_encoded = mb_convert_encoding($p_name, 'Windows-1252', 'UTF-8');
xlsWriteLabel($row, 0, $p_name_encoded);
xlsWriteLabel($row, 1, (string)$code);
xlsWriteLabel($row, 2, (string)$code);
$row++;
}
// Finalizar el archivo Excel
xlsEOF();
exit;

View File

@ -348,6 +348,11 @@ include 'layout_header.php';
<textarea class="form-control" id="notas" name="notas" rows="3"><?php echo htmlspecialchars($pedido['notas']); ?></textarea>
</div>
<div class="mb-3">
<label for="nota_adicional" class="form-label">Nota Adicional (Dedicatorias, etc.)</label>
<textarea class="form-control" id="nota_adicional" name="nota_adicional" rows="3"><?php echo htmlspecialchars($pedido['nota_adicional'] ?? ''); ?></textarea>
</div>
<div class="mb-3">
<label for="observacion" class="form-label">Observación (Verificación de Pago)</label>
<textarea class="form-control" id="observacion" name="observacion" rows="2"><?php echo htmlspecialchars($pedido['observacion'] ?? ''); ?></textarea>

View File

@ -166,6 +166,7 @@ include 'layout_header.php';
<th> De Orden</th>
<th>Codigo De Orden</th>
<th>CLAVE</th>
<th>NOTA ADICIONAL</th>
<th>Estado</th>
<?php if ($user_role !== 'Asesor'): ?><th>Asesor</th><?php endif; ?>
<th>Fecha Creación</th>
@ -197,6 +198,9 @@ include 'layout_header.php';
<td class="<?php echo $isEditableClave; ?>" data-id="<?php echo $pedido['id']; ?>" data-field="clave">
<?php echo $canSeeClave ? htmlspecialchars($pedido['clave'] ?? 'N/A') : '<i class="fas fa-eye-slash text-muted" title="Suba el número de operación y seleccione el banco para ver la clave"></i> <span class="text-muted" style="font-size: 0.8rem;">Oculto</span>'; ?>
</td>
<td class="editable-dblclick" data-id="<?php echo $pedido['id']; ?>" data-field="nota_adicional">
<?php echo htmlspecialchars($pedido['nota_adicional'] ?? 'N/A'); ?>
</td>
<td><span class="badge" style="<?php echo getStatusStyle($pedido['estado']); ?>"><?php echo ($pedido['estado'] == 'Gestion') ? 'GESTIONES ⚙️' : htmlspecialchars($pedido['estado']); ?></span></td>
<?php if ($user_role !== 'Asesor'): ?><td><?php echo htmlspecialchars($pedido['asesor_nombre'] ?? 'N/A'); ?></td><?php endif; ?>
<td><?php echo htmlspecialchars($pedido['created_at']); ?></td>
@ -306,5 +310,61 @@ document.addEventListener('DOMContentLoaded', function() {
});
}
});
table.addEventListener('dblclick', function(e) {
if (e.target && e.target.classList.contains('editable-dblclick')) {
const cell = e.target;
if (cell.querySelector('input')) {
return; // Already in edit mode
}
const originalContent = cell.textContent.trim();
const input = document.createElement('input');
input.type = 'text';
input.className = 'form-control form-control-sm';
input.value = originalContent === 'N/A' ? '' : originalContent;
cell.innerHTML = '';
cell.appendChild(input);
input.focus();
const saveChanges = function() {
const newValue = input.value.trim();
const pedidoId = cell.dataset.id;
const field = cell.dataset.field;
// Restore original content visually
cell.textContent = newValue === '' ? 'N/A' : newValue;
// Send data to server
fetch('update_pedido_field.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ id: pedidoId, field: field, value: newValue })
})
.then(response => response.json())
.then(data => {
if (data.error) {
console.error('Error:', data.error);
cell.textContent = originalContent;
}
})
.catch(error => {
console.error('Fetch Error:', error);
cell.textContent = originalContent;
});
};
input.addEventListener('blur', saveChanges);
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
input.blur();
}
});
}
});
});
</script>

View File

@ -82,6 +82,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$banco = trim($_POST['banco'] ?? '');
$estado = $_POST['estado'];
$notas = trim($_POST['notas']);
$nota_adicional = trim($_POST['nota_adicional'] ?? '');
$observacion = trim($_POST['observacion'] ?? '');
$descargo = trim($_POST['descargo'] ?? '');
@ -153,6 +154,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
'monto_debe' => $monto_debe,
'estado' => $estado,
'notas' => $notas,
'nota_adicional' => $nota_adicional,
'observacion' => $observacion,
'descargo' => $descargo,
'voucher_adelanto_path' => $voucher_adelanto_path,
@ -183,6 +185,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
"monto_debe = :monto_debe",
"estado = :estado",
"notas = :notas",
"nota_adicional = :nota_adicional",
"observacion = :observacion",
"descargo = :descargo",
"voucher_adelanto_path = :voucher_adelanto_path",
@ -237,8 +240,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// INSERT: The advisor is the user creating the order.
$params['asesor_id'] = $_SESSION['user_id'];
$columns_sql = "dni_cliente, nombre_completo, celular, agencia, sede_envio, codigo_rastreo, codigo_tracking, clave, pendientes, producto, cantidad, monto_total, monto_adelantado, numero_operacion, banco, monto_debe, estado, asesor_id, notas, observacion, descargo, voucher_adelanto_path, voucher_restante_path";
$values_sql = ":dni_cliente, :nombre_completo, :celular, :agencia, :sede_envio, :codigo_rastreo, :codigo_tracking, :clave, :pendientes, :producto, :cantidad, :monto_total, :monto_adelantado, :numero_operacion, :banco, :monto_debe, :estado, :asesor_id, :notas, :observacion, :descargo, :voucher_adelanto_path, :voucher_restante_path";
$columns_sql = "dni_cliente, nombre_completo, celular, agencia, sede_envio, codigo_rastreo, codigo_tracking, clave, pendientes, producto, cantidad, monto_total, monto_adelantado, numero_operacion, banco, monto_debe, estado, asesor_id, notas, nota_adicional, observacion, descargo, voucher_adelanto_path, voucher_restante_path";
$values_sql = ":dni_cliente, :nombre_completo, :celular, :agencia, :sede_envio, :codigo_rastreo, :codigo_tracking, :clave, :pendientes, :producto, :cantidad, :monto_total, :monto_adelantado, :numero_operacion, :banco, :monto_debe, :estado, :asesor_id, :notas, :nota_adicional, :observacion, :descargo, :voucher_adelanto_path, :voucher_restante_path";
$completed_states = ['Completado', 'COMPLETADO ✅'];
if (in_array($estado, $completed_states)) {

View File

@ -21,7 +21,7 @@ $field = $data['field'];
$value = $data['value'];
// Whitelist allowed fields for security
$allowed_fields = ['numero_operacion', 'banco', 'clave', 'fecha_recojo', 'observacion', 'descargo'];
$allowed_fields = ['numero_operacion', 'banco', 'clave', 'fecha_recojo', 'observacion', 'descargo', 'notas', 'nota_adicional'];
if (!in_array($field, $allowed_fields)) {
http_response_code(400);
echo json_encode(['error' => 'Campo no permitido.']);