2 Commits

Author SHA1 Message Date
ee594effcf Merge branch 'main' into 'mod/permisos_admin'
Main

See merge request jjimenez/safekat!890
2025-07-21 13:44:12 +00:00
e0190fa03c Añadidos permisos 2025-07-21 15:43:10 +02:00
24 changed files with 540 additions and 439 deletions

View File

@ -14,5 +14,4 @@
"username": "sk_imn" "username": "sk_imn"
} }
] ]
} }

29
ci4/app/Config/PedidoXML.php Executable file
View File

@ -0,0 +1,29 @@
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class PedidoXML extends BaseConfig
{
public string $host;
public int $port;
public string $username;
public string $password;
public string $base_dir; # FTP server directory
public bool $xml_enabled;
public int $id_offset;
public function __construct() {
parent::__construct();
$this->host = env("HIDRIVE_HOST","10.5.0.6");
$this->port = env("HIDRIVE_PORT",21);
$this->username = env("HIDRIVE_USER","admin");
$this->password = env("HIDRIVE_PASS","A77h3b0X4OA2rOYAf4w2");
$this->base_dir = env("HIDRIVE_PATH_ROOT","/home/admin/safekat"); # FTP server directory
$this->xml_enabled = env("FTP_XML_ENABLED",false);
$this->id_offset = env("XML_OFFSET_CUSTOMER_ID",1000000);
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class PresupuestoSFTP extends BaseConfig
{
public string $host;
public int $port;
public string $username;
public string $password;
public string $base_dir;
public int $id_offset;
public function __construct()
{
parent::__construct();
$this->host = env("HIDRIVE_FILES_HOST", "sftp.hidrive.ionos.com");
$this->port = (int) env("HIDRIVE_FILES_PORT", 22);
$this->username = env("HIDRIVE_FILES_USER");
$this->password = env("HIDRIVE_FILES_PASS");
$this->id_offset = (int) env("BUDGET_FILES_OFFSET_ID", 1000000);
$domain = parse_url(env("app.baseURL"), PHP_URL_HOST);
$this->base_dir = "/users/{$this->username}/{$domain}";
}
}

View File

@ -500,6 +500,7 @@ $routes->group('pedidos', ['namespace' => 'App\Controllers\Pedidos'], function (
$routes->post('cambiarestado', 'Pedido::cambiarEstado', ['as' => 'cambiarEstadoPedido']); $routes->post('cambiarestado', 'Pedido::cambiarEstado', ['as' => 'cambiarEstadoPedido']);
$routes->post('update/(:any)', 'Pedido::update/$1', ['as' => 'actualizarPedido']); $routes->post('update/(:any)', 'Pedido::update/$1', ['as' => 'actualizarPedido']);
$routes->post('insertfactura', 'Pedido::addFactura'); $routes->post('insertfactura', 'Pedido::addFactura');
$routes->get('xml/(:num)', 'Pedido::get_xml_pedido/$1', ['as' => 'getXMLPedido']);
$routes->post('produccion/(:num)', 'Pedido::to_produccion/$1', ['as' => 'toProduccion']); $routes->post('produccion/(:num)', 'Pedido::to_produccion/$1', ['as' => 'toProduccion']);
$routes->get('pedidosCliente', 'Pedido::tablaClienteForm'); $routes->get('pedidosCliente', 'Pedido::tablaClienteForm');
$routes->get('getSumCliente/(:num)', 'Pedido::obtenerTotalPedidosCliente/$1'); $routes->get('getSumCliente/(:num)', 'Pedido::obtenerTotalPedidosCliente/$1');

View File

@ -38,6 +38,7 @@ class ConfigVariables extends BaseResourceController
public function index() public function index()
{ {
checkPermission('variables-sistema.menu');
$viewData = [ $viewData = [
'currentModule' => static::$controllerSlug, 'currentModule' => static::$controllerSlug,
@ -60,6 +61,8 @@ class ConfigVariables extends BaseResourceController
} }
public function updateVariable(int $config_variable_id) public function updateVariable(int $config_variable_id)
{ {
checkPermission('variables-sistema.edit');
$reqData = []; $reqData = [];
if ($this->request->isAJAX()) { if ($this->request->isAJAX()) {
$reqData = $this->request->getPost(); $reqData = $this->request->getPost();

View File

@ -42,11 +42,16 @@ class FestivoController extends BaseController
public function index() public function index()
{ {
checkPermission('festivos.menu');
return view($this->viewPath . $this->indexRoute); return view($this->viewPath . $this->indexRoute);
} }
public function store_festivo_date() public function store_festivo_date()
{ {
checkPermission('festivos.edit');
$bodyData = $this->request->getPost(); $bodyData = $this->request->getPost();
$date = $bodyData['date']; $date = $bodyData['date'];
$count = $this->model->where('date',$date)->countAllResults(); $count = $this->model->where('date',$date)->countAllResults();

View File

@ -6,6 +6,7 @@ use App\Controllers\Facturacion\Facturas;
use App\Entities\Pedidos\PedidoEntity; use App\Entities\Pedidos\PedidoEntity;
use App\Models\Collection; use App\Models\Collection;
use App\Models\Pedidos\PedidoModel; use App\Models\Pedidos\PedidoModel;
use App\Services\PedidoXMLService;
use App\Services\ProductionService; use App\Services\ProductionService;
use Hermawan\DataTables\DataTable; use Hermawan\DataTables\DataTable;
use CodeIgniter\I18n\Time; use CodeIgniter\I18n\Time;
@ -613,7 +614,12 @@ class Pedido extends \App\Controllers\BaseResourceController
$pedidoEntity->created_at_footer = $pedidoEntity->created_at ? date(' H:i d/m/Y', strtotime($pedidoEntity->created_at)) : ''; $pedidoEntity->created_at_footer = $pedidoEntity->created_at ? date(' H:i d/m/Y', strtotime($pedidoEntity->created_at)) : '';
$pedidoEntity->updated_at_footer = $pedidoEntity->updated_at ? date(' H:i d/m/Y', strtotime($pedidoEntity->updated_at)) : ''; $pedidoEntity->updated_at_footer = $pedidoEntity->updated_at ? date(' H:i d/m/Y', strtotime($pedidoEntity->updated_at)) : '';
} }
public function get_xml_pedido($pedido_id)
{
$data = PedidoXMLService::generate_xml($pedido_id);
// $xml_service = new PedidoXMLService($this->model);
return $this->respond($data);
}
public function to_produccion($pedido_id) public function to_produccion($pedido_id)
{ {

View File

@ -473,6 +473,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
'errors' => $errors 'errors' => $errors
); );
return $this->respond($data); return $this->respond($data);
} else { } else {
return $this->failUnauthorized('Invalid request', 403); return $this->failUnauthorized('Invalid request', 403);
} }
@ -732,6 +733,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
} else { } else {
return $return_data; return $return_data;
} }
} catch (Exception $e) { } catch (Exception $e) {
if ($this->request) { if ($this->request) {
if ($this->request->isAJAX()) if ($this->request->isAJAX())
@ -740,6 +742,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
return "Error: " . $e->getMessage(); return "Error: " . $e->getMessage();
} }
} }
} }
@ -846,9 +849,11 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$maxSolapa = (865 - floor($anchoTotal)) / 2; $maxSolapa = (865 - floor($anchoTotal)) / 2;
$maxSolapa = min($maxSolapa, 0.95 * $datosPedido->ancho); $maxSolapa = min($maxSolapa, 0.95 * $datosPedido->ancho);
return $this->respond($maxSolapa); return $this->respond($maxSolapa);
} else { } else {
return $this->failUnauthorized('Invalid request', 403); return $this->failUnauthorized('Invalid request', 403);
} }
} }
@ -869,6 +874,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
'menu' => $data, 'menu' => $data,
$csrfTokenName => $newTokenHash $csrfTokenName => $newTokenHash
]); ]);
} else { } else {
return $this->failUnauthorized('Invalid request', 403); return $this->failUnauthorized('Invalid request', 403);
} }
@ -1295,6 +1301,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$coste_envio += ($coste_direccion->coste / $tirada[$i]); $coste_envio += ($coste_direccion->coste / $tirada[$i]);
$resultado_presupuesto['info']['totales'][$i]['coste_envio'] += $coste_direccion->coste - $coste_direccion->margen; $resultado_presupuesto['info']['totales'][$i]['coste_envio'] += $coste_direccion->coste - $coste_direccion->margen;
$resultado_presupuesto['info']['totales'][$i]['margen_envio'] += $coste_direccion->margen; $resultado_presupuesto['info']['totales'][$i]['margen_envio'] += $coste_direccion->margen;
} }
} }
$resultado_presupuesto['coste_envio'][$i] = round($coste_envio, 2); $resultado_presupuesto['coste_envio'][$i] = round($coste_envio, 2);
@ -1329,6 +1336,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
} else { } else {
$resumen_totales = $resultado_presupuesto['info']['totales'][$i]; $resumen_totales = $resultado_presupuesto['info']['totales'][$i];
$resumen_totales['precio_unidad'] = round($resultado_presupuesto['precio_u'][$i], 4); $resumen_totales['precio_unidad'] = round($resultado_presupuesto['precio_u'][$i], 4);
} }
} }
@ -1567,6 +1575,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
if (count($direccionesFP1) > 0) { if (count($direccionesFP1) > 0) {
$this->guardarLineaEnvio($id, $direccionesFP1, $peso_libro, true, true, 1); $this->guardarLineaEnvio($id, $direccionesFP1, $peso_libro, true, true, 1);
} }
if (count($direccionesFP2) > 0) { if (count($direccionesFP2) > 0) {
$this->guardarLineaEnvio($id, $direccionesFP2, $peso_libro, true, true, 2); $this->guardarLineaEnvio($id, $direccionesFP2, $peso_libro, true, true, 2);
@ -1749,12 +1758,11 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
public function get_files() public function get_files()
{ {
// Aceptar solo POST (puedes cambiar a GET si lo necesitas)
if ($this->request->getMethod(true) !== 'POST') {
return $this->response->setStatusCode(405)->setJSON(['message' => 'Método no permitido']);
}
$presupuesto_id = $this->request->getPost('presupuesto_id') ?? 0; // Check if the request is a POST request
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$presupuesto_id = $this->request->getPost()['presupuesto_id'] ?? 0;
$model = model('App\Models\Presupuestos\PresupuestoFicheroModel'); $model = model('App\Models\Presupuestos\PresupuestoFicheroModel');
$files = $model->getFiles($presupuesto_id); $files = $model->getFiles($presupuesto_id);
@ -1762,91 +1770,78 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$result = []; $result = [];
foreach ($files as $file) { foreach ($files as $file) {
$relativePath = $file->file_path;
$fullPath = WRITEPATH . ltrim($relativePath, '/');
$relativePath = $file->file_path; $size = filesize($file->file_path);
$basename = basename($relativePath); // solo el nombre del archivo $splitPath = explode("presupuestos/", $file->file_path);
$result[] = (object) [ // se crea un objeto con el nombre del fichero y el tamaño
$obj = (object) array(
'name' => $file->nombre, 'name' => $file->nombre,
'size' => file_exists(WRITEPATH . $relativePath) ? filesize(WRITEPATH . $relativePath) : 0, 'size' => $size,
'hash' => $basename 'hash' => $splitPath[1] ?? $file->file_path
]; );
// se añade el objeto al array
array_push($result, $obj);
} }
return $this->response->setJSON($result); return json_encode($result);
}
} }
public function upload_files() public function upload_files()
{ {
$request = service('request');
$model = model('App\Models\Presupuestos\PresupuestoFicheroModel'); $model = model('App\Models\Presupuestos\PresupuestoFicheroModel');
$files = $request->getFiles()['file'] ?? [];
$presupuesto_id = $request->getPost('presupuesto_id');
$old_files = json_decode($request->getPost('oldFiles') ?? '[]');
if (!is_array($files)) { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$files = [$files];
$presupuesto_id = $_POST['presupuesto_id'];
$old_files = json_decode($_POST['oldFiles']);
$ftp = new SafekatFtpClient();
// Comprobar si se han subido archivos
if (!empty($_FILES['file']) || !empty($old_files)) {
// Borrar los archivos existentes del presupuesto
$ftp->removeFiles($presupuesto_id);
$model->deleteFiles($presupuesto_id, $old_files);
if (!empty($_FILES['file'])) {
$files = $_FILES['file'];
// Iterar sobre los archivos
for ($i = 0; $i < count($files['name']); $i++) {
// Aquí puedes acceder a las propiedades del archivo
$name = $files['name'][$i];
$extension = explode('.', $files['name'][$i])[1];
$tmp_name = $files['tmp_name'][$i];
$new_name = $model->saveFileInBBDD($presupuesto_id, $name, $extension, auth()->id());
// Se sube el fichero
// Pero primero se comprueba que la carpeta presupuestos exista
if (!is_dir(WRITEPATH . 'uploads/presupuestos')) {
mkdir(WRITEPATH . 'uploads/presupuestos', 0777, true);
} }
// Instanciar servicio con dependencias inyectadas manualmente if (!is_null($new_name)) {
$service = new \App\Services\PresupuestoUploaderService( $path = WRITEPATH . 'uploads/presupuestos/' . $new_name;
new \App\Libraries\SftpClientWrapper(config('PresupuestoSFTP')), move_uploaded_file($tmp_name, $path);
$model,
config('PresupuestoSFTP')
);
// Borrar antiguos del SFTP y de la BD (pero no del disco local)
$service->removeFromRemote($presupuesto_id);
$model->deleteMissingFiles($presupuesto_id, $old_files);
$results = [];
foreach ($files as $file) {
if (!$file->isValid()) {
$results[] = ['name' => $file->getName(), 'status' => 'invalid'];
continue;
} }
$newName = $model->saveFileInBBDD(
$presupuesto_id,
$file->getClientName(),
$file->getClientExtension(),
auth()->id()
);
$uploadPath = WRITEPATH . 'uploads/presupuestos/' . $newName;
$file->move(dirname($uploadPath), basename($uploadPath));
$results[] = ['name' => $file->getClientName(), 'status' => 'uploaded'];
} }
$ftp->uploadFilePresupuesto($presupuesto_id);
// Subida al SFTP
$sftpResult = $service->uploadToRemote($presupuesto_id);
// Preparar notificación
if (!$sftpResult['success']) {
return $this->response->setJSON([
'message' => 'Error al subir uno o más archivos al SFTP.',
'details' => [
'local' => $results,
'sftp' => $sftpResult['files']
]
])->setStatusCode(500);
} }
} else {
return $this->response->setJSON([ // Borrar los archivos existentes del presupuesto
'message' => 'Archivos subidos correctamente al sistema y al SFTP.', $ftp->removeFiles($presupuesto_id);
'details' => [ $model->deleteFiles($presupuesto_id);
'local' => $results, }
'sftp' => $sftpResult['files'] }
] return json_encode(['message' => 'Archivos subidos correctamente']);
]);
} }
@ -1941,7 +1936,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
{ {
if ($tipo == 'encuadernacion') { if ($tipo == 'encuadernacion') {
$model = new PresupuestoEncuadernacionesModel(); $model = new PresupuestoEncuadernacionesModel();
$data = [ $data = [
@ -2218,6 +2212,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$totalImpresion, $totalImpresion,
$margenImpresion $margenImpresion
); );
} }
} }
@ -2279,6 +2274,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$totalImpresion, $totalImpresion,
$margenImpresion $margenImpresion
); );
} }
} }
} }
@ -2389,6 +2385,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$margenServicios += round(floatval($acabadoCubierta[0]->total - $base), 2); $margenServicios += round(floatval($acabadoCubierta[0]->total - $base), 2);
} }
} }
} }
if ($lomoRedondo) { if ($lomoRedondo) {
@ -2425,6 +2422,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total;
$totalServicios += $base; $totalServicios += $base;
$margenServicios += round(floatval($resultado[0]->total - $base), 2); $margenServicios += round(floatval($resultado[0]->total - $base), 2);
} }
} }
@ -2462,6 +2460,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$totalImpresion, $totalImpresion,
$margenImpresion $margenImpresion
); );
} }
if ($coste_sobrecubierta <= 0) { if ($coste_sobrecubierta <= 0) {
@ -2675,6 +2674,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total;
$totalServicios += $base; $totalServicios += $base;
$margenServicios += round(floatval($acabadoFaja[0]->total - $base), 2); $margenServicios += round(floatval($acabadoFaja[0]->total - $base), 2);
} }
} }
} }
@ -2729,6 +2729,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total;
$totalServicios += $base; $totalServicios += $base;
$margenServicios += round(floatval($servicio->total - $base), 2); $margenServicios += round(floatval($servicio->total - $base), 2);
} }
$servDefectoMan = PresupuestoCLienteService::getServiciosManipuladoDefault([ $servDefectoMan = PresupuestoCLienteService::getServiciosManipuladoDefault([
@ -2871,6 +2872,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total;
$totalServicios += $base; $totalServicios += $base;
$margenServicios += round(floatval($resultado[0]->total - $base), 2); $margenServicios += round(floatval($resultado[0]->total - $base), 2);
} else if ($servicio->nombre == "ferro" || $servicio->nombre == "prototipo") { } else if ($servicio->nombre == "ferro" || $servicio->nombre == "prototipo") {
// Extra // Extra
$resultado = PresupuestoCLienteService::getServiciosExtra([ $resultado = PresupuestoCLienteService::getServiciosExtra([
@ -2903,6 +2905,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total;
$totalServicios += $base; $totalServicios += $base;
$margenServicios += round(floatval($resultado[0]->total - $base), 2); $margenServicios += round(floatval($resultado[0]->total - $base), 2);
} else if ($servicio->nombre == 'solapas_cubierta' || $servicio->nombre == 'solapas_sobrecubierta' || $servicio->nombre == 'solapas_faja') { } else if ($servicio->nombre == 'solapas_cubierta' || $servicio->nombre == 'solapas_sobrecubierta' || $servicio->nombre == 'solapas_faja') {
// Servicios manipulado // Servicios manipulado
$resultado = PresupuestoCLienteService::getServiciosManipulado([ $resultado = PresupuestoCLienteService::getServiciosManipulado([
@ -2979,6 +2982,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$totalServicios += $base; $totalServicios += $base;
$margenServicios += round(floatval($resultado[0]->precio - $base), 2); $margenServicios += round(floatval($resultado[0]->precio - $base), 2);
} }
} }
// Plegado de solapas grandes // Plegado de solapas grandes
@ -3218,6 +3222,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$margenImpresion += round($linea['margen_impresion_horas'], 2); $margenImpresion += round($linea['margen_impresion_horas'], 2);
$margenImpresion += round($linea['margen_click_pedido'], 2); $margenImpresion += round($linea['margen_click_pedido'], 2);
$margenImpresion = round($margenImpresion, 2); $margenImpresion = round($margenImpresion, 2);
} }
protected function calcular_lomo($lineas, $lomo_inicial) protected function calcular_lomo($lineas, $lomo_inicial)
@ -3337,7 +3342,8 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$color = 'negro'; $color = 'negro';
$model = model('App\Models\Presupuestos\PresupuestoLineaModel'); $model = model('App\Models\Presupuestos\PresupuestoLineaModel');
$data = $model->where('presupuesto_id', $presupuestoId)->findAll();; $data = $model->where('presupuesto_id', $presupuestoId)->findAll();
;
foreach ($data as $linea) { foreach ($data as $linea) {
if (strpos($linea->tipo, "hq") !== false) { // $linea->tipo contains the substring "hq" if (strpos($linea->tipo, "hq") !== false) { // $linea->tipo contains the substring "hq"
@ -3690,4 +3696,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
return $this->response->setStatusCode(500)->setBody('Error interno'); return $this->response->setStatusCode(500)->setBody('Error interno');
} }
} }
} }

View File

@ -15,24 +15,25 @@ use App\Models\Catalogo\CatalogoLibroModel;
use App\Services\PresupuestoService; use App\Services\PresupuestoService;
use CodeIgniter\Shield\Entities\User; use CodeIgniter\Shield\Entities\User;
use App\Libraries\SftpClientWrapper;
use Config\PresupuestoSFTP;
class Test extends BaseController class Test extends BaseController
{ {
function __construct() {} function __construct()
{
}
public function echo() public function echo()
{ {
echo "echo"; echo "echo";
} }
public function index() public function index()
{ {
} }
@ -74,8 +75,10 @@ class Test extends BaseController
// Insert it // Insert it
$tel_model->insert($tarifasLinea); $tel_model->insert($tarifasLinea);
} }
} }
} }
@ -221,6 +224,7 @@ class Test extends BaseController
} else { } else {
$values = []; $values = [];
} }
} }

View File

@ -34,7 +34,7 @@ return [
"global_prev" => "Anterior", "global_prev" => "Anterior",
"global_next" => "Siguiente", "global_next" => "Siguiente",
"global_save_file" => "Guardar ficheros", "global_save_file" => "Guardar ficheros",
"global_select_files" => "Seleccionar ficheros", "global_upload_files" => "Subir ficheros",
"global_download_files" => "Descargar ficheros", "global_download_files" => "Descargar ficheros",
"global_all" => "Todos", "global_all" => "Todos",
// LOGIN - Index // LOGIN - Index

View File

@ -58,8 +58,12 @@ return [
'papelImpresionSection' => 'Papel impresión', 'papelImpresionSection' => 'Papel impresión',
'usuariosSection' => 'Usuarios', 'usuariosSection' => 'Usuarios',
'rolesPermisosSection' => 'Roles y permisos', 'rolesPermisosSection' => 'Roles y permisos',
'tareasMaquinaSection' => 'Tareas de Máquina',
'imposicionesSection' => 'Imposiciones',
'ubicacionesSection' => 'Ubicaciones', 'ubicacionesSection' => 'Ubicaciones',
'seriesFacturasSection' => 'Series facturas', 'seriesFacturasSection' => 'Series facturas',
'variablesSistemaSection' => 'Variables de sistema',
'festivosSection' => 'Días Festivos',
'ajustesSection' => 'Ajustes', 'ajustesSection' => 'Ajustes',
'actividadSection' => 'Accesos', 'actividadSection' => 'Accesos',
'backupSection' => 'Backups', 'backupSection' => 'Backups',

View File

@ -15,23 +15,52 @@ class SafekatFtpClient
protected string $username; protected string $username;
protected string $password; protected string $password;
protected string $base_dir; protected string $base_dir;
protected bool $xml_enabled;
protected object $pedido_xml_config; protected object $pedido_xml_config;
public function __construct() public function __construct()
{ {
$this->pedido_xml_config = config("PresupuestoSFTP"); $this->pedido_xml_config = config("PedidoXML");
$this->host = $this->pedido_xml_config->host; $this->host = $this->pedido_xml_config->host;
$this->username = $this->pedido_xml_config->username; $this->username = $this->pedido_xml_config->username;
$this->password = $this->pedido_xml_config->password; $this->password = $this->pedido_xml_config->password;
$this->port = $this->pedido_xml_config->port; $this->port = $this->pedido_xml_config->port;
$this->base_dir = $this->pedido_xml_config->base_dir; $this->base_dir = $this->pedido_xml_config->base_dir;
$this->xml_enabled = $this->pedido_xml_config->xml_enabled;
$this->ftp = new SFTP($this->host); $this->ftp = new SFTP($this->host);
} }
/**
* Upload the content of $filename to the base directory declared in App\Config\FTP.php
*
* @param string $content
* @param string $filename
* @return boolean
*/
public function uploadXML(string $content, string $filename): bool
{
try {
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);
}
$this->ftp->put($remotePath . '/' . $filename, $content);
return true;
} catch (\Throwable $th) {
throw $th;
log_message('error', $th->getMessage());
return false;
}
}
public function uploadFilePresupuesto(int $presupuesto_id) public function uploadFilePresupuesto(int $presupuesto_id)
{ {
try { try {
if ($this->xml_enabled == false)
return false;
$model = model(PresupuestoFicheroModel::class); $model = model(PresupuestoFicheroModel::class);
$modelPedidoLinea = model(PedidoLineaModel::class); $modelPedidoLinea = model(PedidoLineaModel::class);
$pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id); $pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id);
@ -57,6 +86,7 @@ class SafekatFtpClient
public function removeFiles(int $presupuesto_id) public function removeFiles(int $presupuesto_id)
{ {
try { try {
// if ($this->xml_enabled == false) return false;
$model = model(PresupuestoFicheroModel::class); $model = model(PresupuestoFicheroModel::class);
$modelPedidoLinea = model(PedidoLineaModel::class); $modelPedidoLinea = model(PedidoLineaModel::class);
$pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id); $pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id);

View File

@ -1,43 +0,0 @@
<?php
namespace App\Libraries;
use phpseclib3\Net\SFTP;
use Config\PresupuestoSFTP;
class SftpClientWrapper
{
protected SFTP $client;
public function __construct(PresupuestoSFTP $config)
{
$this->client = new SFTP($config->host, $config->port);
$this->client->login($config->username, $config->password);
}
public function upload(string $local, string $remote): bool
{
return $this->client->put($remote, $local, SFTP::SOURCE_LOCAL_FILE);
}
public function delete(string $remote): bool
{
return $this->client->delete($remote);
}
public function exists(string $remote): bool
{
return $this->client->file_exists($remote);
}
public function mkdir(string $remote): bool
{
return $this->client->mkdir($remote, true);
}
public function chmod(string $path, int $permissions): bool
{
return $this->client->chmod($permissions, $path);
}
}

View File

@ -32,7 +32,7 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel
$this->db->table($this->table . " t1") $this->db->table($this->table . " t1")
->set('presupuesto_id', $presupuesto_id) ->set('presupuesto_id', $presupuesto_id)
->set('nombre', $filename) ->set('nombre', $filename)
->set('file_path', 'uploads/presupuestos/' . $new_filename) ->set('file_path', WRITEPATH . 'uploads/presupuestos/' . $new_filename)
->set('upload_by', $user_id) ->set('upload_by', $user_id)
->set('upload_at', date('Y-m-d H:i:s')) ->set('upload_at', date('Y-m-d H:i:s'))
->insert(); ->insert();
@ -54,9 +54,8 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel
// se comprueba que el $file->nombre no sea igual a ninguno de los elementos del array $old_files // se comprueba que el $file->nombre no sea igual a ninguno de los elementos del array $old_files
if (!in_array($file->nombre, $old_files)) { if (!in_array($file->nombre, $old_files)) {
$fullPath = WRITEPATH . $file->file_path; if (file_exists($file->file_path)) {
if (file_exists($fullPath)) { unlink($file->file_path);
unlink($fullPath);
} }
$this->db $this->db
@ -83,17 +82,14 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel
$hash = $this->generateFileHash($file->nombre); $hash = $this->generateFileHash($file->nombre);
// se copia el fichero a la nueva ubicación // se copia el fichero a la nueva ubicación
$originalPath = WRITEPATH . $file->file_path; if (!file_exists(WRITEPATH . $file->file_path)) {
$newPath = 'uploads/presupuestos/' . $hash; copy($file->file_path, WRITEPATH . 'uploads/presupuestos/' . $hash);
if (file_exists($originalPath)) {
copy($originalPath, WRITEPATH . $newPath);
} }
$this->db->table($this->table . " t1") $this->db->table($this->table . " t1")
->set('presupuesto_id', $presupuesto_id_destino) ->set('presupuesto_id', $presupuesto_id_destino)
->set('nombre', $file->nombre) ->set('nombre', $file->nombre)
->set('file_path', $newPath) ->set('file_path', WRITEPATH . 'uploads/presupuestos/' . $hash)
->set('upload_by', auth()->user()->id) ->set('upload_by', auth()->user()->id)
->set('upload_at', date('Y-m-d H:i:s')) ->set('upload_at', date('Y-m-d H:i:s'))
->insert(); ->insert();
@ -109,25 +105,6 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel
->where('presupuesto_id', $presupuesto_id)->get()->getResult(); ->where('presupuesto_id', $presupuesto_id)->get()->getResult();
} }
public function deleteMissingFiles(int $presupuesto_id, array $keepNames = [])
{
$files = $this->getFiles($presupuesto_id);
foreach ($files as $file) {
if (!in_array($file->nombre, $keepNames)) {
$fullPath = WRITEPATH . $file->file_path;
if (file_exists($fullPath)) {
unlink($fullPath);
}
$this->db->table($this->table)
->where('presupuesto_id', $presupuesto_id)
->where('nombre', $file->nombre)
->delete();
}
}
}
/** /**
* Función para convertir el nombre y extensión de un fichero en un hash único * Función para convertir el nombre y extensión de un fichero en un hash único
@ -140,4 +117,6 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel
{ {
return hash('sha256', $filename); return hash('sha256', $filename);
} }
} }

View File

@ -0,0 +1,222 @@
<?php
namespace App\Services;
use DOMDocument;
use App\Libraries\SafekatFtpClient;
use CodeIgniter\Config\BaseService;
use App\Models\Pedidos\PedidoModel;
use App\Models\Presupuestos\PresupuestoModel;
class PedidoXMLService extends BaseService
{
public static function get_pedido_presupuesto(int $pedido_id)
{
$data_xml = [];
$pedidoModel = model(PedidoModel::class);
$presupuestoModel = model(PresupuestoModel::class);
$data_xml['pedido_cliente_presupuesto'] = $pedidoModel->getPedidoClientePresupuesto($pedido_id);
$data_xml['pedido_presupuesto_direcciones'] = $pedidoModel->getPedidoPresupuestoDirecciones($pedido_id);
$data_xml['pedido_presupuesto_lineas'] = $pedidoModel->getPedidoPresupuestoLineas($pedido_id);
$servicios = $presupuestoModel->getServiciosPresupuesto($data_xml['pedido_cliente_presupuesto']->presupuestoId);
$data_xml['servicios'] = $servicios;
$data_xml['preimpresion'] = PedidoXMLService::parse_servicio_preimpresion($servicios['preimpresion']);
$data_xml["acabado"] = PedidoXMLService::parse_servicio_acabado($servicios['acabado']);
$data_xml["binding"] = PedidoXMLService::get_binding_code($data_xml['pedido_cliente_presupuesto']->codigoTipoImpresion,$data_xml['pedido_cliente_presupuesto']->solapas);
return $data_xml;
}
protected static function parse_servicio_acabado(array $data_xml_servicios_acabado)
{
$xml_element = [];
$service_xml_key_value = [
"ShrinkWrapping" => fn($nombre) => str_contains($nombre,"retractilado"),
"Finish" => fn($nombre) => str_contains($nombre,"brillo"),
"PlakeneT" =>fn($nombre) => str_contains($nombre,"plakene traslúcido"),];
foreach($data_xml_servicios_acabado as $servicio_acabado)
{
$service_name = strtolower($servicio_acabado->nombre);
foreach ($service_xml_key_value as $key => $value) {
$xml_element[$key] = $value($service_name) ? 1 : 0 ;
}
}
return $xml_element;
}
protected static function parse_servicio_preimpresion(array $data_xml_servicios_preimpresion)
{
$xml_element = [];
$service_xml_key_value = [
"Urgent" => fn($nombre) => str_contains($nombre,"Pedido urgente"),
"Prototype" => fn($nombre) => str_contains($nombre,"Prototipo"),
"Layout" =>fn($nombre) => str_contains($nombre,"Maquetación"),
"Correction" =>fn($nombre) => str_contains($nombre,"Corrección ortográfica"),
// "Review" =>fn($nombre) => str_contains($nombre,"Revisión Profesional de archivo"),
"Design" =>fn($nombre) => str_contains($nombre,'Diseño de Cubierta'),
];
foreach($data_xml_servicios_preimpresion as $servicio_pre)
{
$service_name = $servicio_pre->nombre;
foreach ($service_xml_key_value as $key => $value) {
$value_service = $value($service_name) ? 1 : 0 ;
if( $value_service){
$xml_element[$key] = $servicio_pre->precio ;
}else if(!isset($xml_element[$key])){
$xml_element[$key] = $value_service;
}
}
}
return $xml_element;
}
public static function generate_xml($pedido_id)
{
$papel_formato_ancho = 0;
$papel_formato_alto = 0;
$data = PedidoXMLService::get_pedido_presupuesto($pedido_id);
$xml = new DOMDocument('1.0', 'utf-8');
$xml_order_el = $xml->createElement('Order');
$xml_header_el = $xml->createElement('Header');
$offset_pedido_id = env('XML_OFFSET_CUSTOMER_ID',1000000) + $data["pedido_cliente_presupuesto"]->pedidoId;
$xml_header_el->appendChild($xml->createElement('CustomerCode', $data["pedido_cliente_presupuesto"]->presupuestoClienteId));
$xml_header_el->appendChild($xml->createElement('CodeNode', env('NODE_CODE_XML','SFK')));
$xml_header_el->appendChild($xml->createElement('ExternId', $offset_pedido_id));
$xml_header_el->appendChild($xml->createElement('NumProducts', 1));
$xml_header_el->appendChild($xml->createElement('Date', now_db()));
$xml_order_el->appendChild($xml_header_el);
$xml_products_el = $xml->createElement('Products');
$xml_product_el = $xml->createElement('Product');
$xml_product_el->appendChild($xml->createElement('ItemId', $offset_pedido_id));
$xml_product_el->appendChild($xml->createElement('Quantity', $data["pedido_cliente_presupuesto"]->tirada));
$xml_product_el->appendChild($xml->createElement('Title', $data["pedido_cliente_presupuesto"]->titulo));
$xml_product_el->appendChild($xml->createElement('Pages', $data["pedido_cliente_presupuesto"]->paginas));
$xml_product_el->appendChild($xml->createElement('Reprint', $data["pedido_cliente_presupuesto"]->inc_rei ?? 0));
if ($data["pedido_cliente_presupuesto"]->papel_formato_personalizado) {
$papel_formato_ancho = $data["pedido_cliente_presupuesto"]->papelAnchoPersonalidado;
$papel_formato_alto = $data["pedido_cliente_presupuesto"]->papelAltoPersonalidado;
} else {
$papel_formato_ancho = $data["pedido_cliente_presupuesto"]->lgPapelFormatoAncho;
$papel_formato_alto = $data["pedido_cliente_presupuesto"]->lgPapelFormatoAlto;
}
$xml_product_el->appendChild($xml->createElement('Width', $papel_formato_ancho));
$xml_product_el->appendChild($xml->createElement('Height', $papel_formato_alto));
$presupuestoLineaTipoCubierta = null;
$xml_presupuesto_lineas_el = $xml->createElement('Lines');
## Iterate throught presupuesto_lineas
foreach ($data["pedido_presupuesto_lineas"] as $row) {
if (str_contains($row->tipo, "rot") || str_contains($row->tipo, "bn") || str_contains($row->tipo, "color")) {
$colorInterior = PedidoXMLService::get_color_interior($row);
$xmlInside = $xml->createElement('Inside');
$xmlInside->appendChild($xml->createElement('TypeOfPrint', $colorInterior));
$xmlInside->appendChild($xml->createElement('HQ', str_contains($row->tipo, 'hq') ? 1 : 0));
$xmlInside->appendChild($xml->createElement('Pages', $row->paginas));
$xmlInside->appendChild($xml->createElement('Paper', $row->papelCode));
$xmlInside->appendChild($xml->createElement('Weight', $row->gramaje));
$xml_presupuesto_lineas_el->appendChild($xmlInside);
} else if (str_contains($row->tipo, "lp_cubierta") ) {//|| str_contains($row->tipo, "sobrecubierta")
//? If both exists presupuestoLineaTipoCubierta is override by sobreCubierta making null and not adding
$papelCubiertaCode = $row->papelCode;
$papelCubiertaGramaje = $row->gramaje;
$presupuestoLineaTipoCubierta = $row->tipo == "lp_cubierta" ? $row : null;
}
}
$xml_product_el->appendChild($xml_presupuesto_lineas_el);
if ($presupuestoLineaTipoCubierta) {
$containsTarifaAcabadoBrillo = isset($data['acabado']['Finish']) ? true : false;
if ($containsTarifaAcabadoBrillo) {
$acabado = "brillo";
} else {
$acabado = "mate";
}
$xmlCover = $xml->createElement('Cover');
$xmlCover->appendChild($xml->createElement('Sides', $presupuestoLineaTipoCubierta->paginas / 2));
$xmlCover->appendChild($xml->createElement('Paper', $presupuestoLineaTipoCubierta->papelCode));
$xmlCover->appendChild($xml->createElement('Weight', $presupuestoLineaTipoCubierta->gramaje));
$xmlCover->appendChild($xml->createElement('Flaps', $data["pedido_cliente_presupuesto"]->solapas));
$xmlCover->appendChild($xml->createElement('WidthFlaps', $data["pedido_cliente_presupuesto"]->solapas_ancho));
$xmlCover->appendChild($xml->createElement('Finish', $acabado));
$xml_product_el->appendChild($xmlCover);
}
$xml_product_el->appendChild($xml->createElement('Binding', $data['binding']));
$xml_services_el = $xml->createElement('Services');
$xml_services_el->appendChild($xml->createElement('Bookmark', $data["pedido_cliente_presupuesto"]->marcapaginas));
foreach ($data['preimpresion'] as $key => $value) {
$xml_services_el->appendChild($xml->createElement($key, $value));
}
foreach ($data['acabado'] as $key => $value) {
$xml_services_el->appendChild($xml->createElement($key, $value));
}
$xml_product_el->appendChild($xml_services_el);
$xml_envios_el = $xml->createElement('Shipments');
foreach ($data["pedido_presupuesto_direcciones"] as $pedido_presupuesto_direccion) {
$xml_envio_el = $xml->createElement('Shipment');
$xml_envio_el->appendChild($xml->createElement('Qty', $pedido_presupuesto_direccion->cantidad));
$xml_envio_el->appendChild($xml->createElement('Price', $pedido_presupuesto_direccion->precio));
$xml_envio_el->appendChild($xml->createElement('Attention', $pedido_presupuesto_direccion->att));
$xml_envio_el->appendChild($xml->createElement('Email', $pedido_presupuesto_direccion->email));
$xml_envio_el->appendChild($xml->createElement('Address', $pedido_presupuesto_direccion->direccion));
$xml_envio_el->appendChild($xml->createElement('Province', $pedido_presupuesto_direccion->provincia));
$xml_envio_el->appendChild($xml->createElement('City', $pedido_presupuesto_direccion->municipio));
$xml_envio_el->appendChild($xml->createElement('Zip', $pedido_presupuesto_direccion->cp));
$xml_envio_el->appendChild($xml->createElement('CountryCode', $pedido_presupuesto_direccion->paisCode3));
$xml_envio_el->appendChild($xml->createElement('Telephone', $pedido_presupuesto_direccion->telefono));
$xml_envios_el->appendChild($xml_envio_el);
}
$xml_product_el->appendChild($xml_envios_el);
$xml_product_el->appendChild($xml->createElement('Comments', $data["pedido_cliente_presupuesto"]->comentarios_safekat));
$xml_product_el->appendChild($xml->createElement('CommentsClient', $data["pedido_cliente_presupuesto"]->comentarios_cliente));
$xml_products_el->appendChild($xml_product_el);
$xml_order_el->appendChild($xml_products_el);
$xml->appendChild($xml_order_el);
$file_has_suffix = hash('sha512',$offset_pedido_id);
$file_name = PedidoXMLService::generate_xml_file_name($file_has_suffix);
$ftp = new SafekatFtpClient();
$ftp->uploadXML($xml->saveXML(),$file_name);
return $data;
}
protected static function generate_xml_file_name(string $hash) : string
{
return implode("",["SafekatNew_",$hash,".xml"]);
}
protected static function get_binding_code(string $tipo_impresion_nombre,bool $solapas) : ?string
{
$solapa = $solapas ? '1' : '0';
$key = implode("_",[$tipo_impresion_nombre,$solapa]);
$xml_mapping_binding =
[
"libroFresadoTapaBlanda_0" => 'RF',
"libroFresadoTapaBlanda_1" => 'RFS',
"libroCosidoTapaBlanda_0" => 'RCHV',
"libroCosidoTapaBlanda_1" => 'RCHVS',
"libroGrapado_0" => 'CC2',
"libroGrapado_1" => 'CC2S',
"libroCosidoTapaDura_0" => 'TDC',
"libroCosidoTapaDura_1" => 'TDC',
"libroFresadoTapaDura_0" => 'RDF',
"libroFresadoTapaDura_1" => 'RDF',
"libroEspiralTapaBlanda_0" => 'ESP',
"libroEspiralTapaBlanda_1" => 'ESP',
"libroWireoTapaBlanda_0" => 'WIO',
"libroWireoTapaBlanda_1" => 'WIO',
];
return $xml_mapping_binding[$key] ?? null;
}
protected static function get_color_interior($pre_linea): ?string
{
$color_interior = null;
$bn_tipo_array = ['lp_bn', 'lp_bnhq', 'lp_rot_bn'];
$color_tipo_array = ['lp_color', 'lp_color_hq', 'lp_rot_color'];
if (in_array($pre_linea->tipo, $bn_tipo_array)) {
$color_interior = "bn";
};
if (in_array($pre_linea->tipo, $color_tipo_array)) {
$color_interior = "color";
};
return $color_interior;
}
}

View File

@ -1903,6 +1903,7 @@ class PresupuestoService extends BaseService
"user_updated_id" => auth()->user()->id, "user_updated_id" => auth()->user()->id,
]; ];
$id_linea = $model_pedido_linea->insert($data_pedido_linea); $id_linea = $model_pedido_linea->insert($data_pedido_linea);
//PedidoXMLService::generate_xml($pedido_id);
} }
if ($id_linea != 0 && $pedido_id != 0) { if ($id_linea != 0 && $pedido_id != 0) {

View File

@ -1,76 +0,0 @@
<?php
namespace App\Services;
use App\Models\Presupuestos\PresupuestoFicheroModel;
use App\Models\Pedidos\PedidoLineaModel;
use App\Libraries\SftpClientWrapper;
use Config\PresupuestoSFTP;
class PresupuestoUploaderService
{
public function __construct(
protected SftpClientWrapper $ftp,
protected PresupuestoFicheroModel $fileModel,
protected PresupuestoSFTP $config
) {}
public function uploadToRemote(int $presupuestoId): array
{
$remoteDir = "{$this->config->base_dir}/ficheros/" . ($presupuestoId + $this->config->id_offset);
if (!$this->ftp->exists($remoteDir)) {
if (!$this->ftp->mkdir($remoteDir, true)) {
return [
'success' => false,
'message' => "No se pudo crear el directorio remoto: $remoteDir"
];
}
$this->ftp->chmod($remoteDir, 0755);
}
$files = $this->fileModel->getFiles($presupuestoId);
$results = [];
foreach ($files as $file) {
$remotePath = $remoteDir . '/' . basename($file->file_path);
$localPath = WRITEPATH . $file->file_path;
$ok = $this->ftp->upload($localPath, $remotePath);
$results[] = [
'file' => $file->nombre,
'remotePath' => $remotePath,
'success' => $ok
];
}
$allOk = !in_array(false, array_column($results, 'success'));
return [
'success' => $allOk,
'files' => $results
];
}
public function removeFromRemote(int $presupuestoId): void
{
$remoteDir = "{$this->config->base_dir}/pedidos_files/" . ($presupuestoId + $this->config->id_offset);
$files = $this->fileModel->getFiles($presupuestoId);
foreach ($files as $file) {
$this->ftp->delete($remoteDir . '/' . basename($file->file_path));
}
}
public function removeMissingFromRemote(int $presupuestoId, array $keepFileNames): void
{
$remoteDir = "{$this->config->base_dir}/pedidos_files/" . ($presupuestoId + $this->config->id_offset);
$files = $this->fileModel->getFiles($presupuestoId);
foreach ($files as $file) {
if (!in_array($file->nombre, $keepFileNames)) {
$this->ftp->delete($remoteDir . '/' . basename($file->file_path));
}
}
}
}

View File

@ -24,7 +24,7 @@
</div> </div>
<div class="col-md-12 gap-2"> <div class="col-md-12 gap-2">
<button id="<?= $id ?>_btnUploadFiles" class="btn mt-3 btn-sm btn-primary waves-effect waves-light ml-2 "> <button id="<?= $id ?>_btnUploadFiles" class="btn mt-3 btn-sm btn-primary waves-effect waves-light ml-2 ">
<span class="align-middle d-sm-inline-block d-none me-sm-1"><?= lang('App.global_select_files') ?></span> <span class="align-middle d-sm-inline-block d-none me-sm-1"><?= lang('App.global_upload_files') ?></span>
<i class="ti ti-upload ti-xs"></i> <i class="ti ti-upload ti-xs"></i>
</button> </button>
<button id="<?= $id ?>_btnSubmitFiles" class="btn mt-3 btn-success btn-sm waves-effect waves-light ml-2"> <button id="<?= $id ?>_btnSubmitFiles" class="btn mt-3 btn-success btn-sm waves-effect waves-light ml-2">

View File

@ -113,6 +113,7 @@
</div><!-- //.card --> </div><!-- //.card -->
</div><!--//.col --> </div><!--//.col -->
</div><!--//.row --> </div><!--//.row -->
<?= $this->endSection() ?> <?= $this->endSection() ?>
<?= $this->section('additionalInlineJs') ?> <?= $this->section('additionalInlineJs') ?>

View File

@ -11,6 +11,7 @@
<div class="container-xxl flex-grow-1 container-p-y"> <div class="container-xxl flex-grow-1 container-p-y">
<!-- Role cards --> <!-- Role cards -->
<div class="row g-4"> <div class="row g-4">
<?php if (auth()->user()->can('roles-permisos.create')): ?>
<div class="col-xl-4 col-lg-6 col-md-6"> <div class="col-xl-4 col-lg-6 col-md-6">
<div class="card h-100"> <div class="card h-100">
<div class="row h-100"> <div class="row h-100">
@ -32,6 +33,7 @@
</div> </div>
</div> </div>
</div> </div>
<?php endif; ?>
<?php foreach ($userGroupList as $item): ?> <?php foreach ($userGroupList as $item): ?>
<?php $item->users = $model->getUsersByRol($item->keyword); ?> <?php $item->users = $model->getUsersByRol($item->keyword); ?>
@ -45,7 +47,8 @@
</h6> </h6>
<ul class="list-unstyled d-flex align-items-center avatar-group mb-0"> <ul class="list-unstyled d-flex align-items-center avatar-group mb-0">
<?php foreach ($item->users as $user): ?> <?php foreach ($item->users as $user): ?>
<li data-bs-toggle="tooltip" data-popup="tooltip-custom" data-bs-placement="top" <li data-bs-toggle="tooltip" data-popup="tooltip-custom"
data-bs-placement="top"
title="<?= esc($user->first_name . ' ' . $user->last_name) ?>" title="<?= esc($user->first_name . ' ' . $user->last_name) ?>"
class="avatar avatar-sm pull-up"> class="avatar avatar-sm pull-up">
<img class="rounded-circle" src="<?= gravatar_url($user->email, 30) ?>" <img class="rounded-circle" src="<?= gravatar_url($user->email, 30) ?>"
@ -57,12 +60,15 @@
<div class="d-flex justify-content-between align-items-end mt-1"> <div class="d-flex justify-content-between align-items-end mt-1">
<div class="role-heading"> <div class="role-heading">
<h4 class="mb-1"><?= esc($item->title) ?></h4> <h4 class="mb-1"><?= esc($item->title) ?></h4>
<?php if (auth()->user()->can('roles-permisos.edit')): ?>
<a href="<?= route_to('editGroup', $item->id) ?>"> <a href="<?= route_to('editGroup', $item->id) ?>">
<span><?= lang('Basic.global.edit') ?></span> <span><?= lang('Basic.global.edit') ?></span>
</a> </a>
<?php endif; ?>
</div> </div>
<?= <?php
anchor( if (auth()->user()->can('roles-permisos.delete')) {
echo anchor(
'#confirm2delete', '#confirm2delete',
"<i class='ti ti-trash ti-md'></i>", "<i class='ti ti-trash ti-md'></i>",
[ [
@ -72,6 +78,7 @@
'data-bs-target' => '#confirm2delete' 'data-bs-target' => '#confirm2delete'
] ]
); );
}
?> ?>
</div> </div>
</div> </div>

View File

@ -206,7 +206,7 @@
</div> </div>
</div> </div>
<button id="btnUploadFile" class="btn mt-3 btn-primary btn-submit waves-effect waves-light ml-2 "> <button id="btnUploadFile" class="btn mt-3 btn-primary btn-submit waves-effect waves-light ml-2 ">
<span class="align-middle d-sm-inline-block d-none me-sm-1"><?= lang('App.global_select_files') ?></span> <span class="align-middle d-sm-inline-block d-none me-sm-1"><?= lang('App.global_upload_files') ?></span>
<i class="ti ti-upload ti-xs"></i> <i class="ti ti-upload ti-xs"></i>
</button> </button>
<button id="submit-all-files" class="btn mt-3 btn-success btn-submit waves-effect waves-light ml-2"> <button id="submit-all-files" class="btn mt-3 btn-success btn-submit waves-effect waves-light ml-2">

View File

@ -14,7 +14,10 @@ if (
auth()->user()->can('roles-permisos.menu') || auth()->user()->can('roles-permisos.menu') ||
auth()->user()->can('proveedores.menu') || auth()->user()->can('proveedores.menu') ||
auth()->user()->can('ubicaciones.menu') || auth()->user()->can('ubicaciones.menu') ||
auth()->user()->can('series-facturas.menu') auth()->user()->can('series-facturas.menu') ||
auth()->user()->can('tareas-maquina.menu') ||
auth()->user()->can('festivos.menu') ||
auth()->user()->can('variables-sistema.menu')
) { ) {
?> ?>
<li class="menu-item"> <li class="menu-item">
@ -66,14 +69,14 @@ if (
</a> </a>
</li> </li>
<?php } ?> <?php } ?>
<?php if (auth()->user()->inGroup('root')) { ?> <?php if (auth()->user()->can('tareas-maquina.menu')) { ?>
<li class="menu-item"> <li class="menu-item">
<a href="<?= route_to("maquinaTareaList") ?>" class="menu-link"> <a href="<?= route_to("maquinaTareaList") ?>" class="menu-link">
<div> <?= lang("App.menu_maquina_tareas") ?></div> <div> <?= lang("App.menu_maquina_tareas") ?></div>
</a> </a>
</li> </li>
<?php } ?> <?php } ?>
<?php if (auth()->user()->inGroup('root')) { ?> <?php if (auth()->user()->can('imposiciones.menu')) { ?>
<li class="menu-item"> <li class="menu-item">
<a href="<?= route_to("imposicionList") ?>" class="menu-link"> <a href="<?= route_to("imposicionList") ?>" class="menu-link">
<div> <?= lang("App.menu_imposiciones") ?></div> <div> <?= lang("App.menu_imposiciones") ?></div>
@ -122,7 +125,7 @@ if (
</a> </a>
</li> </li>
<?php } ?> <?php } ?>
<?php if (auth()->user()->inGroup('root')) { ?> <?php if (auth()->user()->can('variables-sistema.menu')) { ?>
<li class="menu-item"> <li class="menu-item">
<a href="<?= route_to('variablesIndex') ?>" class="menu-link"> <a href="<?= route_to('variablesIndex') ?>" class="menu-link">
<div> <?= lang("App.menu_variables") ?></div> <div> <?= lang("App.menu_variables") ?></div>
@ -136,7 +139,7 @@ if (
</a> </a>
</li> </li>
<?php } ?> <?php } ?>
<?php if (auth()->user()->inGroup('root')) { ?> <?php if (auth()->user()->can('festivos.menu')) { ?>
<li class="menu-item"> <li class="menu-item">
<a href="<?= route_to('festivosList') ?>" class="menu-link"> <a href="<?= route_to('festivosList') ?>" class="menu-link">
<div> <?= lang("App.menu_config_holidays") ?></div> <div> <?= lang("App.menu_config_holidays") ?></div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -1,13 +1,13 @@
// Importación de utilidades AJAX y alertas personalizadas
import Ajax from '../ajax.js'; import Ajax from '../ajax.js';
import { alertSuccessMessage, alertWarningMessage } from '../alerts/sweetAlert.js' import { alertSuccessMessage, alertWarningMessage } from '../alerts/sweetAlert.js'
// Template HTML para la vista previa de cada archivo en Dropzone
const PREVIEW_TEMPLATE = ` const PREVIEW_TEMPLATE = `
<div class="dz-preview dz-file-preview"> <div class="dz-preview dz-file-preview">
<div class="dz-details"> <div class="dz-details">
<div class="dz-thumbnail"> <div class="dz-thumbnail">
<!-- Miniatura de imagen o PDF --> <!---<img data-dz-thumbnail>
<span class="dz-nopreview">No preview</span> --->
<div class="dz-success-mark"></div> <div class="dz-success-mark"></div>
<div class="dz-error-mark"></div> <div class="dz-error-mark"></div>
<div class="dz-error-message"><span data-dz-errormessage></span></div> <div class="dz-error-message"><span data-dz-errormessage></span></div>
@ -16,69 +16,58 @@ const PREVIEW_TEMPLATE = `
</div> </div>
</div> </div>
<div class="dz-filename" data-dz-name></div> <div class="dz-filename" data-dz-name></div>
<!-- Botón para descargar --> <!-- Estilo uniforme con Eliminar / Ver -->
<a class="dz-download dz-remove" href="javascript:void(0);" style="text-align:center;">Descargar</a> <a class="dz-download dz-remove" href="javascript:void(0);" style="text-align:center;">Descargar</a>
<div class="dz-size" data-dz-size></div> <div class="dz-size" data-dz-size></div>
</div> </div>
</div> </div>
`; `;
// Clase principal que maneja el flujo de Dropzone + AJAX
class FileUploadDropzone { class FileUploadDropzone {
constructor({ domElement, nameId = "presupuesto_id", getUri = null, postUri = null, resourcePath = "presupuestos", otId = null }) { constructor({ domElement, nameId = "presupuesto_id", getUri = null, postUri = null, resourcePath = "presupuestos", otId = null }) {
Dropzone.autoDiscover = false; // Desactiva la auto inicialización de Dropzone Dropzone.autoDiscover = false;
this.domElement = domElement
this.domElement = domElement; this.jqElement = $(domElement)
this.jqElement = $(domElement); // Referencia jQuery al elemento this.modelId = this.jqElement.data('id')
this.btnSelectFiles = $(`#${domElement.replace('#', '')}_btnUploadFiles`)
this.modelId = this.jqElement.data('id'); // ID que asocia los archivos a un modelo (presupuesto, pedido, etc.) this.btnSubmitFile = $(`#${domElement.replace('#', '')}_btnSubmitFiles`)
// Botones asociados
this.btnSelectFiles = $(`#${domElement.replace('#', '')}_btnUploadFiles`);
this.btnSubmitFile = $(`#${domElement.replace('#', '')}_btnSubmitFiles`);
this.btnDownloadFiles = $(`#${domElement.replace('#', '')}_btnDownloadFiles`); this.btnDownloadFiles = $(`#${domElement.replace('#', '')}_btnDownloadFiles`);
this.dataPost = {}
this.nameId = nameId; this.nameId = nameId;
this.otId = otId; this.otId = otId;
this.getUri = getUri; this.getUri = getUri
this.postUri = postUri; this.postUri = postUri
this.resourcePath = resourcePath;
this.dataPost = {};
this.dataPost[nameId] = this.modelId; this.dataPost[nameId] = this.modelId;
} this.resourcePath = resourcePath
// Inicializa Dropzone y los eventos externos }
init() { init() {
if (this.jqElement.length > 0) { if (this.jqElement.length > 0) {
// Vincula botones externos this.btnSubmitFile.on('click', this._handleUploadFiles.bind(this))
this.btnSubmitFile.on('click', this._handleUploadFiles.bind(this)); this.btnSelectFiles.on('click', () => {
this.btnSelectFiles.on('click', () => this.jqElement.trigger('click')); this.jqElement.trigger('click')
this.btnDownloadFiles.on('click', this._handleDownloadFiles.bind(this)); })
this.btnDownloadFiles.on('click', this._handleDownloadFiles.bind(this))
// Inicializa Dropzone
this.dropzone = new Dropzone(this.domElement, { this.dropzone = new Dropzone(this.domElement, {
url: this.postUri, url: this.postUri,
addRemoveLinks: true, addRemoveLinks: true,
previewTemplate: PREVIEW_TEMPLATE, previewTemplate: PREVIEW_TEMPLATE,
paramName: "file", paramName: "file",
uploadMultiple: true, uploadMultiple: true,
parallelUploads: 4, parallelUploads: 4, // Ajusta este número al máximo número de archivos que esperas subir a la vez
maxFiles: 5, maxFiles: 5, // Ajusta este número al máximo número de archivos que esperas subir a la vez
autoProcessQueue: true, autoProcessQueue: true,
dictRemoveFile: "Eliminar", dictRemoveFile: "Eliminar",
acceptedFiles: 'image/*, application/pdf', acceptedFiles: 'image/*, application/pdf',
maxFilesize: 5e+7, // 50 MB maxFilesize: 5e+7, // Bytes
init: this._handleGetFiles.bind(this) // Carga inicial de archivos init: this._handleGetFiles.bind(this)
}); });
// Cuando se añade un archivo (manual o programático)
this.dropzone.on("addedfile", this._handleAddedFile.bind(this)); this.dropzone.on("addedfile", this._handleAddedFile.bind(this));
} }
} }
// Botones "Ver" y "Descargar" para cada archivo
_handleAddedFile(file) { _handleAddedFile(file) {
if (file.hash) { if (file.hash) {
// Botón Ver // Botón Ver
@ -101,86 +90,70 @@ class FileUploadDropzone {
} }
} }
// Acción del botón "Ver"
onViewButton(file) { onViewButton(file) {
const url = `${window.location.protocol}//${window.location.host}/sistema/intranet/${this.resourcePath}/${file.hash}`; console.log(window.location.protocol + "//" + window.location.host + "/sistema/intranet/" + this.resourcePath + "/" + file.hash)
window.open(url, '_blank'); window.open(window.location.protocol + "//" + window.location.host + "/sistema/intranet/" + this.resourcePath + "/" + file.hash, '_blank');
} }
// Prepara el objeto FormData para el envío
_getDropzoneFilesFormData() { _getDropzoneFilesFormData() {
const files = this.dropzone.files; var files = this.dropzone.files;
const formData = new FormData();
const oldFiles = [];
for (let file of files) { var formData = new FormData();
if (file.upload) { var oldFiles = [];
var counter = 0;
for (var i = 0; i < files.length; i++) {
if (files[i].upload) {
var file = files[i];
formData.append('file[]', file); formData.append('file[]', file);
} else { counter += 1;
oldFiles.push(file.name); }
else {
oldFiles.push(files[i].name);
} }
} }
formData.append('oldFiles', JSON.stringify(oldFiles)); formData.append('oldFiles', JSON.stringify(oldFiles));
formData.append(this.nameId, this.modelId);
formData.append(this.nameId, this.modelId);
return formData; return formData;
} }
// Acción al hacer clic en "Subir archivos"
_handleUploadFiles() { _handleUploadFiles() {
$("#loader").modal('show'); $("#loader").modal('show')
const ajax = new Ajax( let ajax = new Ajax(this.postUri,
this.postUri,
this._getDropzoneFilesFormData(), this._getDropzoneFilesFormData(),
null, null,
this._handleUploadFilesSuccess.bind(this), this._handleUploadFilesSuccess.bind(this),
null null)
);
ajax.ajaxForm("POST"); ajax.ajaxForm("POST");
}
// Éxito tras subir archivos }
_handleUploadFilesSuccess(response) { _handleUploadFilesSuccess(response) {
this.dropZoneClean(); // Limpia visualmente this.dropZoneClean()
this._handleGetFiles(); // Recarga archivos desde backend this._handleGetFiles()
alertSuccessMessage(response?.message ?? "Archivos subidos correctamente"); alertSuccessMessage(response?.message ?? "Archivos subidos correctamente");
} }
_handleUploadFilesError(errors) { }
_handleUploadFilesError(errors) {
// No implementado aún
}
// Carga inicial de archivos existentes desde el servidor
_handleGetFiles() { _handleGetFiles() {
const ajax = new Ajax( const ajax = new Ajax(
this.getUri, this.getUri,
this.dataPost, this.dataPost,
null, null,
this._handelGetFilesSuccess.bind(this), this._handelGetFilesSuccess.bind(this),
null null,
);
ajax.post();
}
// Manejo de respuesta del servidor al cargar archivos )
ajax.post()
}
_handelGetFilesSuccess(response) { _handelGetFilesSuccess(response) {
try { try {
$("#loader").modal('hide'); $("#loader").modal('hide')
const files = Array.isArray(response) const files = JSON.parse(response)
? response this.dropZoneUpdateFiles(files)
: typeof response === 'string'
? JSON.parse(response)
: [];
this.dropZoneUpdateFiles(files);
} catch (error) { } catch (error) {
console.error("Error parseando respuesta:", error); $("#loader").modal('hide')
$("#loader").modal('hide');
} }
} }
// Manejo del botón "Descargar archivos ZIP"
_handleDownloadFiles() { _handleDownloadFiles() {
$("#loader").modal('show'); $("#loader").modal('show');
@ -199,7 +172,7 @@ class FileUploadDropzone {
let filename = "archivos.zip"; let filename = "archivos.zip";
if (disposition && disposition.indexOf('attachment') !== -1) { if (disposition && disposition.indexOf('attachment') !== -1) {
const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition); const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
if (match && match[1]) { if (match != null && match[1]) {
filename = match[1].replace(/['"]/g, ''); filename = match[1].replace(/['"]/g, '');
} }
} }
@ -222,57 +195,28 @@ class FileUploadDropzone {
}); });
} }
// Carga archivos simulados (mock) al Dropzone visual
dropZoneUpdateFiles(files) {
files.forEach(file => {
console.log("Iterando archivo:", file.name);
this.dropZoneAddFile(file);
});
}
// Limpia todos los archivos de Dropzone visualmente dropZoneUpdateFiles(files) {
files.forEach(file => {
this.dropZoneAddFile(file)
});
}
dropZoneClean() { dropZoneClean() {
this.dropzone.files.forEach(file => { this.dropzone.files.forEach(file => {
this.dropzone.removeFile(file); this.dropzone.removeFile(file);
}); })
} }
dropZoneAddFile(mockFile) {
// Inserta un archivo en Dropzone manualmente (mock) this.dropzone.files.push(mockFile); // add to files array
dropZoneAddFile(mockFileData) {
const extension = mockFileData.name.split('.').pop().toLowerCase();
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(extension);
const isPDF = extension === 'pdf';
const fileUrl = `${window.location.protocol}//${window.location.host}/sistema/intranet/${this.resourcePath}/${mockFileData.hash}`;
const mockFile = {
name: mockFileData.name,
size: mockFileData.size,
type: isImage ? `image/${extension === 'jpg' ? 'jpeg' : extension}` : 'application/pdf',
hash: mockFileData.hash,
upload: false // Impide que se vuelva a subir
};
this.dropzone.emit("addedfile", mockFile); this.dropzone.emit("addedfile", mockFile);
this.dropzone.emit("thumbnail", mockFile, window.location.host + "/sistema/intranet/" + this.resourcePath + "/" + mockFile.hash);
// Espera a que Dropzone genere el DOM para modificar la miniatura
setTimeout(() => {
if (isImage) {
this.dropzone.emit("thumbnail", mockFile, fileUrl);
} else if (isPDF) {
const preview = mockFile.previewElement?.querySelector('.dz-thumbnail');
if (preview) {
preview.innerHTML = `<img src="/assets/img/pdf.png" alt="PDF" style="width:100%; height:auto;" />`;
}
}
this.dropzone.emit("complete", mockFile); this.dropzone.emit("complete", mockFile);
}, 10); this.dropzone.options.success.call(this.dropzone, mockFile);
this.dropzone.files.push(mockFile);
} }
} }
// Exporta la clase para usarla en otros módulos JS
export default FileUploadDropzone; export default FileUploadDropzone;