diff --git a/ci4/app/Config/Routes.php b/ci4/app/Config/Routes.php
index 5da5a2d7..0278a980 100755
--- a/ci4/app/Config/Routes.php
+++ b/ci4/app/Config/Routes.php
@@ -767,6 +767,7 @@ $routes->group('produccion', ['namespace' => 'App\Controllers\Produccion'], func
$routes->get('tareas/datatable/(:num)', 'Ordentrabajo::tareas_datatable/$1', ['as' => 'datatableTareasOrdenTrabajo']);
$routes->get('maquinas/ots/datatable/(:num)','Ordentrabajo::datatable_maquina_ordenes_trabajo/$1');
$routes->get('maquinas/ots/(:num)','Ordentrabajo::get_maquina_ots/$1');
+
/**======================
* UPDATES
*========================**/
@@ -873,6 +874,8 @@ $routes->group('logistica', ['namespace' => 'App\Controllers\Logistica'], functi
$routes->get('selectForNewEnvio', 'LogisticaController::findForNewEnvio');
$routes->get('selectDireccionForEnvio', 'LogisticaController::selectDireccionForEnvio');
$routes->post('imprimirEtiquetas', 'LogisticaController::imprimirEtiquetas');
+ $routes->post('ficharEmbalaje', 'LogisticaController::ficharEmbalaje');
+ $routes->get('datatableProximosEnvios/(:num)', 'LogisticaController::datatable_proximosEnvios/$1');
$routes->get('listAlbaranes', 'LogisticaController::listAlbaranes', ['as' => 'albaranesList']);
});
diff --git a/ci4/app/Controllers/Importadores/ImportadorBubok.php b/ci4/app/Controllers/Importadores/ImportadorBubok.php
index 27641c64..09527b3a 100644
--- a/ci4/app/Controllers/Importadores/ImportadorBubok.php
+++ b/ci4/app/Controllers/Importadores/ImportadorBubok.php
@@ -450,6 +450,76 @@ class ImportadorBubok extends BaseResourceController
], 400);
}
+
+ // Descarga y subida de archivos al SFTP
+ $presupuestoFicheroModel = model('App\Models\Presupuestos\PresupuestoFicheroModel');
+ $ftp = new \App\Libraries\SafekatFtpClient();
+
+ $archivoUrls = [
+ 'cover' => $producto->cover->file ?? null,
+ 'body' => $producto->body->file ?? null,
+ ];
+
+ foreach ($archivoUrls as $tipo => $url) {
+ if (!$url)
+ continue;
+
+ try {
+ $contenido = @file_get_contents($url); // silenciar errores de PHP
+
+ if ($contenido === false || strlen($contenido) === 0) {
+ // No se pudo descargar el archivo: generar archivo de error para FTP
+ $errorMessage = "ERROR: No se pudo descargar el archivo remoto para $tipo desde la URL: $url";
+
+ $remoteDir = $ftp->getPresupuestoRemotePath($response['sk_id']); // crea esta función si no existe
+ $remoteErrorFile = $remoteDir . '/ERROR_' . strtoupper($tipo) . '.txt';
+
+ // Crear archivo temporal con el mensaje de error
+ $tempErrorFile = WRITEPATH . 'uploads/presupuestos/ERROR_' . $tipo . '.txt';
+ file_put_contents($tempErrorFile, $errorMessage);
+
+ if (!$ftp->is_dir($remoteDir)) {
+ $ftp->mkdir($remoteDir, recursive: true);
+ }
+
+ $ftp->put($remoteErrorFile, $tempErrorFile, $ftp::SOURCE_LOCAL_FILE);
+
+ continue; // no procesar este archivo
+ }
+
+ // ✅ Procesar normalmente si la descarga tuvo éxito
+ $nombreOriginal = basename(parse_url($url, PHP_URL_PATH));
+ $extension = pathinfo($nombreOriginal, PATHINFO_EXTENSION);
+
+ $nombreLimpio = $presupuestoFicheroModel->saveFileInBBDD(
+ $response['sk_id'],
+ $nombreOriginal,
+ $extension,
+ auth()->id()
+ );
+
+ if (is_null($nombreLimpio))
+ continue;
+
+ $rutaLocal = WRITEPATH . 'uploads/presupuestos/';
+ if (!is_dir($rutaLocal)) {
+ mkdir($rutaLocal, 0777, true);
+ }
+
+ file_put_contents($rutaLocal . $nombreLimpio, $contenido);
+ } catch (\Throwable $e) {
+ //log_message('error', 'Error inesperado en descarga de archivo remoto: ' . $e->getMessage());
+ }
+ }
+
+
+ // Subir al FTP después de guardar localmente
+ try {
+ $ftp->uploadFilePresupuesto($response['sk_id']);
+ } catch (\Throwable $e) {
+ log_message('error', 'Error subiendo archivos al FTP: ' . $e->getMessage());
+ }
+
return $this->respond([
'status' => 200,
'data' => [
@@ -458,6 +528,7 @@ class ImportadorBubok extends BaseResourceController
]
]);
+
} catch (\Throwable $e) {
return $this->respond([
'status' => 500,
diff --git a/ci4/app/Controllers/Logistica/EtiquetasTitulosController.php b/ci4/app/Controllers/Logistica/EtiquetasTitulosController.php
index d82805a0..dc0fa78a 100644
--- a/ci4/app/Controllers/Logistica/EtiquetasTitulosController.php
+++ b/ci4/app/Controllers/Logistica/EtiquetasTitulosController.php
@@ -163,10 +163,10 @@ class EtiquetasTitulosController extends BaseController
$modelImpresora = model('App\Models\Configuracion\ImpresoraEtiquetaModel');
- $impresoras = $modelImpresora->select('id, name')
+ $impresoras = $modelImpresora->select('id, name, description')
->where('deleted_at', null)
->where('tipo', 1)
- ->orderBy('name', 'desc')
+ ->orderBy('name', 'asc')
->findAll();
$etiquetaEntity->impresoras = $impresoras;
@@ -440,7 +440,7 @@ class EtiquetasTitulosController extends BaseController
$impresora_id = $this->request->getPost('impresora_id') ?? null;
$modelImpresora = model('App\Models\Configuracion\ImpresoraEtiquetaModel');
- $impresora = $modelImpresora->select('id, name, ip, port, user, pass')
+ $impresora = $modelImpresora->select('id, name, description, ip, port, user, pass')
->where('deleted_at', null)
->where('id', $impresora_id)
->orderBy('name', 'asc')
diff --git a/ci4/app/Controllers/Logistica/LogisticaController.php b/ci4/app/Controllers/Logistica/LogisticaController.php
index b69b0f4c..82a1a6a5 100755
--- a/ci4/app/Controllers/Logistica/LogisticaController.php
+++ b/ci4/app/Controllers/Logistica/LogisticaController.php
@@ -98,7 +98,8 @@ class LogisticaController extends BaseController
return view(static::$viewPath . 'viewImpresionEtiquetas', $viewData);
}
- public function listAlbaranes(){
+ public function listAlbaranes()
+ {
$viewData = [
'currentModule' => static::$controllerSlug,
'boxTitle' => lang('Albaran.albaranes'),
@@ -117,7 +118,7 @@ class LogisticaController extends BaseController
$tipo_envio = $this->request->getGet('tipo_envio') ?? 'estandar';
- if($tipo_envio == 'ferro_prototipo'){
+ if ($tipo_envio == 'ferro_prototipo') {
$query = LogisticaService::findForNewEnvioFerro();
} else {
$query = LogisticaService::findForNewEnvio();
@@ -132,22 +133,25 @@ class LogisticaController extends BaseController
$result = $query->orderBy("name", "asc")->get()->getResultObject();
+ $query = model('App\Models\Logistica\EnvioModel')->db->getLastQuery();
+
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
- public function selectDireccionForEnvio(){
+ public function selectDireccionForEnvio()
+ {
if ($this->request->isAJAX()) {
$ot = $this->request->getGet('ot_id');
- if($ot == null || $ot == 0){
+ if ($ot == null || $ot == 0) {
return [];
}
$searchVal = $this->request->getGet("q") ?? "";
$result = LogisticaService::findDireccionesNewEnvio($ot, $searchVal);
-
+
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
@@ -185,12 +189,12 @@ class LogisticaController extends BaseController
public function imprimirEtiquetas()
{
if ($this->request->isAJAX()) {
- $envio_id = $this->request->getPost('envio_id');
+ $envio_id = $this->request->getPost('envio_id');
$ids = $this->request->getPost('envio_lineas');
$cajas = $this->request->getPost('cajas');
$printer_id = $this->request->getPost('printer_id');
- if($cajas == null || $cajas == 0){
+ if ($cajas == null || $cajas == 0) {
return $this->response->setJSON([
'status' => false,
'message' => 'Cajas no válidas'
@@ -202,7 +206,7 @@ class LogisticaController extends BaseController
->join('clientes', 'clientes.id = envios.cliente_id', 'left')
->where('envios.id', $envio_id)
->first();
- if($envio == null){
+ if ($envio == null) {
return $this->response->setJSON([
'status' => false,
'message' => 'Envio no válido'
@@ -213,7 +217,7 @@ class LogisticaController extends BaseController
$lineas = $model->select('envios_lineas.*, presupuestos.titulo as titulo, presupuestos.referencia_cliente as referencia_cliente')
->join('presupuestos', 'presupuestos.id = envios_lineas.presupuesto_id', 'left')
->whereIn('envios_lineas.id', $ids)->findAll();
- if($lineas == null){
+ if ($lineas == null) {
return $this->response->setJSON([
'status' => false,
'message' => 'Lineas no válidas'
@@ -221,12 +225,12 @@ class LogisticaController extends BaseController
}
$modelImpresora = model('App\Models\Configuracion\ImpresoraEtiquetaModel');
- $impresora = $modelImpresora->select('id, name, ip, port, user, pass')
+ $impresora = $modelImpresora->select('id, name, description, ip, port, user, pass')
->where('deleted_at', null)
->where('id', $printer_id)
->orderBy('name', 'asc')
->first();
- if($impresora == null){
+ if ($impresora == null) {
return $this->response->setJSON([
'status' => false,
'message' => 'Impresora no válida'
@@ -330,22 +334,22 @@ class LogisticaController extends BaseController
if (empty($envioEntity)) {
return redirect()->to(base_url('logistica/selectEnvios/simple'))->with('error', lang('Logistica.errors.noEnvio'));
}
-
+
$modelProveedor = model('App\Models\Compras\ProveedorModel');
$proveedor = $modelProveedor->select('id, nombre')
->where('deleted_at', null)
->where('id', $envioEntity->proveedor_id)
->orderBy('nombre', 'asc')
->first();
- if(!empty($proveedor)){
+ if (!empty($proveedor)) {
$envioEntity->proveedor_nombre = $proveedor->nombre;
}
$modelImpresora = model('App\Models\Configuracion\ImpresoraEtiquetaModel');
- $impresoras = $modelImpresora->select('id, name')
+ $impresoras = $modelImpresora->select('id, name, description')
->where('deleted_at', null)
->where('tipo', 1)
- ->orderBy('name', 'desc')
+ ->orderBy('name', 'asc')
->findAll();
$envioEntity->impresoras = $impresoras;
@@ -384,7 +388,7 @@ class LogisticaController extends BaseController
$id = $this->request->getPost('id') ?? null;
$finalizar_ots = $this->request->getPost('finalizar_ots') ?? false;
-
+
$result = LogisticaService::finalizarEnvio($id, $finalizar_ots);
return $this->response->setJSON($result);
} else {
@@ -392,6 +396,17 @@ class LogisticaController extends BaseController
}
}
+ public function ficharEmbalaje()
+ {
+ if ($this->request->isAJAX()) {
+
+ $ids = $this->request->getPost('ids') ?? [];
+ $result = LogisticaService::ficharEmbalaje($ids);
+ return $this->response->setJSON($result);
+ } else {
+ return $this->failUnauthorized('Invalid request', 403);
+ }
+ }
public function datatable_enviosEdit($idEnvio)
{
@@ -406,6 +421,12 @@ class LogisticaController extends BaseController
return '';
}
)
+ ->edit(
+ "ordenTrabajo",
+ function ($row, $meta) {
+ return '' . $row->ordenTrabajo . '';
+ }
+ )
->edit(
"pedido",
function ($row, $meta) {
@@ -420,17 +441,35 @@ class LogisticaController extends BaseController
)->edit(
"unidadesEnvio",
function ($row, $meta) {
- if($row->finalizado == 1 || $row->tipo_envio == 'ferro_prototipo'){
+ if ($row->finalizado == 1 || $row->tipo_envio == 'ferro_prototipo') {
return $row->unidadesEnvio;
}
return '';
+ data-id="' . $row->id . '" data-name="unidades_envio" value="' . $row->unidadesEnvio . '">';
}
);
return $result->toJson(returnAsObject: true);
}
+ public function datatable_proximosEnvios($envio_id = null)
+ {
+ $q = LogisticaService::findNextEnvios($envio_id);
+
+ $result = DataTable::of($q)
+ ->edit(
+ "ot",
+ function ($row, $meta) {
+ return '' . $row->ot . '';
+ }
+ );
+
+ $result = $result->toJson(returnAsObject: true);
+ $query = model('App\Models\Logistica\EnvioModel')->db->getLastQuery();
+ return $result;
+
+ }
+
public function setCajaLinea()
{
@@ -471,7 +510,7 @@ class LogisticaController extends BaseController
$fieldName = $this->request->getPost('name');
$fieldValue = $this->request->getPost('value');
- if (!$id || !$fieldName || ($fieldName=='unidades_envio' && !$fieldValue)) {
+ if (!$id || !$fieldName || ($fieldName == 'unidades_envio' && !$fieldValue)) {
return $this->response->setJSON([
'status' => false,
'message' => 'Datos inválidos'
@@ -480,7 +519,7 @@ class LogisticaController extends BaseController
$model = model('App\Models\Logistica\EnvioLineaModel');
$updated = $model->update($id, [
- "" . $fieldName => $fieldValue==""? null: $fieldValue,
+ "" . $fieldName => $fieldValue == "" ? null : $fieldValue,
]);
return $this->response->setJSON([
@@ -503,7 +542,7 @@ class LogisticaController extends BaseController
$model = model('App\Models\Logistica\EnvioModel');
$updated = $model->update($id, [
- "codigo_seguimiento" => $fieldValue==""? null: $fieldValue,
+ "codigo_seguimiento" => $fieldValue == "" ? null : $fieldValue,
]);
return $this->response->setJSON([
@@ -526,7 +565,7 @@ class LogisticaController extends BaseController
$model = model('App\Models\Logistica\EnvioModel');
$updated = $model->update($id, [
- "proveedor_id" => $fieldValue==""? null: $fieldValue,
+ "proveedor_id" => $fieldValue == "" ? null : $fieldValue,
]);
return $this->response->setJSON([
diff --git a/ci4/app/Controllers/Produccion/Ordentrabajo.php b/ci4/app/Controllers/Produccion/Ordentrabajo.php
index 4f631279..ce6dacdd 100755
--- a/ci4/app/Controllers/Produccion/Ordentrabajo.php
+++ b/ci4/app/Controllers/Produccion/Ordentrabajo.php
@@ -951,7 +951,7 @@ class Ordentrabajo extends BaseController
}
$modelImpresora = model('App\Models\Configuracion\ImpresoraEtiquetaModel');
- $impresora = $modelImpresora->select('id, name, ip, port, user, pass')
+ $impresora = $modelImpresora->select('id, name, description, ip, port, user, pass')
->where('deleted_at', null)
->where('id', $impresora_id)
->orderBy('name', 'asc')
diff --git a/ci4/app/Language/es/Logistica.php b/ci4/app/Language/es/Logistica.php
index 0ef3e713..4213a476 100755
--- a/ci4/app/Language/es/Logistica.php
+++ b/ci4/app/Language/es/Logistica.php
@@ -44,6 +44,7 @@ return [
'totales' => 'Totales',
'cajas' => 'Cajas',
+ 'ordenTrabajo' => 'OT',
'pedido' => 'Pedido',
'presupuesto' => 'Presupuesto',
'unidadesEnvio' => 'Unidades envío',
@@ -59,9 +60,11 @@ return [
'selectAll' => 'Seleccionar todo',
'peso' => 'Peso (kg): ',
'unidadesTotalesFooter' => 'Unidades:',
+ 'fechaEncuadernado' => 'Fecha encuadernado',
'codigoSeguimiento' => 'Código de seguimiento',
'empresaMensajería' => 'Empresa de mensajería',
+ 'ficharEmbalaje' => 'Fichar embalaje',
'finalizarEnvio' => 'Finalizar envío',
'finalizarEnvioYOTs' => 'Finalizar envío y OTS',
@@ -90,6 +93,7 @@ return [
'errorInsertarEtiqueta' => 'Error al insertar la etiqueta',
'noEtiqueta' => 'No se ha encontrado la etiqueta',
'noEtiquetaLineas' => 'No se han encontrado líneas de etiqueta',
+ 'noLineas' => 'No se ha seleccionado ninguna línea',
],
'success' => [
'finalizado' => 'El envío se ha finalizado correctamente',
@@ -101,6 +105,7 @@ return [
'comentariosUpdated' => 'Comentarios actualizados correctamente',
'successReordenarCajas' => 'Cajas reordenadas correctamente',
'imprimirEtiquetas' => 'Etiquetas impresas correctamente',
+ 'successFicharEmbalaje' => 'Embalaje fichado correctamente',
],
];
\ No newline at end of file
diff --git a/ci4/app/Language/es/Produccion.php b/ci4/app/Language/es/Produccion.php
index 6f085694..a7907544 100755
--- a/ci4/app/Language/es/Produccion.php
+++ b/ci4/app/Language/es/Produccion.php
@@ -182,6 +182,7 @@ return [
'duplicate_estado_tarea_progress' => "Último estado de la tarea repetido",
'task_already_finished' => "La tarea se ha marcado como finalizada.",
'print_label' => "Imprimir etiqueta",
+ 'fichar_embalaje' => "Fichar embalaje",
'click_init' => "Clicks al inicio",
'click_end' => "Clicks al final",
"comentarios" => "Comentarios",
diff --git a/ci4/app/Libraries/SafekatFtpClient.php b/ci4/app/Libraries/SafekatFtpClient.php
index 663e8c7a..c69b2d85 100755
--- a/ci4/app/Libraries/SafekatFtpClient.php
+++ b/ci4/app/Libraries/SafekatFtpClient.php
@@ -40,13 +40,14 @@ class SafekatFtpClient
public function uploadXML(string $content, string $filename): bool
{
try {
- if ($this->xml_enabled == false) return false;
- $remotePath = implode("/", [$this->base_dir,'pedidos','xml_nuevos']);
+ if ($this->xml_enabled == false)
+ return false;
+ $remotePath = implode("/", [$this->base_dir, 'pedidos', 'xml_nuevos']);
$this->ftp->login(username: $this->username, password: $this->password);
- if(!$this->ftp->is_dir($remotePath)){
- $this->ftp->mkdir($remotePath,recursive:true);
+ if (!$this->ftp->is_dir($remotePath)) {
+ $this->ftp->mkdir($remotePath, recursive: true);
}
- $this->ftp->put($remotePath.'/'.$filename, $content);
+ $this->ftp->put($remotePath . '/' . $filename, $content);
return true;
} catch (\Throwable $th) {
@@ -58,22 +59,23 @@ class SafekatFtpClient
public function uploadFilePresupuesto(int $presupuesto_id)
{
try {
- if ($this->xml_enabled == false) return false;
+ if ($this->xml_enabled == false)
+ return false;
$model = model(PresupuestoFicheroModel::class);
$modelPedidoLinea = model(PedidoLineaModel::class);
$pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id);
$rootIdExtern = $this->pedido_xml_config->id_offset + $pedidoLinea->pedido_id;
$presupuestoFiles = $model->getFiles($presupuesto_id);
$this->ftp->login(username: $this->username, password: $this->password);
-
+
foreach ($presupuestoFiles as $key => $value) {
$filename = array_reverse(explode("/", $value->file_path))[0];
- $remoteDir = implode("/", [$this->base_dir,"pedidos_files",$rootIdExtern]);
- $remoteFile = implode("/", [$this->base_dir,"pedidos_files",$rootIdExtern,$filename]);
- if(!$this->ftp->is_dir($remoteDir)){
- $this->ftp->mkdir($remoteDir,recursive:true);
+ $remoteDir = implode("/", [$this->base_dir, "pedidos_files", $rootIdExtern]);
+ $remoteFile = implode("/", [$this->base_dir, "pedidos_files", $rootIdExtern, $filename]);
+ if (!$this->ftp->is_dir($remoteDir)) {
+ $this->ftp->mkdir($remoteDir, recursive: true);
}
- $this->ftp->put($remoteFile,$value->file_path,mode:$this->ftp::SOURCE_LOCAL_FILE);
+ $this->ftp->put($remoteFile, $value->file_path, mode: $this->ftp::SOURCE_LOCAL_FILE);
}
$this->ftp->disconnect();
} catch (Exception $e) {
@@ -91,10 +93,10 @@ class SafekatFtpClient
$rootIdExtern = $this->pedido_xml_config->id_offset + $pedidoLinea->pedido_id;
$presupuestoFiles = $model->getFiles($presupuesto_id);
$this->ftp->login(username: $this->username, password: $this->password);
-
+
foreach ($presupuestoFiles as $key => $value) {
$filename = array_reverse(explode("/", $value->file_path))[0];
- $remoteFile = implode("/", [$this->base_dir,"pedidos_files",$rootIdExtern,$filename]);
+ $remoteFile = implode("/", [$this->base_dir, "pedidos_files", $rootIdExtern, $filename]);
$this->ftp->delete($remoteFile);
}
$this->ftp->disconnect();
@@ -103,4 +105,13 @@ class SafekatFtpClient
throw $e;
}
}
+
+ public function getPresupuestoRemotePath(int $presupuesto_id): string
+ {
+ $modelPedidoLinea = model(PedidoLineaModel::class);
+ $pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id);
+ $rootIdExtern = $this->pedido_xml_config->id_offset + $pedidoLinea->pedido_id;
+
+ return implode('/', [$this->base_dir, 'pedidos_files', $rootIdExtern]);
+ }
}
diff --git a/ci4/app/Models/Logistica/EnvioLineaModel.php b/ci4/app/Models/Logistica/EnvioLineaModel.php
index a50709d3..76ae7b9b 100644
--- a/ci4/app/Models/Logistica/EnvioLineaModel.php
+++ b/ci4/app/Models/Logistica/EnvioLineaModel.php
@@ -35,7 +35,7 @@ class EnvioLineaModel extends Model
$builder = $this->db
->table($this->table . " t1")
->select(
- "t1.id, t1.pedido_id as pedido, t3.id as presupuesto,
+ "t1.id, t1.pedido_id as pedido, t3.id as presupuesto, t4.id as ordenTrabajo,
t3.titulo as titulo, t1.unidades_envio as unidadesEnvio, t1.unidades_envio as unidadesEnvioRaw,
t1.unidades_total as unidadesTotal, t2.tipo_envio as tipo_envio,
IFNULL((
@@ -59,6 +59,7 @@ class EnvioLineaModel extends Model
);
$builder->join("envios t2", "t1.envio_id = t2.id", "left");
$builder->join("presupuestos t3", "t1.presupuesto_id = t3.id", "left");
+ $builder->join("ordenes_trabajo t4", "t1.pedido_id = t4.pedido_id", "left");
$builder->where("t1.envio_id", $envio_id);
diff --git a/ci4/app/Models/OrdenTrabajo/OrdenTrabajoModel.php b/ci4/app/Models/OrdenTrabajo/OrdenTrabajoModel.php
index e28a7e1b..5c81a5ab 100755
--- a/ci4/app/Models/OrdenTrabajo/OrdenTrabajoModel.php
+++ b/ci4/app/Models/OrdenTrabajo/OrdenTrabajoModel.php
@@ -143,4 +143,25 @@ class OrdenTrabajoModel extends Model
->groupBy('orden_trabajo_tareas.id');
return $query;
}
+
+ public function queryProximosEnvios()
+ {
+ $query = $this->builder()
+ ->select([
+ 'ordenes_trabajo.id as ot',
+ 'orden_trabajo_dates.encuadernacion_at as fechaEncuadernado',
+ ])
+ ->join('pedidos', 'pedidos.id = ordenes_trabajo.pedido_id', 'left')
+ ->join('pedidos_linea', 'pedidos.id = pedidos_linea.pedido_id', 'left')
+ ->join('presupuestos', 'presupuestos.id = pedidos_linea.presupuesto_id', 'left')
+ ->join('presupuesto_direcciones', 'presupuestos.id = presupuesto_direcciones.presupuesto_id', 'left')
+ ->join('orden_trabajo_dates', 'orden_trabajo_dates.orden_trabajo_id = ordenes_trabajo.id', 'left')
+ ->where('ordenes_trabajo.deleted_at', null)
+ ->where('orden_trabajo_dates.encuadernacion_at !=', null)
+
+ //->where('orden_trabajo_dates.fecha_encuadernado_at >=', 0)
+ //->where('ordenes_trabajo.fecha_entrega_warning >=', date("Y-m-d H:i:s"))
+ ->groupBy('ordenes_trabajo.id');
+ return $query;
+ }
}
diff --git a/ci4/app/Models/Presupuestos/ImportadorModel.php b/ci4/app/Models/Presupuestos/ImportadorModel.php
index 0a0a6ab5..10f79cf9 100755
--- a/ci4/app/Models/Presupuestos/ImportadorModel.php
+++ b/ci4/app/Models/Presupuestos/ImportadorModel.php
@@ -25,7 +25,7 @@ class ImportadorModel extends \App\Models\BaseModel
$builder = $db->table('pedido_libro');
$builder->select('id as id, CONCAT(id, " - ", titulo) as name');
$builder->where('customer_id', $clienteId);
- $builder->whereIn('estado', ['finalizado', 'validado']);
+ $builder->whereIn('estado', ['finalizado', 'validado', 'presupuesto']);
$builder->where('deleted_at', NULL);
$builder->orderBy('updated_at', 'DESC');
diff --git a/ci4/app/Services/EmailService.php b/ci4/app/Services/EmailService.php
index 5b6f7f99..be7dc57a 100755
--- a/ci4/app/Services/EmailService.php
+++ b/ci4/app/Services/EmailService.php
@@ -13,7 +13,7 @@ class EmailService
// Si no estamos en producción, forzar el destinatario a uno fijo
if ($skEnv !== 'production') {
- $recipient = env('MAIL_DEV_RECIPIENT', 'imnavajas@coit.es'); // fallback opcional
+ $recipient = env('MAIL_DEV_RECIPIENT', 'imnavajas@coit.es,info@jjimenez.eu'); // fallback opcional
}
$settings_model = model('App\Models\Configuracion\ConfigVariableModel');
@@ -43,7 +43,13 @@ class EmailService
$email->setSubject($subject);
$email->setMessage($body);
- return $email->send();
+ if (!$email->send()) {
+ log_message('error', 'Error enviando email: ' . $email->printDebugger(['headers', 'subject', 'body']));
+ return false;
+ }
+
+
+ return true;
} catch (\Throwable $e) {
log_message('error', 'EmailService failed: ' . $e->getMessage());
return false;
diff --git a/ci4/app/Services/LogisticaService.php b/ci4/app/Services/LogisticaService.php
index cb4fb1b5..b5f28237 100644
--- a/ci4/app/Services/LogisticaService.php
+++ b/ci4/app/Services/LogisticaService.php
@@ -57,7 +57,8 @@ class LogisticaService
->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
->whereIn('pr.id', $presupuestoIds)
->whereIn('p.estado', ['finalizado', 'produccion'])
- ->where('ot_dates.embalaje_at IS NOT NULL')
+ ->where('p.fecha_encuadernado IS NOT NULL')
+ ->where('DATE(p.fecha_encuadernado) <=', date('Y-m-d'))
->where("NOT EXISTS (
SELECT 1
FROM envios_lineas el
@@ -78,6 +79,79 @@ class LogisticaService
return $builder;
}
+ public static function findNextEnvios(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');
+ }
+
+ $hoy = date('Y-m-d');
+ $sieteDiasDespues = date('Y-m-d', strtotime('+7 days'));
+
+ // 3. Subconsulta principal
+ $subBuilder = $db->table('pedidos_linea pl')
+ ->select("
+ ot.id AS ot,
+ DATE(p.fecha_encuadernado) as fechaEncuadernado,
+ (
+ 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')
+ ->join('ordenes_trabajo ot', 'ot.pedido_id = p.id')
+ ->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
+ ->whereIn('pr.id', $presupuestoIds)
+ ->whereIn('p.estado', ['finalizado', 'produccion'])
+ ->where('p.fecha_encuadernado IS NOT NULL')
+ ->where('DATE(p.fecha_encuadernado) >=', $hoy)
+ ->where('DATE(p.fecha_encuadernado) <=', $sieteDiasDespues)
+ ->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('ot, fechaEncuadernado');
+ $builder->where('cantidad > unidades_enviadas');
+
+ return $builder;
+ }
+
public static function findForNewEnvio()
{
@@ -103,16 +177,15 @@ class LogisticaService
->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id')
->join('ordenes_trabajo ot', 'ot.pedido_id = p.id')
- ->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
->whereIn('p.estado', ['finalizado', 'produccion'])
- ->where('ot_dates.embalaje_at IS NOT NULL')
+ ->where('p.fecha_encuadernado IS NOT NULL')
+ ->where('DATE(p.fecha_encuadernado) <=', date('Y-m-d'))
->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;
}
@@ -289,6 +362,81 @@ class LogisticaService
}
+ public static function sendConfirmacionEnvio($envio, $lineaEnvio, $isFerro = false)
+ {
+
+ $view = \Config\Services::renderer();
+
+ if ($isFerro)
+ $subject = '[Safekat]' . " El envio del ferro de su pedido se ha realizado";
+ else
+ $subject = '[Safekat]' . " El envio de su pedido se ha realizado";
+
+ $presupuestoModel = model('App\Models\Presupuestos\PresupuestoModel');
+ $presupuesto = $presupuestoModel->find($lineaEnvio->presupuesto_id);
+ $proveedorModel = model('App\Models\Compras\ProveedorModel');
+ $proveedor = $proveedorModel->find($envio->proveedor_id);
+ $userModel = model('App\Models\Usuarios\UserModel');
+ $datos_correo = $userModel->select("CONCAT(users.first_name, ' ', users.last_name) as comercial_nombre, auth_identities.secret as comercial_correo, clientes.email as cliente_email")
+ ->join('auth_identities', 'auth_identities.user_id = users.id')
+ ->join('clientes', 'clientes.comercial_id = users.id')
+ ->where('clientes.id', $presupuesto->cliente_id)
+ ->first();
+
+
+
+ $pedido = (object) [
+ 'pedido_id' => $lineaEnvio->pedido_id,
+ 'titulo' => $presupuesto->titulo,
+ 'cp' => $envio->cp,
+ 'proveedor_nombre' => $proveedor->nombre,
+ 'codigo_seguimiento' => $envio->codigo_seguimiento,
+ 'comercial_nombre' => $datos_correo->comercial_nombre,
+ 'comercial_correo' => $datos_correo->comercial_correo,
+ ];
+
+ if ($proveedor->nombre == "GLS") {
+ $pedido->url = 'https://m.asmred.com/e/' . $envio->codigo_seguimiento . '/' . $envio->cp;
+ }
+
+ $content = $view->setVar('datos_pedido', $pedido)
+ ->render('themes/vuexy/mail/envio_pedido');
+ // Renderiza la plantilla completa
+ if ($isFerro)
+ $finalBody = $view->setVar('emailTitle2', "El ferro de su pedido " . $lineaEnvio->pedido_id . " ha sido enviado el " . date('d/m/Y'))
+ ->setVar('content', $content)
+ ->render('themes/vuexy/mail/mail_layout_2');
+ else
+ $finalBody = $view->setVar('emailTitle2', "Su pedido " . $lineaEnvio->pedido_id . " ha sido enviado el " . date('d/m/Y'))
+ ->setVar('content', $content)
+ ->render('themes/vuexy/mail/mail_layout_2');
+
+
+ $email = service('emailService');
+ $result = $email->send($subject, $finalBody, $datos_correo->cliente_email);
+
+ $chat = Service('chat');
+ $data = [
+ 'chat_department_id' => 5,
+ 'client' => $presupuesto->cliente_id,
+ 'message' => "El pedido " . $lineaEnvio->pedido_id . " ha sido enviado el " . date('d/m/Y') . ".
" .
+ "CP:" . $envio->cp . ".
" .
+ "Proveedor envío: " . $proveedor->nombre . ".
" .
+ "Código de seguimiento: " . $envio->codigo_seguimiento . ".
"
+ ];
+ if ($proveedor->nombre == "GLS") {
+ $data['message'] = $data['message'] . 'URL segumiento: ' .
+ 'https://m.asmred.com/e/' . $envio->codigo_seguimiento . '/' . $envio->cp . '';
+ }
+ $chat->storeChatMessage(5, "pedido", $lineaEnvio->pedido_id, $data);
+
+ return [
+ 'status' => $result,
+ 'message' => $result ? lang('Logistica.success.emailSent') : lang('Logistica.errors.emailNotSent'),
+ ];
+ }
+
+
public static function generateEnvio($ot_id, $direccion = null)
{
@@ -533,16 +681,29 @@ class LogisticaService
"name" => "ferro_en_cliente_at",
"ferro_en_cliente_at" => date('Y-m-d H:i:s')
]);
+
+ LogisticaService::sendConfirmacionEnvio($envio, $linea, true);
+
} else {
- if ($cantidad_enviada + $linea->unidades_envio == $pedido->total_tirada) {
+ 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);
+ $date = $ps->getOrdenTrabajo()->dates()->embalaje_at;
+ if (is_null($date) || empty($date)) {
+ $ps->updateOrdenTrabajoDate([
+ "name" => "embalaje_at",
+ "embalaje_at" => date('Y-m-d H:i:s')
+ ]);
+ }
$ps->updateOrdenTrabajoDate([
"name" => "envio_at",
"envio_at" => date('Y-m-d H:i:s')
]);
+
+ LogisticaService::sendConfirmacionEnvio($envio, $linea);
+
if ($finalizar_ot) {
$ps->updateOrdenTrabajo(
[
@@ -572,6 +733,29 @@ class LogisticaService
return $data_return;
}
+ public static function ficharEmbalaje($ids = null)
+ {
+
+ if (is_null($ids) || empty($ids) || count($ids) == 0) {
+ return [
+ 'status' => false,
+ 'message' => lang('Logistica.errors.noLineas'),
+ ];
+ }
+
+ for ($index = 0; $index < count($ids); $index++) {
+ $ps = (new ProductionService())->init($ids[$index]);
+ $ps->updateOrdenTrabajoDate([
+ "name" => "embalaje_at",
+ "embalaje_at" => date('Y-m-d')
+ ]);
+ }
+ return [
+ 'status' => true,
+ 'message' => lang('Logistica.success.successFicharEmbalaje'),
+ ];
+ }
+
public static function generateEtiquetasTitulos($envio, $lineas, $printer, $cajas)
{
$data = [
diff --git a/ci4/app/Views/themes/vuexy/form/logistica/viewEnvioEditForm.php b/ci4/app/Views/themes/vuexy/form/logistica/viewEnvioEditForm.php
index 7c63bd17..489222a3 100644
--- a/ci4/app/Views/themes/vuexy/form/logistica/viewEnvioEditForm.php
+++ b/ci4/app/Views/themes/vuexy/form/logistica/viewEnvioEditForm.php
@@ -10,7 +10,7 @@
= lang('Logistica.buttonsActions') ?>
-| = lang("Logistica.ordenTrabajo") ?> | += lang("Logistica.fechaEncuadernado") ?> | +
|---|
| - | = lang("Logistica.pedido") ?> | -= lang("Logistica.presupuesto") ?> | -= lang("Logistica.titulo") ?> | -- = lang("Logistica.unidadesEnvio") ?> - | -- = lang("Logistica.unidadesEnviadas") ?> - | -- = lang("Logistica.unidadesTotales") ?> - | -- | - | - |
|---|---|---|---|---|---|---|---|---|---|
|
-
- = lang("Logistica.unidadesTotalesFooter") ?>
-
- |
- |||||||||
|
-
- = lang("Logistica.peso") ?>
-
-
- |
- |||||||||