// ── POST /api/preparar_pedidos.php?action=documentar_envio ────────────────── if ($method === 'POST' && (in_array($action, ['documentar_envio', 'documento_envio', 'documento-envio', 'documentar-envio']) || ($id > 0 && in_array($action, ['documentar_envio', 'documento_envio', 'documento-envio', 'documentar-envio'])))) { $input = !empty($jsonInput) ? $jsonInput : ($_POST ?? []); $pedidoId = intval($input['pedido_id'] ?? $id); if ($pedidoId <= 0) { json_response(["error" => "ID de pedido no especificado"], 400); } // Verificar pedido $stmtPed = $pdo->prepare(" SELECT p.id, p.numero_pedido, p.empresa_id, p.cliente_id, p.clientedestino_id, p.estado, c.nombre_fiscal AS cliente_nombre, c.telefono, d.descripcion AS destino_nombre, d.direccion, d.cp, d.poblacion, d.provincia FROM ge_pedidos p LEFT JOIN ge_clientes c ON p.cliente_id = c.id LEFT JOIN ge_clientes_destinos d ON p.clientedestino_id = d.id WHERE p.id = :id "); $stmtPed->execute(['id' => $pedidoId]); $ped = $stmtPed->fetch(); if (!$ped) { json_response(["error" => "Pedido no encontrado"], 404); } $empresaId = intval($ped['empresa_id'] ?: 1); $configId = intval($input['config_id'] ?? 0); // Si no se pasó config_id, obtener la primera activa if ($configId <= 0) { $stmtCfg = $pdo->prepare("SELECT id FROM ge_envios_config WHERE empresa_id = :emp AND activo = 1 ORDER BY id ASC LIMIT 1"); $stmtCfg->execute(['emp' => $empresaId]); $cfgRow = $stmtCfg->fetch(); if ($cfgRow) { $configId = intval($cfgRow['id']); } } if ($configId <= 0) { json_response(["error" => "No se ha encontrado ninguna configuración de transportista activa"], 400); } // Obtener transportista nombre $stmtTransp = $pdo->prepare(" SELECT t.nombre, t.tipo_api AS codigo, ec.nom_remitente AS nombre_remitente, ec.pob_remitente AS poblacion_remitente FROM ge_envios_config ec JOIN ge_envios_transportistas t ON ec.transportista_id = t.id WHERE ec.id = :id "); $stmtTransp->execute(['id' => $configId]); $transpRow = $stmtTransp->fetch(); $transpNombre = $transpRow ? $transpRow['nombre'] : 'TRANSPORTE'; // Obtener o crear albarán $albaranId = obtener_o_crear_albaran_pedido($pdo, $pedidoId, $user['userId']); $stmtAlb = $pdo->prepare("SELECT serie, numero FROM ge_albaranes WHERE id = :id"); $stmtAlb->execute(['id' => $albaranId]); $albRow = $stmtAlb->fetch(); $albaranCodigo = $albRow ? (trim($albRow['serie'] ?? 'A') . '/' . intval($albRow['numero'] ?? 0)) : ''; $codTipoServ = trim($input['cod_tipo_servicio'] ?? '48'); $numBultos = intval($input['num_bultos'] ?? 1); if ($numBultos <= 0) $numBultos = 1; $peso = floatval($input['peso'] ?? 1.0); $fechaRecogida = trim($input['fecha_recogida'] ?? date('Y-m-d')); $horaIni = trim($input['hora_ini'] ?? '09:00:00'); $horaFin = trim($input['hora_fin'] ?? '18:00:00'); $personaContacto = trim($input['persona_contacto'] ?? ($ped['cliente_nombre'] ?? '')); $conductor = trim($input['conductor'] ?? ''); $matricula = trim($input['matricula'] ?? ''); $firmaDni = trim($input['firma_documento'] ?? ''); $observaciones = trim($input['observaciones'] ?? ''); $direccion = trim($input['direccion'] ?? ($ped['direccion'] ?? '')); $cp = trim($input['cp'] ?? ($ped['cp'] ?? '')); $poblacion = trim($input['poblacion'] ?? ($ped['poblacion'] ?? '')); $provincia = trim($input['provincia'] ?? ($ped['provincia'] ?? '')); $pais = 'ES'; $telefono = trim($ped['telefono'] ?? ''); // Comprobar si ya existe recogida previa activa $stmtCheckRec = $pdo->prepare("SELECT id, cod_recogida FROM ge_envios_recogidas WHERE pedido_id = :ped AND estado_codigo <> 5 ORDER BY id DESC LIMIT 1"); $stmtCheckRec->execute(['ped' => $pedidoId]); $existingRec = $stmtCheckRec->fetch(); $codRecogida = $existingRec ? $existingRec['cod_recogida'] : ('ENV-' . date('Ymd') . '-' . str_pad($pedidoId, 5, '0', STR_PAD_LEFT)); // Generar Documento de Carga en Texto $doc = []; $doc[] = "============================================================"; $doc[] = " DOCUMENTO DE CONTROL DE CARGA Y ENVÍO "; $doc[] = "============================================================"; $doc[] = "Fecha Recogida: " . $fechaRecogida . " Horario: " . $horaIni . " - " . $horaFin; $doc[] = "Agencia / Transp: " . $transpNombre . " (" . $codTipoServ . ")"; $doc[] = "Nº Recogida / Ref: " . $codRecogida; $doc[] = "Pedido Cliente: " . ($ped['numero_pedido'] ?? $pedidoId) . " Albarán Venta: " . $albaranCodigo; $doc[] = "------------------------------------------------------------"; $doc[] = "REMITENTE: " . ($transpRow['nombre_remitente'] ?? 'RAUL ASENCIO PASTELERIAS'); $doc[] = "DESTINATARIO: " . ($ped['destino_nombre'] ?: $ped['cliente_nombre']); $doc[] = "Dirección: " . $direccion . " CP: " . $cp . " Población: " . $poblacion . " (" . $provincia . ")"; $doc[] = "Contacto: " . $personaContacto . " Tlf: " . $telefono; $doc[] = "------------------------------------------------------------"; $doc[] = "BULTOS: " . $numBultos . " PESO TOTAL ESTIMADO: " . number_format($peso, 2, ',', '.') . " Kg"; $doc[] = "CONDUCTOR: " . ($conductor ?: 'NO ESPECIFICADO') . " MATRÍCULA: " . ($matricula ?: 'NO ESPECIFICADA'); if ($firmaDni !== '') { $doc[] = "DNI CONDUCTOR: " . $firmaDni; } if ($observaciones !== '') { $doc[] = "OBSERVACIONES: " . $observaciones; } $doc[] = "------------------------------------------------------------"; $doc[] = "DESGLOSE DE MERCANCÍA / PACKING LIST:"; // Obtener líneas del pedido $stmtLin = $pdo->prepare(" SELECT pl.id_articulo, pl.descripcion, pl.cantidad, COALESCE(a.ean, CAST(pl.id_articulo AS CHAR)) AS articulo_codigo FROM ge_pedidos_lineas pl LEFT JOIN ge_articulos a ON pl.id_articulo = a.id WHERE pl.id_pedido = :id ORDER BY pl.id ASC "); $stmtLin->execute(['id' => $pedidoId]); $lineas = $stmtLin->fetchAll(); foreach ($lineas as $idx => $lin) { $cant = floatval($lin['cantidad']); $doc[] = sprintf(" [%02d] %-15s %-30s Cant: %6.2f", $idx + 1, substr($lin['articulo_codigo'] ?: '', 0, 15), substr($lin['descripcion'] ?: '', 0, 30), $cant); } $doc[] = "============================================================"; $doc[] = "Firma Conductor: ___________________ Firma Almacén: _______"; $doc[] = "============================================================"; $albaranCargaTexto = implode("\n", $doc); $firmaImagen = trim($input['firma_imagen'] ?? ''); $firmaImagenBlob = null; if ($firmaImagen !== '') { if (preg_match('/^data:image\/\w+;base64,/', $firmaImagen)) { $data = substr($firmaImagen, strpos($firmaImagen, ',') + 1); $firmaImagenBlob = base64_decode($data); } else { $firmaImagenBlob = $firmaImagen; } } $firmaNombre = $conductor ?: trim($ped['cliente_nombre'] ?? ''); $firmaFecha = ($firmaImagenBlob !== null) ? date('Y-m-d H:i:s') : null; if ($existingRec) { $recogidaId = intval($existingRec['id']); $stmtUpdRec = $pdo->prepare(" UPDATE ge_envios_recogidas SET albaran_id = :alb, empresa_id = :emp, config_id = :cfg, cod_tipo_servicio = :serv, num_bultos = :bul, fecha_recogida = :frec, hora_ini = :hini, hora_fin = :hfin, persona_contacto = :cont, conductor = :cond, matricula = :mat, observaciones = :obs, direccion = :dir, cp = :cp, poblacion = :pob, provincia = :prov, pais = :pais, telefono = :tel, albaran_carga = :doc, firma_nombre = COALESCE(NULLIF(:fnom, ''), firma_nombre), firma_documento = COALESCE(NULLIF(:fdoc, ''), firma_documento), firma_imagen = COALESCE(:fimg, firma_imagen), firma_fecha = COALESCE(:ffec, firma_fecha), estado_codigo = 0, estado_descripcion = 'Documentado', id_user_update = :u, updated_at = NOW() WHERE id = :id "); $stmtUpdRec->execute([ 'alb' => $albaranId, 'emp' => $empresaId, 'cfg' => $configId, 'serv' => $codTipoServ, 'bul' => $numBultos, 'frec' => $fechaRecogida, 'hini' => $horaIni, 'hfin' => $horaFin, 'cont' => $personaContacto, 'cond' => $conductor, 'mat' => $matricula, 'obs' => $observaciones, 'dir' => $direccion, 'cp' => $cp, 'pob' => $poblacion, 'prov' => $provincia, 'pais' => $pais, 'tel' => $telefono, 'doc' => $albaranCargaTexto, 'fnom' => $firmaNombre, 'fdoc' => $firmaDni, 'fimg' => $firmaImagenBlob, 'ffec' => $firmaFecha, 'u' => $user['userId'], 'id' => $recogidaId ]); } else { // Insertar en ge_envios_recogidas $stmtIns = $pdo->prepare(" INSERT INTO ge_envios_recogidas ( pedido_id, albaran_id, empresa_id, config_id, cod_recogida, cod_tipo_servicio, num_bultos, fecha_recogida, hora_ini, hora_fin, persona_contacto, conductor, matricula, referencia, observaciones, direccion, cp, poblacion, provincia, pais, telefono, albaran_carga, firma_nombre, firma_documento, firma_imagen, firma_fecha, estado_codigo, estado_descripcion, id_user_creator, id_user_update ) VALUES ( :ped, :alb, :emp, :cfg, :cod, :serv, :bul, :frec, :hini, :hfin, :cont, :cond, :mat, :ref, :obs, :dir, :cp, :pob, :prov, :pais, :tel, :doc, :fnom, :fdoc, :fimg, :ffec, 0, 'Documentado', :u1, :u2 ) "); $stmtIns->execute([ 'ped' => $pedidoId, 'alb' => $albaranId, 'emp' => $empresaId, 'cfg' => $configId, 'cod' => $codRecogida, 'serv' => $codTipoServ, 'bul' => $numBultos, 'frec' => $fechaRecogida, 'hini' => $horaIni, 'hfin' => $horaFin, 'cont' => $personaContacto, 'cond' => $conductor, 'mat' => $matricula, 'ref' => $ped['numero_pedido'], 'obs' => $observaciones, 'dir' => $direccion, 'cp' => $cp, 'pob' => $poblacion, 'prov' => $provincia, 'pais' => $pais, 'tel' => $telefono, 'doc' => $albaranCargaTexto, 'fnom' => $firmaNombre, 'fdoc' => $firmaDni, 'fimg' => $firmaImagenBlob, 'ffec' => $firmaFecha, 'u1' => $user['userId'], 'u2' => $user['userId'] ]); $recogidaId = intval($pdo->lastInsertId()); } // Actualizar estado del pedido a ENVIADO $stmtUpdPed = $pdo->prepare("UPDATE ge_pedidos SET estado = 'ENVIADO', updated_at = NOW(), id_user_update = :u WHERE id = :id"); $stmtUpdPed->execute(['u' => $user['userId'], 'id' => $pedidoId]); // Disparar Hito de Calidad EXPEDICION_ENVIO try { $stmtHito = $pdo->prepare("SELECT id, nombre, es_bloqueante, activo FROM ge_calidad_hitos WHERE codigo = 'EXPEDICION_ENVIO' AND activo = 1 LIMIT 1"); $stmtHito->execute(); $hito = $stmtHito->fetch(); if ($hito) { $idHito = intval($hito['id']); $stmtDocs = $pdo->prepare(" SELECT hd.id_documento, d.margen_registro FROM ge_calidad_hitos_documentos hd JOIN ge_calidad_documentos d ON hd.id_documento = d.id WHERE hd.id_hito = :hito AND hd.activo = 1 AND d.activo = 1 "); $stmtDocs->execute(['hito' => $idHito]); $docs = $stmtDocs->fetchAll(); foreach ($docs as $docItem) { $docId = intval($docItem['id_documento']); $margen = intval($docItem['margen_registro'] ?? 24); $limiteStr = (new DateTime())->modify("+$margen hours")->format('Y-m-d H:i:s'); $refStr = 'Envío #' . $codRecogida . ' (Pedido #' . $ped['numero_pedido'] . ')'; $stmtInsAgenda = $pdo->prepare(" INSERT INTO ge_calidad_agenda ( id_empresa, id_documento, id_hito, id_envio_recogida, referencia_origen, fecha_programada, fecha_limite_toma, estado, created_at, id_user_creator ) VALUES ( :emp, :doc, :hito, :orig, :ref, CURDATE(), :lim, 'PENDIENTE', NOW(), :user ) "); $stmtInsAgenda->execute([ 'emp' => $empresaId, 'doc' => $docId, 'hito' => $idHito, 'orig' => $recogidaId, 'ref' => mb_substr($refStr, 0, 100), 'lim' => $limiteStr, 'user' => $user['userId'] ]); } } } catch (Exception $eHito) { // Log o continuar sin interrumpir la confirmación del envío } json_response([ 'success' => true, 'pedido_id' => $pedidoId, 'recogida_id' => $recogidaId, 'albaran_codigo' => $albaranCodigo, 'cod_recogida' => $codRecogida, 'albaran_carga' => $albaranCargaTexto, 'documento_texto' => $albaranCargaTexto, 'message' => '✅ Envío y albarán de carga documentados con éxito. Pedido marcado como ENVIADO.' ]); }