builder(); $builder->select([ 'pedidos.id as pedido_id', 'pedidos_linea.id as linea_id', 'pedidos_linea.cantidad as cantidad_linea', 'presupuestos.id as presupuesto_id', ]); $builder->join('pedidos_linea', 'pedidos_linea.pedido_id = pedidos.id', 'left'); $builder->join('presupuestos', 'presupuestos.id = pedidos_linea.presupuesto_id', 'left'); $builder->join('envios_lineas', 'envios_lineas.pedido_id = pedidos_linea.pedido_id', 'left'); $builder->groupStart() ->where('pedidos.id', $search) ->whereIn('pedidos.estado', ['finalizado', 'produccion']) ->orWhere("REPLACE(presupuestos.isbn, '-', '')", $searchClean) ->groupEnd(); $builder->groupBy('pedidos_linea.id'); $builder->having('IFNULL(SUM(envios_lineas.unidades_envio), 0) < cantidad_linea', null, false); $result = $builder->get()->getResult(); if (empty($result)) { $response = [ 'status' => false, 'message' => lang('Logistica.errors.notFound'), ]; return $response; } $PresupuestoDireccionesModel = model('App\Models\Presupuestos\PresupuestoDireccionesModel'); $numDirecciones = $PresupuestoDireccionesModel->where('presupuesto_id', $result[0]->presupuesto_id) ->countAllResults(); if ($numDirecciones == 0) { $response = [ 'status' => false, 'message' => lang('Logistica.errors.noAddresses'), ]; return $response; } else if ($numDirecciones > 1) { $multienvio = true; $dirs = $PresupuestoDireccionesModel->select('direccion')->where('presupuesto_id', $result[0]->presupuesto_id) ->findAll(); foreach ($dirs as $key => $direccion) { $modelEnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel'); $unidades_en_direccion = $modelEnvioLineasModel->select('SUM(envios_lineas.unidades_envio) as unidades_enviadas, envios_lineas.unidades_total') ->join('envios', 'envios.id = envios_lineas.envio_id') ->where('pedido_id', $result[0]->pedido_id) ->where('envios.direccion', $direccion->direccion) ->where('envios.finalizado', 1) ->groupBy('pedido_id')->get()->getResult(); if (count($unidades_en_direccion) == 0 || $unidades_en_direccion[0]->unidades_enviadas < $unidades_en_direccion[0]->unidades_total) { array_push($direcciones, $direccion->direccion); } } } $response = [ 'status' => true, 'data' => $result[0], ]; if ($multienvio) { $response_envio = [ 'status' => true, 'multienvio' => true, 'direcciones' => $direcciones, 'pedido_id' => $result[0]->pedido_id, ]; return $response_envio; } $direccion = $PresupuestoDireccionesModel->select('direccion')->where('presupuesto_id', $result[0]->presupuesto_id) ->first()->direccion; $response_envio = LogisticaService::generateEnvio($result[0]->pedido_id, $direccion); if ($response_envio['status'] == false) { $response = [ 'status' => false, 'message' => $response_envio['message'], ]; return $response; } else { $response['data']->id_envio = $response_envio['data']['id_envio']; $response['data']->multienvio = false; } return $response; } public static function findLineaEnvioPorEnvio(int $envio_id) { $db = \Config\Database::connect(); // 1. Dirección del envío actual $envio = $db->table('envios')->select('direccion')->where('id', $envio_id)->get()->getRow(); if (!$envio) { return $db->table('(SELECT NULL AS id, NULL AS name) AS empty')->where('1 = 0'); } $direccionNormalizada = str_replace(' ', '', strtolower(trim($envio->direccion))); $direccionSQL = $db->escape($direccionNormalizada); // 2. Obtener presupuestos con esa dirección $presupuestosConEsaDireccion = $db->table('presupuesto_direcciones') ->select('presupuesto_id') ->where("REPLACE(LOWER(TRIM(direccion)), ' ', '') = $direccionSQL", null, false) ->get() ->getResultArray(); $presupuestoIds = array_column($presupuestosConEsaDireccion, 'presupuesto_id'); if (empty($presupuestoIds)) { return $db->table('(SELECT NULL AS id, NULL AS name) AS empty')->where('1 = 0'); } // 3. Subconsulta principal $subBuilder = $db->table('pedidos_linea pl') ->select(" pl.id AS id, CONCAT('[', p.id, '] - ', pr.titulo) AS name, ( SELECT IFNULL(SUM(el.unidades_envio), 0) FROM envios_lineas el JOIN envios e ON e.id = el.envio_id WHERE el.pedido_id = p.id AND el.presupuesto_id = pr.id AND REPLACE(LOWER(TRIM(e.direccion)), ' ', '') = $direccionSQL AND (e.finalizado = 1 OR e.id = $envio_id) ) AS unidades_enviadas, pd.cantidad AS cantidad ") ->join('pedidos p', 'p.id = pl.pedido_id') ->join('presupuestos pr', 'pr.id = pl.presupuesto_id') ->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id') ->whereIn('pr.id', $presupuestoIds) ->whereIn('p.estado', ['finalizado', 'produccion']) ->where("NOT EXISTS ( SELECT 1 FROM envios_lineas el WHERE el.envio_id = $envio_id AND el.pedido_id = p.id AND el.presupuesto_id = pr.id GROUP BY el.pedido_id, el.presupuesto_id HAVING SUM(el.unidades_envio) >= pd.cantidad )", null, false) ->groupBy('pl.id'); // 4. Envolver y filtrar por unidades pendientes $builder = $db->table("({$subBuilder->getCompiledSelect(false)}) AS sub"); $builder->select('id, name'); $builder->where('cantidad > unidades_enviadas'); $builder->orderBy('name', 'ASC'); return $builder; } public static function addLineaEnvio($envio_id = null, $pedido_id = null, $direccion = null) { $db = \Config\Database::connect(); $direccionNormalizada = strtolower(trim($direccion)); // 1. Obtener presupuesto_id y cantidad desde presupuesto_direcciones $builder = $db->table('pedidos_linea pl') ->select(" p.id AS pedido_id, pl.id AS linea_id, pr.id AS presupuesto_id, pd.cantidad AS total_unidades, ( SELECT IFNULL(SUM(el.unidades_envio), 0) FROM envios_lineas el JOIN envios e ON e.id = el.envio_id WHERE el.pedido_id = p.id AND el.presupuesto_id = pr.id AND TRIM(LOWER(e.direccion)) = '$direccionNormalizada' AND (e.finalizado = 1 OR e.id = $envio_id) ) AS unidades_enviadas, ( pd.cantidad - ( SELECT IFNULL(SUM(el2.unidades_envio), 0) FROM envios_lineas el2 JOIN envios e2 ON e2.id = el2.envio_id WHERE el2.pedido_id = p.id AND el2.presupuesto_id = pr.id AND TRIM(LOWER(e2.direccion)) = '$direccionNormalizada' AND (e2.finalizado = 1 OR e2.id = $envio_id) ) ) AS unidades_envio ") ->join('pedidos p', 'p.id = pl.pedido_id') ->join('presupuestos pr', 'pr.id = pl.presupuesto_id') ->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id') ->where('p.id', $pedido_id) ->whereIn('p.estado', ['finalizado', 'produccion']) ->where("TRIM(LOWER(pd.direccion)) = '$direccionNormalizada'", null, false) ->groupBy('pl.id'); $result = $builder->get()->getResultObject(); if (empty($result)) { return [ 'status' => false, 'message' => lang('Logistica.errors.notFound'), ]; } $r = $result[0]; // 2. Insertar solo si hay unidades a enviar if ($r->unidades_envio > 0) { $EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel'); $EnvioLineasModel->save([ 'envio_id' => $envio_id, 'pedido_id' => $r->pedido_id, 'presupuesto_id' => $r->presupuesto_id, 'unidades_envio' => $r->unidades_envio, 'unidades_total' => $r->total_unidades, 'cajas' => null, 'unidades_cajas' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), 'created_by' => auth()->user()->id, 'updated_by' => auth()->user()->id, ]); } return [ 'status' => true, 'data' => [ 'unidades_envio' => $r->unidades_envio, 'unidades_enviadas' => $r->unidades_enviadas, 'total_unidades' => $r->total_unidades, ] ]; } public static function generateEnvio($pedido_id, $direccion = null) { $presupuestoDireccionesModel = model('App\Models\Presupuestos\PresupuestoDireccionesModel'); $direccionNormalizada = strtolower(trim($direccion)); // Consulta con cálculo exacto de unidades pendientes por dirección $datosEnvio = $presupuestoDireccionesModel ->select(" presupuestos.id as presupuesto_id, presupuesto_direcciones.att, presupuesto_direcciones.direccion, presupuesto_direcciones.provincia as ciudad, presupuesto_direcciones.cp, presupuesto_direcciones.telefono, presupuesto_direcciones.email, presupuesto_direcciones.pais_id, presupuesto_direcciones.cantidad as cantidad_total, presupuestos.cliente_id as cliente_id, ( presupuesto_direcciones.cantidad - IFNULL(( SELECT SUM(el.unidades_envio) FROM envios_lineas el JOIN envios e ON e.id = el.envio_id WHERE el.pedido_id = pedidos.id AND el.presupuesto_id = presupuestos.id AND TRIM(LOWER(e.direccion)) = '$direccionNormalizada' AND e.finalizado = 1 ), 0) ) as cantidad ") ->join('pedidos_linea', 'pedidos_linea.presupuesto_id = presupuesto_direcciones.presupuesto_id') ->join('pedidos', 'pedidos.id = pedidos_linea.pedido_id') ->join('presupuestos', 'pedidos_linea.presupuesto_id = presupuestos.id') ->where('pedidos.id', $pedido_id) ->where("TRIM(LOWER(presupuesto_direcciones.direccion)) = '$direccionNormalizada'", null, false) ->groupBy('presupuesto_direcciones.id') ->first(); // Validación si no hay datos o no quedan unidades if (empty($datosEnvio) || $datosEnvio->cantidad <= 0) { return [ 'status' => false, 'message' => lang('Logistica.errors.noAddresses') . ' o no hay unidades pendientes.', ]; } // Crear envío $EnvioModel = model('App\Models\Logistica\EnvioModel'); $EnvioModel->set([ 'cliente_id' => $datosEnvio->cliente_id, 'att' => $datosEnvio->att, 'direccion' => $datosEnvio->direccion, 'ciudad' => $datosEnvio->ciudad, 'cp' => $datosEnvio->cp, 'telefono' => $datosEnvio->telefono, 'email' => $datosEnvio->email, 'pais_id' => $datosEnvio->pais_id, 'cantidad' => $datosEnvio->cantidad, 'cajas' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]); $EnvioModel->insert(); $idEnvio = $EnvioModel->insertID(); // Crear línea de envío $EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel'); $EnvioLineasModel->save([ 'envio_id' => $idEnvio, 'pedido_id' => $pedido_id, 'unidades_envio' => $datosEnvio->cantidad, 'unidades_total' => $datosEnvio->cantidad_total, 'cajas' => 1, 'unidades_cajas' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), 'created_by' => auth()->user()->id, 'updated_by' => auth()->user()->id, 'presupuesto_id' => (int) $datosEnvio->presupuesto_id ]); return [ 'status' => true, 'data' => [ 'id_envio' => $idEnvio, ], ]; } public static function finalizarEnvio($envio_id, $finalizar_ot = false) { // hay que comprobar que para todas las lineas de envio de este envio // se ha enviado toda la cantidad teniendo en cuenta otros envios $EnvioModel = model('App\Models\Logistica\EnvioModel'); $EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel'); $ots = []; $envio = $EnvioModel->find($envio_id); if (empty($envio)) { return [ 'status' => false, 'message' => lang('Logistica.errors.noEnvio'), ]; } $lineasEnvio = $EnvioLineasModel->where('envio_id', $envio_id) ->findAll(); if (empty($lineasEnvio)) { return [ 'status' => false, 'message' => lang('Logistica.errors.noEnvioLineas'), ]; } foreach ($lineasEnvio as $linea) { $pedido = model('App\Models\Pedidos\PedidoModel')->find($linea->pedido_id); if (empty($pedido)) { return [ 'status' => false, 'message' => lang('Logistica.errors.notFound'), ]; } $cantidad_enviada = $EnvioLineasModel->select('SUM(envios_lineas.unidades_envio) as unidades_enviadas') ->where('envios_lineas.pedido_id', $linea->pedido_id) ->where('envios_lineas.envio_id !=', $envio_id) ->where('envios.finalizado', 1) ->join('envios', 'envios.id = envios_lineas.envio_id') ->get()->getResult(); if ( count($cantidad_enviada) == 0 || empty($cantidad_enviada[0]->unidades_enviadas) || $cantidad_enviada[0]->unidades_enviadas == null ) { $cantidad_enviada = 0; } else { $cantidad_enviada = $cantidad_enviada[0]->unidades_enviadas; } if ($cantidad_enviada + $linea->unidades_envio == $pedido->total_tirada) { $otModel = model('App\Models\OrdenTrabajo\OrdenTrabajoModel'); $ot = $otModel->where('pedido_id', $linea->pedido_id) ->first(); $ps = (new ProductionService())->init($ot->id); $ps->updateOrdenTrabajoDate([ "name" => "envio_at", "envio_at" => date('Y-m-d H:i:s') ]); if ($finalizar_ot) { $ps->updateOrdenTrabajo( [ "estado" => 'F' ] ); array_push($ots, $ot->id); } } } $EnvioModel->update($envio_id, ['finalizado' => 1]); $data_return = [ 'status' => true, 'message' => lang('Logistica.success.finalizado'), ]; if ($finalizar_ot) { $data_return['message'] = lang('Logistica.success.finalizadoOTs', [ 'ots' => implode(', ', $ots) ]); $data_return['ots'] = $ots; } return $data_return; } }