diff --git a/ci4/app/Config/Email.php b/ci4/app/Config/Email.php index 43b3edb0..9447a491 100755 --- a/ci4/app/Config/Email.php +++ b/ci4/app/Config/Email.php @@ -6,7 +6,7 @@ use CodeIgniter\Config\BaseConfig; class Email extends BaseConfig { - public string $fromEmail = 'safekat@imnavajas.es'; + public string $fromEmail = 'soporte_erp@safekat.es'; public string $fromName = 'Safekat ERP'; public string $recipients = ''; @@ -28,27 +28,27 @@ class Email extends BaseConfig /** * SMTP Server Hostname */ - public string $SMTPHost = 'imnavajas.es'; + public string $SMTPHost = 'smtp.ionos.es'; /** * SMTP Username */ - public string $SMTPUser = 'safekat@imnavajas.es'; + public string $SMTPUser = 'soporte_erp@safekat.es'; /** * SMTP Password */ - public string $SMTPPass = 'Etkd9~448'; + public string $SMTPPass = 'H%5&qDkDkWnfLTGN'; /** * SMTP Port */ - public int $SMTPPort = 25; + public int $SMTPPort = 465; /** * SMTP Timeout (in seconds) */ - public int $SMTPTimeout = 5; + public int $SMTPTimeout = 15; /** * Enable persistent SMTP connections diff --git a/ci4/app/Config/RBAC/permissionMatrix.php b/ci4/app/Config/RBAC/permissionMatrix.php index ce466841..c9f0a2ee 100644 --- a/ci4/app/Config/RBAC/permissionMatrix.php +++ b/ci4/app/Config/RBAC/permissionMatrix.php @@ -89,6 +89,9 @@ const SK_PERMISSION_MATRIX = [ "roles-permisos.edit", "roles-permisos.delete", "roles-permisos.menu", + "tickets.create", + "tickets.edit", + "tickets.menu", ], "cliente-admin" => [ "presupuesto-cliente.create", @@ -218,6 +221,10 @@ const SK_PERMISSION_MATRIX = [ "tarifa-encuadernacion.edit", "tarifa-encuadernacion.delete", "tarifa-encuadernacion.menu", + "tarifa-extra.create", + "tarifa-extra.edit", + "tarifa-extra.delete", + "tarifa-extra.menu", "tarifa-envio.create", "tarifa-envio.edit", "tarifa-envio.delete", @@ -262,5 +269,8 @@ const SK_PERMISSION_MATRIX = [ "roles-permisos.edit", "roles-permisos.delete", "roles-permisos.menu", + "tickets.create", + "tickets.edit", + "tickets.menu", ], ]; diff --git a/ci4/app/Config/RBAC/permissions.php b/ci4/app/Config/RBAC/permissions.php index 4c640b77..35e8235f 100644 --- a/ci4/app/Config/RBAC/permissions.php +++ b/ci4/app/Config/RBAC/permissions.php @@ -93,4 +93,7 @@ const SK_PERMISSIONS = [ 'roles-permisos.edit' => 'Can edit', 'roles-permisos.delete' => 'Can delete', 'roles-permisos.menu' => 'Menu shall be visualize', + 'tickets.create' => 'Can create', + 'tickets.edit' => 'Can edit', + 'tickets.menu' => 'Menu shall be visualize', ]; diff --git a/ci4/app/Config/Routes.php b/ci4/app/Config/Routes.php index 3b03dfa2..2d01a49f 100644 --- a/ci4/app/Config/Routes.php +++ b/ci4/app/Config/Routes.php @@ -886,8 +886,8 @@ $routes->group('chat', ['namespace' => 'App\Controllers\Chat'], function ($route $routes->get('department/factura/(:num)/(:num)', 'ChatController::get_chat_factura/$1/$2', ['as' => 'getChatFactura']); $routes->get('department/(:num)/users', 'ChatController::get_chat_department_users/$1', ['as' => 'getChatDepartmentUsers']); - $routes->get('(:num)', 'ChatController::get_chat/$1', ['as' => 'getChat']); + $routes->post('message/error/presupuesto', 'ChatController::store_chat_error_message', ['as' => 'storeChatErrorMessage']); $routes->post('message/presupuesto', 'ChatController::store_chat_message_presupuesto', ['as' => 'storeChatMessagePresupuesto']); $routes->post('message/pedido', 'ChatController::store_chat_message_pedido', ['as' => 'storeChatMessagePedido']); $routes->post('message/factura', 'ChatController::store_chat_message_factura', ['as' => 'storeChatMessageFactura']); @@ -909,6 +909,27 @@ $routes->group('chat', ['namespace' => 'App\Controllers\Chat'], function ($route }); +$routes->group('messages', ['namespace' => 'App\Controllers\Chat'], function ($routes) { + $routes->get('datatable', 'ChatController::datatable_messages', ['as' => 'getDatatableMessages']); + $routes->get('datatable/presupuesto', 'ChatController::datatable_presupuesto_messages', ['as' => 'getDatatablePresupuestoMessages']); + $routes->get('datatable/pedido', 'ChatController::datatable_pedido_messages', ['as' => 'getDatatablePedidoMessages']); + $routes->get('datatable/factura', 'ChatController::datatable_factura_messages', ['as' => 'getDatatableFacturaMessages']); + + $routes->post('direct', 'ChatController::store_new_direct_message', ['as' => 'storeNewDirectMessage']); + $routes->post('direct/client', 'ChatController::store_new_direct_message_client', ['as' => 'storeNewDirectMessageClient']); +}); + + +$routes->group('soporte', ['namespace' => 'App\Controllers\Soporte'], function ($routes) { + $routes->get('', 'Ticketcontroller::index', ['as' => 'TicketIndex']); + $routes->get('add', 'Ticketcontroller::add', ['as' => 'NewTicket']); + $routes->post('add', 'Ticketcontroller::add', ['as' => 'createTicket']); + $routes->get('edit/(:num)', 'Ticketcontroller::edit/$1', ['as' => 'editTicket']); + $routes->post('edit/(:num)', 'Ticketcontroller::edit/$1', ['as' => 'updateTicket']); + $routes->post('ticketlist', 'Ticketcontroller::datatable'); + $routes->get('image/(:segment)', 'Ticketcontroller::image/$1'); +}); + $routes->group('produccion', ['namespace' => 'App\Controllers\Produccion'], function ($routes) { $routes->group('ordentrabajo', ['namespace' => 'App\Controllers\Produccion'], function ($routes) { diff --git a/ci4/app/Config/Services.php b/ci4/app/Config/Services.php index 8d209990..33330fa1 100755 --- a/ci4/app/Config/Services.php +++ b/ci4/app/Config/Services.php @@ -4,6 +4,7 @@ namespace Config; use App\Services\FTPService; use App\Services\MaquinaService; +use App\Services\MessageService; use App\Services\PapelImpresionService; use CodeIgniter\Config\BaseService; use App\Services\ProductionService; @@ -48,4 +49,8 @@ class Services extends BaseService { return new MaquinaService(); } + public static function messages() + { + return new MessageService(); + } } diff --git a/ci4/app/Controllers/Chat/ChatController.php b/ci4/app/Controllers/Chat/ChatController.php index 854f562b..0be4e8ed 100644 --- a/ci4/app/Controllers/Chat/ChatController.php +++ b/ci4/app/Controllers/Chat/ChatController.php @@ -766,4 +766,11 @@ class ChatController extends BaseController $this->chatModel->setAsUnviewedChatUserNotifications($chat_id, auth()->user()->id); return $this->response->setJSON(["message" => "ok", "status" => true]); } + public function store_chat_error_message() + { + $bodyData = $this->request->getPost(); + $messageService = service('messages'); + $r = $messageService->createErrorMessagePresupuesto("Error",$bodyData['presupuesto_id']); + return $this->response->setJSON(["message" => "ok","data" => $r]); + } } diff --git a/ci4/app/Controllers/Configuracion/Papelesgenericos.php b/ci4/app/Controllers/Configuracion/Papelesgenericos.php index 82392fca..37e2ca7c 100755 --- a/ci4/app/Controllers/Configuracion/Papelesgenericos.php +++ b/ci4/app/Controllers/Configuracion/Papelesgenericos.php @@ -156,6 +156,9 @@ class Papelesgenericos extends \App\Controllers\BaseResourceController if ($this->request->getPost('show_in_client') == null) { $sanitizedData['show_in_client'] = false; } + if ($this->request->getPost('activo') == null) { + $sanitizedData['activo'] = false; + } if ($this->request->getPost('show_in_client_special') == null) { $sanitizedData['show_in_client_special'] = false; } @@ -163,6 +166,9 @@ class Papelesgenericos extends \App\Controllers\BaseResourceController if ($sanitizedData['show_in_client_special']) { $sanitizedData['show_in_client'] = true; } + if ($sanitizedData['activo']) { + $sanitizedData['activo'] = true; + } $noException = true; if ($successfulResult = $this->canValidate()): // if ($successfulResult = $this->validate($this->formValidationRules) ) : diff --git a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php index 919d141a..fdb162cd 100755 --- a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php +++ b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php @@ -18,6 +18,7 @@ use App\Models\Presupuestos\PresupuestoModel; use App\Models\Presupuestos\PresupuestoPreimpresionesModel; use App\Models\Presupuestos\PresupuestoServiciosExtraModel; use App\Models\Presupuestos\ErrorPresupuesto; +use App\Services\MessageService; use App\Services\PresupuestoClienteService; use App\Services\PresupuestoService; use Exception; @@ -42,7 +43,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController protected $indexRoute = 'listaPresupuestos'; - + protected MessageService $messageService; public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger) { @@ -61,6 +62,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController ['title' => lang("App.menu_presupuestos"), 'route' => "javascript:void(0);", 'active' => false], ['title' => "Listado", 'route' => site_url('presupuestocliente/list'), 'active' => true] ]; + $this->messageService = service('messages'); parent::initController($request, $response, $logger); $this->model = new PresupuestoModel(); diff --git a/ci4/app/Controllers/Sistema/Intranet.php b/ci4/app/Controllers/Sistema/Intranet.php index 99d3b206..73aa48f2 100644 --- a/ci4/app/Controllers/Sistema/Intranet.php +++ b/ci4/app/Controllers/Sistema/Intranet.php @@ -32,4 +32,29 @@ class Intranet extends Controller } + function tickets($resource_name) + { + helper('file'); + + $resource_path = WRITEPATH . 'uploads/tickets/' . $resource_name; + + if (file_exists($resource_path)) { + // Get the mime type of the file + $mime_type = mime_content_type($resource_path); + + // Get an instance of the Response class + $response = service('response'); + + // Set the content type + $response->setContentType($mime_type); + + // Set the output + $response->setBody(file_get_contents($resource_path)); + + // Send the response to the browser + $response->send(); + } + + } + } \ No newline at end of file diff --git a/ci4/app/Controllers/Soporte/Ticketcontroller.php b/ci4/app/Controllers/Soporte/Ticketcontroller.php new file mode 100644 index 00000000..a50c7889 --- /dev/null +++ b/ci4/app/Controllers/Soporte/Ticketcontroller.php @@ -0,0 +1,429 @@ +viewData['pageTitle'] = lang('Tickets.moduleTitle'); + + // Breadcrumbs + $this->viewData['breadcrumb'] = [ + ['title' => lang("App.menu_soporte"), 'route' => "javascript:void(0);", 'active' => false], + ['title' => lang("App.menu_soporte_ticket_list"), 'route' => route_to('TicketIndex'), 'active' => true] + ]; + + parent::initController($request, $response, $logger); + } + + public function index() + { + //checkPermission('tickets.menu'); + + $viewData = [ + 'currentModule' => static::$controllerSlug, + 'pageSubTitle' => lang('Basic.global.ManageAllRecords', [lang('Tickets.tickets')]), + 'usingServerSideDataTable' => true, + 'userType' => auth()->user()->can('tickets.edit') ? 1 : 0, + ]; + + $viewData = array_merge($this->viewData, $viewData); // merge any possible values from the parent controller class + + return view(static::$viewPath . 'viewTicketList', $viewData); + } + + private function sendMail($subject, $body, $recipient) + { + $settings_model = new SettingsModel(); + $config = $settings_model->first()->toArray(); + $gateway = $config['email_gateway']; + $body = html_entity_decode($body); + + if ($gateway == 'smtp') { + try { + //https://codeigniter.com/user_guide/libraries/email.html + $email = \Config\Services::email(); + $config['protocol'] = $config['email_gateway']; + $config['SMTPHost'] = $config['email_smtp']; + $config['SMTPUser'] = $config['email_address']; + $config['SMTPPass'] = $config['email_pass']; + $config['SMTPPort'] = intval($config['email_port']); + $config['SMTPCrypto'] = $config['email_cert'] == 'none' ? '' : $config['email_cert']; + $config['SMTPTimeout'] = 15; + $config['mailType'] = 'html'; + $config['wordWrap'] = true; + + $email->initialize($config); + + $email->setFrom($config['email_address'], $config['email_name']); + $email->setTo($recipient); + + $email->setSubject($subject); + $email->setMessage($body); + + if (!$email->send()) { + return false; + } else { + return true; + } + } catch (\Exception $ex) { + return false; + } + } + return false; + } + + public function add() + { + + //checkPermission('tickets.create', $this->indexRoute); + + if ($this->request->getPost()): + + $nullIfEmpty = false; // !(phpversion() >= '8.1'); + + $postData = $this->request->getPost(); + + // get user id + $postData['usuario_id'] = auth()->user()->id; + $postData['user_soporte_id'] = model('App\Models\Configuracion\ConfigVariableModel')->getVariable('default_soporte_user_id')->value; + + $sanitizedData = $this->sanitized($postData, $nullIfEmpty); + + $noException = true; + if ($successfulResult = $this->canValidate()): // if ($successfulResult = $this->validate($this->formValidationRules) ) : + + + if ($this->canValidate()): + try { + $successfulResult = $this->model->skipValidation(true)->save($sanitizedData); + } catch (\Exception $e) { + $noException = false; + $this->dealWithException($e); + } + else: + $this->viewData['errorMessage'] = lang('Basic.global.formErr1', [lang('Basic.global.record')]); + $this->session->setFlashdata('formErrors', $this->model->errors()); + endif; + + $thenRedirect = true; // Change this to false if you want your user to stay on the form after submission + endif; + if ($noException && $successfulResult): + + $id = $this->model->db->insertID(); + + $this->saveImages($id, $this->request->getFiles()); + + $message = lang('Basic.global.saveSuccess', [lang('Basic.global.record')]) . '.'; + + $userModel = new \App\Models\UserModel(); + + $this->sendMail(lang('Tickets.newTicket'), lang('Tickets.newTicketBody') . base_url(route_to('editTicket', $id)), $userModel->find($sanitizedData['user_soporte_id'])->email); + + if ($thenRedirect): + if (!empty($this->indexRoute)): + return redirect()->to(route_to($this->indexRoute))->with('successMessage', $message); + else: + return $this->redirect2listView('successMessage', $message); + endif; + else: + $this->session->setFlashData('sweet-success', $message); + endif; + + endif; // $noException && $successfulResult + + endif; // ($requestMethod === 'post') + + $this->viewData['ticket'] = isset($sanitizedData) ? new TicketEntity($sanitizedData) : new TicketEntity(); + + $this->viewData['formAction'] = route_to('NewTicket'); + + $this->viewData['categorias'] = $this->model->getCategorias(); + $this->viewData['estados'] = $this->model->getEstados(); + $this->viewData['secciones'] = $this->model->getSecciones(); + + $this->viewData['supportUsers'] = $this->getSupportUsers(); + + $this->viewData['boxTitle'] = lang('Basic.global.addNew') . ' ' . lang('Tickets.ticket') . ' ' . lang('Basic.global.addNewSuffix'); + + + return $this->displayForm(__METHOD__); + } // end function add() + + public function edit($requestedId = null) + { + $modelRespuesta = new \App\Models\Soporte\TicketRespuestaModel(); + + if ($requestedId == null): + return $this->redirect2listView(); + endif; + $id = filter_var($requestedId, FILTER_SANITIZE_URL); + $ticket = $this->model->find($id); + + if ($ticket == false): + $message = lang('Basic.global.notFoundWithIdErr', [mb_strtolower(lang('Tickets.ticket')), $id]); + return $this->redirect2listView('errorMessage', $message); + endif; + + if (!auth()->user()->can('Tickets.edit') && auth()->user()->id != $ticket->usuario_id) { + return redirect()->to(route_to('TicketIndex'))->with('errorMessage', lang('Basic.global.noPermission')); + } + + if ($this->request->getPost()): + + $oldUserSupport = $ticket->user_soporte_id; + $oldState = $ticket->estado_id; + + $postData = $this->request->getPost(); + $sanitizedData = $this->sanitized($postData, false); + + $noException = true; + if ($successfulResult = $this->canValidate()): // if ($successfulResult = $this->validate($this->formValidationRules) ) : + if ($this->canValidate()): + try { + $successfulResult = $this->model->skipValidation(true)->update($id, $sanitizedData); + + $this->saveImages($id, $this->request->getFiles()); + + if (auth()->user()->can('Tickets.edit')) { + + $respuesta = $modelRespuesta->where('ticket_id', $id)->first(); + if ($respuesta == null) { + $modelRespuesta->insert([ + 'ticket_id' => $id, + 'usuario_id' => auth()->user()->id, + 'mensaje' => $sanitizedData['respuesta_mensaje'] + ]); + } else { + $modelRespuesta->update($respuesta->id, [ + 'mensaje' => $sanitizedData['respuesta_mensaje'], + 'usuario_id' => auth()->user()->id, + ]); + } + + // envio de correos + $userModel = new \App\Models\UserModel(); + if ($oldUserSupport != $sanitizedData['user_soporte_id']) { + $this->sendMail(lang('Tickets.asgignToChanged'), lang('Tickets.asgignToChangedBody') . base_url(route_to('editTicket', $id)), $userModel->find($sanitizedData['user_soporte_id'])->email); + } + + if ($oldState != $sanitizedData['estado_id']) { + $this->sendMail(lang('Tickets.stateChange'), lang('Tickets.stateChangeBody') . base_url(route_to('editTicket', $id)), $userModel->find($ticket->usuario_id)->email); + } + + } + + } catch (\Exception $e) { + $noException = false; + $this->dealWithException($e); + } + else: + $this->viewData['warningMessage'] = lang('Basic.global.formErr1', [mb_strtolower(lang('Tickets.ticket'))]); + $this->session->setFlashdata('formErrors', $this->model->errors()); + + endif; + + $ticket->fill($sanitizedData); + + $thenRedirect = false; + endif; + if ($noException && $successfulResult): + $id = $ticket->id ?? $id; + $message = lang('Basic.global.updateSuccess', [lang('Basic.global.record')]) . '.'; + + if ($thenRedirect): + if (!empty($this->indexRoute)): + return redirect()->to(route_to($this->indexRoute))->with('successMessage', $message); + else: + return $this->redirect2listView('successMessage', $message); + endif; + else: + return redirect()->to(route_to("editTicket", $id))->with('successMessage', $message); + //$this->session->setFlashData('sweet-success', $message); + endif; + + endif; // $noException && $successfulResult + endif; // ($requestMethod === 'post') + + $this->viewData['ticket'] = $ticket; + $this->viewData['respuesta'] = $modelRespuesta->where('ticket_id', $id)->first(); + + $this->viewData['formAction'] = route_to('updateTicket', $id); + + $this->viewData['categorias'] = $this->model->getCategorias(); + $this->viewData['estados'] = $this->model->getEstados(); + $this->viewData['secciones'] = $this->model->getSecciones(); + $this->viewData['supportUsers'] = $this->getSupportUsers(); + + $this->viewData['imagesTicket'] = $this->getImages('ticket', $id); + $this->viewData['imagesRespuesta'] = $this->getImages('respuesta', $id); + + $this->viewData['boxTitle'] = lang('Basic.global.edit2') . ' ' . lang('Tickets.ticket') . ' ' . lang('Basic.global.edit3'); + + + return $this->displayForm(__METHOD__, $id); + } // end function edit(...) + + public function datatable() + { + if ($this->request->isAJAX()) { + $reqData = $this->request->getPost(); + if (!isset($reqData['draw']) || !isset($reqData['columns'])) { + $errstr = 'No data available in response to this specific request.'; + $response = $this->respond(Collection::datatable([], 0, 0, $errstr), 400, $errstr); + return $response; + } + $start = $reqData['start'] ?? 0; + $length = $reqData['length'] ?? 5; + + $requestedOrder = $reqData['order'] ?? []; + + $searchValues = get_filter_datatables_columns($reqData); + + if (auth()->user()->can('tickets.edit')) { + $user_id = null; + } else { + $user_id = auth()->user()->id; + } + + $resourceData = $this->model->getResource($searchValues, $user_id); + foreach ($requestedOrder as $order) { + $column = $order['column'] ?? 0; + $dir = $order['dir'] ?? 'asc'; + $orderColumn = $this->model::SORTABLE[$column] ?? null; + if ($orderColumn) { + $resourceData->orderBy($orderColumn, $dir); + } + } + $resourceData = $resourceData->limit($length, $start)->get()->getResultObject(); + + return $this->respond(Collection::datatable( + $resourceData, + $this->model->getResource($searchValues)->countAllResults(), + $this->model->getResource($searchValues)->countAllResults() + )); + } else { + return $this->failUnauthorized('Invalid request', 403); + } + } + + public function image($imageName) + { + $filePath = WRITEPATH . "uploads/tickets/" . $imageName; + + if (!file_exists($filePath)) { + return $this->response->setStatusCode(404, 'Imagen no encontrada'); + } + + $mimeType = mime_content_type($filePath); + + return $this->response + ->setHeader('Content-Type', $mimeType) + ->setBody(file_get_contents($filePath)); + } + + private function saveImages($ticket_id, $files = []) + { + $uploadPath = WRITEPATH . 'uploads/tickets/'; + + $fileModel = new ticketFileModel(); + + if ($files && isset($files['files'])) { + foreach ($files['files'] as $file) { + if ($file->isValid() && !$file->hasMoved()) { + $originalName = $file->getClientName(); + $fileExt = $file->getExtension(); + + // Generar hash SHA-256 basado en el contenido del archivo + $fileHash = hash_file("sha256", $file->getTempName()); + $newFileName = $fileHash . '.' . $fileExt; + + // Mover el archivo con el nombre basado en el hash + $file->move($uploadPath, $newFileName); + + // Guardar en la base de datos + $fileModel->insert([ + 'nombre' => $originalName, + 'ticket_id' => $ticket_id, + 'hash' => $fileHash, + 'path' => 'uploads/tickets/' . $newFileName + ]); + } + } + } + } + + private function getImages($tipo = 'ticket', $id = null) + { + $images = []; + + $model = new ticketFileModel(); + if ($tipo == 'ticket') { + $files = $model->where('ticket_id', $id)->findAll(); + foreach ($files as $file) { + $ext = pathinfo($file['nombre'], PATHINFO_EXTENSION); + array_push($images, array( + "path" => '/soporte/image/' . $file['hash'] . "." . $ext, + "name" => $file['nombre'] + )); + } + } else { + $files = $model->where('respuesta_id', $id)->findAll(); + foreach ($files as $file) { + $ext = pathinfo($file['nombre'], PATHINFO_EXTENSION); + array_push($images, array( + "path" => '/soporte/image/' . $file['hash'] . "." . $ext, + "name" => $file['nombre'] + )); + } + } + return $images; + } + + private function getSupportUsers() + { + $defatulSoporteUserId = model('App\Models\Configuracion\ConfigVariableModel')->getVariable('default_soporte_user_id')->value; + $supportUsers = array( + array( + 'id' => $defatulSoporteUserId, + 'name' => model('App\Models\UserModel')->getFullName($defatulSoporteUserId) + ), + array( + 'id' => 2, + 'name' => model('App\Models\UserModel')->getFullName(2) + ), + array( + 'id' => 1, + 'name' => model('App\Models\UserModel')->getFullName(1) + ), + ); + + return $supportUsers; + } +} diff --git a/ci4/app/Controllers/Tarifas/Tarifaextra.php b/ci4/app/Controllers/Tarifas/Tarifaextra.php index 28ea2f1b..049c07d6 100755 --- a/ci4/app/Controllers/Tarifas/Tarifaextra.php +++ b/ci4/app/Controllers/Tarifas/Tarifaextra.php @@ -65,7 +65,9 @@ class Tarifaextra extends \App\Controllers\GoBaseController if ($this->request->getPost('mostrar_en_presupuesto') == null) { $sanitizedData['mostrar_en_presupuesto'] = false; } - + if ($this->request->getPost('mostrar_en_presupuesto_cliente') == null) { + $sanitizedData['mostrar_en_presupuesto_cliente'] = false; + } $noException = true; if ($successfulResult = $this->canValidate()) : // if ($successfulResult = $this->validate($this->formValidationRules) ) : @@ -142,7 +144,9 @@ class Tarifaextra extends \App\Controllers\GoBaseController if ($this->request->getPost('mostrar_en_presupuesto') == null) { $sanitizedData['mostrar_en_presupuesto'] = false; } - + if ($this->request->getPost('mostrar_en_presupuesto_cliente') == null) { + $sanitizedData['mostrar_en_presupuesto_cliente'] = false; + } $noException = true; if ($successfulResult = $this->canValidate()) : // if ($successfulResult = $this->validate($this->formValidationRules) ) : if ($this->canValidate()) : diff --git a/ci4/app/Controllers/Tarifas/Tarifapreimpresion.php b/ci4/app/Controllers/Tarifas/Tarifapreimpresion.php index 6d26178e..474d60f1 100755 --- a/ci4/app/Controllers/Tarifas/Tarifapreimpresion.php +++ b/ci4/app/Controllers/Tarifas/Tarifapreimpresion.php @@ -63,7 +63,9 @@ class Tarifapreimpresion extends \App\Controllers\GoBaseController if ($this->request->getPost('mostrar_en_presupuesto') == null) { $sanitizedData['mostrar_en_presupuesto'] = false; } - + if ($this->request->getPost('mostrar_en_presupuesto_cliente') == null) { + $sanitizedData['mostrar_en_presupuesto_cliente'] = false; + } $noException = true; if ($successfulResult = $this->canValidate()) : // if ($successfulResult = $this->validate($this->formValidationRules) ) : @@ -144,7 +146,9 @@ class Tarifapreimpresion extends \App\Controllers\GoBaseController if ($this->request->getPost('mostrar_en_presupuesto') == null) { $sanitizedData['mostrar_en_presupuesto'] = false; } - + if ($this->request->getPost('mostrar_en_presupuesto_cliente') == null) { + $sanitizedData['mostrar_en_presupuesto_cliente'] = false; + } $noException = true; if ($successfulResult = $this->canValidate()) : // if ($successfulResult = $this->validate($this->formValidationRules) ) : if ($this->canValidate()) : diff --git a/ci4/app/Database/Migrations/2025-02-14-114500_create_tickets_system.php b/ci4/app/Database/Migrations/2025-02-14-114500_create_tickets_system.php new file mode 100644 index 00000000..c0bbab1f --- /dev/null +++ b/ci4/app/Database/Migrations/2025-02-14-114500_create_tickets_system.php @@ -0,0 +1,99 @@ +forge->addField([ + 'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true], + 'keyword' => ['type' => 'VARCHAR', 'constraint' => 100, 'unique' => true], + ]); + $this->forge->addPrimaryKey('id'); + $this->forge->createTable('tickets_categorias'); + + // Tabla de secciones + $this->forge->addField([ + 'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true], + 'keyword' => ['type' => 'VARCHAR', 'constraint' => 100, 'unique' => true], + ]); + $this->forge->addPrimaryKey('id'); + $this->forge->createTable('tickets_secciones'); + + // Tabla de Estados + $this->forge->addField([ + 'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true], + 'keyword' => ['type' => 'VARCHAR', 'constraint' => 100, 'unique' => true], + ]); + $this->forge->addPrimaryKey('id'); + $this->forge->createTable('tickets_estados'); + + // Tabla de Tickets + $this->forge->addField([ + 'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true], + 'usuario_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true], + 'user_soporte_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true], + 'categoria_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true], + 'seccion_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true], + 'estado_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'default' => 1], + 'prioridad' => ['type' => 'ENUM', 'constraint' => ['alta', 'media', 'baja'], 'default' => 'media'], + 'titulo' => ['type' => 'VARCHAR', 'constraint' => 255], + 'descripcion' => ['type' => 'TEXT'], + 'created_at' => ['type' => 'DATETIME', 'null' => true], + 'updated_at' => ['type' => 'DATETIME', 'null' => true], + ]); + $this->forge->addPrimaryKey('id'); + $this->forge->addForeignKey('usuario_id', 'users', 'id', 'NO ACTION', 'NO ACTION'); + $this->forge->addForeignKey('user_soporte_id', 'users', 'id', 'NO ACTION', 'NO ACTION'); + $this->forge->addForeignKey('categoria_id', 'tickets_categorias', 'id', 'NO ACTION', 'NO ACTION'); + $this->forge->addForeignKey('seccion_id', 'tickets_secciones', 'id', 'NO ACTION', 'NO ACTION'); + $this->forge->addForeignKey('estado_id', 'tickets_estados', 'id', 'NO ACTION', 'NO ACTION'); + $this->forge->createTable('tickets'); + + // Tabla de Respuestas + $this->forge->addField([ + 'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true], + 'ticket_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true], + 'usuario_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true], + 'mensaje' => ['type' => 'TEXT'], + 'created_at' => ['type' => 'DATETIME', 'null' => true], + 'updated_at' => ['type' => 'DATETIME', 'null' => true], + ]); + $this->forge->addPrimaryKey('id'); + $this->forge->addForeignKey('ticket_id', 'tickets', 'id', '', 'NO ACTION'); + $this->forge->addForeignKey('usuario_id', 'users', 'id', 'NO ACTION', 'NO ACTION'); + $this->forge->createTable('tickets_respuestas'); + + // Tabla de Adjuntos + $this->forge->addField([ + 'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true], + 'ticket_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true], + 'respuesta_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true], + 'nombre' => ['type' => 'VARCHAR', 'constraint' => 255], + 'hash' => ['type' => 'VARCHAR', 'constraint' => 255], + 'path' => ['type' => 'VARCHAR', 'constraint' => 255], + 'created_at' => ['type' => 'DATETIME', 'null' => true], + 'updated_at' => ['type' => 'DATETIME', 'null' => true], + ]); + $this->forge->addPrimaryKey('id'); + $this->forge->addForeignKey('ticket_id', 'tickets', 'id', 'NO ACTION', 'NO ACTION'); + $this->forge->addForeignKey('respuesta_id', 'tickets_respuestas', 'id', 'NO ACTION', 'NO ACTION'); + $this->forge->createTable('tickets_adjuntos'); + } + + public function down() + { + $this->forge->dropTable('tickets_adjuntos'); + $this->forge->dropTable('tickets_respuestas'); + $this->forge->dropTable('tickets'); + $this->forge->dropTable('tickets_categorias'); + $this->forge->dropTable('tickets_estados'); + $this->forge->dropTable('tickets_secciones'); + + $this->db->table('config_variables_app')->where('name', 'default_soporte_user_id')->delete(); + } +} diff --git a/ci4/app/Database/Migrations/2025-02-16-194726_AlterTableTarifasCheckPresupuestoClienteMigration.php b/ci4/app/Database/Migrations/2025-02-16-194726_AlterTableTarifasCheckPresupuestoClienteMigration.php new file mode 100644 index 00000000..1d4f8510 --- /dev/null +++ b/ci4/app/Database/Migrations/2025-02-16-194726_AlterTableTarifasCheckPresupuestoClienteMigration.php @@ -0,0 +1,29 @@ + [ + 'type' => 'BOOLEAN', + 'default' => false, + ] + ]; + $this->forge->addColumn('lg_tarifa_acabado',$field); + $this->forge->addColumn('lg_tarifa_manipulado',$field); + $this->forge->addColumn('tarifa_encuadernacion',$field); + + } + + public function down() + { + $this->forge->dropColumn('lg_tarifa_acabado','mostrar_en_presupuesto_cliente'); + $this->forge->dropColumn('lg_tarifa_manipulado','mostrar_en_presupuesto_cliente'); + $this->forge->dropColumn('tarifa_encuadernacion','mostrar_en_presupuesto_cliente'); + } +} diff --git a/ci4/app/Database/Migrations/2025-02-16-224244_AlterAddColumnActivoPapelGenerico.php b/ci4/app/Database/Migrations/2025-02-16-224244_AlterAddColumnActivoPapelGenerico.php new file mode 100644 index 00000000..ae73c86b --- /dev/null +++ b/ci4/app/Database/Migrations/2025-02-16-224244_AlterAddColumnActivoPapelGenerico.php @@ -0,0 +1,26 @@ + [ + 'type' => 'BOOLEAN', + 'default' => true, + ] + ]; + $this->forge->addColumn('lg_papel_generico',$field); + + } + + public function down() + { + $this->forge->dropColumn('lg_papel_generico','activo'); + } +} + diff --git a/ci4/app/Database/Migrations/2025-02-20-080000_AlterTableTarifasExtraPreimpresionCheckPresupuestoClienteMigration.php b/ci4/app/Database/Migrations/2025-02-20-080000_AlterTableTarifasExtraPreimpresionCheckPresupuestoClienteMigration.php new file mode 100644 index 00000000..ee5e2428 --- /dev/null +++ b/ci4/app/Database/Migrations/2025-02-20-080000_AlterTableTarifasExtraPreimpresionCheckPresupuestoClienteMigration.php @@ -0,0 +1,30 @@ + [ + 'type' => 'BOOLEAN', + 'default' => false, + ] + ]; + $this->forge->addColumn('tarifa_extra',$field); + $this->forge->addColumn('lg_tarifa_preimpresion',$field); + + + } + + public function down() + { + $this->forge->dropColumn('tarifa_extra','mostrar_en_presupuesto_cliente'); + $this->forge->dropColumn('lg_tarifa_preimpresion','mostrar_en_presupuesto_cliente'); + + + } +} diff --git a/ci4/app/Database/Seeds/TicketsSeeder.php b/ci4/app/Database/Seeds/TicketsSeeder.php new file mode 100644 index 00000000..f7d0ea93 --- /dev/null +++ b/ci4/app/Database/Seeds/TicketsSeeder.php @@ -0,0 +1,76 @@ + 'errores', + ], + [ + 'keyword' => 'consultas', + ], + [ + 'keyword' => 'sugerencias', + ], + ]; + $this->db->table('tickets_categorias')->insertBatch($data); + + // secciones + $data = [ + [ + 'keyword' => 'presupuestos', + ], + [ + 'keyword' => 'pedidos', + ], + [ + 'keyword' => 'facturacion', + ], + [ + 'keyword' => 'logistica', + ], + [ + 'keyword' => 'configuracion', + ], + [ + 'keyword' => 'general', + ], + ]; + $this->db->table('tickets_secciones')->insertBatch($data); + + // estados + $data = [ + [ + 'keyword' => 'abierto', + ], + [ + 'keyword' => 'en_proceso', + ], + [ + 'keyword' => 'resuelto', + ], + [ + 'keyword' => 'archivado', + ], + ]; + $this->db->table('tickets_estados')->insertBatch($data); + + // config variables + $data = [ + [ + 'name' => 'default_soporte_user_id', + 'value' => '10', + 'description' => 'ID del usuario por defecto para asignar tickets', + ], + ]; + $this->db->table('config_variables_app')->insertBatch($data); + } +} \ No newline at end of file diff --git a/ci4/app/Entities/Configuracion/PapelGenerico.php b/ci4/app/Entities/Configuracion/PapelGenerico.php index a6bc5faa..344d1832 100755 --- a/ci4/app/Entities/Configuracion/PapelGenerico.php +++ b/ci4/app/Entities/Configuracion/PapelGenerico.php @@ -13,11 +13,13 @@ class PapelGenerico extends \CodeIgniter\Entity\Entity "show_in_client" => false, "show_in_client_special" => false, "is_deleted" => 0, + "activo" => false, "created_at" => null, "updated_at" => null, ]; protected $casts = [ "show_in_client" => "boolean", + "activo" => "boolean", "show_in_client_special" => "boolean", "is_deleted" => "int", ]; diff --git a/ci4/app/Entities/Soporte/TicketEntity.php b/ci4/app/Entities/Soporte/TicketEntity.php new file mode 100644 index 00000000..192e8d51 --- /dev/null +++ b/ci4/app/Entities/Soporte/TicketEntity.php @@ -0,0 +1,52 @@ + null, + 'usuario_id' => null, + 'tecnico_id' => null, + 'categoria_id'=> null, + 'estado_id' => null, + 'prioridad' => 'media', + 'titulo' => '', + 'descripcion' => '', + 'created_at' => null, + 'updated_at' => null, + ]; + + protected $dates = ['created_at', 'updated_at']; + + public function setTitulo(string $titulo) + { + $this->attributes['titulo'] = ucfirst($titulo); // Capitaliza el título + return $this; + } + + public function setDescripcion(string $descripcion) + { + $this->attributes['descripcion'] = ucfirst($descripcion); + return $this; + } + + public function getPrioridad(): string + { + return ucfirst($this->attributes['prioridad']); + } + + public function asignarTecnico(int $tecnicoId) + { + $this->attributes['tecnico_id'] = $tecnicoId; + return $this; + } + + public function cambiarEstado(int $estadoId) + { + $this->attributes['estado_id'] = $estadoId; + return $this; + } +} diff --git a/ci4/app/Entities/Soporte/TicketRespuestaEntity.php b/ci4/app/Entities/Soporte/TicketRespuestaEntity.php new file mode 100644 index 00000000..d7a082a3 --- /dev/null +++ b/ci4/app/Entities/Soporte/TicketRespuestaEntity.php @@ -0,0 +1,20 @@ + null, + 'ticket_id' => null, + 'usuario_id' => null, + 'mensaje' => null, + 'created_at' => null, + 'updated_at' => null, + ]; + + protected $dates = ['created_at', 'updated_at']; + +} diff --git a/ci4/app/Entities/Tarifas/Acabados/TarifaAcabadoEntity.php b/ci4/app/Entities/Tarifas/Acabados/TarifaAcabadoEntity.php index 37554c8d..0e384c7f 100644 --- a/ci4/app/Entities/Tarifas/Acabados/TarifaAcabadoEntity.php +++ b/ci4/app/Entities/Tarifas/Acabados/TarifaAcabadoEntity.php @@ -13,6 +13,7 @@ class TarifaAcabadoEntity extends \CodeIgniter\Entity\Entity "precio_min" => 0, "importe_fijo" => 0, "mostrar_en_presupuesto" => 1, + "mostrar_en_presupuesto_cliente" => 0, "acabado_cubierta" => 0, "acabado_sobrecubierta" => 0, "user_created_id" => 0, diff --git a/ci4/app/Entities/Tarifas/TarifaEncuadernacionEntity.php b/ci4/app/Entities/Tarifas/TarifaEncuadernacionEntity.php index 1aa85454..b8d01575 100755 --- a/ci4/app/Entities/Tarifas/TarifaEncuadernacionEntity.php +++ b/ci4/app/Entities/Tarifas/TarifaEncuadernacionEntity.php @@ -11,6 +11,7 @@ class TarifaEncuadernacionEntity extends \CodeIgniter\Entity\Entity "nombre" => null, "code" => null, "mostrar_en_presupuesto" => 1, + "mostrar_en_presupuesto_cliente" => 0, "tipo_encuadernacion" => 0, "servicio_encuadernacion" => 0, "por_horas" => 0, @@ -23,6 +24,7 @@ class TarifaEncuadernacionEntity extends \CodeIgniter\Entity\Entity ]; protected $casts = [ "mostrar_en_presupuesto" => "int", + "mostrar_en_presupuesto_cliente" => "bool", "code" => "string", "tipo_encuadernacion" => "int", "servicio_encuadernacion" => "int", diff --git a/ci4/app/Entities/Tarifas/TarifaManipuladoEntity.php b/ci4/app/Entities/Tarifas/TarifaManipuladoEntity.php index d9f3c2f7..d976de71 100755 --- a/ci4/app/Entities/Tarifas/TarifaManipuladoEntity.php +++ b/ci4/app/Entities/Tarifas/TarifaManipuladoEntity.php @@ -14,6 +14,7 @@ class TarifaManipuladoEntity extends \CodeIgniter\Entity\Entity "precio_min" => 0, "importe_fijo" => 0, "mostrar_en_presupuesto" => 1, + "mostrar_en_presupuesto_cliente" => 0, "user_created_id" => 0, "user_updated_id" => 0, "is_deleted" => 0, @@ -27,6 +28,7 @@ class TarifaManipuladoEntity extends \CodeIgniter\Entity\Entity "code" => "string", "comment" => "string", "mostrar_en_presupuesto" => "int", + "mostrar_en_presupuesto_cliente" => "bool", "user_created_id" => "int", "user_updated_id" => "int", "is_deleted" => "int", diff --git a/ci4/app/Entities/Tarifas/TarifaextraEntity.php b/ci4/app/Entities/Tarifas/TarifaextraEntity.php index db2b0594..7134684d 100755 --- a/ci4/app/Entities/Tarifas/TarifaextraEntity.php +++ b/ci4/app/Entities/Tarifas/TarifaextraEntity.php @@ -13,6 +13,7 @@ class TarifaextraEntity extends \CodeIgniter\Entity\Entity "precio" => null, "margen" => 0, "mostrar_en_presupuesto" => 1, + "mostrar_en_presupuesto_cliente" => false, "user_created_id" => 1, "user_update_id" => 1, "is_deleted" => 0, @@ -26,6 +27,7 @@ class TarifaextraEntity extends \CodeIgniter\Entity\Entity "comment" => "string", "margen" => "float", "mostrar_en_presupuesto" => "int", + "mostrar_en_presupuesto_cliente" => "bool", "user_created_id" => "int", "user_update_id" => "int", "is_deleted" => "int", diff --git a/ci4/app/Entities/Tarifas/TarifapreimpresionEntity.php b/ci4/app/Entities/Tarifas/TarifapreimpresionEntity.php index b1873597..86bd7f14 100755 --- a/ci4/app/Entities/Tarifas/TarifapreimpresionEntity.php +++ b/ci4/app/Entities/Tarifas/TarifapreimpresionEntity.php @@ -13,6 +13,7 @@ class TarifapreimpresionEntity extends \CodeIgniter\Entity\Entity "precio" => null, "margen" => 0, "mostrar_en_presupuesto" => 1, + "mostrar_en_presupuesto_cliente" => false, "user_created_id" => 1, "user_update_id" => 1, "is_deleted" => 0, @@ -26,6 +27,7 @@ class TarifapreimpresionEntity extends \CodeIgniter\Entity\Entity "code" => "string", "comment" => "string", "mostrar_en_presupuesto" => "int", + "mostrar_en_presupuesto_cliente" => "bool", "user_created_id" => "int", "user_update_id" => "int", "is_deleted" => "int", diff --git a/ci4/app/Language/es/App.php b/ci4/app/Language/es/App.php index 44552d73..a24597ea 100755 --- a/ci4/app/Language/es/App.php +++ b/ci4/app/Language/es/App.php @@ -818,5 +818,9 @@ return [ "menu_oauth" => "Autenticaciones", "menu_template" => "Plantillas", + "menu_soporte" => "Soporte", + "menu_soporte_new_ticket" => "Crear ticket", + "menu_soporte_ticket_list" => "Mis tickets", + ]; \ No newline at end of file diff --git a/ci4/app/Language/es/Basic.php b/ci4/app/Language/es/Basic.php index be74f2d1..3f1c5bf5 100755 --- a/ci4/app/Language/es/Basic.php +++ b/ci4/app/Language/es/Basic.php @@ -88,6 +88,7 @@ return [ 'line' => 'la línea', 'error_tittle' => 'Error', ], + 'noPermission' => 'No tiene permiso para acceder a esta página.', 'ok' => 'Ok', 'wait' => 'Espere', 'yes' => 'Si', diff --git a/ci4/app/Language/es/PapelGenerico.php b/ci4/app/Language/es/PapelGenerico.php index c38d8c1c..5df63224 100755 --- a/ci4/app/Language/es/PapelGenerico.php +++ b/ci4/app/Language/es/PapelGenerico.php @@ -8,6 +8,7 @@ return [ 'createdAt' => 'Creado el', 'deletedAt' => 'Deleted At', 'id' => 'ID', + 'activo' => 'Activo', 'isDeleted' => 'Is Deleted', 'moduleTitle' => 'Papel Genérico', 'nombre' => 'Nombre', diff --git a/ci4/app/Language/es/RolesPermisos.php b/ci4/app/Language/es/RolesPermisos.php index 84e56b7f..ccc4176a 100644 --- a/ci4/app/Language/es/RolesPermisos.php +++ b/ci4/app/Language/es/RolesPermisos.php @@ -55,7 +55,7 @@ return [ 'ajustesSection' => 'Ajustes', 'actividadSection' => 'Accesos', - + "ticketsSection" => "Tickets", 'validation' => [ 'id' => [ diff --git a/ci4/app/Language/es/Tarifaacabado.php b/ci4/app/Language/es/Tarifaacabado.php index 5bdd61e9..1b5af0c8 100755 --- a/ci4/app/Language/es/Tarifaacabado.php +++ b/ci4/app/Language/es/Tarifaacabado.php @@ -13,6 +13,7 @@ return [ 'precioMin' => 'Precio Mínimo', 'importeFijo' => 'Importe Fijo', 'mostrar_en_presupuesto' => 'Mostrar en presupuesto', + 'mostrar_en_presupuesto_cliente' => 'Mostrar en presupuesto (cliente)', "acabado_cubierta" => "Acabado cubierta", "acabado_sobrecubierta" => "Acabado sobrecubierta", 'tarifaacabado' => 'Tarifa Acabado', diff --git a/ci4/app/Language/es/Tarifaencuadernacion.php b/ci4/app/Language/es/Tarifaencuadernacion.php index fbc59ef5..a4e15cde 100755 --- a/ci4/app/Language/es/Tarifaencuadernacion.php +++ b/ci4/app/Language/es/Tarifaencuadernacion.php @@ -23,6 +23,7 @@ return [ 'tiradaMax' => 'Tirada Max', 'tiradaMin' => 'Tirada Min', 'mostrar_en_presupuesto' => 'Mostrar en presupuesto', + 'mostrar_en_presupuesto_cliente' => 'Mostrar en presupuesto (cliente)', 'tipo_encuadernacion' => 'Tipo encuardernación', 'servicio_encuadernacion' => 'Servicio encuardernación', 'updatedAt' => 'Actualizado en', diff --git a/ci4/app/Language/es/Tarifaextra.php b/ci4/app/Language/es/Tarifaextra.php index ea711c63..f1263281 100755 --- a/ci4/app/Language/es/Tarifaextra.php +++ b/ci4/app/Language/es/Tarifaextra.php @@ -14,6 +14,7 @@ return [ 'precioMin' => 'Precio Mínimo', 'importeFijo' => 'Importe Fijo', 'mostrar_en_presupuesto' => 'Mostrar en presupuesto', + 'mostrar_en_presupuesto_cliente' => 'Mostrar en presupuesto (Cliente)', 'margen' => 'Margen', 'tarifaextra' => 'Tarifa Servicios Extra', 'tarifaextraList' => 'Lista Tarifas Servicios Extra', diff --git a/ci4/app/Language/es/Tarifamanipulado.php b/ci4/app/Language/es/Tarifamanipulado.php index 80afab6a..0bd0c413 100755 --- a/ci4/app/Language/es/Tarifamanipulado.php +++ b/ci4/app/Language/es/Tarifamanipulado.php @@ -18,6 +18,7 @@ return [ 'precioMin' => 'Precio T. Máx', 'importeFijo' => 'Importe Fijo', 'mostrar_en_presupuesto' => 'Mostrar en presupuesto', + 'mostrar_en_presupuesto_cliente' => 'Mostrar en presupuesto (cliente)', 'tarifamanipulado' => 'Tarifa Manipulado', 'tarifamanipuladoList' => 'Lista Tarifas Manipulado', 'tarifasmanipulado' => 'Tarifas Manipulado', diff --git a/ci4/app/Language/es/Tarifapreimpresion.php b/ci4/app/Language/es/Tarifapreimpresion.php index f98cd490..a1c885dd 100755 --- a/ci4/app/Language/es/Tarifapreimpresion.php +++ b/ci4/app/Language/es/Tarifapreimpresion.php @@ -14,6 +14,7 @@ return [ 'precioMin' => 'Precio Mínimo', 'importeFijo' => 'Importe Fijo', 'mostrar_en_presupuesto' => 'Mostrar en presupuesto', + 'mostrar_en_presupuesto_cliente' => 'Mostrar en presupuesto (Cliente)', 'margen' => 'Margen', 'tarifapreimpresion' => 'Tarifa Preimpresión', 'tarifapreimpresionList' => 'Lista Tarifas Preimpresión', diff --git a/ci4/app/Language/es/Tickets.php b/ci4/app/Language/es/Tickets.php new file mode 100644 index 00000000..020d304f --- /dev/null +++ b/ci4/app/Language/es/Tickets.php @@ -0,0 +1,70 @@ + 'Ticket', + 'tickets' => 'Tickets', + 'moduleTitle' => 'Tickets', + + "asunto" => "Asunto", + "tipo" => "Tipo", + "seccion" => "Sección", + "estado" => "Estado", + "prioridad" => "Prioridad", + "descripcion" => "Descripción", + "asignarTo" => "Asignado a", + "usuario" => "Creado por", + "createTicket" => "Crear Ticket", + "fechaCreacion" => "Fecha de creación", + "respuesta" => "Respuesta", + + // categorías + 'errores' => 'Errores', + 'consultas' => 'Consultas', + 'sugerencias' => 'Sugerencias', + + // secciones + 'presupuestos' => 'Presupuestos', + 'pedidos' => 'Pedidos', + 'facturacion' => 'Facturación', + 'logistica' => 'Logística', + 'configuracion' => 'Configuración', + 'general' => 'General', + + // estados + 'abierto' => 'Abierto', + 'en_proceso' => 'En proceso', + 'resuelto' => 'Resuelto', + 'archivado' => 'Archivado', + + //Prioridades + 'alta' => 'Alta', + 'media' => 'Media', + 'baja' => 'Baja', + + // FIcheros + 'adjuntos' => 'Adjuntar imágenes', + 'adjuntos_ticket' => 'Imágenes adjuntas', + + // Mail + 'newTicket' => 'Nuevo Ticket en ERP Safekat', + 'stateChange' => 'Cambio de estado en ticket en ERP Safekat', + 'asgignToChanged' => 'Asignado ticket en ERP Safekat', + 'newTicketBody' => '

Se ha creado un nuevo ticket en el sistema de soporte de Safekat ERP.

Puede verlo en el siguiente enlace:

', + 'stateChangeBody' => '

El estado de un ticket en el sistema de soporte de Safekat ERP ha cambiado.

Puede verlo en el siguiente enlace:

', + 'asgignToChangedBody' => '

Se le ha asignado un ticket en el sistema de soporte de Safekat ERP.

Puede verlo en el siguiente enlace:

', + + 'validation' => [ + 'titulo' => [ + 'max_length' => 'El campo {field} no puede exceder {param} caracteres en longitud.', + 'required' => 'El campo {field} es obligatorio.', + + ], + + 'descripcion' => [ + 'required' => 'El campo {field} es obligatorio.', + + ], + ], +]; diff --git a/ci4/app/Models/Configuracion/PapelGenericoModel.php b/ci4/app/Models/Configuracion/PapelGenericoModel.php index 5e235d43..118dadf2 100755 --- a/ci4/app/Models/Configuracion/PapelGenericoModel.php +++ b/ci4/app/Models/Configuracion/PapelGenericoModel.php @@ -22,7 +22,7 @@ class PapelGenericoModel extends \App\Models\BaseModel 4 => "t1.show_in_client_special", ]; - protected $allowedFields = ["nombre", "code", "code_ot", "show_in_client", "show_in_client_special", "deleted_at", "is_deleted"]; + protected $allowedFields = ["nombre", "code", "code_ot", "show_in_client", "show_in_client_special", "deleted_at", "is_deleted", "activo"]; protected $returnType = "App\Entities\Configuracion\PapelGenerico"; protected $useTimestamps = true; @@ -120,25 +120,28 @@ class PapelGenericoModel extends \App\Models\BaseModel { $builder = $this->db ->table($this->table . " t1") - ->select( - "t1.id AS id, t1.nombre AS nombre, t1.code AS code, t1.code_ot AS code_ot, - t1.show_in_client AS show_in_client, t1.show_in_client_special AS show_in_client_special" + ->select("t1.id AS id, t1.nombre AS nombre, + t1.code AS code, + t1.code_ot AS code_ot, + t1.activo AS activo, + t1.show_in_client AS show_in_client, + t1.show_in_client_special AS show_in_client_special" ) ->where("is_deleted", 0); return empty($search) ? $builder : $builder - ->groupStart() - ->like("t1.id", $search) - ->orLike("t1.nombre", $search) - ->orLike("t1.code", $search) - ->orLike("t1.code_ot", $search) - ->orLike("t1.id", $search) - ->orLike("t1.nombre", $search) - ->orLike("t1.code", $search) - ->orLike("t1.code_ot", $search) - ->groupEnd(); + ->groupStart() + ->like("t1.id", $search) + ->orLike("t1.nombre", $search) + ->orLike("t1.code", $search) + ->orLike("t1.code_ot", $search) + ->orLike("t1.id", $search) + ->orLike("t1.nombre", $search) + ->orLike("t1.code", $search) + ->orLike("t1.code_ot", $search) + ->groupEnd(); } diff --git a/ci4/app/Models/Soporte/TicketModel.php b/ci4/app/Models/Soporte/TicketModel.php new file mode 100644 index 00000000..0ab09a66 --- /dev/null +++ b/ci4/app/Models/Soporte/TicketModel.php @@ -0,0 +1,165 @@ + "t1.id", + 1 => "t1.categoria_id", + 2 => "t1.seccion_id", + 3 => "t1.estado_id", + 4 => "t1.prioridad", + 5 => "t1.titulo", + 6 => "t1.usuario_id", + 7 => "t1.usuario_soporte_id", + 8 => "t1.created_at", + ]; + + protected $validationRules = [ + "titulo" => [ + "label" => "Tickets.asunto", + "rules" => "trim|required|max_length[255]", + ], + "descripcion" => [ + "label" => "Tickets.descripcion", + "rules" => "trim|required", + ], + ]; + + protected $validationMessages = [ + "titulo" => [ + "max_length" => "Tickets.validation.titulo.max_length", + "required" => "Tickets.validation.titulo.required", + ], + "descripcion" => [ + "decimal" => "Tickets.validation.descripcion.decimal", + "required" => "Tickets.validation.descripcion.required", + ], + ]; + + public function getEstados() + { + $values = $this->db->table('tickets_estados')->get()->getResultArray(); + + for ($i = 0; $i < count($values); $i++) { + $values[$i]['text'] = lang("Tickets." . $values[$i]['keyword']); + } + + return $values; + } + + public function getCategorias() + { + $values = $this->db->table('tickets_categorias')->get()->getResultArray(); + + for ($i = 0; $i < count($values); $i++) { + $values[$i]['text'] = lang("Tickets." . $values[$i]['keyword']); + } + + return $values; + } + + public function getSecciones() + { + $values = $this->db->table('tickets_secciones')->get()->getResultArray(); + + for ($i = 0; $i < count($values); $i++) { + $values[$i]['text'] = lang("Tickets." . $values[$i]['keyword']); + } + + return $values; + } + + public function getTickets($id = null) + { + if ($id === null) { + return $this->select('tickets.*, users.nombre as usuario, categorias.nombre as categoria, estados.nombre as estado') + ->join('users', 'users.id = tickets.usuario_id') + ->join('categorias', 'categorias.id = tickets.categoria_id') + ->join('estados', 'estados.id = tickets.estado_id') + ->findAll(); + } + + return $this->find($id); + } + + public function getResource($search = [], $user_id = null) + { + $builder = $this->db + ->table($this->table . " t1") + ->select( + "t1.id as id, t1.usuario_id AS usuario_id, CONCAT(t2.first_name, ' ', t2.last_name) AS usuario, + t1.user_soporte_id AS user_soporte_id, CONCAT(t6.first_name, ' ', t6.last_name) AS user_soporte, + t1.categoria_id AS categoria_id, t3.keyword AS categoria, + t1.seccion_id AS seccion_id, t5.keyword AS seccion, + t1.estado_id AS estado_id, t4.keyword AS estado, + t1.prioridad AS prioridad, t1.titulo AS titulo, t1.created_at AS created_at + " + ); + + $builder->join("users t2", "t1.usuario_id = t2.id", "left"); + $builder->join("users t6", "t1.user_soporte_id = t6.id", "left"); + $builder->join("tickets_categorias t3", "t1.categoria_id = t3.id", "left"); + $builder->join("tickets_estados t4", "t1.estado_id = t4.id", "left"); + $builder->join("tickets_secciones t5", "t1.seccion_id = t5.id", "left"); + + if ($user_id !== null) + $builder->where("t1.usuario_id", $user_id); + + if (empty($search)) + return $builder; + else { + $builder->groupStart(); + foreach ($search as $col_search) { + if ($col_search[1] == "seccion_id") { + $id = $this->getIdFromKeyword("tickets_secciones", $col_search[2]); + $builder->where("t1." . $col_search[1], $id); + } else if ($col_search[1] == "categoria_id") { + $id = $this->getIdFromKeyword("tickets_categorias", $col_search[2]); + $builder->where("t1." . $col_search[1], $id); + } else if ($col_search[1] == "estado_id") { + $id = $this->getIdFromKeyword("tickets_estados", $col_search[2]); + $builder->where("t1." . $col_search[1], $id); + } else if ($col_search[1] == "prioridad") { + $builder->where("t1." . $col_search[1], $col_search[2]); + } else if ($col_search[1] == "created_at") { + $dates = explode(" ", $col_search[2]); + $builder->where("t1." . $col_search[1] . ">=", $dates[0]); + $builder->where("t1." . $col_search[1] . "<=", $dates[1]); + } else if ($col_search[1] == "usuario_id") { + $builder->like("t2.first_name", $col_search[2]); + $builder->orLike("t2.last_name", $col_search[2]); + } else if ($col_search[1] == "user_soporte_id") { + $builder->like("t6.first_name", $col_search[2]); + $builder->orLike("t6.last_name", $col_search[2]); + } + else + $builder->like("t1." . $col_search[1], $col_search[2]); + } + $builder->groupEnd(); + return $builder; + + } + } + + private function getIdFromKeyword($table, $keyword) + { + $subquery = $this->db->table($table) + ->select('id') + ->where('keyword', $keyword) + ->get() + ->getRow(); + return $subquery->id; + } +} diff --git a/ci4/app/Models/Soporte/TicketRespuestaModel.php b/ci4/app/Models/Soporte/TicketRespuestaModel.php new file mode 100644 index 00000000..5d616c85 --- /dev/null +++ b/ci4/app/Models/Soporte/TicketRespuestaModel.php @@ -0,0 +1,17 @@ +db->table($this->table . " t1")->select( "t1.id AS id, t1.nombre AS nombre,t1.code AS code, t1.precio_min AS precio_min, t1.importe_fijo AS importe_fijo, - t1.mostrar_en_presupuesto AS mostrar_en_presupuesto, t1.acabado_cubierta AS acabado_cubierta, t1.acabado_sobrecubierta AS acabado_sobrecubierta" + t1.mostrar_en_presupuesto AS mostrar_en_presupuesto,t1.mostrar_en_presupuesto_cliente AS mostrar_en_presupuesto_cliente, t1.acabado_cubierta AS acabado_cubierta, t1.acabado_sobrecubierta AS acabado_sobrecubierta" ); //JJO diff --git a/ci4/app/Models/Tarifas/TarifaEncuadernacionModel.php b/ci4/app/Models/Tarifas/TarifaEncuadernacionModel.php index 3c710108..9cf03991 100755 --- a/ci4/app/Models/Tarifas/TarifaEncuadernacionModel.php +++ b/ci4/app/Models/Tarifas/TarifaEncuadernacionModel.php @@ -25,6 +25,7 @@ class TarifaEncuadernacionModel extends \App\Models\BaseModel "code", "comment", "mostrar_en_presupuesto", + "mostrar_en_presupuesto_cliente", "tipo_encuadernacion", "servicio_encuadernacion", "por_horas", @@ -69,6 +70,7 @@ class TarifaEncuadernacionModel extends \App\Models\BaseModel { $builder = $this->db->table($this->table . " t1")->select("t1.id AS id, t1.nombre AS nombre, t1.code AS code, t1.mostrar_en_presupuesto AS mostrar_en_presupuesto, + t1.mostrar_en_presupuesto_cliente AS mostrar_en_presupuesto_cliente, t1.tipo_encuadernacion AS tipo_encuadernacion, t1.servicio_encuadernacion AS servicio_encuadernacion, t1.por_horas AS por_horas"); diff --git a/ci4/app/Models/Tarifas/TarifaManipuladoModel.php b/ci4/app/Models/Tarifas/TarifaManipuladoModel.php index cd228b7b..19a7ce6d 100755 --- a/ci4/app/Models/Tarifas/TarifaManipuladoModel.php +++ b/ci4/app/Models/Tarifas/TarifaManipuladoModel.php @@ -29,7 +29,8 @@ class TarifaManipuladoModel extends \App\Models\BaseModel "deleted_at", "is_deleted", "user_created_id", - "user_updated_id" + "user_updated_id", + "mostrar_en_presupuesto_cliente" ]; protected $returnType = "App\Entities\Tarifas\TarifaManipuladoEntity"; @@ -83,7 +84,7 @@ class TarifaManipuladoModel extends \App\Models\BaseModel public function getResource(string $search = "") { $builder = $this->db->table($this->table . " t1")->select("t1.id AS id,t1.code AS code, t1.nombre AS nombre, t1.precio_min AS precio_min, t1.importe_fijo AS importe_fijo - ,t1.mostrar_en_presupuesto AS mostrar_en_presupuesto"); + ,t1.mostrar_en_presupuesto AS mostrar_en_presupuesto,t1.mostrar_en_presupuesto_cliente AS mostrar_en_presupuesto_cliente"); //JJO $builder->where("t1.is_deleted", 0); diff --git a/ci4/app/Models/Tarifas/TarifaextraModel.php b/ci4/app/Models/Tarifas/TarifaextraModel.php index 8b1001a1..bfe7a1f1 100755 --- a/ci4/app/Models/Tarifas/TarifaextraModel.php +++ b/ci4/app/Models/Tarifas/TarifaextraModel.php @@ -19,6 +19,7 @@ class TarifaextraModel extends \App\Models\BaseModel "precio", "margen", "mostrar_en_presupuesto", + "mostrar_en_presupuesto_cliente", "deleted_at", "is_deleted", "user_created_id", diff --git a/ci4/app/Models/Tarifas/TarifapreimpresionModel.php b/ci4/app/Models/Tarifas/TarifapreimpresionModel.php index 56ed6999..23f977ee 100755 --- a/ci4/app/Models/Tarifas/TarifapreimpresionModel.php +++ b/ci4/app/Models/Tarifas/TarifapreimpresionModel.php @@ -19,6 +19,7 @@ class TarifapreimpresionModel extends \App\Models\BaseModel "precio", "margen", "mostrar_en_presupuesto", + "mostrar_en_presupuesto_cliente", "deleted_at", "is_deleted", "user_created_id", diff --git a/ci4/app/Models/UserModel.php b/ci4/app/Models/UserModel.php index cd7d2944..34f1767a 100644 --- a/ci4/app/Models/UserModel.php +++ b/ci4/app/Models/UserModel.php @@ -122,6 +122,19 @@ class UserModel extends ShieldUserModel } + public function getFullName($id=0){ + $builder = $this->db + ->table("users" . " t1") + ->select( + "CONCAT(t1.first_name, ' ', t1.last_name) AS name" + ); + + $builder->where('t1.deleted_at', null); + $builder->where('t1.id', $id); + + return $builder->get()->getRow()->name; + } + // Método para comprobar si el email ya está registrado public function isEmailUnique($email) { diff --git a/ci4/app/Services/MessageService.php b/ci4/app/Services/MessageService.php new file mode 100644 index 00000000..3ad4effa --- /dev/null +++ b/ci4/app/Services/MessageService.php @@ -0,0 +1,62 @@ +chatModel = model(ChatModel::class); + $this->chatMessageModel = model(ChatMessageModel::class); + $this->chatNotificationModel = model(ChatNotification::class); + $this->chatUserModel = model(ChatUser::class); + $this->presupuestoModel = model(PresupuestoModel::class); + $this->chatDepartmentModel = model(ChatDeparmentModel::class); + $this->configVariables = model(ConfigVariableModel::class); + } + public function createErrorMessagePresupuesto(string $error, int $presupuesto_id) + { + $dataResponse = null; + $chat_department_name = $this->configVariables->getVariable('send_error_to_chat_department_name')?->value; + $chat_department_id = $this->chatDepartmentModel->where('name', $chat_department_name)->first()?->id; + if ($chat_department_id) { + $existChat = $this->chatModel->existChatPresupuesto($chat_department_id, $presupuesto_id); + if ($existChat == false) { + $chatId = $this->chatModel->createChatPresupuesto($chat_department_id, $presupuesto_id); + } else { + $chat = $this->chatModel->getChatPresupuesto($chat_department_id, $presupuesto_id); + $chatId = $chat->id; + } + $chat_message_id = $this->chatMessageModel->insert(["chat_id" => $chatId, "sender_id" => auth()->user()->id, "message" => $error]); + $dataResponse = $this->chatMessageModel->find($chat_message_id); + $chatDepartmentUsers = $this->chatDepartmentModel->getChatDepartmentUsers($chat_department_id); + foreach ($chatDepartmentUsers as $user) { + if ($user->id != auth()->user()->id) { + $this->chatNotificationModel->insert(["chat_message_id" => $chat_message_id, "user_id" => $user->id]); + } + } + } + return $dataResponse; + + } +} diff --git a/ci4/app/Views/themes/vuexy/form/configuracion/papel/_papelGenericoFormItems.php b/ci4/app/Views/themes/vuexy/form/configuracion/papel/_papelGenericoFormItems.php index 501b1124..797b136d 100644 --- a/ci4/app/Views/themes/vuexy/form/configuracion/papel/_papelGenericoFormItems.php +++ b/ci4/app/Views/themes/vuexy/form/configuracion/papel/_papelGenericoFormItems.php @@ -23,7 +23,15 @@ - +
+
+ + activo == true ? 'checked' : ''; ?>> +
+
diff --git a/ci4/app/Views/themes/vuexy/form/configuracion/papel/viewPapelGenericoForm.php b/ci4/app/Views/themes/vuexy/form/configuracion/papel/viewPapelGenericoForm.php index b9b1b815..d347898b 100644 --- a/ci4/app/Views/themes/vuexy/form/configuracion/papel/viewPapelGenericoForm.php +++ b/ci4/app/Views/themes/vuexy/form/configuracion/papel/viewPapelGenericoForm.php @@ -144,6 +144,7 @@ } }); + endSection() ?> diff --git a/ci4/app/Views/themes/vuexy/form/configuracion/papel/viewPapelGenericoList.php b/ci4/app/Views/themes/vuexy/form/configuracion/papel/viewPapelGenericoList.php index 8f8e0009..56b043d8 100644 --- a/ci4/app/Views/themes/vuexy/form/configuracion/papel/viewPapelGenericoList.php +++ b/ci4/app/Views/themes/vuexy/form/configuracion/papel/viewPapelGenericoList.php @@ -21,6 +21,7 @@ + @@ -92,13 +93,14 @@ { 'data': 'code_ot' }, { 'data': 'show_in_client' }, { 'data': 'show_in_client_special' }, + { 'data': 'activo' }, { 'data': actionBtns } ] }); theTable.on( 'draw.dt', function () { - const boolCols = [3, 4]; + const boolCols = [3, 4, 5]; for (let coln of boolCols) { theTable.column(coln, { page: 'current' }).nodes().each( function (cell, i) { cell.innerHTML = cell.innerHTML == '1' ? '' : ''; diff --git a/ci4/app/Views/themes/vuexy/form/soporte/viewTicketForm.php b/ci4/app/Views/themes/vuexy/form/soporte/viewTicketForm.php new file mode 100644 index 00000000..4a12a566 --- /dev/null +++ b/ci4/app/Views/themes/vuexy/form/soporte/viewTicketForm.php @@ -0,0 +1,192 @@ +include("themes/_commonPartialsBs/sweetalert") ?> +include('themes/_commonPartialsBs/datatables') ?> +extend('themes/vuexy/main/defaultlayout') ?> +section('content'); ?> + + + + +
+
+
+
+

+
+
+
+ + + getErrors()) ? $validation->listErrors("bootstrap_style") : "" ?> +
+
+ + id)) ? "readonly" : "" ?> + value="titulo) ?>"> +
+
+ +
+ +
+ + +
+ + +
+ + +
+ +
+ + user()->can('tickets.edit')): ?> + + + + + + +
+ +
+ + user()->can('tickets.edit')): ?> + +
+
+ + +
+ +
+ + +
+ +
+ + + +
+ + +
+ + user()->can('tickets.edit'))) : ?> + +

+ +
+ + +
+ + + + user()->can('tickets.edit'))): ?> + +
+
+ + +
+
+ + + +

+
+ +
+
+ +
+
+ +
+ + + +
+ user()->can('tickets.edit')) + ): ?> + " /> + + "btn btn-secondary"]) ?> + +
+
+
+
+
+
+ +endSection(); ?> + +section('css') ?> + +endSection() ?> + + +section('additionalExternalJs') ?> + + + +endSection() ?> \ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/form/soporte/viewTicketList.php b/ci4/app/Views/themes/vuexy/form/soporte/viewTicketList.php new file mode 100644 index 00000000..b022bbee --- /dev/null +++ b/ci4/app/Views/themes/vuexy/form/soporte/viewTicketList.php @@ -0,0 +1,76 @@ +include('themes/_commonPartialsBs/datatables') ?> +extend('themes/vuexy/main/defaultlayout') ?> +section('content')?> + +
+
+ +
+
+

+ 'btn btn-primary float-end']); ?> +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
ID
+
+ +
+
+
+ +endSection() ?> + +section('css') ?> +"> + +endSection() ?> + + +section('additionalExternalJs') ?> + + + + + + + + + + + + + + +endSection() ?> diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/acabado/_tarifaAcabadoFormItems.php b/ci4/app/Views/themes/vuexy/form/tarifas/acabado/_tarifaAcabadoFormItems.php index 6ff12438..778f02e4 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/acabado/_tarifaAcabadoFormItems.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/acabado/_tarifaAcabadoFormItems.php @@ -54,7 +54,16 @@
+
+
+
+ +
+
+
+
+ +
+
diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/encuadernacion/viewTarifaEncuadernacionList.php b/ci4/app/Views/themes/vuexy/form/tarifas/encuadernacion/viewTarifaEncuadernacionList.php index 89f0f498..917cc756 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/encuadernacion/viewTarifaEncuadernacionList.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/encuadernacion/viewTarifaEncuadernacionList.php @@ -19,6 +19,7 @@ + @@ -91,6 +92,7 @@ { 'data': 'nombre' }, { 'data': 'code' }, { 'data': 'mostrar_en_presupuesto' }, + { 'data': 'mostrar_en_presupuesto_cliente' }, { 'data': 'tipo_encuadernacion' }, { 'data': 'servicio_encuadernacion' }, { 'data': actionBtns } @@ -98,7 +100,7 @@ }); theTable.on( 'draw.dt', function () { - const boolCols = [1,2,3, 4]; + const boolCols = [1,2,3,4,5]; for (let coln of boolCols) { theTable.column(coln, { page: 'current' }).nodes().each( function (cell, i) { cell.innerHTML = cell.innerHTML == '1' ? '' : ''; diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/extra/_tarifaextraFormItems.php b/ci4/app/Views/themes/vuexy/form/tarifas/extra/_tarifaextraFormItems.php index e655dcd2..622f6cea 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/extra/_tarifaextraFormItems.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/extra/_tarifaextraFormItems.php @@ -81,7 +81,19 @@
- +
+
+ mostrar_en_presupuesto_cliente == true ? 'checked' : ''; ?> + > + +
+
\ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/extra/viewTarifaextraForm.php b/ci4/app/Views/themes/vuexy/form/tarifas/extra/viewTarifaextraForm.php index 19cea842..66359035 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/extra/viewTarifaextraForm.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/extra/viewTarifaextraForm.php @@ -1,6 +1,6 @@ include("themes/_commonPartialsBs/select2bs5") ?> include("themes/_commonPartialsBs/sweetalert") ?> -extend('themes/vuexy/main/defaultlayout') ?> +extend('themes/vuexy/main/defaultlayout')?> section("content") ?>
diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/extra/viewTarifaextraList.php b/ci4/app/Views/themes/vuexy/form/tarifas/extra/viewTarifaextraList.php index 13b5d264..6127547f 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/extra/viewTarifaextraList.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/extra/viewTarifaextraList.php @@ -1,6 +1,6 @@ include('themes/_commonPartialsBs/datatables') ?> extend('themes/vuexy/main/defaultlayout') ?> -section('content'); ?> +section('content')?>
@@ -20,6 +20,7 @@ + @@ -46,6 +47,9 @@ mostrar_en_presupuesto)==1?'':"" ?> + + mostrar_en_presupuesto_cliente)==1?'':"" ?> + id), "", ['class'=>'text-body', 'data-id'=>$item->id,]); ?> ", ['class'=>'text-body', 'data-href'=>route_to('deleteTarifaextra', $item->id), 'data-bs-toggle'=>'modal', 'data-bs-target'=>'#confirm2delete']); ?> diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/manipulado/_tarifaManipuladoFormItems.php b/ci4/app/Views/themes/vuexy/form/tarifas/manipulado/_tarifaManipuladoFormItems.php index e048e22b..f57527fc 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/manipulado/_tarifaManipuladoFormItems.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/manipulado/_tarifaManipuladoFormItems.php @@ -52,7 +52,14 @@
- +
+
+ +
+
\ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/manipulado/viewTarifaManipuladoList.php b/ci4/app/Views/themes/vuexy/form/tarifas/manipulado/viewTarifaManipuladoList.php index 8febbc74..f14d33fa 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/manipulado/viewTarifaManipuladoList.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/manipulado/viewTarifaManipuladoList.php @@ -21,6 +21,7 @@ + @@ -91,7 +92,8 @@ { 'data': 'code' }, { 'data': 'precio_min' }, { 'data': 'importe_fijo' }, - { 'data': 'mostrar_en_presupuesto' }, + { 'data': 'mostrar_en_presupuesto',render : (d) => d == true ? '' : "" }, + { 'data': 'mostrar_en_presupuesto_cliente',render : (d) => d == true ? '' : "" }, { 'data': actionBtns } ] }); diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/preimpresion/_tarifapreimpresionFormItems.php b/ci4/app/Views/themes/vuexy/form/tarifas/preimpresion/_tarifapreimpresionFormItems.php index 2ce1b770..8db87c2a 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/preimpresion/_tarifapreimpresionFormItems.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/preimpresion/_tarifapreimpresionFormItems.php @@ -81,6 +81,19 @@ +
+
+ mostrar_en_presupuesto_cliente == true ? 'checked' : ''; ?> + > + +
+
\ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/form/tarifas/preimpresion/viewTarifapreimpresionList.php b/ci4/app/Views/themes/vuexy/form/tarifas/preimpresion/viewTarifapreimpresionList.php index 3f14ec35..34c1c32f 100644 --- a/ci4/app/Views/themes/vuexy/form/tarifas/preimpresion/viewTarifapreimpresionList.php +++ b/ci4/app/Views/themes/vuexy/form/tarifas/preimpresion/viewTarifapreimpresionList.php @@ -20,6 +20,7 @@ + @@ -45,6 +46,9 @@ mostrar_en_presupuesto)==1?'':"" ?> + + mostrar_en_presupuesto_cliente)==1?'':"" ?> + id), "", ['class'=>'text-body', 'data-id'=>$item->id,]); ?> ", ['class'=>'text-body', 'data-href'=>route_to('deleteTarifapreimpresion', $item->id), 'data-bs-toggle'=>'modal', 'data-bs-target'=>'#confirm2delete']); ?> diff --git a/ci4/app/Views/themes/vuexy/main/menu_impresion.php b/ci4/app/Views/themes/vuexy/main/menu_impresion.php index 2ba24ff5..fc43702e 100644 --- a/ci4/app/Views/themes/vuexy/main/menu_impresion.php +++ b/ci4/app/Views/themes/vuexy/main/menu_impresion.php @@ -48,6 +48,8 @@ require "menus/mensajes_menu.php"; + require "menus/soporte_menu.php"; + require "menus/sistema_menu.php"; ?> diff --git a/ci4/app/Views/themes/vuexy/main/menus/soporte_menu.php b/ci4/app/Views/themes/vuexy/main/menus/soporte_menu.php new file mode 100644 index 00000000..be2b5866 --- /dev/null +++ b/ci4/app/Views/themes/vuexy/main/menus/soporte_menu.php @@ -0,0 +1,22 @@ + + diff --git a/ci4/writable/logs/index.html b/ci4/writable/logs/index.html deleted file mode 100755 index b702fbc3..00000000 --- a/ci4/writable/logs/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - diff --git a/httpdocs/assets/js/safekat/components/alerts/sweetAlert.js b/httpdocs/assets/js/safekat/components/alerts/sweetAlert.js index 97f9bd66..449f9a7c 100644 --- a/httpdocs/assets/js/safekat/components/alerts/sweetAlert.js +++ b/httpdocs/assets/js/safekat/components/alerts/sweetAlert.js @@ -1,37 +1,37 @@ -export const alertConfirmationDelete = (title,type="primary") => { - return Swal.fire({ - title: '¿Está seguro?', - text: "Esta acción es irreversible.", - icon: 'warning', - showCancelButton: true, - confirmButtonColor: '#3085d6', - cancelButtonColor: '#d33', - confirmButtonText: 'Sí', - cancelButtonText: 'Cancelar', - customClass: { - confirmButton: 'btn btn-danger me-1', - cancelButton: 'btn btn-label-secondary' - }, - buttonsStyling: false - }) -} - -export const alertSuccessMessage = (title,type="primary") => { - return Swal.fire({ - showCancelButton: false, - showConfirmButton : false, - title: title, - text: title, - icon: "success", - timer : 2000 +export const alertConfirmationDelete = (title, type = "primary") => { + return Swal.fire({ + title: '¿Está seguro?', + text: "Esta acción es irreversible.", + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#3085d6', + cancelButtonColor: '#d33', + confirmButtonText: 'Sí', + cancelButtonText: 'Cancelar', + customClass: { + confirmButton: 'btn btn-danger me-1', + cancelButton: 'btn btn-label-secondary' + }, + buttonsStyling: false }) } -export const alertWarningMessage = (title,message,type="primary") => { - return Swal.fire({ +export const alertSuccessMessage = (title, type = "primary") => { + return Swal.fire({ + showCancelButton: false, + showConfirmButton: false, + title: title, + text: title, + icon: "success", + timer: 2000 + }) +} + +export const alertWarningMessage = (title, message, type = "primary") => { + return Swal.fire({ title: title, text: message, icon: "warning", @@ -40,4 +40,28 @@ export const alertWarningMessage = (title,message,type="primary") => { }, buttonsStyling: false }) +} + +export const toastPresupuestoSummary = (value, target = 'body') => { + return Swal.mixin({ + toast: true, + position: 'bottom-end', + html: ` +
+

Total presupuesto

+ ${value} +
`, + customClass: { + popup: 'bg-primary text-white', + }, + target: target, + height : "100px", + width : "200px", + padding : '1px', + allowEscapeKey: false, + showConfirmButton: false, + timer: 0, + timerProgressBar: false, + stopKeydownPropagation: false, + }) } \ No newline at end of file diff --git a/httpdocs/assets/js/safekat/pages/presupuestoAdmin/sections/resumen.js b/httpdocs/assets/js/safekat/pages/presupuestoAdmin/sections/resumen.js index e391bbef..58a2d142 100644 --- a/httpdocs/assets/js/safekat/pages/presupuestoAdmin/sections/resumen.js +++ b/httpdocs/assets/js/safekat/pages/presupuestoAdmin/sections/resumen.js @@ -1,3 +1,5 @@ +import { toastPresupuestoSummary } from "../../../components/alerts/sweetAlert.js"; + class Resumen { constructor() { @@ -7,7 +9,7 @@ class Resumen { init() { const self = this; - + this.toastPresupuestoTotal = null $(".update-totales").on("change", function () { self.updateTotales(true, true, true) }); @@ -16,6 +18,7 @@ class Resumen { await self.updateTotales(); $(document).trigger('update-totales-completed'); }); + $("#totalDespuesDecuento").on('change',this.updateToastSummary.bind(this)) } cargar(resumen) { @@ -44,7 +47,7 @@ class Resumen { $("#totalAntesDescuento").text(resumen.total_antes_descuento + "€" || "0€"); $("#total_descuentoPercent").val(resumen.total_descuentoPercent || "0"); $("#descuentoTotal").text(resumen.total_descuento + "€" || "0€"); - $("#totalDespuesDecuento").text(resumen.total_presupuesto + "€" || "0€"); + $("#totalDespuesDecuento").text(resumen.total_presupuesto + "€" || "0€").trigger("change") $("#precioUnidadPresupuesto").text(resumen.total_precio_unidad + "€" || "0€"); $("#factor").text(resumen.total_factor || "0"); $("#factor_ponderado").text(resumen.total_factor_ponderado || "0"); @@ -52,7 +55,7 @@ class Resumen { if (resumen.total_aceptado !== undefined) { $("#totalAceptado").val(resumen.total_aceptado + "€" || "0€"); } - + } update() { @@ -352,6 +355,25 @@ class Resumen { roundToTwoDecimals(num) { return parseFloat(num.toFixed(2)); } + updateToastSummary() + { + if(this.toastPresupuestoTotal){ + this.toastPresupuestoTotal.close() + } + + this.toastPresupuestoTotal = toastPresupuestoSummary($("#totalDespuesDecuento").text() ?? 0) + this.toastPresupuestoTotal.fire().then((result) => { + if(result.isDismissed){ + this.updateToastSummary() + $('html, body').animate( + { + scrollTop: $("#totalDespuesDecuento").offset().top + }, + 1000) + } + }) + + } } export default Resumen; \ No newline at end of file diff --git a/httpdocs/assets/js/safekat/pages/soporte/tickets.js b/httpdocs/assets/js/safekat/pages/soporte/tickets.js new file mode 100644 index 00000000..c442772f --- /dev/null +++ b/httpdocs/assets/js/safekat/pages/soporte/tickets.js @@ -0,0 +1,309 @@ +import Table from '../../components/table.js'; +import Ajax from '../../components/ajax.js'; + + + +class Ticket { + + constructor() { + + // check if url includes "add" + this.action = 'list'; + if (window.location.href.includes("add")) + this.action = "add"; + else if (window.location.href.includes("edit")) + this.action = "edit"; + + this.table = null; + + this.init(); + } + + + init() { + + if (this.action == "edit") { + + $('.gallery-img').on('click', function () { + let imageUrl = $(this).data('src'); // Obtiene la URL de la imagen + $('#modalImage').attr('src', imageUrl); // Cambia la imagen en el modal + $('#imageModal').modal('show'); // Muestra el modal + }); + + } + else if (this.action == "list") { + this.#initDatatable(); + } + } + + #initDatatable() { + + const self = this; + + self.#headerSearcher(); + + const actions = ['view']; + + let columns = []; + if (window.userType == 1) { + columns = [ + { 'data': 'id' }, + { + 'data': 'categoria_id', + render: function (data, type, row) { + return window.language.Tickets[row.categoria]; + } + }, + { + 'data': 'seccion_id', + render: function (data, type, row) { + return window.language.Tickets[row.seccion]; + } + }, + { + 'data': 'estado_id', + render: function (data, type, row) { + return window.language.Tickets[row.estado]; + } + }, + { + 'data': 'prioridad', + render: function (data, type, row) { + return window.language.Tickets[data]; + }, + }, + { 'data': 'titulo' }, + { + 'data': 'usuario_id', + render: function (data, type, row) { + return row.usuario; + }, + }, + { + 'data': 'user_soporte_id', + render: function (data, type, row) { + return row.user_soporte; + }, + }, + { 'data': 'created_at' }, + ]; + } + else { + columns = [ + { 'data': 'id' }, + { + 'data': 'categoria_id', + render: function (data, type, row) { + return window.language.Tickets[row.categoria]; + } + }, + { + 'data': 'seccion_id', + render: function (data, type, row) { + return window.language.Tickets[row.seccion]; + } + }, + { + 'data': 'estado_id', + render: function (data, type, row) { + return window.language.Tickets[row.estado]; + } + }, + { 'data': 'titulo' }, + { 'data': 'created_at' }, + ]; + } + + + this.table = new Table( + $('#tableOfTickets'), + 'tickets', + '/soporte/ticketlist', + columns, + ); + + + this.table.init({ + actions: actions, + buttonsExport: true, + }); + + this.table.table.on('init.dt', function () { + + self.table.table.page.len(100).draw(); + self.table.table.draw(); + + }); + + this.table.setEditCallback(function (id) { + window.location.href = '/soporte/edit/' + id; + }) + } + + #headerSearcher() { + const self = this; + + $('#tableOfTickets thead tr.search-header').remove(); + let searchRow = $('#tableOfTickets thead tr').first().clone(true); + searchRow.addClass('search-header').appendTo('#tableOfTickets thead'); + + $('#tableOfTickets thead tr:eq(1) th').each(function (i) { + if (!$(this).hasClass("noFilter")) { + var title = $(this).text(); + if (title == window.language.Tickets.fechaCreacion) { + + $(this).html(''); + var bsRangePickerRange = $('#bs-rangepicker-range') + bsRangePickerRange.daterangepicker({ + ranges: { + [window.language.datePicker.hoy]: [moment(), moment()], + [window.language.datePicker.ayer]: [moment().subtract(1, 'days'), moment().subtract(1, 'days')], + [window.language.datePicker.ultimos7]: [moment().subtract(6, 'days'), moment()], + [window.language.datePicker.ultimos30]: [moment().subtract(29, 'days'), moment()], + [window.language.datePicker.esteMes]: [moment().startOf('month'), moment().endOf('month')], + [window.language.datePicker.ultimoMes]: [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')] + }, + opens: 'right', + language: $('html').attr('lang'), + "locale": { + "customRangeLabel": window.language.datePicker.personalizar, + "format": "YYYY-MM-DD", + "separator": " ", + "applyLabel": window.language.datePicker.aplicar, + "cancelLabel": window.language.datePicker.limpiar, + + }, + "alwaysShowCalendars": true, + autoUpdateInput: false, + + }); + + bsRangePickerRange.on('apply.daterangepicker', function (ev, picker) { + $(this).val(picker.startDate.format('YYYY-MM-DD') + ' ' + picker.endDate.format('YYYY-MM-DD')); + self.table.table + .column(i) + .search(this.value) + .draw(); + }); + + bsRangePickerRange.on('cancel.daterangepicker', function (ev, picker) { + $(this).val(''); + self.table.table + .column(i) + .search(this.value) + .draw(); + }); + + } + else if (title == window.language.Tickets.tipo) { + // Agregar un selector en la tercera columna + $(this).html(''); + + // Agregar opciones al selector + var selectorTipo = $('select', this); + selectorTipo.append(''); // Opción vacía + selectorTipo.append(''); + selectorTipo.append(''); + selectorTipo.append(''); + + selectorTipo.on('change', function () { + var val = $.fn.dataTable.util.escapeRegex( + $(this).val() + ); + self.table.table.column(i).search(val).draw(); + }); + } + + else if (title == window.language.Tickets.seccion) { + // Agregar un selector en la tercera columna + $(this).html(''); + + // Agregar opciones al selector + var selectorSeccion = $('select', this); + selectorSeccion.append(''); // Opción vacía + selectorSeccion.append(''); + selectorSeccion.append(''); + selectorSeccion.append(''); + selectorSeccion.append(''); + selectorSeccion.append(''); + selectorSeccion.append(''); + + selectorSeccion.on('change', function () { + var val = $.fn.dataTable.util.escapeRegex( + $(this).val() + ); + self.table.table.column(i).search(val).draw(); + }); + } + else if (title == window.language.Tickets.estado) { + // Agregar un selector en la tercera columna + $(this).html(''); + + // Agregar opciones al selector + var selectorEstado = $('select', this); + selectorEstado.append(''); // Opción vacía + selectorEstado.append(''); + selectorEstado.append(''); + selectorEstado.append(''); + + selectorEstado.on('change', function () { + var val = $.fn.dataTable.util.escapeRegex( + $(this).val() + ); + self.table.table.column(i).search(val).draw(); + }); + } + else if (title == window.language.Tickets.prioridad) { + // Agregar un selector en la tercera columna + $(this).html(''); + + // Agregar opciones al selector + var selectorPrioridad = $('select', this); + selectorPrioridad.append(''); // Opción vacía + selectorPrioridad.append(''); + selectorPrioridad.append(''); + selectorPrioridad.append(''); + + selectorPrioridad.on('change', function () { + var val = $.fn.dataTable.util.escapeRegex( + $(this).val() + ); + self.table.table.column(i).search(val).draw(); + }); + } + else { + $(this).html(''); + + $('input', this).on('change clear', function () { + if (self.table.table.column(i).search() !== this.value) { + self.table.table + .column(i) + .search(this.value) + .draw(); + } + }); + } + } + else { + $(this).html(''); + } + }); + } +} + +document.addEventListener('DOMContentLoaded', function () { + + const locale = document.querySelector('meta[name="locale"]').getAttribute('content'); + + new Ajax('/translate/getTranslation', { locale: locale, translationFile: ['Tickets', 'datePicker'] }, {}, + function (translations) { + window.language = JSON.parse(translations); + new Ticket(); + }, + function (error) { + console.log("Error getting translations:", error); + } + ).post(); +}); + +export default Ticket; \ No newline at end of file