1377 lines
56 KiB
PHP
1377 lines
56 KiB
PHP
<?php
|
|
session_start();
|
|
require_once 'db/config.php';
|
|
require_once 'includes/callcenter_test_helpers.php';
|
|
require_once 'includes/contraentrega_cobertura.php';
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
if (!isset($_SESSION['user_id']) || !cc_test_current_user_can_access_module(db())) {
|
|
http_response_code(403);
|
|
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
|
exit;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
http_response_code(405);
|
|
echo json_encode(['success' => false, 'message' => 'Método no permitido']);
|
|
exit;
|
|
}
|
|
|
|
function cc_test_normalize_nullable_text(string $key, int $maxLen = 3000): ?string
|
|
{
|
|
$value = trim((string) ($_POST[$key] ?? ''));
|
|
if ($value === '') {
|
|
return null;
|
|
}
|
|
|
|
if (mb_strlen($value) > $maxLen) {
|
|
throw new RuntimeException('El campo ' . $key . ' es demasiado largo.');
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
function cc_test_normalize_optional_image_upload(string $key): ?array
|
|
{
|
|
if (!isset($_FILES[$key]) || !is_array($_FILES[$key])) {
|
|
return null;
|
|
}
|
|
|
|
$file = $_FILES[$key];
|
|
$error = (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE);
|
|
if ($error === UPLOAD_ERR_NO_FILE) {
|
|
return null;
|
|
}
|
|
|
|
if ($error !== UPLOAD_ERR_OK) {
|
|
throw new RuntimeException('No se pudo subir la imagen de sustento.');
|
|
}
|
|
|
|
$tmpName = (string) ($file['tmp_name'] ?? '');
|
|
if ($tmpName === '' || !is_uploaded_file($tmpName)) {
|
|
throw new RuntimeException('La imagen de sustento no es válida.');
|
|
}
|
|
|
|
$size = (int) ($file['size'] ?? 0);
|
|
if ($size <= 0) {
|
|
throw new RuntimeException('La imagen de sustento está vacía.');
|
|
}
|
|
|
|
if ($size > 8 * 1024 * 1024) {
|
|
throw new RuntimeException('La imagen de sustento no puede pesar más de 8 MB.');
|
|
}
|
|
|
|
return $file;
|
|
}
|
|
|
|
function cc_test_store_tracking_evidence_image(array $file, string $prefix, string $sourceKey, int $userId): array
|
|
{
|
|
$tmpName = (string) ($file['tmp_name'] ?? '');
|
|
$imgInfo = @getimagesize($tmpName);
|
|
if ($imgInfo === false || empty($imgInfo['mime'])) {
|
|
throw new RuntimeException('El archivo no parece ser una imagen válida.');
|
|
}
|
|
|
|
$mime = (string) $imgInfo['mime'];
|
|
$allowed = [
|
|
'image/jpeg' => 'jpg',
|
|
'image/png' => 'png',
|
|
'image/webp' => 'webp',
|
|
];
|
|
|
|
if (!isset($allowed[$mime])) {
|
|
throw new RuntimeException('Formato no permitido. Use JPG, PNG o WEBP.');
|
|
}
|
|
|
|
$uploadDir = __DIR__ . '/assets/uploads/callcenter_tracking';
|
|
if (!is_dir($uploadDir) && !mkdir($uploadDir, 0775, true) && !is_dir($uploadDir)) {
|
|
throw new RuntimeException('No se pudo preparar la carpeta de imágenes.');
|
|
}
|
|
|
|
$filename = sprintf(
|
|
'%s_%s_%d_%d_%s.%s',
|
|
$prefix,
|
|
substr($sourceKey, 0, 12),
|
|
max(0, $userId),
|
|
time(),
|
|
bin2hex(random_bytes(4)),
|
|
$allowed[$mime]
|
|
);
|
|
|
|
$targetPath = $uploadDir . '/' . $filename;
|
|
if (!move_uploaded_file($tmpName, $targetPath)) {
|
|
throw new RuntimeException('No se pudo guardar la imagen en el servidor.');
|
|
}
|
|
|
|
return [
|
|
'absolute_path' => $targetPath,
|
|
'relative_path' => 'assets/uploads/callcenter_tracking/' . $filename,
|
|
];
|
|
}
|
|
|
|
function cc_test_fetch_source_order(PDO $pdo, string $sourceKey): ?array
|
|
{
|
|
$stmt = $pdo->prepare(
|
|
'SELECT
|
|
source_key,
|
|
store_key,
|
|
codigo,
|
|
import_id,
|
|
drive_imported_at,
|
|
first_seen_at,
|
|
nombre,
|
|
celular,
|
|
producto,
|
|
cantidad,
|
|
precio,
|
|
coordenadas,
|
|
direccion_drive,
|
|
referencia_drive,
|
|
sede_drive,
|
|
ciudad_drive,
|
|
distrito_drive,
|
|
dni_drive,
|
|
observaciones_drive
|
|
FROM callcenter_test_orders
|
|
WHERE source_key = ?
|
|
LIMIT 1'
|
|
);
|
|
$stmt->execute([$sourceKey]);
|
|
|
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
return $row ?: null;
|
|
}
|
|
|
|
function cc_test_build_contraentrega_notes(array $parts): string
|
|
{
|
|
$cleanParts = [];
|
|
foreach ($parts as $part) {
|
|
$part = trim((string) $part);
|
|
if ($part !== '') {
|
|
$cleanParts[] = $part;
|
|
}
|
|
}
|
|
|
|
return implode("\n\n", $cleanParts);
|
|
}
|
|
|
|
function cc_test_prepare_route_order_payload(array $sourceOrder, array $formData, int $fallbackUserId): array
|
|
{
|
|
if (($sourceOrder['store_key'] ?? '') !== 'otra_tienda') {
|
|
throw new RuntimeException('En este primer paso la subida automática a ruta está activa solo para TUANI.');
|
|
}
|
|
|
|
$nombreCompleto = trim((string) ($sourceOrder['nombre'] ?? ''));
|
|
$celular = trim((string) ($sourceOrder['celular'] ?? ''));
|
|
$direccion = trim((string) ($formData['direccion'] ?? ''));
|
|
$referencia = trim((string) ($formData['referencia'] ?? ''));
|
|
$departamento = trim((string) ($formData['sede'] ?? ''));
|
|
$provincia = trim((string) ($formData['ciudad'] ?? ''));
|
|
$distrito = trim((string) ($formData['distrito'] ?? ''));
|
|
$dni = trim((string) (($formData['dni'] ?? '') !== '' ? ($formData['dni'] ?? '') : ($sourceOrder['dni_drive'] ?? '')));
|
|
$observacionesPedido = trim((string) (($formData['observaciones'] ?? '') !== '' ? ($formData['observaciones'] ?? '') : ($sourceOrder['observaciones_drive'] ?? '')));
|
|
$notaSeguimiento = trim((string) ($formData['nota_seguimiento'] ?? ''));
|
|
$fechaEntrega = $formData['fecha_entrega_programada'] ?? null;
|
|
$coordenadasRaw = trim((string) ($formData['coordenadas'] ?? ''));
|
|
|
|
if ($nombreCompleto === '' || $celular === '') {
|
|
throw new RuntimeException('El pedido no tiene nombre o celular suficientes para enviarlo a Ruta Contraentrega.');
|
|
}
|
|
|
|
if ($fechaEntrega === null || $fechaEntrega === '') {
|
|
throw new RuntimeException('Debes seleccionar la fecha de entrega antes de subir el pedido.');
|
|
}
|
|
|
|
if ($direccion === '' || $departamento === '' || $provincia === '' || $distrito === '') {
|
|
throw new RuntimeException('Para subir el pedido completa dirección, departamento, provincia y distrito.');
|
|
}
|
|
|
|
$departamentos = contraentregaProvinciasPorDepartamento();
|
|
$distritosPorProvincia = contraentregaDistritosPorProvincia();
|
|
|
|
if (!array_key_exists($departamento, $departamentos)) {
|
|
throw new RuntimeException('Selecciona un departamento válido para Ruta Contraentrega.');
|
|
}
|
|
|
|
$provinciasPermitidas = $departamentos[$departamento] ?? [];
|
|
if ($provincia === '' || !in_array($provincia, $provinciasPermitidas, true)) {
|
|
throw new RuntimeException('Selecciona una provincia válida para el departamento elegido.');
|
|
}
|
|
|
|
$distritosPermitidos = $distritosPorProvincia[$provincia] ?? null;
|
|
if (is_array($distritosPermitidos) && !empty($distritosPermitidos) && !in_array($distrito, $distritosPermitidos, true)) {
|
|
throw new RuntimeException('Selecciona un distrito válido para la provincia elegida.');
|
|
}
|
|
|
|
$coordenadas = cc_test_normalize_contraentrega_coordinates($coordenadasRaw);
|
|
if ($coordenadas === null) {
|
|
throw new RuntimeException('Ingresa coordenadas válidas con el formato -12.082029, -77.069024.');
|
|
}
|
|
|
|
$productoBase = trim((string) ($formData['producto'] ?? ''));
|
|
if ($productoBase === '') {
|
|
$productoBase = trim((string) ($sourceOrder['producto'] ?? ''));
|
|
}
|
|
$cantidadBase = trim((string) ($formData['cantidad'] ?? ''));
|
|
if ($cantidadBase === '') {
|
|
$cantidadBase = trim((string) ($sourceOrder['cantidad'] ?? ''));
|
|
}
|
|
$precioBase = trim((string) ($formData['precio'] ?? ''));
|
|
if ($precioBase === '') {
|
|
$precioBase = trim((string) ($sourceOrder['precio'] ?? ''));
|
|
}
|
|
|
|
$productoPrincipal = trim((string) ($formData['confirmacion_producto'] ?? ''));
|
|
if ($productoPrincipal === '') {
|
|
$productoPrincipal = $productoBase;
|
|
}
|
|
|
|
$cantidadPrincipalRaw = trim((string) ($formData['confirmacion_cantidad'] ?? ''));
|
|
if ($cantidadPrincipalRaw === '') {
|
|
$cantidadPrincipalRaw = $cantidadBase;
|
|
}
|
|
|
|
$precioPrincipalRaw = trim((string) ($formData['confirmacion_precio'] ?? ''));
|
|
if ($precioPrincipalRaw === '') {
|
|
$precioPrincipalRaw = $precioBase;
|
|
}
|
|
|
|
if ($productoPrincipal === '') {
|
|
throw new RuntimeException('El pedido no tiene producto principal para subir a Ruta Contraentrega.');
|
|
}
|
|
|
|
$cantidadPrincipal = cc_test_parse_quantity($cantidadPrincipalRaw);
|
|
if ($cantidadPrincipal === null) {
|
|
throw new RuntimeException('La cantidad principal del pedido no es válida.');
|
|
}
|
|
|
|
$precioPrincipal = cc_test_parse_amount($precioPrincipalRaw);
|
|
if ($precioPrincipal === null) {
|
|
throw new RuntimeException('El precio principal del pedido no es válido.');
|
|
}
|
|
|
|
$productoExtra = trim((string) ($formData['confirmacion_producto_extra'] ?? ''));
|
|
$cantidadExtraRaw = trim((string) ($formData['confirmacion_cantidad_extra'] ?? ''));
|
|
$precioExtraRaw = trim((string) ($formData['confirmacion_precio_extra'] ?? ''));
|
|
$hayExtra = $productoExtra !== '' || $cantidadExtraRaw !== '' || $precioExtraRaw !== '';
|
|
|
|
$productos = [$productoPrincipal];
|
|
$detalleProductos = [$productoPrincipal . ' (x' . $cantidadPrincipal . ')'];
|
|
$cantidadTotal = $cantidadPrincipal;
|
|
$montoTotal = $precioPrincipal;
|
|
|
|
if ($hayExtra) {
|
|
if ($productoExtra === '' || $cantidadExtraRaw === '' || $precioExtraRaw === '') {
|
|
throw new RuntimeException('Si usas producto adicional, completa producto, cantidad y precio adicional.');
|
|
}
|
|
|
|
$cantidadExtra = cc_test_parse_quantity($cantidadExtraRaw);
|
|
if ($cantidadExtra === null) {
|
|
throw new RuntimeException('La cantidad del producto adicional no es válida.');
|
|
}
|
|
|
|
$precioExtra = cc_test_parse_amount($precioExtraRaw);
|
|
if ($precioExtra === null) {
|
|
throw new RuntimeException('El precio del producto adicional no es válido.');
|
|
}
|
|
|
|
$productos[] = $productoExtra;
|
|
$detalleProductos[] = $productoExtra . ' (x' . $cantidadExtra . ')';
|
|
$cantidadTotal += $cantidadExtra;
|
|
$montoTotal += $precioExtra;
|
|
}
|
|
|
|
$identificadorPedido = trim((string) ($sourceOrder['codigo'] ?? ''));
|
|
if ($identificadorPedido === '') {
|
|
$identificadorPedido = trim((string) ($sourceOrder['import_id'] ?? ''));
|
|
}
|
|
if ($identificadorPedido === '') {
|
|
$identificadorPedido = 'source:' . substr((string) ($sourceOrder['source_key'] ?? ''), 0, 12);
|
|
}
|
|
|
|
$notas = cc_test_build_contraentrega_notes([
|
|
'Origen: Call Center TUANI',
|
|
'Pedido base: ' . $identificadorPedido,
|
|
'Detalle confirmado: ' . implode(', ', $detalleProductos),
|
|
$observacionesPedido !== '' ? 'Observaciones del pedido: ' . $observacionesPedido : '',
|
|
$notaSeguimiento !== '' ? 'Nota interna Call Center: ' . $notaSeguimiento : '',
|
|
]);
|
|
|
|
$seguimiento = 'Subido desde Call Center TUANI';
|
|
$codigoTracking = trim((string) ($sourceOrder['codigo'] ?? ''));
|
|
|
|
return [
|
|
'source_key' => (string) ($sourceOrder['source_key'] ?? ''),
|
|
'dni_cliente' => $dni,
|
|
'nombre_completo' => $nombreCompleto,
|
|
'celular' => $celular,
|
|
'agencia' => 'CONTRAENTREGA',
|
|
'sede_envio' => $departamento,
|
|
'codigo_rastreo' => trim($provincia . ' / ' . $distrito, ' /'),
|
|
'codigo_tracking' => $codigoTracking !== '' ? $codigoTracking : null,
|
|
'direccion_exacta' => $direccion,
|
|
'referencia_domicilio' => $referencia !== '' ? $referencia : null,
|
|
'coordenadas' => $coordenadas,
|
|
'producto' => implode(', ', $productos),
|
|
'cantidad' => $cantidadTotal,
|
|
'monto_total' => round($montoTotal, 2),
|
|
'asesor_id' => $fallbackUserId > 0 ? $fallbackUserId : null,
|
|
'notas' => $notas,
|
|
'nota_adicional' => $notaSeguimiento !== '' ? $notaSeguimiento : null,
|
|
'observacion' => $observacionesPedido !== '' ? $observacionesPedido : null,
|
|
'descargo' => $observacionesPedido !== '' ? $observacionesPedido : null,
|
|
'seguimiento' => $seguimiento,
|
|
'fecha_entrega' => $fechaEntrega,
|
|
'tipo_paquete' => 'CONTRAENTREGA',
|
|
'estado' => 'RUTA_CONTRAENTREGA',
|
|
];
|
|
}
|
|
|
|
function cc_test_sync_route_order(PDO $pdo, ?int $existingPedidoId, array $payload): array
|
|
{
|
|
$existing = null;
|
|
if ($existingPedidoId !== null && $existingPedidoId > 0) {
|
|
$stmtExisting = $pdo->prepare('SELECT * FROM pedidos WHERE id = ? LIMIT 1');
|
|
$stmtExisting->execute([$existingPedidoId]);
|
|
$existing = $stmtExisting->fetch(PDO::FETCH_ASSOC) ?: null;
|
|
}
|
|
|
|
if ($existing) {
|
|
$montoAdelantado = round((float) ($existing['monto_adelantado'] ?? 0), 2);
|
|
$montoDebe = round(max(0, $payload['monto_total'] - $montoAdelantado), 2);
|
|
|
|
$stmtUpdate = $pdo->prepare(
|
|
'UPDATE pedidos SET
|
|
dni_cliente = :dni_cliente,
|
|
nombre_completo = :nombre_completo,
|
|
celular = :celular,
|
|
agencia = :agencia,
|
|
sede_envio = :sede_envio,
|
|
codigo_rastreo = :codigo_rastreo,
|
|
codigo_tracking = COALESCE(NULLIF(:codigo_tracking, \'\'), codigo_tracking),
|
|
referencia_domicilio = :referencia_domicilio,
|
|
direccion_exacta = :direccion_exacta,
|
|
coordenadas = :coordenadas,
|
|
producto = :producto,
|
|
cantidad = :cantidad,
|
|
monto_total = :monto_total,
|
|
monto_debe = :monto_debe,
|
|
asesor_id = :asesor_id,
|
|
notas = :notas,
|
|
nota_adicional = :nota_adicional,
|
|
observacion = :observacion,
|
|
descargo = :descargo,
|
|
seguimiento = :seguimiento,
|
|
fecha_entrega = :fecha_entrega,
|
|
tipo_paquete = :tipo_paquete,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = :id'
|
|
);
|
|
|
|
$stmtUpdate->execute([
|
|
':dni_cliente' => $payload['dni_cliente'],
|
|
':nombre_completo' => $payload['nombre_completo'],
|
|
':celular' => $payload['celular'],
|
|
':agencia' => $payload['agencia'],
|
|
':sede_envio' => $payload['sede_envio'],
|
|
':codigo_rastreo' => $payload['codigo_rastreo'],
|
|
':codigo_tracking' => $payload['codigo_tracking'] ?? '',
|
|
':referencia_domicilio' => $payload['referencia_domicilio'],
|
|
':direccion_exacta' => $payload['direccion_exacta'],
|
|
':coordenadas' => $payload['coordenadas'],
|
|
':producto' => $payload['producto'],
|
|
':cantidad' => $payload['cantidad'],
|
|
':monto_total' => $payload['monto_total'],
|
|
':monto_debe' => $montoDebe,
|
|
':asesor_id' => $payload['asesor_id'],
|
|
':notas' => !empty(trim((string) ($existing['notas'] ?? ''))) ? $existing['notas'] : $payload['notas'],
|
|
':nota_adicional' => !empty(trim((string) ($existing['nota_adicional'] ?? ''))) ? $existing['nota_adicional'] : $payload['nota_adicional'],
|
|
':observacion' => !empty(trim((string) ($existing['observacion'] ?? ''))) ? $existing['observacion'] : $payload['observacion'],
|
|
':descargo' => !empty(trim((string) ($existing['descargo'] ?? ''))) ? $existing['descargo'] : $payload['descargo'],
|
|
':seguimiento' => !empty(trim((string) ($existing['seguimiento'] ?? ''))) ? $existing['seguimiento'] : $payload['seguimiento'],
|
|
':fecha_entrega' => $payload['fecha_entrega'],
|
|
':tipo_paquete' => $payload['tipo_paquete'],
|
|
':id' => (int) $existing['id'],
|
|
]);
|
|
|
|
return [
|
|
'pedido_id' => (int) $existing['id'],
|
|
'action' => 'updated',
|
|
];
|
|
}
|
|
|
|
$stmtInsert = $pdo->prepare(
|
|
'INSERT INTO pedidos (
|
|
dni_cliente,
|
|
nombre_completo,
|
|
celular,
|
|
agencia,
|
|
sede_envio,
|
|
codigo_rastreo,
|
|
codigo_tracking,
|
|
referencia_domicilio,
|
|
direccion_exacta,
|
|
coordenadas,
|
|
producto,
|
|
cantidad,
|
|
monto_total,
|
|
monto_adelantado,
|
|
monto_debe,
|
|
estado,
|
|
asesor_id,
|
|
notas,
|
|
nota_adicional,
|
|
observacion,
|
|
descargo,
|
|
seguimiento,
|
|
fecha_entrega,
|
|
tipo_paquete
|
|
) VALUES (
|
|
:dni_cliente,
|
|
:nombre_completo,
|
|
:celular,
|
|
:agencia,
|
|
:sede_envio,
|
|
:codigo_rastreo,
|
|
:codigo_tracking,
|
|
:referencia_domicilio,
|
|
:direccion_exacta,
|
|
:coordenadas,
|
|
:producto,
|
|
:cantidad,
|
|
:monto_total,
|
|
0,
|
|
:monto_debe,
|
|
:estado,
|
|
:asesor_id,
|
|
:notas,
|
|
:nota_adicional,
|
|
:observacion,
|
|
:descargo,
|
|
:seguimiento,
|
|
:fecha_entrega,
|
|
:tipo_paquete
|
|
)'
|
|
);
|
|
|
|
$stmtInsert->execute([
|
|
':dni_cliente' => $payload['dni_cliente'],
|
|
':nombre_completo' => $payload['nombre_completo'],
|
|
':celular' => $payload['celular'],
|
|
':agencia' => $payload['agencia'],
|
|
':sede_envio' => $payload['sede_envio'],
|
|
':codigo_rastreo' => $payload['codigo_rastreo'],
|
|
':codigo_tracking' => $payload['codigo_tracking'],
|
|
':referencia_domicilio' => $payload['referencia_domicilio'],
|
|
':direccion_exacta' => $payload['direccion_exacta'],
|
|
':coordenadas' => $payload['coordenadas'],
|
|
':producto' => $payload['producto'],
|
|
':cantidad' => $payload['cantidad'],
|
|
':monto_total' => $payload['monto_total'],
|
|
':monto_debe' => $payload['monto_total'],
|
|
':estado' => $payload['estado'],
|
|
':asesor_id' => $payload['asesor_id'],
|
|
':notas' => $payload['notas'],
|
|
':nota_adicional' => $payload['nota_adicional'],
|
|
':observacion' => $payload['observacion'],
|
|
':descargo' => $payload['descargo'],
|
|
':seguimiento' => $payload['seguimiento'],
|
|
':fecha_entrega' => $payload['fecha_entrega'],
|
|
':tipo_paquete' => $payload['tipo_paquete'],
|
|
]);
|
|
|
|
return [
|
|
'pedido_id' => (int) $pdo->lastInsertId(),
|
|
'action' => 'created',
|
|
];
|
|
}
|
|
|
|
|
|
function cc_test_prepare_rotulado_order_payload(array $sourceOrder, array $formData, int $fallbackUserId): array
|
|
{
|
|
if (($sourceOrder['store_key'] ?? '') !== 'otra_tienda') {
|
|
throw new RuntimeException('En este primer paso la subida automática a Pedidos Rotulados está activa solo para TUANI.');
|
|
}
|
|
|
|
$nombreCompleto = trim((string) ($sourceOrder['nombre'] ?? ''));
|
|
$celular = trim((string) ($sourceOrder['celular'] ?? ''));
|
|
$dni = trim((string) (($formData['dni'] ?? '') !== '' ? ($formData['dni'] ?? '') : ($sourceOrder['dni_drive'] ?? '')));
|
|
$agencia = cc_test_normalize_shipping_agency($formData['agencia'] ?? '');
|
|
$sedeEnvio = trim((string) ($formData['sede_agencia'] ?? ''));
|
|
$montoAdelantado = cc_test_parse_amount((string) ($formData['monto_adelantado'] ?? ''));
|
|
$observacionesPedido = trim((string) (($formData['observaciones'] ?? '') !== '' ? ($formData['observaciones'] ?? '') : ($sourceOrder['observaciones_drive'] ?? '')));
|
|
$notaSeguimiento = trim((string) ($formData['nota_seguimiento'] ?? ''));
|
|
|
|
if ($nombreCompleto === '' || $celular === '') {
|
|
throw new RuntimeException('El pedido no tiene nombre o celular suficientes para enviarlo a Pedidos Rotulados.');
|
|
}
|
|
|
|
if ($dni === '') {
|
|
throw new RuntimeException('Debes completar el DNI antes de subir el pedido a Pedidos Rotulados.');
|
|
}
|
|
|
|
if ($agencia === null || !in_array($agencia, cc_test_shipping_agency_options(), true)) {
|
|
throw new RuntimeException('Selecciona una agencia válida para Pedidos Rotulados.');
|
|
}
|
|
|
|
if ($sedeEnvio === '') {
|
|
throw new RuntimeException('Debes completar la sede de envío antes de subir el pedido a Pedidos Rotulados.');
|
|
}
|
|
|
|
if ($montoAdelantado === null) {
|
|
throw new RuntimeException('Debes ingresar un monto de adelanto válido antes de subir el pedido a Pedidos Rotulados.');
|
|
}
|
|
|
|
$productoBase = trim((string) ($formData['producto'] ?? ''));
|
|
if ($productoBase === '') {
|
|
$productoBase = trim((string) ($sourceOrder['producto'] ?? ''));
|
|
}
|
|
$cantidadBase = trim((string) ($formData['cantidad'] ?? ''));
|
|
if ($cantidadBase === '') {
|
|
$cantidadBase = trim((string) ($sourceOrder['cantidad'] ?? ''));
|
|
}
|
|
$precioBase = trim((string) ($formData['precio'] ?? ''));
|
|
if ($precioBase === '') {
|
|
$precioBase = trim((string) ($sourceOrder['precio'] ?? ''));
|
|
}
|
|
|
|
$productoPrincipal = trim((string) ($formData['confirmacion_producto'] ?? ''));
|
|
if ($productoPrincipal === '') {
|
|
$productoPrincipal = $productoBase;
|
|
}
|
|
|
|
$cantidadPrincipalRaw = trim((string) ($formData['confirmacion_cantidad'] ?? ''));
|
|
if ($cantidadPrincipalRaw === '') {
|
|
$cantidadPrincipalRaw = $cantidadBase;
|
|
}
|
|
|
|
$precioPrincipalRaw = trim((string) ($formData['confirmacion_precio'] ?? ''));
|
|
if ($precioPrincipalRaw === '') {
|
|
$precioPrincipalRaw = $precioBase;
|
|
}
|
|
|
|
if ($productoPrincipal === '') {
|
|
throw new RuntimeException('El pedido no tiene producto principal para subir a Pedidos Rotulados.');
|
|
}
|
|
|
|
$cantidadPrincipal = cc_test_parse_quantity($cantidadPrincipalRaw);
|
|
if ($cantidadPrincipal === null) {
|
|
throw new RuntimeException('La cantidad principal del pedido no es válida.');
|
|
}
|
|
|
|
$precioPrincipal = cc_test_parse_amount($precioPrincipalRaw);
|
|
if ($precioPrincipal === null) {
|
|
throw new RuntimeException('El precio principal del pedido no es válido.');
|
|
}
|
|
|
|
$productoExtra = trim((string) ($formData['confirmacion_producto_extra'] ?? ''));
|
|
$cantidadExtraRaw = trim((string) ($formData['confirmacion_cantidad_extra'] ?? ''));
|
|
$precioExtraRaw = trim((string) ($formData['confirmacion_precio_extra'] ?? ''));
|
|
$hayExtra = $productoExtra !== '' || $cantidadExtraRaw !== '' || $precioExtraRaw !== '';
|
|
|
|
$productos = [$productoPrincipal];
|
|
$detalleProductos = [$productoPrincipal . ' (x' . $cantidadPrincipal . ')'];
|
|
$cantidadTotal = $cantidadPrincipal;
|
|
$montoTotal = $precioPrincipal;
|
|
|
|
if ($hayExtra) {
|
|
if ($productoExtra === '' || $cantidadExtraRaw === '' || $precioExtraRaw === '') {
|
|
throw new RuntimeException('Si usas producto adicional, completa producto, cantidad y precio adicional.');
|
|
}
|
|
|
|
$cantidadExtra = cc_test_parse_quantity($cantidadExtraRaw);
|
|
if ($cantidadExtra === null) {
|
|
throw new RuntimeException('La cantidad del producto adicional no es válida.');
|
|
}
|
|
|
|
$precioExtra = cc_test_parse_amount($precioExtraRaw);
|
|
if ($precioExtra === null) {
|
|
throw new RuntimeException('El precio del producto adicional no es válido.');
|
|
}
|
|
|
|
$productos[] = $productoExtra;
|
|
$detalleProductos[] = $productoExtra . ' (x' . $cantidadExtra . ')';
|
|
$cantidadTotal += $cantidadExtra;
|
|
$montoTotal += $precioExtra;
|
|
}
|
|
|
|
$identificadorPedido = trim((string) ($sourceOrder['codigo'] ?? ''));
|
|
if ($identificadorPedido === '') {
|
|
$identificadorPedido = trim((string) ($sourceOrder['import_id'] ?? ''));
|
|
}
|
|
if ($identificadorPedido === '') {
|
|
$identificadorPedido = 'source:' . substr((string) ($sourceOrder['source_key'] ?? ''), 0, 12);
|
|
}
|
|
|
|
$notas = cc_test_build_contraentrega_notes([
|
|
'Origen: Call Center TUANI',
|
|
'Pedido base: ' . $identificadorPedido,
|
|
'Detalle confirmado: ' . implode(', ', $detalleProductos),
|
|
'Agencia: ' . $agencia,
|
|
'Sede de envío: ' . $sedeEnvio,
|
|
'Monto de adelanto: S/ ' . number_format($montoAdelantado, 2, '.', ''),
|
|
$observacionesPedido !== '' ? 'Observaciones del pedido: ' . $observacionesPedido : '',
|
|
$notaSeguimiento !== '' ? 'Nota interna Call Center: ' . $notaSeguimiento : '',
|
|
]);
|
|
|
|
$codigoTracking = trim((string) ($sourceOrder['codigo'] ?? ''));
|
|
$montoTotal = round($montoTotal, 2);
|
|
$montoAdelantado = round($montoAdelantado, 2);
|
|
$montoDebe = round(max(0, $montoTotal - $montoAdelantado), 2);
|
|
|
|
return [
|
|
'source_key' => (string) ($sourceOrder['source_key'] ?? ''),
|
|
'dni_cliente' => $dni,
|
|
'nombre_completo' => $nombreCompleto,
|
|
'celular' => $celular,
|
|
'agencia' => $agencia,
|
|
'sede_envio' => $sedeEnvio,
|
|
'codigo_tracking' => $codigoTracking !== '' ? $codigoTracking : null,
|
|
'producto' => implode(', ', $productos),
|
|
'cantidad' => $cantidadTotal,
|
|
'monto_total' => $montoTotal,
|
|
'monto_adelantado' => $montoAdelantado,
|
|
'monto_debe' => $montoDebe,
|
|
'asesor_id' => $fallbackUserId > 0 ? $fallbackUserId : null,
|
|
'notas' => $notas,
|
|
'nota_adicional' => $notaSeguimiento !== '' ? $notaSeguimiento : null,
|
|
'observacion' => $observacionesPedido !== '' ? $observacionesPedido : null,
|
|
'descargo' => $observacionesPedido !== '' ? $observacionesPedido : null,
|
|
'seguimiento' => 'Subido desde Call Center TUANI',
|
|
'estado' => 'ROTULADO 📦',
|
|
];
|
|
}
|
|
|
|
function cc_test_sync_rotulado_order(PDO $pdo, ?int $existingPedidoId, array $payload): array
|
|
{
|
|
$existing = null;
|
|
if ($existingPedidoId !== null && $existingPedidoId > 0) {
|
|
$stmtExisting = $pdo->prepare('SELECT * FROM pedidos WHERE id = ? LIMIT 1');
|
|
$stmtExisting->execute([$existingPedidoId]);
|
|
$existing = $stmtExisting->fetch(PDO::FETCH_ASSOC) ?: null;
|
|
}
|
|
|
|
if ($existing) {
|
|
$stmtUpdate = $pdo->prepare(
|
|
'UPDATE pedidos SET
|
|
dni_cliente = :dni_cliente,
|
|
nombre_completo = :nombre_completo,
|
|
celular = :celular,
|
|
agencia = :agencia,
|
|
sede_envio = :sede_envio,
|
|
codigo_tracking = COALESCE(NULLIF(:codigo_tracking, \'\'), codigo_tracking),
|
|
producto = :producto,
|
|
cantidad = :cantidad,
|
|
monto_total = :monto_total,
|
|
monto_adelantado = :monto_adelantado,
|
|
monto_debe = :monto_debe,
|
|
asesor_id = :asesor_id,
|
|
notas = :notas,
|
|
nota_adicional = :nota_adicional,
|
|
observacion = :observacion,
|
|
descargo = :descargo,
|
|
seguimiento = :seguimiento,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = :id'
|
|
);
|
|
|
|
$stmtUpdate->execute([
|
|
':dni_cliente' => $payload['dni_cliente'],
|
|
':nombre_completo' => $payload['nombre_completo'],
|
|
':celular' => $payload['celular'],
|
|
':agencia' => $payload['agencia'],
|
|
':sede_envio' => $payload['sede_envio'],
|
|
':codigo_tracking' => $payload['codigo_tracking'] ?? '',
|
|
':producto' => $payload['producto'],
|
|
':cantidad' => $payload['cantidad'],
|
|
':monto_total' => $payload['monto_total'],
|
|
':monto_adelantado' => $payload['monto_adelantado'],
|
|
':monto_debe' => $payload['monto_debe'],
|
|
':asesor_id' => $payload['asesor_id'],
|
|
':notas' => !empty(trim((string) ($existing['notas'] ?? ''))) ? $existing['notas'] : $payload['notas'],
|
|
':nota_adicional' => !empty(trim((string) ($existing['nota_adicional'] ?? ''))) ? $existing['nota_adicional'] : $payload['nota_adicional'],
|
|
':observacion' => !empty(trim((string) ($existing['observacion'] ?? ''))) ? $existing['observacion'] : $payload['observacion'],
|
|
':descargo' => !empty(trim((string) ($existing['descargo'] ?? ''))) ? $existing['descargo'] : $payload['descargo'],
|
|
':seguimiento' => !empty(trim((string) ($existing['seguimiento'] ?? ''))) ? $existing['seguimiento'] : $payload['seguimiento'],
|
|
':id' => (int) $existing['id'],
|
|
]);
|
|
|
|
return [
|
|
'pedido_id' => (int) $existing['id'],
|
|
'action' => 'updated',
|
|
];
|
|
}
|
|
|
|
$stmtInsert = $pdo->prepare(
|
|
'INSERT INTO pedidos (
|
|
dni_cliente,
|
|
nombre_completo,
|
|
celular,
|
|
agencia,
|
|
sede_envio,
|
|
codigo_tracking,
|
|
producto,
|
|
cantidad,
|
|
monto_total,
|
|
monto_adelantado,
|
|
monto_debe,
|
|
estado,
|
|
asesor_id,
|
|
notas,
|
|
nota_adicional,
|
|
observacion,
|
|
descargo,
|
|
seguimiento
|
|
) VALUES (
|
|
:dni_cliente,
|
|
:nombre_completo,
|
|
:celular,
|
|
:agencia,
|
|
:sede_envio,
|
|
:codigo_tracking,
|
|
:producto,
|
|
:cantidad,
|
|
:monto_total,
|
|
:monto_adelantado,
|
|
:monto_debe,
|
|
:estado,
|
|
:asesor_id,
|
|
:notas,
|
|
:nota_adicional,
|
|
:observacion,
|
|
:descargo,
|
|
:seguimiento
|
|
)'
|
|
);
|
|
|
|
$stmtInsert->execute([
|
|
':dni_cliente' => $payload['dni_cliente'],
|
|
':nombre_completo' => $payload['nombre_completo'],
|
|
':celular' => $payload['celular'],
|
|
':agencia' => $payload['agencia'],
|
|
':sede_envio' => $payload['sede_envio'],
|
|
':codigo_tracking' => $payload['codigo_tracking'],
|
|
':producto' => $payload['producto'],
|
|
':cantidad' => $payload['cantidad'],
|
|
':monto_total' => $payload['monto_total'],
|
|
':monto_adelantado' => $payload['monto_adelantado'],
|
|
':monto_debe' => $payload['monto_debe'],
|
|
':estado' => $payload['estado'],
|
|
':asesor_id' => $payload['asesor_id'],
|
|
':notas' => $payload['notas'],
|
|
':nota_adicional' => $payload['nota_adicional'],
|
|
':observacion' => $payload['observacion'],
|
|
':descargo' => $payload['descargo'],
|
|
':seguimiento' => $payload['seguimiento'],
|
|
]);
|
|
|
|
return [
|
|
'pedido_id' => (int) $pdo->lastInsertId(),
|
|
'action' => 'created',
|
|
];
|
|
}
|
|
|
|
$sourceKey = trim((string) ($_POST['source_key'] ?? ''));
|
|
$estado = cc_test_normalize_state(trim((string) ($_POST['estado'] ?? 'POR LLAMAR')));
|
|
$validStates = cc_test_valid_states();
|
|
$subirALogistica = (string) ($_POST['subir_a_logistica'] ?? ($_POST['subir_a_ruta'] ?? '')) === '1';
|
|
$moverAPromoFinal = (string) ($_POST['mover_a_promo_final'] ?? '') === '1';
|
|
$eliminarPedido = (string) ($_POST['eliminar_pedido'] ?? '') === '1';
|
|
|
|
if ($sourceKey === '' || !preg_match('/^[a-f0-9]{40}$/', $sourceKey)) {
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'message' => 'Pedido de prueba inválido']);
|
|
exit;
|
|
}
|
|
|
|
if (!in_array($estado, $validStates, true)) {
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'message' => 'Estado inválido']);
|
|
exit;
|
|
}
|
|
|
|
$createdFiles = [];
|
|
|
|
try {
|
|
$nota = cc_test_normalize_nullable_text('nota_seguimiento', 3000);
|
|
$direccion = cc_test_normalize_nullable_text('direccion', 1000);
|
|
$referencia = cc_test_normalize_nullable_text('referencia', 1000);
|
|
$agencia = cc_test_normalize_nullable_text('agencia', 80);
|
|
$sedeAgencia = cc_test_normalize_nullable_text('sede_agencia', 120);
|
|
$sede = cc_test_normalize_nullable_text('sede', 120);
|
|
$ciudad = cc_test_normalize_nullable_text('ciudad', 120);
|
|
$distrito = cc_test_normalize_nullable_text('distrito', 120);
|
|
$coordenadas = cc_test_normalize_nullable_text('coordenadas', 255);
|
|
$dni = cc_test_normalize_nullable_text('dni', 40);
|
|
$numeroCuentaSedeId = array_key_exists('numero_cuenta_sede_id', $_POST)
|
|
? cc_test_normalize_nullable_text('numero_cuenta_sede_id', 120)
|
|
: null;
|
|
$numeroCuentaDni = array_key_exists('numero_cuenta_dni', $_POST)
|
|
? cc_test_normalize_nullable_text('numero_cuenta_dni', 40)
|
|
: null;
|
|
$observaciones = cc_test_normalize_nullable_text('observaciones', 3000);
|
|
$producto = cc_test_normalize_nullable_text('producto', 255);
|
|
$cantidad = cc_test_normalize_nullable_text('cantidad', 50);
|
|
$precio = cc_test_normalize_nullable_text('precio', 80);
|
|
$montoAdelantadoRaw = cc_test_normalize_nullable_text('monto_adelantado', 80);
|
|
$montoAdelantado = null;
|
|
if ($montoAdelantadoRaw !== null) {
|
|
$montoAdelantado = cc_test_parse_amount($montoAdelantadoRaw);
|
|
if ($montoAdelantado === null) {
|
|
throw new RuntimeException('Ingresa un monto de adelanto válido.');
|
|
}
|
|
}
|
|
if ($agencia !== null) {
|
|
$agencia = cc_test_normalize_shipping_agency($agencia) ?? $agencia;
|
|
}
|
|
$confirmacionProducto = cc_test_normalize_nullable_text('confirmacion_producto', 255);
|
|
$confirmacionCantidad = cc_test_normalize_nullable_text('confirmacion_cantidad', 50);
|
|
$confirmacionPrecio = cc_test_normalize_nullable_text('confirmacion_precio', 80);
|
|
$confirmacionProductoExtra = cc_test_normalize_nullable_text('confirmacion_producto_extra', 255);
|
|
$confirmacionCantidadExtra = cc_test_normalize_nullable_text('confirmacion_cantidad_extra', 50);
|
|
$confirmacionPrecioExtra = cc_test_normalize_nullable_text('confirmacion_precio_extra', 80);
|
|
$promoFinalEvidenceFile = cc_test_normalize_optional_image_upload('promo_final_evidencia');
|
|
$canceladoEvidenceFile = cc_test_normalize_optional_image_upload('cancelado_evidencia');
|
|
|
|
$pdo = db();
|
|
cc_test_ensure_tracking_table($pdo);
|
|
|
|
$stmtCurrent = $pdo->prepare('SELECT estado, numero_cuenta_enviado_at, numero_cuenta_sede_id, numero_cuenta_dni, user_id, promo_final_evidencia_path, promo_final_evidencia_subido_at, promo_final_evidencia_subido_por, cancelado_evidencia_path, cancelado_evidencia_subido_at, cancelado_evidencia_subido_por, eliminado_at, eliminado_por, ruta_contraentrega_pedido_id, pedido_rotulado_pedido_id FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1');
|
|
$stmtCurrent->execute([$sourceKey]);
|
|
$currentTracking = $stmtCurrent->fetch(PDO::FETCH_ASSOC) ?: null;
|
|
|
|
if (!array_key_exists('numero_cuenta_sede_id', $_POST)) {
|
|
$numeroCuentaSedeIdCurrent = trim((string) ($currentTracking['numero_cuenta_sede_id'] ?? ''));
|
|
$numeroCuentaSedeId = $numeroCuentaSedeIdCurrent !== '' ? $numeroCuentaSedeIdCurrent : null;
|
|
}
|
|
|
|
if (!array_key_exists('numero_cuenta_dni', $_POST)) {
|
|
$numeroCuentaDniCurrent = trim((string) ($currentTracking['numero_cuenta_dni'] ?? ''));
|
|
$numeroCuentaDni = $numeroCuentaDniCurrent !== '' ? $numeroCuentaDniCurrent : null;
|
|
}
|
|
|
|
$promoFinalEvidencePath = trim((string) ($currentTracking['promo_final_evidencia_path'] ?? ''));
|
|
$promoFinalEvidencePath = $promoFinalEvidencePath !== '' ? $promoFinalEvidencePath : null;
|
|
$promoFinalEvidenceUploadedAt = $currentTracking['promo_final_evidencia_subido_at'] ?? null;
|
|
$promoFinalEvidenceUploadedBy = isset($currentTracking['promo_final_evidencia_subido_por']) && (int) $currentTracking['promo_final_evidencia_subido_por'] > 0
|
|
? (int) $currentTracking['promo_final_evidencia_subido_por']
|
|
: null;
|
|
$canceladoEvidencePath = trim((string) ($currentTracking['cancelado_evidencia_path'] ?? ''));
|
|
$canceladoEvidencePath = $canceladoEvidencePath !== '' ? $canceladoEvidencePath : null;
|
|
$canceladoEvidenceUploadedAt = $currentTracking['cancelado_evidencia_subido_at'] ?? null;
|
|
$canceladoEvidenceUploadedBy = isset($currentTracking['cancelado_evidencia_subido_por']) && (int) $currentTracking['cancelado_evidencia_subido_por'] > 0
|
|
? (int) $currentTracking['cancelado_evidencia_subido_por']
|
|
: null;
|
|
|
|
$role = (string) ($_SESSION['user_role'] ?? '');
|
|
$isAdmin = in_array($role, ['Administrador', 'admin'], true);
|
|
if (!$isAdmin) {
|
|
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
|
|
$trackingUserId = $currentTracking ? (int) ($currentTracking['user_id'] ?? 0) : null;
|
|
if (!$currentTracking || $trackingUserId !== $currentUserId) {
|
|
http_response_code(403);
|
|
echo json_encode(['success' => false, 'message' => 'No autorizado para gestionar este pedido.']);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
$deletedAtCurrent = trim((string) ($currentTracking['eliminado_at'] ?? ''));
|
|
$isDeleted = $deletedAtCurrent !== '';
|
|
|
|
if ($eliminarPedido) {
|
|
if (!$isAdmin) {
|
|
http_response_code(403);
|
|
echo json_encode(['success' => false, 'message' => 'Solo un administrador puede eliminar pedidos del panel.']);
|
|
exit;
|
|
}
|
|
|
|
if ($isDeleted) {
|
|
echo json_encode(['success' => true, 'message' => 'El pedido ya estaba eliminado del panel.']);
|
|
exit;
|
|
}
|
|
|
|
$deleteState = cc_test_normalize_state((string) ($currentTracking['estado'] ?? $estado));
|
|
if (!in_array($deleteState, $validStates, true)) {
|
|
$deleteState = 'POR LLAMAR';
|
|
}
|
|
|
|
$deleteUserId = $currentTracking && (int) ($currentTracking['user_id'] ?? 0) > 0
|
|
? (int) $currentTracking['user_id']
|
|
: null;
|
|
$deletedAt = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
|
|
$deletedBy = (int) ($_SESSION['user_id'] ?? 0);
|
|
|
|
$stmtDelete = $pdo->prepare(
|
|
'INSERT INTO callcenter_test_tracking (
|
|
source_key,
|
|
estado,
|
|
user_id,
|
|
eliminado_at,
|
|
eliminado_por,
|
|
ultima_gestion_at
|
|
) VALUES (
|
|
:source_key,
|
|
:estado,
|
|
:user_id,
|
|
:eliminado_at,
|
|
:eliminado_por,
|
|
CURRENT_TIMESTAMP
|
|
)
|
|
ON DUPLICATE KEY UPDATE
|
|
estado = VALUES(estado),
|
|
user_id = VALUES(user_id),
|
|
eliminado_at = VALUES(eliminado_at),
|
|
eliminado_por = VALUES(eliminado_por),
|
|
ultima_gestion_at = CURRENT_TIMESTAMP,
|
|
updated_at = CURRENT_TIMESTAMP'
|
|
);
|
|
$stmtDelete->bindValue(':source_key', $sourceKey, PDO::PARAM_STR);
|
|
$stmtDelete->bindValue(':estado', $deleteState, PDO::PARAM_STR);
|
|
if ($deleteUserId === null) {
|
|
$stmtDelete->bindValue(':user_id', null, PDO::PARAM_NULL);
|
|
} else {
|
|
$stmtDelete->bindValue(':user_id', $deleteUserId, PDO::PARAM_INT);
|
|
}
|
|
$stmtDelete->bindValue(':eliminado_at', $deletedAt, PDO::PARAM_STR);
|
|
if ($deletedBy > 0) {
|
|
$stmtDelete->bindValue(':eliminado_por', $deletedBy, PDO::PARAM_INT);
|
|
} else {
|
|
$stmtDelete->bindValue(':eliminado_por', null, PDO::PARAM_NULL);
|
|
}
|
|
$stmtDelete->execute();
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => 'Pedido eliminado del panel correctamente.',
|
|
'eliminado_at' => $deletedAt,
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
if ($isDeleted) {
|
|
throw new RuntimeException('Este pedido fue eliminado del panel por un administrador.');
|
|
}
|
|
|
|
$userIdToSet = $isAdmin
|
|
? ($currentTracking ? ((int) ($currentTracking['user_id'] ?? 0) > 0 ? (int) $currentTracking['user_id'] : null) : null)
|
|
: (int) $_SESSION['user_id'];
|
|
|
|
$proximaRaw = trim((string) ($_POST['proxima_llamada_at'] ?? ''));
|
|
$proximaLlamada = null;
|
|
if ($proximaRaw !== '') {
|
|
$proximaLlamada = DateTimeImmutable::createFromFormat('Y-m-d\TH:i', $proximaRaw);
|
|
if (!$proximaLlamada) {
|
|
throw new RuntimeException('La fecha de próxima llamada no es válida.');
|
|
}
|
|
$proximaLlamada = $proximaLlamada->format('Y-m-d H:i:s');
|
|
}
|
|
|
|
$fechaEntregaRaw = trim((string) ($_POST['fecha_entrega_programada'] ?? ''));
|
|
$fechaEntrega = null;
|
|
if ($fechaEntregaRaw !== '') {
|
|
$fechaEntregaDate = DateTimeImmutable::createFromFormat('Y-m-d', $fechaEntregaRaw);
|
|
if (!$fechaEntregaDate) {
|
|
throw new RuntimeException('La fecha de entrega no es válida.');
|
|
}
|
|
$fechaEntrega = $fechaEntregaDate->format('Y-m-d');
|
|
}
|
|
|
|
if (cc_test_requires_delivery_date($estado) && $fechaEntrega === null) {
|
|
throw new RuntimeException('Debes seleccionar la fecha de entrega para CONFIRMADO CONTRAENTREGA.');
|
|
}
|
|
|
|
if (!in_array($estado, cc_test_open_states(), true)) {
|
|
$proximaLlamada = null;
|
|
}
|
|
|
|
if (!cc_test_requires_delivery_date($estado)) {
|
|
$fechaEntrega = null;
|
|
}
|
|
|
|
$numeroCuentaEnviadoAt = $currentTracking['numero_cuenta_enviado_at'] ?? null;
|
|
$currentState = cc_test_normalize_state((string) ($currentTracking['estado'] ?? ''));
|
|
if ($estado === 'SE ENVIO NUMERO DE CUENTA') {
|
|
if ($currentState !== 'SE ENVIO NUMERO DE CUENTA' || trim((string) $numeroCuentaEnviadoAt) === '') {
|
|
$numeroCuentaEnviadoAt = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
|
|
}
|
|
}
|
|
|
|
if (cc_test_requires_shipping_details($estado)) {
|
|
if ($dni === null || trim($dni) === '') {
|
|
throw new RuntimeException('Debes completar el DNI para CONFIRMADO ENVIO.');
|
|
}
|
|
|
|
if ($agencia === null || !in_array($agencia, cc_test_shipping_agency_options(), true)) {
|
|
throw new RuntimeException('Selecciona una agencia válida para CONFIRMADO ENVIO.');
|
|
}
|
|
|
|
if ($sedeAgencia === null || trim($sedeAgencia) === '') {
|
|
throw new RuntimeException('Debes completar la sede de envío para CONFIRMADO ENVIO.');
|
|
}
|
|
|
|
if ($montoAdelantado === null) {
|
|
throw new RuntimeException('Debes ingresar el monto de adelanto para CONFIRMADO ENVIO.');
|
|
}
|
|
}
|
|
|
|
if ($subirALogistica && !in_array($estado, ['CONFIRMADO CONTRAENTREGA', 'CONFIRMADO ENVIO'], true)) {
|
|
throw new RuntimeException('Para subir el pedido, el estado debe ser CONFIRMADO CONTRAENTREGA o CONFIRMADO ENVIO.');
|
|
}
|
|
|
|
if ($moverAPromoFinal) {
|
|
if (!in_array($estado, cc_test_followup_tracking_states(), true)) {
|
|
throw new RuntimeException('Solo puedes mover a Promo Final pedidos que sigan en seguimiento.');
|
|
}
|
|
|
|
$sourceOrderForPromo = cc_test_fetch_source_order($pdo, $sourceKey);
|
|
if (!$sourceOrderForPromo) {
|
|
throw new RuntimeException('No encontré el pedido base para moverlo a Promo Final.');
|
|
}
|
|
|
|
$promoFinalDayInfo = cc_test_followup_day_info([
|
|
'estado' => $estado,
|
|
'drive_imported_at' => $sourceOrderForPromo['drive_imported_at'] ?? null,
|
|
'first_seen_at' => $sourceOrderForPromo['first_seen_at'] ?? null,
|
|
'import_id' => $sourceOrderForPromo['import_id'] ?? null,
|
|
]);
|
|
if (empty($promoFinalDayInfo['is_promo_final'])) {
|
|
throw new RuntimeException('Este pedido aún no puede pasar a Promo Final. Recién se habilita desde el Día 4.');
|
|
}
|
|
|
|
if ($promoFinalEvidenceFile === null && $promoFinalEvidencePath === null) {
|
|
throw new RuntimeException('Debes subir una imagen de sustento para mover el pedido a Promo Final.');
|
|
}
|
|
}
|
|
|
|
if ($estado === 'CANCELADO' && $canceladoEvidenceFile === null && $canceladoEvidencePath === null) {
|
|
throw new RuntimeException('Debes subir una imagen de sustento para guardar el pedido en estado Cancelado.');
|
|
}
|
|
|
|
$actorUserId = (int) ($_SESSION['user_id'] ?? 0);
|
|
if ($moverAPromoFinal && $promoFinalEvidenceFile !== null) {
|
|
$storedPromoFinalEvidence = cc_test_store_tracking_evidence_image($promoFinalEvidenceFile, 'promo_final', $sourceKey, $actorUserId);
|
|
$createdFiles[] = $storedPromoFinalEvidence['absolute_path'];
|
|
$promoFinalEvidencePath = $storedPromoFinalEvidence['relative_path'];
|
|
$promoFinalEvidenceUploadedAt = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
|
|
$promoFinalEvidenceUploadedBy = $actorUserId > 0 ? $actorUserId : null;
|
|
}
|
|
|
|
if ($estado === 'CANCELADO' && $canceladoEvidenceFile !== null) {
|
|
$storedCanceladoEvidence = cc_test_store_tracking_evidence_image($canceladoEvidenceFile, 'cancelado', $sourceKey, $actorUserId);
|
|
$createdFiles[] = $storedCanceladoEvidence['absolute_path'];
|
|
$canceladoEvidencePath = $storedCanceladoEvidence['relative_path'];
|
|
$canceladoEvidenceUploadedAt = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
|
|
$canceladoEvidenceUploadedBy = $actorUserId > 0 ? $actorUserId : null;
|
|
}
|
|
|
|
$formData = [
|
|
'nota_seguimiento' => $nota,
|
|
'direccion' => $direccion,
|
|
'referencia' => $referencia,
|
|
'agencia' => $agencia,
|
|
'sede_agencia' => $sedeAgencia,
|
|
'sede' => $sede,
|
|
'ciudad' => $ciudad,
|
|
'distrito' => $distrito,
|
|
'coordenadas' => $coordenadas,
|
|
'dni' => $dni,
|
|
'numero_cuenta_sede_id' => $numeroCuentaSedeId,
|
|
'numero_cuenta_dni' => $numeroCuentaDni,
|
|
'observaciones' => $observaciones,
|
|
'producto' => $producto,
|
|
'cantidad' => $cantidad,
|
|
'precio' => $precio,
|
|
'monto_adelantado' => $montoAdelantado,
|
|
'confirmacion_producto' => $confirmacionProducto,
|
|
'confirmacion_cantidad' => $confirmacionCantidad,
|
|
'confirmacion_precio' => $confirmacionPrecio,
|
|
'confirmacion_producto_extra' => $confirmacionProductoExtra,
|
|
'confirmacion_cantidad_extra' => $confirmacionCantidadExtra,
|
|
'confirmacion_precio_extra' => $confirmacionPrecioExtra,
|
|
'fecha_entrega_programada' => $fechaEntrega,
|
|
];
|
|
|
|
$pdo->beginTransaction();
|
|
|
|
$stmt = $pdo->prepare(
|
|
'INSERT INTO callcenter_test_tracking (
|
|
source_key,
|
|
estado,
|
|
nota_seguimiento,
|
|
user_id,
|
|
direccion,
|
|
referencia,
|
|
agencia,
|
|
sede_agencia,
|
|
sede,
|
|
ciudad,
|
|
distrito,
|
|
coordenadas,
|
|
dni,
|
|
numero_cuenta_sede_id,
|
|
numero_cuenta_dni,
|
|
observaciones,
|
|
producto,
|
|
cantidad,
|
|
precio,
|
|
monto_adelantado,
|
|
confirmacion_producto,
|
|
confirmacion_cantidad,
|
|
confirmacion_precio,
|
|
confirmacion_producto_extra,
|
|
confirmacion_cantidad_extra,
|
|
confirmacion_precio_extra,
|
|
proxima_llamada_at,
|
|
fecha_entrega_programada,
|
|
numero_cuenta_enviado_at,
|
|
promo_final_evidencia_path,
|
|
promo_final_evidencia_subido_at,
|
|
promo_final_evidencia_subido_por,
|
|
cancelado_evidencia_path,
|
|
cancelado_evidencia_subido_at,
|
|
cancelado_evidencia_subido_por,
|
|
ultima_gestion_at
|
|
) VALUES (
|
|
:source_key,
|
|
:estado,
|
|
:nota,
|
|
:user_id,
|
|
:direccion,
|
|
:referencia,
|
|
:agencia,
|
|
:sede_agencia,
|
|
:sede,
|
|
:ciudad,
|
|
:distrito,
|
|
:coordenadas,
|
|
:dni,
|
|
:numero_cuenta_sede_id,
|
|
:numero_cuenta_dni,
|
|
:observaciones,
|
|
:producto,
|
|
:cantidad,
|
|
:precio,
|
|
:monto_adelantado,
|
|
:confirmacion_producto,
|
|
:confirmacion_cantidad,
|
|
:confirmacion_precio,
|
|
:confirmacion_producto_extra,
|
|
:confirmacion_cantidad_extra,
|
|
:confirmacion_precio_extra,
|
|
:proxima_llamada_at,
|
|
:fecha_entrega_programada,
|
|
:numero_cuenta_enviado_at,
|
|
:promo_final_evidencia_path,
|
|
:promo_final_evidencia_subido_at,
|
|
:promo_final_evidencia_subido_por,
|
|
:cancelado_evidencia_path,
|
|
:cancelado_evidencia_subido_at,
|
|
:cancelado_evidencia_subido_por,
|
|
CURRENT_TIMESTAMP
|
|
)
|
|
ON DUPLICATE KEY UPDATE
|
|
estado = VALUES(estado),
|
|
nota_seguimiento = VALUES(nota_seguimiento),
|
|
user_id = VALUES(user_id),
|
|
direccion = VALUES(direccion),
|
|
referencia = VALUES(referencia),
|
|
agencia = VALUES(agencia),
|
|
sede_agencia = VALUES(sede_agencia),
|
|
sede = VALUES(sede),
|
|
ciudad = VALUES(ciudad),
|
|
distrito = VALUES(distrito),
|
|
coordenadas = VALUES(coordenadas),
|
|
dni = VALUES(dni),
|
|
numero_cuenta_sede_id = VALUES(numero_cuenta_sede_id),
|
|
numero_cuenta_dni = VALUES(numero_cuenta_dni),
|
|
observaciones = VALUES(observaciones),
|
|
producto = VALUES(producto),
|
|
cantidad = VALUES(cantidad),
|
|
precio = VALUES(precio),
|
|
monto_adelantado = VALUES(monto_adelantado),
|
|
confirmacion_producto = VALUES(confirmacion_producto),
|
|
confirmacion_cantidad = VALUES(confirmacion_cantidad),
|
|
confirmacion_precio = VALUES(confirmacion_precio),
|
|
confirmacion_producto_extra = VALUES(confirmacion_producto_extra),
|
|
confirmacion_cantidad_extra = VALUES(confirmacion_cantidad_extra),
|
|
confirmacion_precio_extra = VALUES(confirmacion_precio_extra),
|
|
proxima_llamada_at = VALUES(proxima_llamada_at),
|
|
fecha_entrega_programada = VALUES(fecha_entrega_programada),
|
|
numero_cuenta_enviado_at = VALUES(numero_cuenta_enviado_at),
|
|
promo_final_evidencia_path = VALUES(promo_final_evidencia_path),
|
|
promo_final_evidencia_subido_at = VALUES(promo_final_evidencia_subido_at),
|
|
promo_final_evidencia_subido_por = VALUES(promo_final_evidencia_subido_por),
|
|
cancelado_evidencia_path = VALUES(cancelado_evidencia_path),
|
|
cancelado_evidencia_subido_at = VALUES(cancelado_evidencia_subido_at),
|
|
cancelado_evidencia_subido_por = VALUES(cancelado_evidencia_subido_por),
|
|
ultima_gestion_at = CURRENT_TIMESTAMP,
|
|
updated_at = CURRENT_TIMESTAMP'
|
|
);
|
|
|
|
$stmt->execute([
|
|
':source_key' => $sourceKey,
|
|
':estado' => $estado,
|
|
':nota' => $nota,
|
|
':user_id' => $userIdToSet,
|
|
':direccion' => $direccion,
|
|
':referencia' => $referencia,
|
|
':agencia' => $agencia,
|
|
':sede_agencia' => $sedeAgencia,
|
|
':sede' => $sede,
|
|
':ciudad' => $ciudad,
|
|
':distrito' => $distrito,
|
|
':coordenadas' => $coordenadas,
|
|
':dni' => $dni,
|
|
':numero_cuenta_sede_id' => $numeroCuentaSedeId,
|
|
':numero_cuenta_dni' => $numeroCuentaDni,
|
|
':observaciones' => $observaciones,
|
|
':producto' => $producto,
|
|
':cantidad' => $cantidad,
|
|
':precio' => $precio,
|
|
':monto_adelantado' => $montoAdelantado,
|
|
':confirmacion_producto' => $confirmacionProducto,
|
|
':confirmacion_cantidad' => $confirmacionCantidad,
|
|
':confirmacion_precio' => $confirmacionPrecio,
|
|
':confirmacion_producto_extra' => $confirmacionProductoExtra,
|
|
':confirmacion_cantidad_extra' => $confirmacionCantidadExtra,
|
|
':confirmacion_precio_extra' => $confirmacionPrecioExtra,
|
|
':proxima_llamada_at' => $proximaLlamada,
|
|
':fecha_entrega_programada' => $fechaEntrega,
|
|
':numero_cuenta_enviado_at' => $numeroCuentaEnviadoAt,
|
|
':promo_final_evidencia_path' => $promoFinalEvidencePath,
|
|
':promo_final_evidencia_subido_at' => $promoFinalEvidenceUploadedAt,
|
|
':promo_final_evidencia_subido_por' => $promoFinalEvidenceUploadedBy,
|
|
':cancelado_evidencia_path' => $canceladoEvidencePath,
|
|
':cancelado_evidencia_subido_at' => $canceladoEvidenceUploadedAt,
|
|
':cancelado_evidencia_subido_por' => $canceladoEvidenceUploadedBy,
|
|
]);
|
|
|
|
$rutaPedidoId = isset($currentTracking['ruta_contraentrega_pedido_id']) && (int) $currentTracking['ruta_contraentrega_pedido_id'] > 0
|
|
? (int) $currentTracking['ruta_contraentrega_pedido_id']
|
|
: null;
|
|
$rutaSubidoAt = null;
|
|
$rutaAction = null;
|
|
$pedidoRotuladoId = isset($currentTracking['pedido_rotulado_pedido_id']) && (int) $currentTracking['pedido_rotulado_pedido_id'] > 0
|
|
? (int) $currentTracking['pedido_rotulado_pedido_id']
|
|
: null;
|
|
$pedidoRotuladoSubidoAt = null;
|
|
$pedidoRotuladoAction = null;
|
|
|
|
if ($subirALogistica) {
|
|
$sourceOrder = cc_test_fetch_source_order($pdo, $sourceKey);
|
|
if (!$sourceOrder) {
|
|
throw new RuntimeException('No encontré el pedido base para enviarlo a logística.');
|
|
}
|
|
|
|
if ($estado === 'CONFIRMADO CONTRAENTREGA') {
|
|
$routePayload = cc_test_prepare_route_order_payload(
|
|
$sourceOrder,
|
|
$formData,
|
|
(int) ($userIdToSet ?: ($_SESSION['user_id'] ?? 0))
|
|
);
|
|
|
|
$routeSync = cc_test_sync_route_order($pdo, $rutaPedidoId, $routePayload);
|
|
$rutaPedidoId = (int) $routeSync['pedido_id'];
|
|
$rutaAction = $routeSync['action'];
|
|
$rutaSubidoAt = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
|
|
|
|
$stmtRuta = $pdo->prepare(
|
|
'UPDATE callcenter_test_tracking
|
|
SET ruta_contraentrega_pedido_id = :pedido_id,
|
|
ruta_contraentrega_subido_at = :subido_at,
|
|
ruta_contraentrega_subido_por = :subido_por,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE source_key = :source_key'
|
|
);
|
|
$stmtRuta->execute([
|
|
':pedido_id' => $rutaPedidoId,
|
|
':subido_at' => $rutaSubidoAt,
|
|
':subido_por' => (int) ($_SESSION['user_id'] ?? 0),
|
|
':source_key' => $sourceKey,
|
|
]);
|
|
}
|
|
|
|
if ($estado === 'CONFIRMADO ENVIO') {
|
|
$rotuladoPayload = cc_test_prepare_rotulado_order_payload(
|
|
$sourceOrder,
|
|
$formData,
|
|
(int) ($userIdToSet ?: ($_SESSION['user_id'] ?? 0))
|
|
);
|
|
|
|
$rotuladoSync = cc_test_sync_rotulado_order($pdo, $pedidoRotuladoId, $rotuladoPayload);
|
|
$pedidoRotuladoId = (int) $rotuladoSync['pedido_id'];
|
|
$pedidoRotuladoAction = $rotuladoSync['action'];
|
|
$pedidoRotuladoSubidoAt = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
|
|
|
|
$stmtRotulado = $pdo->prepare(
|
|
'UPDATE callcenter_test_tracking
|
|
SET pedido_rotulado_pedido_id = :pedido_id,
|
|
pedido_rotulado_subido_at = :subido_at,
|
|
pedido_rotulado_subido_por = :subido_por,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE source_key = :source_key'
|
|
);
|
|
$stmtRotulado->execute([
|
|
':pedido_id' => $pedidoRotuladoId,
|
|
':subido_at' => $pedidoRotuladoSubidoAt,
|
|
':subido_por' => (int) ($_SESSION['user_id'] ?? 0),
|
|
':source_key' => $sourceKey,
|
|
]);
|
|
}
|
|
}
|
|
|
|
$pdo->commit();
|
|
|
|
$message = 'Gestión actualizada correctamente.';
|
|
if ($moverAPromoFinal && $promoFinalEvidencePath !== null) {
|
|
$message = 'Pedido movido a Promo Final correctamente.';
|
|
}
|
|
if ($subirALogistica && $estado === 'CONFIRMADO CONTRAENTREGA' && $rutaPedidoId !== null) {
|
|
$message = $rutaAction === 'updated'
|
|
? 'Pedido actualizado en Ruta Contraentrega #' . $rutaPedidoId . '.'
|
|
: 'Pedido subido a Ruta Contraentrega #' . $rutaPedidoId . '.';
|
|
}
|
|
if ($subirALogistica && $estado === 'CONFIRMADO ENVIO' && $pedidoRotuladoId !== null) {
|
|
$message = $pedidoRotuladoAction === 'updated'
|
|
? 'Pedido actualizado en Pedidos Rotulados #' . $pedidoRotuladoId . '.'
|
|
: 'Pedido subido a Pedidos Rotulados #' . $pedidoRotuladoId . '.';
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => $message,
|
|
'estado' => $estado,
|
|
'proxima_llamada_at' => $proximaLlamada,
|
|
'fecha_entrega_programada' => $fechaEntrega,
|
|
'numero_cuenta_enviado_at' => $numeroCuentaEnviadoAt,
|
|
'numero_cuenta_sede_id' => $numeroCuentaSedeId,
|
|
'numero_cuenta_dni' => $numeroCuentaDni,
|
|
'promo_final_evidencia_path' => $promoFinalEvidencePath,
|
|
'promo_final_evidencia_subido_at' => $promoFinalEvidenceUploadedAt,
|
|
'cancelado_evidencia_path' => $canceladoEvidencePath,
|
|
'cancelado_evidencia_subido_at' => $canceladoEvidenceUploadedAt,
|
|
'ruta_contraentrega_pedido_id' => $rutaPedidoId,
|
|
'ruta_contraentrega_subido_at' => $rutaSubidoAt,
|
|
'pedido_rotulado_pedido_id' => $pedidoRotuladoId,
|
|
'pedido_rotulado_subido_at' => $pedidoRotuladoSubidoAt,
|
|
]);
|
|
} catch (Throwable $exception) {
|
|
if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
|
|
foreach ($createdFiles as $createdFile) {
|
|
if (is_string($createdFile) && $createdFile !== '' && file_exists($createdFile)) {
|
|
@unlink($createdFile);
|
|
}
|
|
}
|
|
|
|
http_response_code(500);
|
|
error_log('update_callcenter_test_tracking.php: ' . $exception->getMessage());
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => $exception instanceof RuntimeException ? $exception->getMessage() : 'No se pudo guardar la gestión.',
|
|
]);
|
|
}
|