diff --git a/assets/uploads/vouchers/6a1225e5ee363-144.png b/assets/uploads/vouchers/6a1225e5ee363-144.png
new file mode 100644
index 00000000..6f6f9bfd
Binary files /dev/null and b/assets/uploads/vouchers/6a1225e5ee363-144.png differ
diff --git a/assets/uploads/vouchers/6a122c079c70d-Captura de pantalla 2026-05-23 173637.png b/assets/uploads/vouchers/6a122c079c70d-Captura de pantalla 2026-05-23 173637.png
new file mode 100644
index 00000000..78244d30
Binary files /dev/null and b/assets/uploads/vouchers/6a122c079c70d-Captura de pantalla 2026-05-23 173637.png differ
diff --git a/assets/uploads/vouchers/6a122fa11cf0c-7075.png b/assets/uploads/vouchers/6a122fa11cf0c-7075.png
new file mode 100644
index 00000000..6191e86d
Binary files /dev/null and b/assets/uploads/vouchers/6a122fa11cf0c-7075.png differ
diff --git a/assets/uploads/vouchers/6a1233751865f-038.png b/assets/uploads/vouchers/6a1233751865f-038.png
new file mode 100644
index 00000000..26796a04
Binary files /dev/null and b/assets/uploads/vouchers/6a1233751865f-038.png differ
diff --git a/db/migrations/070_add_pendiente_to_estado_enum.sql b/db/migrations/070_add_pendiente_to_estado_enum.sql
new file mode 100644
index 00000000..5d2bf730
--- /dev/null
+++ b/db/migrations/070_add_pendiente_to_estado_enum.sql
@@ -0,0 +1,17 @@
+-- Add PENDIENTE as a valid route status for contraentrega pedidos.
+ALTER TABLE pedidos MODIFY COLUMN estado ENUM(
+ 'ROTULADO 📦',
+ 'EN TRANSITO 🚛',
+ 'EN DESTINO 🏬',
+ 'COMPLETADO ✅',
+ 'Gestion',
+ 'RUTA_CONTRAENTREGA',
+ 'ENTREGA EXITOSA',
+ 'RETORNADO',
+ 'Duplicados',
+ 'NO CONTESTO, DEVOLVER LLAMADA',
+ 'CANCELADO',
+ 'REPROGRAMADO',
+ 'NO CONTESTO, VOLVER A LLAMAR',
+ 'PENDIENTE'
+) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 'ROTULADO 📦';
diff --git a/db/migrations/072_add_ean_to_products.sql b/db/migrations/072_add_ean_to_products.sql
new file mode 100644
index 00000000..43fc9680
--- /dev/null
+++ b/db/migrations/072_add_ean_to_products.sql
@@ -0,0 +1,3 @@
+-- Add EAN code column to products for admin-maintained barcode/catalog codes.
+ALTER TABLE products
+ ADD COLUMN IF NOT EXISTS ean VARCHAR(32) NULL AFTER sku;
diff --git a/download_ruta_contraentrega.php b/download_ruta_contraentrega.php
new file mode 100644
index 00000000..7472ee0d
--- /dev/null
+++ b/download_ruta_contraentrega.php
@@ -0,0 +1,173 @@
+prepare("SELECT nombre, ean FROM products WHERE nombre IN ($placeholders)");
+ $stmt->execute($productNames);
+
+ $map = [];
+ while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $ean = trim((string)($row['ean'] ?? ''));
+ if ($ean !== '') {
+ $map[$row['nombre']] = $ean;
+ }
+ }
+
+ $eans = [];
+ foreach ($productNames as $name) {
+ if (!empty($map[$name])) {
+ $eans[] = $map[$name];
+ }
+ }
+
+ return implode(' | ', array_values(array_unique($eans)));
+}
+
+try {
+ $pdo = db();
+
+ $user_id = $_SESSION['user_id'];
+ $user_role = $_SESSION['user_role'] ?? 'Asesor';
+
+ $selected_month = $_GET['mes'] ?? '';
+ $selected_year = $_GET['año'] ?? '';
+ $search_query = trim($_GET['q'] ?? '');
+
+ $sql = "SELECT p.* FROM pedidos p WHERE p.estado IN ('RUTA_CONTRAENTREGA', 'PENDIENTE', 'NO CONTESTO, VOLVER A LLAMAR', 'NO CONTESTO, DEVOLVER LLAMADA', 'CANCELADO', 'REPROGRAMADO', 'ENTREGA EXITOSA', 'RETORNADO')";
+ $params = [];
+
+ if ($user_role === 'Asesor') {
+ $sql .= " AND p.asesor_id = ?";
+ $params[] = $user_id;
+ }
+
+ if ($search_query !== '') {
+ $sql .= " AND (p.nombre_completo LIKE ? OR p.dni_cliente LIKE ? OR p.celular LIKE ?)";
+ $params[] = "%$search_query%";
+ $params[] = "%$search_query%";
+ $params[] = "%$search_query%";
+ }
+
+ if ($selected_month !== '') {
+ $sql .= " AND MONTH(p.created_at) = ?";
+ $params[] = $selected_month;
+ }
+
+ if ($selected_year !== '') {
+ $sql .= " AND YEAR(p.created_at) = ?";
+ $params[] = $selected_year;
+ }
+
+ $sql .= " ORDER BY p.created_at DESC";
+ $stmt = $pdo->prepare($sql);
+ $stmt->execute($params);
+ $pedidos = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ $rows = [];
+ $rows[] = [
+ 'Nombre y apellido',
+ 'Celular',
+ 'Pais',
+ 'Departamento',
+ 'Provincia',
+ 'Distrito',
+ 'Direccion',
+ 'Referencia',
+ 'Coordenadas',
+ 'Codigo EAN',
+ 'Cantidad',
+ 'Precio',
+ 'Total'
+ ];
+
+ foreach ($pedidos as $pedido) {
+ [$provincia, $distrito] = splitProvinciaDistrito($pedido['codigo_rastreo'] ?? '');
+ $cantidad = (int)($pedido['cantidad'] ?? 0);
+ $total = (float)($pedido['monto_total'] ?? 0);
+ $precio = $cantidad > 0 ? round($total / $cantidad, 2) : 0;
+ $ean = getEansForProducts($pdo, extractProductNames($pedido));
+
+ $rows[] = [
+ (string)($pedido['nombre_completo'] ?? ''),
+ (string)($pedido['celular'] ?? ''),
+ 'Perú',
+ (string)($pedido['sede_envio'] ?? ''),
+ $provincia,
+ $distrito,
+ (string)($pedido['direccion_exacta'] ?? ''),
+ (string)($pedido['referencia_domicilio'] ?? ''),
+ (string)($pedido['coordenadas'] ?? ''),
+ (string)$ean,
+ $cantidad,
+ $precio,
+ $total
+ ];
+ }
+
+ $filename = 'ruta_contraentrega_' . date('Y-m-d_H-i') . '.xlsx';
+ SimpleXLSXGen::fromArray($rows, 'Ruta Contraentrega')->downloadAs($filename);
+ exit;
+} catch (Throwable $e) {
+ error_log('Error exportando Ruta Contraentrega: ' . $e->getMessage());
+ header('HTTP/1.1 500 Internal Server Error');
+ echo 'Error al generar el Excel de Ruta Contraentrega.';
+}
diff --git a/edit_product.php b/edit_product.php
index 538b9104..90caf8ff 100644
--- a/edit_product.php
+++ b/edit_product.php
@@ -94,6 +94,11 @@ include 'layout_header.php';
+
diff --git a/includes/contraentrega_cobertura.php b/includes/contraentrega_cobertura.php
new file mode 100644
index 00000000..c4ce2f6c
--- /dev/null
+++ b/includes/contraentrega_cobertura.php
@@ -0,0 +1,72 @@
+ ['Carhuaz', 'Huaraz', 'Huaylas', 'Santa', 'Yungay'],
+ 'Apurimac' => ['Abancay'],
+ 'Arequipa' => ['Arequipa'],
+ 'Ayacucho' => ['Huamanga'],
+ 'Cajamarca' => ['Cajamarca'],
+ 'Cusco' => ['Cusco'],
+ 'Huanuco' => ['Huanuco'],
+ 'Ica' => ['Ica'],
+ 'Junin' => ['Chupaca', 'Huancayo'],
+ 'La Libertad' => ['Trujillo'],
+ 'Lambayeque' => ['Ferreñafe', 'Chiclayo', 'Lambayeque'],
+ 'Lima' => ['Callao', 'Lima'],
+ 'Loreto' => ['Maynas'],
+ 'Madre de Dios' => ['Tambopata'],
+ 'Moquegua' => ['Ilo', 'Mariscal Nieto'],
+ 'Piura' => ['Paita', 'Piura', 'Sullana', 'Talara'],
+ 'Puno' => ['Puno', 'San Roman'],
+ 'San Martin' => ['Lamas', 'San Martin'],
+ 'Tacna' => ['Tacna'],
+ 'Ucayali' => ['Coronel Portillo'],
+ ];
+ }
+}
+
+if (!function_exists('contraentregaDistritosPorProvincia')) {
+ function contraentregaDistritosPorProvincia(): array
+ {
+ return [
+ 'Abancay' => ['Abancay'],
+ 'Arequipa' => ['Alto Selva Alegre', 'Arequipa', 'Cayma', 'Cerro Colorado', 'Characato', 'Jacobo Hunter', 'Jose Luis Bustamante y Rivero', 'Mariano Melgar', 'Miraflores', 'Paucarpata', 'Sabandia', 'Sachaca', 'Socabaya', 'Tiabaya', 'Yanahuara'],
+ 'Cajamarca' => ['Los Baqos del Inca', 'Cajamarca'],
+ 'Callao' => ['Bellavista', 'Callao', 'Carmen de la Legua Reynoso', 'La Perla', 'La Punta', 'Ventanilla'],
+ 'Carhuaz' => ['Carhuaz'],
+ 'Chupaca' => ['Chongos Bajo', 'Chupaca', 'Huamancaca Chico', 'Tres de Diciembre'],
+ 'Cusco' => ['Cusco', 'San Jeronimo', 'San Sebastian', 'Santiago', 'Saylla', 'Wanchaq'],
+ 'Huamanga' => ['Ayacucho', 'Jesús Nazareno', 'San Juan Bautista'],
+ 'Huancayo' => ['Chilca', 'El Tambo', 'Hualhuas', 'Huancan', 'Huancayo', 'Huayucachi', 'Pariahuanca', 'Pilcomayo', 'Pucara', 'Quilcas', 'San Agustin', 'San Jeronimo de Tunan', 'Sapallanga', 'Sicaya', 'Viques'],
+ 'Huanuco' => ['Amarilis', 'Huanuco', 'Pillcomarca'],
+ 'Huaraz' => ['Huaraz', 'Independencia'],
+ 'Huaylas' => ['Caraz', 'Huaylas'],
+ 'Ilo' => ['El Algarrobal', 'Ilo', 'Pacocha'],
+ 'Lima' => ['Ancon', 'Ate', 'Barranco', 'Breña', 'Carabayllo', 'Cercado de Lima', 'Chaclacayo', 'Chorrillos', 'Cieneguilla', 'Comas', 'El Agustino', 'Independencia', 'Jesus Maria', 'La Molina', 'La Victoria', 'Lince', 'Los Olivos', 'Lurigancho - Chosica', 'Lurin', 'Magdalena del Mar', 'Miraflores', 'Pachacamac', 'Pueblo Libre', 'Puente Piedra', 'Punta Hermosa', 'Punta Negra', 'Rimac', 'San Borja', 'San Isidro', 'San Juan de Lurigancho', 'San Juan de Miraflores', 'San Luis', 'San Martin de Porres', 'San Miguel', 'Santa Anita', 'Santa Maria del Mar', 'Santa Rosa', 'Santiago de Surco', 'Surquillo', 'Villa El Salvador', 'Villa Maria del Triunfo'],
+ 'Mariscal Nieto' => ['Carumas', 'Cuchumbaya', 'Moquegua', 'Samegua', 'San Cristobal', 'Torata'],
+ 'Paita' => ['Amotape', 'Arenal', 'Colan', 'La Huaca', 'Paita', 'Tamarindo', 'Vichayal'],
+ 'Piura' => ['26 de Octubre', 'Castilla', 'Catacaos', 'Piura'],
+ 'Santa' => ['Chimbote', 'Coishco', 'Nuevo Chimbote'],
+ 'Sullana' => ['Sullana'],
+ 'Talara' => ['Pariñas'],
+ 'Trujillo' => ['El Porvenir', 'Florencia de Mora', 'Huanchaco', 'La Esperanza', 'Laredo', 'Moche', 'Salaverry', 'Trujillo', 'Victor Larco Herrera'],
+ 'Yungay' => ['Yungay'],
+ ];
+ }
+}
+
+if (!function_exists('splitProvinciaDistritoContraentrega')) {
+ function splitProvinciaDistritoContraentrega(?string $value): array
+ {
+ $value = trim((string)$value);
+ if ($value === '') {
+ return ['', ''];
+ }
+
+ $parts = preg_split('/\s*(?:\/|-|,|\|)\s*/', $value, 2);
+ return [trim($parts[0] ?? ''), trim($parts[1] ?? '')];
+ }
+}
diff --git a/panel_inventario.php b/panel_inventario.php
index aa041531..2a99bd02 100644
--- a/panel_inventario.php
+++ b/panel_inventario.php
@@ -27,7 +27,7 @@ try {
$sedes_stmt = $pdo->query("SELECT id, nombre FROM sedes ORDER BY nombre");
$sedes = $sedes_stmt->fetchAll(PDO::FETCH_ASSOC);
- $products_stmt = $pdo->query("SELECT id, nombre, sku FROM products ORDER BY nombre");
+ $products_stmt = $pdo->query("SELECT id, nombre, sku, ean FROM products ORDER BY nombre");
$products = $products_stmt->fetchAll(PDO::FETCH_ASSOC);
$stock_stmt = $pdo->query("SELECT product_id AS producto_id, sede_id, quantity FROM stock_sedes");
@@ -38,6 +38,7 @@ try {
$inventario[$product['id']] = [
'nombre' => $product['nombre'],
'sku' => $product['sku'],
+ 'ean' => $product['ean'] ?? '',
'total' => 0,
'sedes' => array_fill_keys(array_column($sedes, 'id'), 0)
];
@@ -146,6 +147,29 @@ try {
}
?>
+
+
+
Dashboard de Inventario
@@ -211,6 +235,7 @@ try {
| Producto |
SKU |
+ EAN |
Stock Total |
|
@@ -219,12 +244,13 @@ try {
- | No hay productos. |
+ | No hay productos. |
$datos_producto): ?>
|
- |
+ |
+ |
|
$cantidad): ?>
@@ -386,6 +412,62 @@ document.addEventListener('DOMContentLoaded', function () {
+
\ No newline at end of file
+
+
+
+
diff --git a/ruta_contraentrega.php b/ruta_contraentrega.php
index 61a15332..e15d4cdc 100644
--- a/ruta_contraentrega.php
+++ b/ruta_contraentrega.php
@@ -145,6 +145,22 @@ $months = [
7 => 'Julio', 8 => 'Agosto', 9 => 'Septiembre', 10 => 'Octubre', 11 => 'Noviembre', 12 => 'Diciembre'
];
+function splitProvinciaDistrito($value) {
+ $value = trim((string)($value ?? ''));
+ if ($value === '') {
+ return ['N/A', 'N/A'];
+ }
+
+ $parts = preg_split('/\s*(?:\/|\||,|-)\s*/', $value, 2);
+ $provincia = trim($parts[0] ?? '');
+ $distrito = trim($parts[1] ?? '');
+
+ return [
+ $provincia !== '' ? $provincia : 'N/A',
+ $distrito !== '' ? $distrito : 'N/A',
+ ];
+}
+
?>
/* Ajustes puntuales para Ruta Contraentrega: opciones largas sin invadir columnas vecinas */
#pedidos-table {
- min-width: 1750px;
+ min-width: 1850px;
}
#pedidos-table th:nth-child(3),
#pedidos-table td:nth-child(3) {
@@ -224,6 +240,19 @@ include 'layout_header.php';
Limpiar
+ $search_query,
+ 'mes' => $selected_month,
+ 'año' => $selected_year,
+ ], static function ($value) {
+ return $value !== '' && $value !== null;
+ });
+ $excelUrl = 'download_ruta_contraentrega.php' . (!empty($excelParams) ? '?' . http_build_query($excelParams) : '');
+ ?>
+
+ Descargar Excel
+
@@ -244,8 +273,9 @@ include 'layout_header.php';
| Celular |
Dirección |
Referencia |
- Ciudad |
- Provincia/Distrito |
+ Departamento |
+ Provincia |
+ Distrito |
Coordenadas |
Producto |
Cant. |
@@ -313,8 +343,10 @@ include 'layout_header.php';
|
|
+
|
- |
+ |
+ |
|
|
|
diff --git a/save_pedido_contraentrega.php b/save_pedido_contraentrega.php
index 5111bb9c..052741f6 100644
--- a/save_pedido_contraentrega.php
+++ b/save_pedido_contraentrega.php
@@ -10,6 +10,7 @@ if (!isset($_SESSION['user_id'])) {
}
require_once 'db/config.php';
+require_once __DIR__ . '/includes/contraentrega_cobertura.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = db();
@@ -20,8 +21,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nombre_completo = trim($_POST['nombre_completo'] ?? '');
$celular = trim($_POST['celular'] ?? '');
$agencia = trim($_POST['agencia'] ?? 'CONTRAENTREGA');
+ $provinciasPorDepartamentoContraentrega = contraentregaProvinciasPorDepartamento();
+ $distritosPorProvinciaContraentrega = contraentregaDistritosPorProvincia();
+ $departamentosContraentrega = array_keys($provinciasPorDepartamentoContraentrega);
$sede_envio = trim($_POST['sede_envio'] ?? '');
+ $provincia = trim($_POST['provincia'] ?? '');
+ $distrito = trim($_POST['distrito'] ?? '');
$codigo_rastreo = trim($_POST['codigo_rastreo'] ?? '');
+ if ($provincia !== '' || $distrito !== '') {
+ $codigo_rastreo = trim($provincia . ' / ' . $distrito, ' /');
+ }
$codigo_tracking = trim($_POST['codigo_tracking'] ?? '');
$direccion_exacta = trim($_POST['direccion_exacta'] ?? '');
$referencia_domicilio = trim($_POST['referencia_domicilio'] ?? '');
@@ -76,9 +85,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!empty($productos_detalle)) {
$notas .= "\n\n" . $notas_adicionales;
}
+
+ if (!in_array($sede_envio, $departamentosContraentrega, true)) {
+ $error_message = urlencode('Seleccione un departamento válido para el pedido contra entrega.');
+ $id_param = $id ? '&id=' . $id : '';
+ header('Location: pedidos_contraentrega.php?error=' . $error_message . $id_param);
+ exit;
+ }
+
+ if ($provincia !== '' && !in_array($provincia, $provinciasPorDepartamentoContraentrega[$sede_envio] ?? [], true)) {
+ $error_message = urlencode('Seleccione una provincia válida para el departamento elegido.');
+ $id_param = $id ? '&id=' . $id : '';
+ header('Location: pedidos_contraentrega.php?error=' . $error_message . $id_param);
+ exit;
+ }
+
+ if ($distrito !== '' && isset($distritosPorProvinciaContraentrega[$provincia]) && !in_array($distrito, $distritosPorProvinciaContraentrega[$provincia], true)) {
+ $error_message = urlencode('Seleccione un distrito válido para la provincia elegida.');
+ $id_param = $id ? '&id=' . $id : '';
+ header('Location: pedidos_contraentrega.php?error=' . $error_message . $id_param);
+ exit;
+ }
- if (empty($nombre_completo) || empty($celular) || empty($sede_envio) || empty($producto) || $cantidad === false || $monto_total === false || empty($coordenadas)) {
- $error_message = urlencode('Por favor, complete todos los campos obligatorios, incluyendo las coordenadas.');
+ if (empty($nombre_completo) || empty($celular) || empty($sede_envio) || empty($provincia) || empty($distrito) || empty($producto) || $cantidad === false || $monto_total === false || empty($coordenadas)) {
+ $error_message = urlencode('Por favor, complete todos los campos obligatorios: departamento, provincia, distrito y coordenadas.');
$id_param = $id ? '&id=' . $id : '';
header('Location: pedidos_contraentrega.php?error=' . $error_message . $id_param);
exit;
diff --git a/save_product.php b/save_product.php
index 4d741883..04e4c9f6 100644
--- a/save_product.php
+++ b/save_product.php
@@ -26,6 +26,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nombre = $_POST['nombre'] ?? '';
$codigo_base = $_POST['codigo_base'] ?? '';
$sku = !empty($_POST['sku']) ? $_POST['sku'] : null;
+ $ean = isset($_POST['ean']) ? preg_replace('/\s+/', '', trim($_POST['ean'])) : '';
+ $ean = $ean !== '' ? $ean : null;
+ if ($ean !== null && !preg_match('/^[0-9]{1,32}$/', $ean)) {
+ $_SESSION['error_message'] = 'El EAN solo debe contener números.';
+ header('Location: ' . (!empty($id) ? 'edit_product.php?id=' . urlencode($id) : 'edit_product.php'));
+ exit;
+ }
$costo = !empty($_POST['costo']) ? (float)$_POST['costo'] : 0.00;
$precio_venta = !empty($_POST['precio_venta']) ? (float)$_POST['precio_venta'] : 0.00;
$description = $_POST['description'] ?? '';
@@ -42,15 +49,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!empty($id)) {
// Actualizar producto existente
- $sql = "UPDATE products SET nombre = ?, sku = ?, costo = ?, precio_venta = ?, description = ?, provincia_id = ?, show_on_panel = ?, codigo_base = ? WHERE id = ?";
+ $sql = "UPDATE products SET nombre = ?, sku = ?, ean = ?, costo = ?, precio_venta = ?, description = ?, provincia_id = ?, show_on_panel = ?, codigo_base = ? WHERE id = ?";
$stmt = $db->prepare($sql);
- $stmt->execute([$nombre, $sku, $costo, $precio_venta, $description, $provincia_id, $show_on_panel, $codigo_base, $id]);
+ $stmt->execute([$nombre, $sku, $ean, $costo, $precio_venta, $description, $provincia_id, $show_on_panel, $codigo_base, $id]);
$_SESSION['success_message'] = "Producto actualizado exitosamente.";
} else {
// Crear nuevo producto
- $sql = "INSERT INTO products (nombre, sku, costo, precio_venta, description, provincia_id, show_on_panel, codigo_base, unidades_vendidas) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0)";
+ $sql = "INSERT INTO products (nombre, sku, ean, costo, precio_venta, description, provincia_id, show_on_panel, codigo_base, unidades_vendidas) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0)";
$stmt = $db->prepare($sql);
- $stmt->execute([$nombre, $sku, $costo, $precio_venta, $description, $provincia_id, $show_on_panel, $codigo_base]);
+ $stmt->execute([$nombre, $sku, $ean, $costo, $precio_venta, $description, $provincia_id, $show_on_panel, $codigo_base]);
$_SESSION['success_message'] = "Producto creado exitosamente.";
}
diff --git a/update_product_ean.php b/update_product_ean.php
new file mode 100644
index 00000000..cb7ac5c0
--- /dev/null
+++ b/update_product_ean.php
@@ -0,0 +1,43 @@
+prepare('UPDATE products SET ean = :ean WHERE id = :id');
+ $stmt->bindValue(':ean', $eanToSave, $eanToSave === null ? PDO::PARAM_NULL : PDO::PARAM_STR);
+ $stmt->bindValue(':id', $productId, PDO::PARAM_INT);
+ $stmt->execute();
+
+ if ($stmt->rowCount() === 0) {
+ $check = db()->prepare('SELECT id FROM products WHERE id = :id');
+ $check->bindValue(':id', $productId, PDO::PARAM_INT);
+ $check->execute();
+ if (!$check->fetchColumn()) {
+ throw new Exception('Producto no encontrado.');
+ }
+ }
+
+ echo json_encode(['success' => true, 'ean' => $eanToSave ?? '']);
+} catch (Exception $e) {
+ http_response_code(400);
+ echo json_encode(['success' => false, 'message' => $e->getMessage()]);
+}