Herramientas Informaticas

Etiqueta: GNU/LINUX Página 9 de 13

SOFTWARE DE PUNTO DE VENTA OSPOS DESKTOP PARA WINDOWS 10

Open Source Point of Sale es un sistema de punto de venta basado en la web. La aplicación está escrita en PHP, utiliza MySQL (o MariaDB) como back-end de almacenamiento de datos y tiene una interfaz de usuario simple pero intuitiva.

La última versión 3.x es una revisión completa del software original. Utiliza CodeIgniter 3 como marco y se basa en Bootstrap 3 usando temas de Bootswatch. Junto con funcionalidad y seguridad mejoradas.

Es este caso hicimos una versión desktop para hacer una instalación mas fácil y cómoda en el caso de usar Windows 10

Programa ideal para papelerías, tiendas pequeñas, expendios del pacifico o tecate ETC

Las características incluyen:

  • Las características incluyen:
  • Gestión de stock (artículos y kits con una lista extensible de atributos)
  • IVA, GST, clientes e impuestos de varios niveles
  • Registro de venta con registro de transacciones
  • Cotización y facturación
  • Registro de gastos
  • Función de cobro
  • Impresión y envío por correo electrónico de recibos, facturas y cotizaciones
  • Generación e impresión de códigos de barras.
  • Base de datos de clientes y proveedores.
  • Multiusuario con control de permisos
  • Informes sobre ventas, pedidos, gastos, estado del inventario y más
  • Recepciones
  • Tarjetas de regalo
  • Recompensas
  • Mesas de restaurante
  • Mensajería (SMS)
  • Multi lenguaje
  • Tema de interfaz de usuario basado en Bootstrap seleccionable con Bootswatch
  • integración de mailchimp
  • Google reCAPTCHA opcional para proteger la página de inicio de sesión de ataques de fuerza bruta
  • Listo para RGPD

El proceso de instalación se divide en dos partes, la instalación de la base de datos y la instalación del sistema en general

Instalando la base de datos

Para instalar la base de datos ocuparemos el instalador de MariaDB 11 y el archivo de base de datos OSPOS.SQL les dejo los enlaces de descarga desde Mega.nz

Para instalar es prácticamente siguiente siguiente solo que en password del root le pondremos ospos

En el nombre de servicio le ponemos MariaDBOSPOS y en el puerto le pones el 3315
Nos conectamos vía HeidiSQL, le ponemos la contraseña ospos y el puerto 3315
Creamos la base de datos con nombre ospos, con la intercalación de latin1_spanish_ci
Le damos click a la base de datos creada, se le tiene que poner la palomita verde luego nos vamos al menú archivo->Ejecutar Archivo SQL seleccionamos el archivo OSPOS.SQL y ya tendremos instalada nuestra base de datos

Instalación de OSPOS Desktop

Descargamos el instalador donde viene el programa

Aquí es prácticamente siguiente siguiente, al final te creara un icono en el escritorio
Y listo ya tenemos nuestra aplicación de escritorio instalada
Usuario:admin
Contraseña:pointofsale

No se olviden de programar los respaldos automáticos pueden ver como generarles aquí GENERAR RESPALDOS AUTOMATICOS MARIADB

Como recortar silencios incómodos automáticamente en los videos, Linux Mint 21.2

Dentro del software libre tenemos muchas herramientas que nos permiten realizar nuestro trabajo y en este caso que es la edición de video hay una herramienta hecha en Python que nos permite quitar los silencios en un video, es decir, quitar esas regiones del video en las que no hablamos.

Bien para ello necesitaremos descargar el software jumpcutter que la pueden descargar desde Github, para descargarlo usan el siguiente comando

git clone https://github.com/carykh/jumpcutter

Para usarlo necesitamos la dependencia de Python python3-pip, podemos instalarla con el siguiente comando

sudo apt install python3-pip

entramos al directorio de jumpcutter que descargamos

cd jumpcutter/

Ahora instalamos las dependencias de jumpcutter

pip install --user -r requirements.txt

Ahora si podemos usar la utilitaria para quitarle los silencios a nuestros videos, colocamos el video con nombre 1.mkv, si nuestro video es de 30 fotogramas por segundo le ponemos en el –frame_rate 30, se es de 24 le ponemos –frame_rate 24

python3 jumpcutter.py --input_file 1.mkv --sounded_speed 1 --silent_speed 999999 --frame_margin 2 --frame_rate 30

Y listo esto nos generara un archivo 1_altered.mkv

Que hacer después de instalar Linux Mint 21

Ya hemos vuelvo después de varios años instalamos Linux Mint 21 dejando atrás a Zorin Linux

Vamos a dejar la maquina lista para la edición de videos, programación y edición de música para ello le instalaremos el siguiente software

  • RustDesk Software de conexión a escritorio remoto
  • Netbeans IDE de Programación
  • Gambas3 Software para programar al estilo de Visual Basic
  • PHP8 Archivos necesarios para programar en PHP
  • Composer Herramienta de gestor de dependencias para PHP
  • XAMPP Pila PHP, Apache, MySQL
  • Telegram Programa para Chat
  • OBS STUDIO Para transmitir y grabar video
  • KdenLive Herramienta para editar videos
  • Audicity Programa para edición de video, al estilo de cool edit pro / adobe audition
  • Ardour DAW Multipista para grabación profesional de audio al estilo de ProTools
  • Guitarix Programa con efectos para Guitarra Eléctrica
  • Visual Studio Code IDE de programación para diferentes lenguajes
  • Remmina Software de escritorio remoto RDP
  • Filezilla Utilidad para subir y descargar archivos vía FTP y SFTP
  • Brasero Programa para grabación de CD/DVD
  • Minder Programa para crear mapas conceptuales
  • DigiKam Para la administración de fotografías e imágenes
  • Pomodoro Programa para cronometrar nuestras tareas, puede servir para tomar tiempo de cuanto nos lleva las tareas de desarrollo y así poder ponerles un precio por hora
  • Dbeaver Cliente para diferentes bases de datos como MySQL, MariaDB, SQL Server, SQLite, Progress, Postgress, Firebird etc
  • GIMP Un editor de fotos similar a PhotoShop

Primero hacemos el clásico update y upgrade

sudo apt update
sudo apt upgrade

Instalamos RustDesk que es un software de escritorio remoto similar al AnyDesk

wget https://github.com/rustdesk/rustdesk/releases/download/1.2.0/rustdesk-1.2.0-x86_64.deb

Le asignamos el bit de ejecutable

sudo chmod +x rustdesk-1.2.0-x86_64.deb

Y lo instalamos con este comando

sudo dpkg -i rustdesk-1.2.0-x86_64.deb

Ahora vamos a instalar Java y el IDE Netbeans

Instalamos Java

sudo apt install default-jdk

Borramos las preferencias del Snap

sudo rm /etc/apt/preferences.d/nosnap.pref

Instalamos snapd

sudo apt install snapd

Instalamos Netbeans

sudo snap install netbeans --classic

Ahora instalaremos PHP
Agregamos el repositorio

sudo add-apt-repository ppa:ondrej/php

Actualizamos

sudo apt update

Después instalamos los siguientes comandos uno por uno

sudo apt install php8.1
sudo apt install php8.1-mysql
sudo apt install php8.1-xdebug
sudo apt install php8.1-mbstring
sudo apt-get install php-intl
sudo apt install php
sudo apt install php8.2-pdo-mysql  

Ahora para instalar composer solo ejecutamos el siguiente instrucción

sudo apt install composer

Ahora instalamos la pila XAMPP que contiene PHP8.2 MySQL y Apache

Primero descargamos el archivo .run con el siguiente comando

wget https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/8.2.4/xampp-linux-x64-8.2.4-0-installer.run

Le asignamos el bit de ejecutable

sudo chmod +x xampp-linux-x64-8.2.4-0-installer.run

Y por ultimo ejecutamos el archivo

sudo ./xampp-linux-x64-8.2.4-0-installer.run

Instalamos Telegram que es como un WhatsApp pero mucho mejor

sudo apt install telegram-desktop -y

Instalamos OBS Studio para nuestras transmisiones en vivo y grabaciones de nuevo escritorio, aunque realmente para grabar prefiero el simplescreenrecorder

sudo apt install obs-studio

Instalamos Simple Screen Recorder para grabar nuestro escritorio para los videos tutoriales

sudo apt install simplescreenrecorder

Instalamos Kdenlive para editar videos de gran calidad

sudo apt install obs-studio

Instalamos audacity para edición, corrección y mejoramiento de pistas de audio

sudo apt install audacity

Instalamos Ardour un DAW muy profesional similar a Pro Tools para crear música y podcast

sudo apt install ardour

Instalamos guitarix para efectos de guitarra eléctrica

sudo apt install guitarix

Instalamos Remmina, es el equivalente a Escritorio Remoto de Windows pero mejor

sudo apt install remmina

Instalamos Filezilla un programa para subir archivos a nuestros hosting compartido o VPS

sudo apt install filezilla

Instalamos Gambas3 Un lenguaje de programación RAD similar a Visual Basic, nos servira para crear programas rapidamente tanto de escritorio como Web

sudo apt install gambas3

instalamos Visual Studio Code, anteriormente usábamos Atom, podremos programar en PHP y subir facilmente el fuente en GitHub

Instalamos los certificados

sudo apt install dirmngr ca-certificates software-properties-common apt-transport-https -y

Agregamos el repositorio

curl -fSsL https://packages.microsoft.com/keys/microsoft.asc | sudo gpg --dearmor | sudo tee /usr/share/keyrings/vscode.gpg > /dev/null
echo deb [arch=amd64 signed-by=/usr/share/keyrings/vscode.gpg] https://packages.microsoft.com/repos/vscode stable main | sudo tee /etc/apt/sources.list.d/vscode.list

Actualizamos e instalamos

sudo apt update
sudo apt install code -y

Instalamos quemador de discos brasero, si aun puede usarse

sudo apt install brasero

Instalamos programa para crear mapas conceptuales

sudo apt install minder

Instalamos administrador de fotos, el mejor que he visto libre, identifica rostros ademas ubica en el globo en que lugar se tomo la fotografía

Para instalarlo corremos el siguiente comando

sudo apt install digikam

Ahora nos toca instalar pomodoro una útil herramienta la cual nos permitirá cronometrar nuestras actividades, dar minutos de descanso y anotar cuanto tiempo nos llevo una actividad, muy útil para los programadores

sudo apt install gnomo-shell-pomodoro

Ahora procederemos instalar DBeaver un cliente muy potente para diferentes motores de base de datos

Primero descargamos el archivo .deb

wget https://dbeaver.io/files/dbeaver-ce_latest_amd64.deb

Asignamos el bit de ejecutable

sudo chmod +x dbeaver-ce_latest_amd64.deb

Instalamos dbeaver con el siguiente comando

sudo dpkg -i dbeaver-ce_latest_amd64.deb 

Para instalar GIMP un potente editor de fotografía corremos el siguiente comando

sudo apt install gimp

Generador DIOT para Windows 10

La DIOT es la declaración informativa de operaciones con Terceros, la cual es una obligación fiscal prevista más del IVA donde se da a conocer el estado de las operaciones para con terceros (como su nombre lo dice) o lo que es lo mismo con los proveedores.

Leer Mas: Generador DIOT para Windows 10

Si eres persona física o moral que tributa en el Régimen Simplificado de Confianza; o persona física con ingresos por actividades empresariales, profesionales y/o arrendamiento, cuyos ingresos totales del ejercicio inmediato anterior no excedieron de 4 millones de pesos o iniciaste actividades en el ejercicio y estimas que tus ingresos no excederán de la cantidad señalada, quedarás relevado de cumplir con la presentación de la Información de Operaciones con Terceros (DIOT) a que se refiere el artículo 32, fracción VIII de la Ley del IVA.

Hemos desarrollado un programa para generar la DIOT en base a un layout

Incluimos un layout de ejemplo en cual pueden obtener desde la aplicación

Se puede cambiar la configuración por RFC

Creando CRUD de clientes #06

Ya vimos como usar AutoCrud para crear los catálogos, en esta ocasión tambien usamos autocrud, aun así dejaremos el código fuente completo de este modulo

Creamos el archivo modelo al app/models/CustumersModel.php con el siguiente contenido

Leer Mas: Creando CRUD de clientes #06
<?php
namespace App\Models;
use CodeIgniter\Model;
class CustumersModel extends Model{
    protected $table      = 'custumers';
    protected $primaryKey = 'id';
    protected $useAutoIncrement = true;
    protected $returnType     = 'array';
    protected $useSoftDeletes = true;
    protected $allowedFields = ['id','firstname','lastname','taxID','email','direction','birthdate','created_at','updated_at','deleted_at'];
    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';
    protected $validationRules    =  [
    ];
    protected $validationMessages = [];
    protected $skipValidation     = false;
}
        

Creamos nuestro archivo controlador app/controllers/CustumersControllers.php

<?php
 namespace App\Controllers;
 use App\Controllers\BaseController;
 use \App\Models\{CustumersModel};
 use App\Models\LogModel;
 use CodeIgniter\API\ResponseTrait;
 class CustumersController extends BaseController {
     use ResponseTrait;
     protected $log;
     protected $custumers;
     public function __construct() {
         $this->custumers = new CustumersModel();
         $this->log = new LogModel();
         helper('menu');
         helper('utilerias');
     }
     public function index() {
         if ($this->request->isAJAX()) {
             $datos = $this->custumers->select('id,firstname,lastname,taxID,email,direction,birthdate,created_at,updated_at,deleted_at')->where('deleted_at', null);
             return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
         }


         $fechaActual =  fechaMySQLADateTimeHTML5(fechaHoraActual());

         $titulos["title"] = lang('custumers.title');
         $titulos["subtitle"] = lang('custumers.subtitle');
         $titulos["fecha"] = $fechaActual;
         return view('custumers', $titulos);
     }
     /**
      * Read Custumers
      */
     public function getCustumers() {
         $idCustumers = $this->request->getPost("idCustumers");
         $datosCustumers = $this->custumers->find($idCustumers);
         echo json_encode($datosCustumers);
     }
     /**
      * Save or update Custumers
      */
     public function save() {
         helper('auth');
         $userName = user()->username;
         $idUser = user()->id;
         $datos = $this->request->getPost();
         if ($datos["idCustumers"] == 0) {
             try {
                 if ($this->custumers->save($datos) === false) {
                     $errores = $this->custumers->errors();
                     foreach ($errores as $field => $error) {
                         echo $error . " ";
                     }
                     return;
                 }
                 $dateLog["description"] = lang("vehicles.logDescription") . json_encode($datos);
                 $dateLog["user"] = $userName;
                 $this->log->save($dateLog);
                 echo "Guardado Correctamente";
             } catch (\PHPUnit\Framework\Exception $ex) {
                 echo "Error al guardar " . $ex->getMessage();
             }
         } else {
             if ($this->custumers->update($datos["idCustumers"], $datos) == false) {
                 $errores = $this->custumers->errors();
                 foreach ($errores as $field => $error) {
                     echo $error . " ";
                 }
                 return;
             } else {
                 $dateLog["description"] = lang("custumers.logUpdated") . json_encode($datos);
                 $dateLog["user"] = $userName;
                 $this->log->save($dateLog);
                 echo "Actualizado Correctamente";
                 return;
             }
         }
         return;
     }
     /**
      * Delete Custumers
      * @param type $id
      * @return type
      */
     public function delete($id) {
         $infoCustumers = $this->custumers->find($id);
         helper('auth');
         $userName = user()->username;
         if (!$found = $this->custumers->delete($id)) {
             return $this->failNotFound(lang('custumers.msg.msg_get_fail'));
         }
         $this->custumers->purgeDeleted();
         $logData["description"] = lang("custumers.logDeleted") . json_encode($infoCustumers);
         $logData["user"] = $userName;
         $this->log->save($logData);
         return $this->respondDeleted($found, lang('custumers.msg_delete'));
     }
 }
        

Creamos nuestro archivo de la visa en app/views/custumers.php con el siguiente contenido

<?= $this->include('julio101290\boilerplate\Views\load\select2') ?>
<?= $this->include('julio101290\boilerplate\Views\load\datatables') ?>
<?= $this->include('julio101290\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('julio101290\boilerplate\Views\layout\index') ?>

<!-- Section content -->
<?= $this->section('content') ?>

<?= $this->include('modulesCustumers/modalCaptureCustumers') ?>

<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
 <div class="card-header">
     <div class="float-right">
         <div class="btn-group">

             <button class="btn btn-primary btnAddCustumers" data-toggle="modal" data-target="#modalAddCustumers"><i class="fa fa-plus"></i>

                 <?= lang('custumers.add') ?>

             </button>

         </div>
     </div>
 </div>
 <div class="card-body">
     <div class="row">
         <div class="col-md-12">
             <div class="table-responsive">
                 <table id="tableCustumers" class="table table-striped table-hover va-middle tableCustumers">
                     <thead>
                         <tr>

                             <th>#</th>
                             <th><?= lang('custumers.fields.firstname') ?></th>
<th><?= lang('custumers.fields.lastname') ?></th>
<th><?= lang('custumers.fields.taxID') ?></th>
<th><?= lang('custumers.fields.email') ?></th>
<th><?= lang('custumers.fields.direction') ?></th>
<th><?= lang('custumers.fields.birthdate') ?></th>
<th><?= lang('custumers.fields.created_at') ?></th>
<th><?= lang('custumers.fields.updated_at') ?></th>
<th><?= lang('custumers.fields.deleted_at') ?></th>

                             <th><?= lang('custumers.fields.actions') ?> </th>

                         </tr>
                     </thead>
                     <tbody>
                     </tbody>
                 </table>
             </div>
         </div>
     </div>
 </div>
</div>
<!-- /.card -->

<?= $this->endSection() ?>


<?= $this->section('js') ?>
<script>

 /**
  * Cargamos la tabla
  */

 var tableCustumers = $('#tableCustumers').DataTable({
     processing: true,
     serverSide: true,
     responsive: true,
     autoWidth: false,
     order: [[1, 'asc']],

     ajax: {
         url: '<?= base_url(route_to('admin/custumers')) ?>',
         method: 'GET',
         dataType: "json"
     },
     columnDefs: [{
             orderable: false,
             targets: [10],
             searchable: false,
             targets: [10]

         }],
     columns: [{
             'data': 'id'
         },
        
          
{
    'data': 'firstname'
},
 
{
    'data': 'lastname'
},
 
{
    'data': 'taxID'
},
 
{
    'data': 'email'
},
 
{
    'data': 'direction'
},
 
{
    'data': 'birthdate'
},
 
{
    'data': 'created_at'
},
 
{
    'data': 'updated_at'
},
 
{
    'data': 'deleted_at'
},

         {
             "data": function (data) {
                 return `<td class="text-right py-0 align-middle">
                         <div class="btn-group btn-group-sm">
                             <button class="btn btn-warning btnEditCustumers" data-toggle="modal" idCustumers="${data.id}" data-target="#modalAddCustumers">  <i class=" fa fa-edit"></i></button>
                             <button class="btn btn-danger btn-delete" data-id="${data.id}"><i class="fas fa-trash"></i></button>
                         </div>
                         </td>`
             }
         }
     ]
 });



 $(document).on('click', '#btnSaveCustumers', function (e) {

     
var idCustumers = $("#idCustumers").val();
var firstname = $("#firstname").val();
var lastname = $("#lastname").val();
var taxID = $("#taxID").val();
var email = $("#email").val();
var direction = $("#direction").val();
var birthdate = $("#birthdate").val();

     $("#btnSaveCustumers").attr("disabled", true);

     var datos = new FormData();
    datos.append("idCustumers", idCustumers);
    datos.append("firstname", firstname);
    datos.append("lastname", lastname);
    datos.append("taxID", taxID);
    datos.append("email", email);
    datos.append("direction", direction);
    datos.append("birthdate", birthdate);


     $.ajax({

         url: "<?= route_to('admin/custumers/save') ?>",
         method: "POST",
         data: datos,
         cache: false,
         contentType: false,
         processData: false,
         success: function (respuesta) {
             if (respuesta.match(/Correctamente.*/)) {
        
                 Toast.fire({
                     icon: 'success',
                     title: "Guardado Correctamente"
                 });

                 tableCustumers.ajax.reload();
                 $("#btnSaveCustumers").removeAttr("disabled");


                 $('#modalAddCustumers').modal('hide');
             } else {

                 Toast.fire({
                     icon: 'error',
                     title: respuesta
                 });

                 $("#btnSaveCustumers").removeAttr("disabled");
                

             }

         }

     }

     )

 });



 /**
  * Carga datos actualizar
  */


 /*=============================================
  EDITAR Custumers
  =============================================*/
 $(".tableCustumers").on("click", ".btnEditCustumers", function () {

     var idCustumers = $(this).attr("idCustumers");
        
     var datos = new FormData();
     datos.append("idCustumers", idCustumers);

     $.ajax({

         url: "<?= base_url(route_to('admin/custumers/getCustumers')) ?>",
         method: "POST",
         data: datos,
         cache: false,
         contentType: false,
         processData: false,
         dataType: "json",
         success: function (respuesta) {
             $("#idCustumers").val(respuesta["id"]);
             
             $("#firstname").val(respuesta["firstname"]);
$("#lastname").val(respuesta["lastname"]);
$("#taxID").val(respuesta["taxID"]);
$("#email").val(respuesta["email"]);
$("#direction").val(respuesta["direction"]);
$("#birthdate").val(respuesta["birthdate"]);


         }

     })

 })


 /*=============================================
  ELIMINAR custumers
  =============================================*/
 $(".tableCustumers").on("click", ".btn-delete", function () {

     var idCustumers = $(this).attr("data-id");

     Swal.fire({
         title: '<?= lang('boilerplate.global.sweet.title') ?>',
         text: "<?= lang('boilerplate.global.sweet.text') ?>",
         icon: 'warning',
         showCancelButton: true,
         confirmButtonColor: '#3085d6',
         cancelButtonColor: '#d33',
         confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
     })
             .then((result) => {
                 if (result.value) {
                     $.ajax({
                         url: `<?= base_url(route_to('admin/custumers')) ?>/` + idCustumers,
                         method: 'DELETE',
                     }).done((data, textStatus, jqXHR) => {
                         Toast.fire({
                             icon: 'success',
                             title: jqXHR.statusText,
                         });


                         tableCustumers.ajax.reload();
                     }).fail((error) => {
                         Toast.fire({
                             icon: 'error',
                             title: error.responseJSON.messages.error,
                         });
                     })
                 }
             })
 })

 $(function () {
    $("#modalAddCustumers").draggable();
    
});


</script>
<?= $this->endSection() ?>
        

Para el modal creamos el siguiente archivo app/views/modulesCustumers/modalCaptureCustumers.php con el siguiente contenido

<!-- Modal Custumers -->
  <div class="modal fade" id="modalAddCustumers" tabindex="-1" role="dialog" aria-labelledby="modalAddCustumers" aria-hidden="true">
      <div class="modal-dialog modal-lg" role="document">
          <div class="modal-content">
              <div class="modal-header">
                  <h5 class="modal-title"><?= lang('custumers.createEdit') ?></h5>
                  <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                      <span aria-hidden="true">&times;</span>
                  </button>
              </div>
              <div class="modal-body">
                  <form id="form-custumers" class="form-horizontal">
                      <input type="hidden" id="idCustumers" name="idCustumers" value="0">

                      <div class="form-group row">
    <label for="firstname" class="col-sm-2 col-form-label"><?= lang('custumers.fields.firstname') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
            </div>
            <input type="text" name="firstname" id="firstname" class="form-control <?= session('error.firstname') ? 'is-invalid' : '' ?>" value="<?= old('firstname') ?>" placeholder="<?= lang('custumers.fields.firstname') ?>" autocomplete="off">
        </div>
    </div>
</div>
<div class="form-group row">
    <label for="lastname" class="col-sm-2 col-form-label"><?= lang('custumers.fields.lastname') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
            </div>
            <input type="text" name="lastname" id="lastname" class="form-control <?= session('error.lastname') ? 'is-invalid' : '' ?>" value="<?= old('lastname') ?>" placeholder="<?= lang('custumers.fields.lastname') ?>" autocomplete="off">
        </div>
    </div>
</div>
<div class="form-group row">
    <label for="taxID" class="col-sm-2 col-form-label"><?= lang('custumers.fields.taxID') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
            </div>
            <input type="text" name="taxID" id="taxID" class="form-control <?= session('error.taxID') ? 'is-invalid' : '' ?>" value="<?= old('taxID') ?>" placeholder="<?= lang('custumers.fields.taxID') ?>" autocomplete="off">
        </div>
    </div>
</div>
<div class="form-group row">
    <label for="email" class="col-sm-2 col-form-label"><?= lang('custumers.fields.email') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
            </div>
            <input type="text" name="email" id="email" class="form-control <?= session('error.email') ? 'is-invalid' : '' ?>" value="<?= old('email') ?>" placeholder="<?= lang('custumers.fields.email') ?>" autocomplete="off">
        </div>
    </div>
</div>
<div class="form-group row">
    <label for="direction" class="col-sm-2 col-form-label"><?= lang('custumers.fields.direction') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
            </div>
            <input type="text" name="direction" id="direction" class="form-control <?= session('error.direction') ? 'is-invalid' : '' ?>" value="<?= old('direction') ?>" placeholder="<?= lang('custumers.fields.direction') ?>" autocomplete="off">
        </div>
    </div>
</div>
<div class="form-group row">
    <label for="birthdate" class="col-sm-2 col-form-label"><?= lang('custumers.fields.birthdate') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
            </div>
            
            <input type="datetime-local" name="birthdate" id="birthdate" class="form-control <?= session('error.birthdate') ? 'is-invalid' : '' ?>" value="<?= $fecha ?>" placeholder="<?= lang('custumers.fields.birthdate') ?>" autocomplete="off">
        </div>
    </div>
</div>

        
                  </form>
              </div>
              <div class="modal-footer">
                  <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                  <button type="button" class="btn btn-primary btn-sm" id="btnSaveCustumers"><?= lang('boilerplate.global.save') ?></button>
              </div>
          </div>
      </div>
  </div>

  <?= $this->section('js') ?>


  <script>

      $(document).on('click', '.btnAddCustumers', function (e) {


          $(".form-control").val("");

          $("#idCustumers").val("0");

          $("#birthdate").val("<?= $fecha ?>");

          $("#btnSaveCustumers").removeAttr("disabled");

      });

      /* 
       * AL hacer click al editar
       */



      $(document).on('click', '.btnEditCustumers', function (e) {


          var idCustumers = $(this).attr("idCustumers");

          //LIMPIAMOS CONTROLES
        $(".form-control").val("");

          $("#idCustumers").val(idCustumers);
          $("#btnGuardarCustumers").removeAttr("disabled");

      });




  </script>


  <?= $this->endSection() ?>
        

Para la traducción en ingles creamos el archivo app/languaje/es/custumers.php con el siguiente contenido

 <?php

$custumers["logDescription"] = "The custumers was saved with the following data:";
$custumers["logUpdate"] = "The custumers was updated  with the following data:";
$custumers["logDeleted"] = "The custumers was deleted  with the following data:";
$custumers["msg_delete"] = "The custumers was deleted  correctly:";

$custumers["add"] = "Add Custumers";
$custumers["edit"] = "Edit custumers";
$custumers["createEdit"] = "Create / Edit";
$custumers["title"] = "custumers management";
$custumers["subtitle"] = "custumers list";
$custumers["fields"]["firstname"] = "Firstname";
$custumers["fields"]["lastname"] = "Lastname";
$custumers["fields"]["taxID"] = "TaxID";
$custumers["fields"]["email"] = "Email";
$custumers["fields"]["direction"] = "Direction";
$custumers["fields"]["birthdate"] = "Birthdate";
$custumers["fields"]["created_at"] = "Created_at";
$custumers["fields"]["updated_at"] = "Updated_at";
$custumers["fields"]["deleted_at"] = "Deleted_at";

$custumers["fields"]["actions"] = "Actions";
$custumers["msg"]["msg_insert"] = "The custumers has been correctly added.";
$custumers["msg"]["msg_update"] = "The custumers has been correctly modified.";
$custumers["msg"]["msg_delete"] = "The custumers has been correctly deleted.";
$custumers["msg"]["msg_get"] = "The Custumers has been successfully get.";
$custumers["msg"]["msg_get_fail"] = "The custumers not found or already deleted.";

return $custumers;
        

Para nuestra traduccion en español creamos el archivo app/languaje/es/custumers.php

 <?php
$custumers["logDescription"] = "El registro en clientes fue guardado con los siguientes datos:";
$custumers["logUpdate"] = "El registro en clientes fue actualizado con los siguientes datos:";
$custumers["logDeleted"] = "El registro en clientes fue eliminado con los siguientes datos:";
$custumers["msg_delete"] = "El Registro en clientes fue eliminado correctamente:";
$custumers["add"] = "Agregar Cliente";
$custumers["edit"] = "Editar Cliente";
$custumers["createEdit"] = "Crear / Editar";
$custumers["title"] = "Admon. Clientes";
$custumers["subtitle"] = "Lista de Clientes";
$custumers["fields"]["firstname"] = "Nombre";
$custumers["fields"]["lastname"] = "Apellido";
$custumers["fields"]["taxID"] = "RFC";
$custumers["fields"]["email"] = "Correo Electronico";
$custumers["fields"]["direction"] = "Direccion";
$custumers["fields"]["birthdate"] = "Fecha de nacimiento";
$custumers["fields"]["created_at"] = "Fecha de creacion";
$custumers["fields"]["updated_at"] = "Ultima modificacion";
$custumers["fields"]["deleted_at"] = "Fecha de eliminacion";

 $custumers["fields"]["actions"] = "Acciones";       
$custumers["msg"]["msg_insert"] = "Registro agregado correctamente.";
$custumers["msg"]["msg_update"] = "Registro modificado correctamente.";
$custumers["msg"]["msg_delete"] = "Registro eliminado correctamente.";
$custumers["msg"]["msg_get"] = "Registro obtenido correctamente.";
$custumers["msg"]["msg_get_fail"] = "Registro no encontrado o eliminado.";
return $custumers;
        

Creamos nuestro archivo de migración en app/databases/migrations/2023-04-21063335_Custumers.php con el siguiente contenido

<?php
    namespace App\Database\Migrations;
    use CodeIgniter\Database\Migration;
    class Custumers extends Migration
    {
    public function up()
    {
     // Custumers
    $this->forge->addField([
        'id'                    => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
        'firstname'             => ['type' => 'varchar', 'constraint' => 128, 'null' => true],
        'lastname'             => ['type' => 'varchar', 'constraint' => 128, 'null' => true],
        'taxID'             => ['type' => 'varchar', 'constraint' => 64, 'null' => true],
        'email'             => ['type' => 'varchar', 'constraint' => 128, 'null' => true],
        'direction'             => ['type' => 'varchar', 'constraint' => 1024, 'null' => true],
        'birthdate'             => ['type' => 'datetime' , 'null' => true],
        'created_at'       => ['type' => 'datetime', 'null' => true],
        'updated_at'       => ['type' => 'datetime', 'null' => true],
        'deleted_at'       => ['type' => 'datetime', 'null' => true],
    ]);
    $this->forge->addKey('id', true);
    $this->forge->createTable('custumers', true);
    }
    public function down(){
        $this->forge->dropTable('custumers', true);
        }
    }

Corremos el comando php serve migrate para levantar la tabla custumers

Listo ya solo queda configurar los permisos y menú

Demostración en video

Creando CRUD de categorías usando AutoCrud #05

Ya vimos como crear módulos y catalogo ahora mostraremos como automatizar esa parte del proceso de desarrollo para ser un poco mas eficientes para ello escribimos un código que escribe el código repetitivo

Creamos el archivo app/controller/AutoCrudController.php con el siguiente código

Leer Mas: Creando CRUD de categorías usando AutoCrud #05
<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use App\Controllers\Time;
use CodeIgniter\API\ResponseTrait;
use Faker\Provider\zh_CN\DateTime;

class AutoCrudController extends BaseController {

    use ResponseTrait;

    protected $db;

    public function __construct() {
        $this->db = \Config\Database::connect();
        helper('utilerias');
    }

    public function index($table) {
     /*
       $this->generateModel($table);
       $this->generateController($table);
       $this->generateView($table);
       $this->generateViewModal($table);
       $this->generateLanguage($table);
        */
        $this->generateMigration($table);
       
     //$this->generateLanguageES($table);

        $tableUpCase = ucfirst($table);
        $route = <<<EOF
                 \$routes->resource('$table', [
                                'filter' => 'permission:$table-permission',
                                'controller' => '{$table}Controller',
                                'except' => 'show'
                            ]);
                \$routes->post('$table/save', '{$tableUpCase}Controller::save');
                \$routes->post('{$table}/get{$tableUpCase}', '{$tableUpCase}Controller::get$tableUpCase');
        EOF;

        echo $route;
    }

    /**
     * Generate Model
     */
    public function generateModel($table) {

        $tableUpCase = ucfirst($table);
        $query = $this->db->getFieldNames($table);

        $fields = "";

        foreach ($query as $field) {
            $fields .= "'" . $field . "'" . ",";
        }


        $fields = substr($fields, 0, strlen($fields) - 1);

        $nombreClase = ucfirst($table) . "Model";

        $model = <<<EOF
        <?php
        namespace App\Models;
        use CodeIgniter\Model;
        class $nombreClase extends Model{
            protected \$table      = '$table';
            protected \$primaryKey = 'id';
            protected \$useAutoIncrement = true;
            protected \$returnType     = 'array';
            protected \$useSoftDeletes = true;
            protected \$allowedFields = [$fields];
            protected \$useTimestamps = true;
            protected \$createdField  = 'created_at';
            protected \$deletedField  = 'deleted_at';
            protected \$validationRules    =  [
            ];
            protected \$validationMessages = [];
            protected \$skipValidation     = false;
        }
                
        EOF;

        file_put_contents(ROOTPATH . "app/Models/$nombreClase.php", $model);
    }

    /**
     * Generate Controller
     * 
     */

    /**
     * Generate Controller
     */
    public function generateController($table) {

        $query = $this->db->getFieldNames($table);

        $fields = "";

        foreach ($query as $field) {
            $fields .= $field . ",";
        }


        $fields = substr($fields, 0, strlen($fields) - 1);

        $nameClassModel = ucfirst($table) . "Model";

        $tableUpCase = ucfirst($table);

        $controller = <<<EOF
        <?php
         namespace App\Controllers;
         use App\Controllers\BaseController;
         use \App\Models\{$nameClassModel};
         use App\Models\LogModel;
         use CodeIgniter\API\ResponseTrait;
         class {$tableUpCase}Controller extends BaseController {
             use ResponseTrait;
             protected \$log;
             protected $$table;
             public function __construct() {
                 \$this->$table = new $nameClassModel();
                 \$this->log = new LogModel();
                 helper('menu');
             }
             public function index() {
                 if (\$this->request->isAJAX()) {
                     \$datos = \$this->{$table}->select('$fields')->where('deleted_at', null);
                     return \Hermawan\DataTables\DataTable::of(\$datos)->toJson(true);
                 }
                 \$titulos["title"] = lang('$table.title');
                 \$titulos["subtitle"] = lang('$table.subtitle');
                 return view('$table', \$titulos);
             }
             /**
              * Read $tableUpCase
              */
             public function get$tableUpCase() {
                 \$id$tableUpCase = \$this->request->getPost("id$tableUpCase");
                 \$datos$tableUpCase = \$this->{$table}->find(\$id$tableUpCase);
                 echo json_encode(\$datos$tableUpCase);
             }
             /**
              * Save or update $tableUpCase
              */
             public function save() {
                 helper('auth');
                 \$userName = user()->username;
                 \$idUser = user()->id;
                 \$datos = \$this->request->getPost();
                 if (\$datos["id$tableUpCase"] == 0) {
                     try {
                         if (\$this->{$table}->save(\$datos) === false) {
                             \$errores = \$this->{$table}->errors();
                             foreach (\$errores as \$field => \$error) {
                                 echo \$error . " ";
                             }
                             return;
                         }
                         \$dateLog["description"] = lang("vehicles.logDescription") . json_encode(\$datos);
                         \$dateLog["user"] = \$userName;
                         \$this->log->save(\$dateLog);
                         echo "Guardado Correctamente";
                     } catch (\PHPUnit\Framework\Exception \$ex) {
                         echo "Error al guardar " . \$ex->getMessage();
                     }
                 } else {
                     if (\$this->{$table}->update(\$datos["id$tableUpCase"], \$datos) == false) {
                         \$errores = \$this->{$table}->errors();
                         foreach (\$errores as \$field => \$error) {
                             echo \$error . " ";
                         }
                         return;
                     } else {
                         \$dateLog["description"] = lang("$table.logUpdated") . json_encode(\$datos);
                         \$dateLog["user"] = \$userName;
                         \$this->log->save(\$dateLog);
                         echo "Actualizado Correctamente";
                         return;
                     }
                 }
                 return;
             }
             /**
              * Delete $tableUpCase
              * @param type \$id
              * @return type
              */
             public function delete(\$id) {
                 \$info$tableUpCase = \$this->{$table}->find(\$id);
                 helper('auth');
                 \$userName = user()->username;
                 if (!\$found = \$this->{$table}->delete(\$id)) {
                     return \$this->failNotFound(lang('$table.msg.msg_get_fail'));
                 }
                 \$this->{$table}->purgeDeleted();
                 \$logData["description"] = lang("{$table}.logDeleted") . json_encode(\$info$tableUpCase);
                 \$logData["user"] = \$userName;
                 \$this->log->save(\$logData);
                 return \$this->respondDeleted(\$found, lang('$table.msg_delete'));
             }
         }
                
        EOF;

        file_put_contents(ROOTPATH . "app/Controllers/{$tableUpCase}Controller.php", $controller);
    }

    /**
     * Generate View
     */
    public function generateView($table) {

        $tableUpCase = ucfirst($table);
        $query = $this->db->getFieldNames($table);

        $fields = "";

        $columnaDatatable = "";
        $datosDatatable = "";
        $variablesGuardar1 = "";
        $variablesFormData = "";
        $inputsEdit = "";
        $contador = 0;
        foreach ($query as $field) {
            $fields .= $field . ",";

            if ($contador > 0) {

                $columnaDatatable .= "<th><?= lang('$table.fields.$field') ?></th>" . PHP_EOL;

                $datosDatatable .= <<<EOF
                         
                        {
                            'data': '$field'
                        },
                        EOF . PHP_EOL;
                if ($field <> "created_at" && $field <> "updated_at" && $field <> "deleted_at") {
                    $variablesGuardar1 .= "var $field = $(\"#$field\").val();" . PHP_EOL;
                    $variablesFormData .= "datos.append(\"$field\", $field);" . PHP_EOL;
                    $inputsEdit .= "$(\"#$field\").val(respuesta[\"$field\"]);" . PHP_EOL;
                    ;
                }
            } else {
                $variablesGuardar1 .= <<<EOF
                    
                    var id$tableUpCase = $("#id$tableUpCase").val();
                    EOF . PHP_EOL;

                $variablesFormData .= <<<EOF
                        var datos = new FormData();
                        datos.append("id$tableUpCase", id$tableUpCase);
                        EOF . PHP_EOL;
            }
            $contador++;
        }
        $fields = substr($fields, 0, strlen($fields) - 1);
        $nameClassModel = ucfirst($table) . "Model";
        $view = <<<EOF
        <?= \$this->include('julio101290\boilerplate\Views\load\select2') ?>
        <?= \$this->include('julio101290\boilerplate\Views\load\datatables') ?>
        <?= \$this->include('julio101290\boilerplate\Views\load\\nestable') ?>
        <!-- Extend from layout index -->
        <?= \$this->extend('julio101290\boilerplate\Views\layout\index') ?>

        <!-- Section content -->
        <?= \$this->section('content') ?>

        <?= \$this->include('modules$tableUpCase/modalCapture$tableUpCase') ?>

        <!-- SELECT2 EXAMPLE -->
        <div class="card card-default">
         <div class="card-header">
             <div class="float-right">
                 <div class="btn-group">

                     <button class="btn btn-primary btnAdd$tableUpCase" data-toggle="modal" data-target="#modalAdd$tableUpCase"><i class="fa fa-plus"></i>

                         <?= lang('$table.add') ?>

                     </button>

                 </div>
             </div>
         </div>
         <div class="card-body">
             <div class="row">
                 <div class="col-md-12">
                     <div class="table-responsive">
                         <table id="table$tableUpCase" class="table table-striped table-hover va-middle table$tableUpCase">
                             <thead>
                                 <tr>

                                     <th>#</th>
                                     $columnaDatatable
                                     <th><?= lang('$table.fields.actions') ?> </th>

                                 </tr>
                             </thead>
                             <tbody>
                             </tbody>
                         </table>
                     </div>
                 </div>
             </div>
         </div>
        </div>
        <!-- /.card -->

        <?= \$this->endSection() ?>


        <?= \$this->section('js') ?>
        <script>

         /**
          * Cargamos la tabla
          */

         var table{$tableUpCase} = $('#table{$tableUpCase}').DataTable({
             processing: true,
             serverSide: true,
             responsive: true,
             autoWidth: false,
             order: [[1, 'asc']],

             ajax: {
                 url: '<?= base_url(route_to('admin/$table')) ?>',
                 method: 'GET',
                 dataType: "json"
             },
             columnDefs: [{
                     orderable: false,
                     targets: [$contador],
                     searchable: false,
                     targets: [$contador]

                 }],
             columns: [{
                     'data': 'id'
                 },
                
                 $datosDatatable
                 {
                     "data": function (data) {
                         return `<td class="text-right py-0 align-middle">
                                 <div class="btn-group btn-group-sm">
                                     <button class="btn btn-warning btnEdit$tableUpCase" data-toggle="modal" id$tableUpCase="\${data.id}" data-target="#modalAdd$tableUpCase">  <i class=" fa fa-edit"></i></button>
                                     <button class="btn btn-danger btn-delete" data-id="\${data.id}"><i class="fas fa-trash"></i></button>
                                 </div>
                                 </td>`
                     }
                 }
             ]
         });



         $(document).on('click', '#btnSave$tableUpCase', function (e) {

             $variablesGuardar1
             $("#btnSave$tableUpCase").attr("disabled", true);

             $variablesFormData

             $.ajax({

                 url: "<?= route_to('admin/$table/save') ?>",
                 method: "POST",
                 data: datos,
                 cache: false,
                 contentType: false,
                 processData: false,
                 success: function (respuesta) {
                     if (respuesta.match(/Correctamente.*/)) {
                
                         Toast.fire({
                             icon: 'success',
                             title: "Guardado Correctamente"
                         });

                         table$tableUpCase.ajax.reload();
                         $("#btnSave$tableUpCase").removeAttr("disabled");


                         $('#modalAdd$tableUpCase').modal('hide');
                     } else {

                         Toast.fire({
                             icon: 'error',
                             title: respuesta
                         });

                         $("#btnSave$tableUpCase").removeAttr("disabled");
                        

                     }

                 }

             }

             )

         });



         /**
          * Carga datos actualizar
          */


         /*=============================================
          EDITAR $tableUpCase
          =============================================*/
         $(".table$tableUpCase").on("click", ".btnEdit$tableUpCase", function () {

             var id$tableUpCase = $(this).attr("id$tableUpCase");
                
             var datos = new FormData();
             datos.append("id$tableUpCase", id$tableUpCase);

             $.ajax({

                 url: "<?= base_url(route_to('admin/$table/get$tableUpCase')) ?>",
                 method: "POST",
                 data: datos,
                 cache: false,
                 contentType: false,
                 processData: false,
                 dataType: "json",
                 success: function (respuesta) {
                     $("#id$tableUpCase").val(respuesta["id"]);
                     
                     $inputsEdit

                 }

             })

         })


         /*=============================================
          ELIMINAR $table
          =============================================*/
         $(".table$tableUpCase").on("click", ".btn-delete", function () {

             var id$tableUpCase = $(this).attr("data-id");

             Swal.fire({
                 title: '<?= lang('boilerplate.global.sweet.title') ?>',
                 text: "<?= lang('boilerplate.global.sweet.text') ?>",
                 icon: 'warning',
                 showCancelButton: true,
                 confirmButtonColor: '#3085d6',
                 cancelButtonColor: '#d33',
                 confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
             })
                     .then((result) => {
                         if (result.value) {
                             $.ajax({
                                 url: `<?= base_url(route_to('admin/$table')) ?>/` + id$tableUpCase,
                                 method: 'DELETE',
                             }).done((data, textStatus, jqXHR) => {
                                 Toast.fire({
                                     icon: 'success',
                                     title: jqXHR.statusText,
                                 });


                                 table$tableUpCase.ajax.reload();
                             }).fail((error) => {
                                 Toast.fire({
                                     icon: 'error',
                                     title: error.responseJSON.messages.error,
                                 });
                             })
                         }
                     })
         })

         $(function () {
            $("#modalAdd$tableUpCase").draggable();
            
        });


        </script>
        <?= \$this->endSection() ?>
                
        EOF;

        file_put_contents(ROOTPATH . "app/Views/{$table}.php", $view);
    }

    /**
     * Generate ViewModal
     */
    public function generateViewModal($table) {

        $tableUpCase = ucfirst($table);
        $query = $this->db->getFieldNames($table);

        $fields = "";

        $campos = "";
        $contador = 0;
        foreach ($query as $field) {
            $fields .= $field . ",";

            if ($contador > 0) {




                if ($field <> "created_at" && $field <> "updated_at" && $field <> "deleted_at") {

                    $campos .= <<<EOF
                        <div class="form-group row">
                            <label for="$field" class="col-sm-2 col-form-label"><?= lang('$table.fields.$field') ?></label>
                            <div class="col-sm-10">
                                <div class="input-group">
                                    <div class="input-group-prepend">
                                        <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                    </div>
                                    <input type="text" name="$field" id="$field" class="form-control <?= session('error.$field') ? 'is-invalid' : '' ?>" value="<?= old('$field') ?>" placeholder="<?= lang('$table.fields.$field') ?>" autocomplete="off">
                                </div>
                            </div>
                        </div>
                        EOF . PHP_EOL;
                }
            }




            $contador++;
        }


        $fields = substr($fields, 0, strlen($fields) - 1);

        $nameClassModel = ucfirst($table) . "Model";

        $viewModal = <<<EOF
        <!-- Modal $tableUpCase -->
          <div class="modal fade" id="modalAdd$tableUpCase" tabindex="-1" role="dialog" aria-labelledby="modalAdd$tableUpCase" aria-hidden="true">
              <div class="modal-dialog modal-lg" role="document">
                  <div class="modal-content">
                      <div class="modal-header">
                          <h5 class="modal-title"><?= lang('$table.createEdit') ?></h5>
                          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                              <span aria-hidden="true">&times;</span>
                          </button>
                      </div>
                      <div class="modal-body">
                          <form id="form-$table" class="form-horizontal">
                              <input type="hidden" id="id$tableUpCase" name="id$tableUpCase" value="0">

                              $campos
                
                          </form>
                      </div>
                      <div class="modal-footer">
                          <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                          <button type="button" class="btn btn-primary btn-sm" id="btnSave$tableUpCase"><?= lang('boilerplate.global.save') ?></button>
                      </div>
                  </div>
              </div>
          </div>

          <?= \$this->section('js') ?>


          <script>

              $(document).on('click', '.btnAdd$tableUpCase', function (e) {


                  $(".form-control").val("");

                  $("#id$tableUpCase").val("0");

                  $("#btnSave$tableUpCase").removeAttr("disabled");

              });

              /* 
               * AL hacer click al editar
               */



              $(document).on('click', '.btnEdit$tableUpCase', function (e) {


                  var id$tableUpCase = $(this).attr("id$tableUpCase");

                  //LIMPIAMOS CONTROLES
                  $(".form-control").val("");

                  $("#id$tableUpCase").val(id$tableUpCase);
                  $("#btnGuardar$tableUpCase").removeAttr("disabled");

              });




          </script>


          <?= \$this->endSection() ?>
                
        EOF;

        $path = ROOTPATH . "app/Views/modules$tableUpCase/";
        if (!is_dir($path))
            mkdir($path, 0777, TRUE);

        file_put_contents($path . "modalCapture{$tableUpCase}.php", $viewModal);
    }

    /**
     * Generate Languaje EN
     */
    public function generateLanguage($table) {

        $tableUpCase = ucfirst($table);
        $query = $this->db->getFieldNames($table);

        $fields = "";

        $campos = "";
        $contador = 0;
        foreach ($query as $field) {
            $fields .= $field . ",";

            if ($contador > 0) {






                $campos .= "\${$table}[\"fields\"][\"$field\"] = \"" . ucfirst($field) . "\";" . PHP_EOL;
            }




            $contador++;
        }


        $fields = substr($fields, 0, strlen($fields) - 1);

        $nameClassModel = ucfirst($table) . "Model";

        $languaje = <<<EOF
         <?php

        \${$table}["logDescription"] = "The $table was saved with the following data:";
        \${$table}["logUpdate"] = "The $table was updated  with the following data:";
        \${$table}["logDeleted"] = "The $table was deleted  with the following data:";
        \${$table}["msg_delete"] = "The $table was deleted  correctly:";

        \${$table}["add"] = "Add $tableUpCase";
        \${$table}["edit"] = "Edit $table";
        \${$table}["createEdit"] = "Create / Edit";
        \${$table}["title"] = "$table management";
        \${$table}["subtitle"] = "$table list";
        $campos
        \${$table}["fields"]["actions"] = "Actions";
        \${$table}["msg"]["msg_insert"] = "The $table has been correctly added.";
        \${$table}["msg"]["msg_update"] = "The $table has been correctly modified.";
        \${$table}["msg"]["msg_delete"] = "The $table has been correctly deleted.";
        \${$table}["msg"]["msg_get"] = "The $tableUpCase has been successfully get.";
        \${$table}["msg"]["msg_get_fail"] = "The $table not found or already deleted.";

        return $$table;
                
        EOF;

        $path = ROOTPATH . "app/Language/en/";
        if (!is_dir($path))
            mkdir($path, 0777, TRUE);

        file_put_contents($path . "$table.php", $languaje);
    }

    /**
     * Lenguaje ES
     * @param type $table
     */
    public function generateLanguageES($table) {

        $tableUpCase = ucfirst($table);
        $query = $this->db->getFieldNames($table);

        $fields = "";

        $campos = "";
        $contador = 0;
        foreach ($query as $field) {
            $fields .= $field . ",";

            if ($contador > 0) {


                $campos .= "\${$table}[\"fields\"][\"$field\"] = \"" . ucfirst($field) . "\";" . PHP_EOL;
            }




            $contador++;
        }


        $fields = substr($fields, 0, strlen($fields) - 1);

        $nameClassModel = ucfirst($table) . "Model";

        $languaje = <<<EOF
         <?php
        \${$table}["logDescription"] = "El registro en $table fue guardado con los siguientes datos:";
        \${$table}["logUpdate"] = "El registro en $table fue actualizado con los siguientes datos:";
        \${$table}["logDeleted"] = "El registro en $table fue eliminado con los siguientes datos:";
        \${$table}["msg_delete"] = "El Registro en $table fue eliminado correctamente:";
        \${$table}["add"] = "Agregar $tableUpCase";
        \${$table}["edit"] = "Editar $table";
        \${$table}["createEdit"] = "Crear / Editar";
        \${$table}["title"] = "Admon. $table";
        \${$table}["subtitle"] = "Lista $table";
        $campos
         \${$table}["fields"]["actions"] = "Acciones";       
        \${$table}["msg"]["msg_insert"] = "Registro agregado correctamente.";
        \${$table}["msg"]["msg_update"] = "Registro modificado correctamente.";
        \${$table}["msg"]["msg_delete"] = "Registro eliminado correctamente.";
        \${$table}["msg"]["msg_get"] = "Registro obtenido correctamente.";
        \${$table}["msg"]["msg_get_fail"] = "Registro no encontrado o eliminado.";
        return $$table;
                
        EOF;

        $path = ROOTPATH . "app/Language/es/";
        if (!is_dir($path))
            mkdir($path, 0777, TRUE);

        file_put_contents($path . "$table.php", $languaje);
    }

    /**
     * Generate Migration
     * @param type $table
     */
    public function generateMigration($table) {

        $tableUpCase = ucfirst($table);
        $query = $this->db->getFieldData($table);

        $campos = "";
        $contador = 0;
        foreach ($query as $field) {


            
            if ($contador > 0) {

                
                if($field->nullable==1){
                    
                    $nullable="true";
                    
                }   else{
                    
                    $nullable="false";
                    
                } 
                if ($field->name <> "created_at" && $field->name <> "updated_at" && $field->name <> "deleted_at") {
                    $campos .= "'{$field->name}'             => ['type' => '{$field->type}', 'constraint' => {$field->max_length}, 'null' => $nullable]," . PHP_EOL;
                }
            } else {

                    $campos .= "'id'                    => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true]," . PHP_EOL;
            }



            $contador++;
        }




        $nameClassModel = ucfirst($table) . "Model";

        $migration = <<<EOF
            <?php
            namespace App\Database\Migrations;
            use CodeIgniter\Database\Migration;
            class $tableUpCase extends Migration
            {
            public function up()
            {
             // $tableUpCase
            \$this->forge->addField([
                $campos
                'created_at'       => ['type' => 'datetime', 'null' => true],
                'updated_at'       => ['type' => 'datetime', 'null' => true],
                'deleted_at'       => ['type' => 'datetime', 'null' => true],
            ]);
            \$this->forge->addKey('id', true);
            \$this->forge->createTable('$table', true);
            }
            public function down(){
                \$this->forge->dropTable('$table', true);
                }
            }
        EOF;

        $path = ROOTPATH . "app/Database/Migrations/";
        if (!is_dir($path))
            mkdir($path, 0777, TRUE);

        $fechaActual = fechaParaMigraciones(fechaHoraActual());

        file_put_contents($path . "{$fechaActual}_{$tableUpCase}.php", $migration);

        //file_put_contents($path . "2023-02-07-165411_$tableUpCase.php", $viewModal);
    }
    
    

}

Creamos el archivo app/helpers/utilerias_helper.php con el siguiente codigo

<?php

function strMenuActivo($strMenu1, $strMenu2) {
    if ($strMenu1 == $strMenu2) {
        $respuesta = 'class="active"';
    } else {
        $respuesta = "";
    }
    return $respuesta;
}

//SI LA VARIABLE ESTA VACIA O NO SETA DECLARADA MANDARA CERO SIEMPRE, ES COMO EL VAL DE VISUAL BASIC 6.0

function esCero($value) {

    if (empty($value)) {
        return "0";
    } else {
        return $value;
    }
}

//FECHA SQL PARA GUARDAR EN BASE DE DATOS

function fechaSQL($fecha) {

    return date("Ymd", strtotime($fecha));
}

// CONVIERTE FECHA MYSQLDATETIME A HTML5
function fechaMySQLADateTimeHTML5($fecha) {

    return date("Y-m-d", strtotime($fecha)) . "T" . date("H:i:s", strtotime($fecha));
}

function agregarMinutos($fecha, $minutos) {


    return date("Y/m/d h:i:s", strtotime($fecha . "+ $minutos minutes"));

    /*
      $fecha1= new DateTime($fecha);
      $fecha1->add(new DateInterval('PT10H30S'));
      return $date->format('Y-m-d H:i:s') . "\n";
     * 
     */
}

//CONVIERTE LA FECHA EN PERIODO
function fechaPeriodo($fecha) {

    return date("Ym", strtotime($fecha));
}

//OBTIENE FECHA ACTUAL
function fechaActual() {

    return date("Y/m/d");
}

//OBTIENE FECHA HORA ACTUAL

function fechaHoraActual() {

    return date("Y-m-d H:i:s ", time());
}

//DIFERENCIA ENTRE MINUTOS
function diferenciaMinutos($fecha_i, $fecha_f) {
    $minutos = (strtotime($fecha_i) - strtotime($fecha_f)) / 60;

    $minutos = abs($minutos);
    $minutos = floor($minutos);

    return $minutos;
}

function strSellar($llave, $password, $cadenaOriginal) {


    $archivoPem = "/tmp/llave.key.pem";
    $comando = "openssl pkcs8 -inform DER -in $llave -passin pass:$password -out $archivoPem";

    exec($comando);
    $sello = "ok";

    //Sellar
    $archivo = openssl_pkey_get_private(file_get_contents($archivoPem));
    $sig = "";
    openssl_sign($cadenaOriginal, $sig, $archivo, OPENSSL_ALGO_SHA256);

    $sello = base64_encode($sig);

    return $sello;
}

//SOLO DIA
function dia($fecha) {

    return date("d", strtotime($fecha));
}

//SOLO MES
function mes($fecha) {

    return date("m", strtotime($fecha));
}

//SOLO AÑO
function año($fecha) {

    return date("Y", strtotime($fecha));
}

//GENERA UUID
function generaUUID() {


    $uuid = service('uuid');
    $uuid4 = $uuid->uuid4();
    $string = $uuid4->toString();

    return $string;
}

function satinizar($var, $type) {
    switch ($type) {
        case 'html':
            $safe = htmlspecialchars($var);
            break;
        case 'sql':
            $safe = mysql_real_escape_string($var);
            break;
        case 'file':
            $safe = preg_replace('/(\/|-|_)/', '', $var);
            break;
        case 'shell':
            $safe = escapeshellcmd($var);
            break;
        default:
            $safe = htmlspecialchars($var);
    }
    return $safe;
}

function limpiaCadena($cadena) {

    $cadena = str_replace('"', "", $cadena);
    $cadena = str_replace('\n', "", $cadena);
    $cadena = str_replace('\t', "", $cadena);
    $cadena = trim($cadena);

    $cadena = preg_replace("[\n|\r|\n\r]", "", $cadena);

    $descripcion = preg_replace("[\n|\r|\n\r]", "", $descripcion);

    return $cadena;
}


// CONVIERTE FECHA MYSQLDATETIME A HTML5
function fechaParaMigraciones($fecha) {

    return date("Y-m-d", strtotime($fecha)) . date("His", strtotime($fecha));
    
}

En rutas ponemos el siguiente codigo

$routes->get('generateCRUD/(:any)', 'AutoCrudController::index/$1');

Creamos la tabla de categorías

-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 21-04-2023 a las 20:19:25
-- Versión del servidor: 10.4.17-MariaDB
-- Versión de PHP: 7.4.15

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Base de datos: `ci4jcpos`
--

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `categorias`
--

CREATE TABLE `categorias` (
  `id` int(11) NOT NULL,
  `descripcion` varchar(128) COLLATE utf8mb4_spanish2_ci DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish2_ci;

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `categorias`
--
ALTER TABLE `categorias`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `categorias`
--
ALTER TABLE `categorias`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Corremos la siguiente URL en el navegador para crear todo el catalogo

http://localhost:8080/admin/generateCRUD/categorias

Esto nos generara los archivos necesarios y ademas la ruta para agregarlo en routes.php


         $routes->resource('categorias', [
                        'filter' => 'permission:categorias-permission',
                        'controller' => 'categoriasController',
                        'except' => 'show'
                    ]);
        $routes->post('categorias/save', 'CategoriasController::save');
        $routes->post('categorias/getCategorias', 'CategoriasController::getCategorias');
Ya solo hacemos los cambios estéticos necesarios, agregamos los permisos necesarios corregimos la ruta en el menú y listo
Video Demostrativo

Creando catalogo de Empresas #03

Ya tenemos nuestros menús creados ahora sigue meter los catálogos de configuración, el primero seria el catalogo de empresas en el cual se van a capturar los datos de la empresa como nombre, razón social, domicilio logo y datos de facturación del SAT, estos datos saldrán en las impresiones de cotizaciones, ventas y reportes.

Primero para hacer los datatables mas fácil instalamos el paquete de hermawan/codeigniter4-datatables esto solo lo haremos una vez y nos servira para los demás catálogos para instalarlo corremos el siguiente código en nuestro proyecto

composer require hermawan/codeigniter4-datatables

Luego en app/config/autoload.php en la variable $psr4 agregamos el namespace

    public $psr4 = [
        APP_NAMESPACE => APPPATH, // For custom app namespace
        'Config'      => APPPATH . 'Config',
        'Hermawan\DataTables'   => APPPATH .'ThirdParty/codeigniter4-datatables/src', // <-- namespace for this library
    ];

Ahora para obtener todos los catálogos del SAT instalamos el paquete de phpcfdi/sat-catalogos

composer require phpcfdi/sat-catalogos

El archivo de app/controller/BaseController.php quedaria de la siguiente forma para poder utilizar los catalogos del SAT en todo momento.

<?php

namespace App\Controllers;

use CodeIgniter\Controller;
use CodeIgniter\HTTP\CLIRequest;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
use PhpCfdi\SatCatalogos\Factory;

/**
 * Class BaseController
 *
 * BaseController provides a convenient place for loading components
 * and performing functions that are needed by all your controllers.
 * Extend this class in any new controllers:
 *     class Home extends BaseController
 *
 * For security be sure to declare any new methods as protected or private.
 */


abstract class BaseController extends Controller
{
    /**
     * Instance of the main Request object.
     *
     * @var CLIRequest|IncomingRequest
     */
    protected $request;

    /**
     * An array of helpers to be loaded automatically upon
     * class instantiation. These helpers will be available
     * to all other controllers that extend BaseController.
     *
     * @var array
     */
    protected $helpers = [];
    
    
    public $catalogosSAT;

    /**
     * Constructor.
     */
    public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
    {
        // Do Not Edit This Line
        parent::initController($request, $response, $logger);

        // Preload any models, libraries, etc, here.

        // E.g.: $this->session = \Config\Services::session();
        
          
        $dsn = "sqlite:".ROOTPATH."writable\database\catalogossat.db";
        $factory = new Factory();
        $satCatalogos = $factory->catalogosFromDsn($dsn);
        $this->catalogosSAT = $satCatalogos;
        
        
    }
}

Si se dan cuenta nosotros tenemos un archivo en writable/database/catalogossat.db para generar el archivo de base de datos SQLITE con los datos que proporciona el SAT tenemos el siguiente script en PHP, cortesía de nuestro amigo Rene

<?php

if(file_exists ( 'sat.db' )){
	
	unlink('sat.db' );
	
}
$sat = new SQLite3('sat.db');

$list = "https://raw.githubusercontent.com/phpcfdi/resources-sat-catalogs/master/database/tables.list";
$schema = "https://raw.githubusercontent.com/phpcfdi/resources-sat-catalogs/master/database/schemas/";
$data = "https://raw.githubusercontent.com/phpcfdi/resources-sat-catalogs/master/database/data/";

$listaText = file_get_contents($list);
$lines = array_filter(array_map('trim',explode("\n", $listaText)));

foreach ($lines as $line) {
    echo "\x1b[33m" . $line . "\x1b[0m \n";

    $schemaText = file_get_contents($schema . $line . ".sql");
    echo $schemaText . "\n";
    $sat->exec($schemaText);

    $dataText = file_get_contents($data . $line . ".sql");
    $dataLines = array_filter(array_map('trim',explode("\n", $dataText)));
    foreach ($dataLines as $dataLine) {
        echo "\x1b[2m" . $dataLine . "\x1b[0m \n";
        $sat->exec($dataLine);
    }

    echo "\x1b[32m data done\n \x1b[0m \n";
}

Ahora si los archivos del catalogo, creamos el archivo app/controller/EmpresasController.php con el siguiente contenido

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use \App\Models\EmpresasModel;
use App\Models\LogModel;
use CodeIgniter\API\ResponseTrait;

class EmpresasController extends BaseController {

    use ResponseTrait;

    protected $log;
    protected $empresas;

    public function __construct() {
        $this->empresas = new EmpresasModel();
        $this->log = new LogModel();
        helper('menu', 'filesystem');
    }

    public function index() {


        if ($this->request->isAJAX()) {




            $datos = $this->empresas->select('id,nombre,direccion,rfc,telefono,correoElectronico,'
                            . 'diasEntrega,caja,logo,certificado,archivoKey,contraCertificado,regimenFiscal,razonSocial,codigoPostal,'
                            . 'CURP,created_at,updated_at')->where('deleted_at', null);

            return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
        }



        $regimenesFiscales = $this->catalogosSAT->regimenesFiscales40()->searchByField("texto", "%%", 1000);
        


        $titulos["title"] = lang('empresas.title');
        $titulos["subtitle"] = lang('empresas.subtitle');
        $titulos["regimenesFiscales"] = $regimenesFiscales;

        //$data["data"] = $datos;
        return view('empresas', $titulos);
    }

    /**
     * Read Vehicle
     */
    public function obtenerEmpresa() {


        $idEmpresa = $this->request->getPost("idEmpresa");
        $datosEmpresa = $this->empresas->find($idEmpresa);

        echo json_encode($datosEmpresa);
    }

    /**
     * Save or update Vehicle
     */
    public function save() {


        helper('auth');
        $userName = user()->username;
        $idUser = user()->id;

        $datos = $this->request->getPost();

        unset($datos["certificado"]);
        unset($datos["archivoKey"]);
        unset($datos["logo"]);

        $certificado = $this->request->getFile('certificado');
        $archivoKey = $this->request->getFile('archivoKey');
        $logo = $this->request->getFile('logo');

        if ($certificado <> null) {
            if ($certificado->getClientExtension() <> "cer") {

                return lang("empresas.certExtensionIncorrect");
            }

            $datos["certificado"] = $datos["rfc"] . "_certificado.cer";
        }

        if ($archivoKey <> null) {
            if ($archivoKey->getClientExtension() <> "key") {

                return lang("empresas.keyFileExtensionIncorrect");
            }

            $datos["archivoKey"] = $datos["rfc"] . "_certificado.key";
        }

        if ($logo) {

            if ($logo->getClientExtension() <> "png") {

                return lang("empresas.pngFileExtensionIncorrect");
            }
            $datos["logo"] = $datos["rfc"] . "_logo.png";
        }

        if ($datos["idEmpresa"] == 0) {


            try {
                
                if ($this->empresas->save($datos) === false) {

                    $errores = $this->empresas->errors();

                    foreach ($errores as $field => $error) {

                        echo $error . " ";
                    }

                    return;
                }

                $dateLog["description"] = lang("empresas.logDescription") . json_encode($datos);
                $dateLog["user"] = $userName;

                $this->log->save($dateLog);

                if ($certificado <> null) {
                    $certificado->move(WRITEPATH . "uploads\certificates", $datos["rfc"] . "_certificado.cer");
                }

                if ($archivoKey <> null) {
                    $archivoKey->move(WRITEPATH . "uploads\certificates", $datos["rfc"] . "_certificado.key");
                }

                if ($logo <> null) {
                    $logo->move("images\logo", $datos["rfc"] . "_logo.png");
                }

                echo "Guardado Correctamente";
            } catch (\PHPUnit\Framework\Exception $ex) {


                echo "Error al guardar " . $ex->getMessage();
            }
        } else {


            $datosAnteriores = $this->empresas->find($datos["idEmpresa"]);

            if ($this->empresas->update($datos["idEmpresa"], $datos) == false) {

                $errores = $this->empresas->errors();
                foreach ($errores as $field => $error) {

                    echo $error . " ";
                }

                return;
            } else {


                if ($certificado <> null) {
                    
                    unlink(WRITEPATH . "uploads\certificates\\" . $datosAnteriores["rfc"] . "_certificado.cer");
                    $certificado->move(WRITEPATH . "uploads\certificates", $datos["rfc"] . "_certificado.cer");
                }

                if ($archivoKey <> null) {
                    unlink(WRITEPATH . "uploads\certificates\\" . $datosAnteriores["rfc"] . "_certificado.key");
                    $archivoKey->move(WRITEPATH . "uploads\certificates", $datos["rfc"] . "_certificado.key");
                }

                if ($logo <> null) {
                    unlink("images\logo\\" . $datosAnteriores["rfc"] . "_logo.png");
                    $logo->move("images\logo", $datos["rfc"] . "_logo.png");
                }

                $dateLog["description"] = lang("empresas.logUpdated") . json_encode($datosAnteriores);
                $dateLog["user"] = $userName;

                $this->log->save($dateLog);
                echo "Actualizado Correctamente";

                return;
            }
        }

        return;
    }

    /**
     * Delete Empresas
     * @param type $id
     * @return type
     */
    public function delete($id) {

        $infoEmpresa = $this->empresas->find($id);
        helper('auth');
        $userName = user()->username;

        if (!$found = $this->empresas->delete($id)) {
            return $this->failNotFound(lang('empresas.msg.msg_get_fail'));
        }

        $logData["description"] = lang("empresas.logDeleted") . json_encode($infoEmpresa);
        $logData["user"] = $userName;

        if(file_exists(WRITEPATH . "uploads\certificates\\" . $infoEmpresa["rfc"] . "_certificado.cer")){

            unlink(WRITEPATH . "uploads\certificates\\" . $infoEmpresa["rfc"] . "_certificado.cer");

        }

        if(file_exists(WRITEPATH . "uploads\certificates\\" . $infoEmpresa["rfc"] . "_certificado.key")){
            
            unlink(WRITEPATH . "uploads\certificates\\" . $infoEmpresa["rfc"] . "_certificado.key");

        }
        
        if(file_exists("images\logo\\" . $infoEmpresa["rfc"] . "_logo.png")){

            unlink("images\logo\\" . $infoEmpresa["rfc"] . "_logo.png");

        }

        $this->empresas->purgeDeleted();

        $this->log->save($logData);
        return $this->respondDeleted($found, lang('empresas.msg_delete'));
    }

}

Para crear el modelo creamos el archivo app/models/EmpresasModel.php con el siguiente contenido

<?php

namespace App\Models;

use CodeIgniter\Model;

/**
 * @method User|null first()
 */
class EmpresasModel extends Model {

    protected $table = 'empresas';
    protected $primaryKey = 'id';
    protected $useSoftDeletes = true;
    protected $allowedFields = [
        'id', 'nombre', 'direccion', 'rfc', 'telefono', 'correoElectronico', 'diasEntrega',
        'caja', 'logo', 'certificado', 'archivoKey', 'contraCertificado', 'regimenFiscal', 'razonSocial', 'CURP',
        'codigoPostal', 'created_at ', 'updated_at ', 'deleted_at'
    ];
    protected $useTimestamps = true;
    protected $validationRules = [
        'correoElectronico' => 'required|valid_email',
        'razonSocial ' => 'required|alpha_numeric_punct|min_length[3]|is_unique[empresas.razonSocial]',
        'rfc ' => 'is_unique[empresas.rfc]',
    ];
    protected $validationMessages = [];
    protected $skipValidation = false;

}

Ahora los archivos de la vista, creamos el archivo principal de la vista en app/views/empresas.php con el siguiente código

<?= $this->include('julio101290\boilerplate\Views\load\select2') ?>
<?= $this->include('julio101290\boilerplate\Views\load\datatables') ?>
<?= $this->include('julio101290\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('julio101290\boilerplate\Views\layout\index') ?>

<!-- Section content -->
<?= $this->section('content') ?>

<?= $this->include('modulosEmpresas/modalCapturaEmpresas') ?>

<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="float-right">
            <div class="btn-group">

                <button class="btn btn-primary btnAddEmpresa" data-toggle="modal" data-target="#modalAddEmpresa"><i class="fa fa-plus"></i>

                    <?= lang('empresas.add') ?>

                </button>

            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tableEmpresas" class="table table-striped table-hover va-middle tableEmpresas">
                        <thead>
                            <tr>



                                <th>#</th>
                                <th><?= lang('empresas.fields.nombre') ?></th>
                                <th><?= lang('empresas.fields.direccion') ?></th>
                                <th><?= lang('empresas.fields.rfc') ?></th>
                                <th><?= lang('empresas.fields.logo') ?></th>
                                <th><?= lang('empresas.fields.regimenFiscal') ?></th>
                                <th><?= lang('empresas.fields.razonSocial') ?></th>
                                <th><?= lang('empresas.fields.codigoPostal') ?></th>
                                <th><?= lang('empresas.fields.CURP') ?></th>
                                <th><?= lang('empresas.fields.Created_at') ?></th>
                                <th><?= lang('empresas.fields.Update_At') ?></th>
                                <th><?= lang('empresas.fields.acciones') ?> </th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->

<?= $this->endSection() ?>




<?= $this->section('js') ?>
<script>

    /**
     * Cargamos la tabla
     */

    var tableEmpresas = $('#tableEmpresas').DataTable({
        processing: true,
        serverSide: true,
        responsive: true,
        autoWidth: false,
        order: [[1, 'asc']],

        ajax: {
            url: '<?= base_url(route_to('admin/empresas')) ?>',
            method: 'GET',
            dataType: "json"
        },
        columnDefs: [{
                orderable: false,
                targets: [12],
                searchable: false,
                targets: [12]

            }],
        columns: [{
                'data': 'id'
            },
            {
                'data': 'nombre'
            },

            {
                'data': 'direccion'
            },

            {
                'data': 'rfc'
            },

            {
                'data': 'logo'
            },

            {
                'data': 'regimenFiscal'
            },

            {
                'data': 'razonSocial'
            },

            {
                'data': 'codigoPostal'
            },

            {
                'data': 'CURP'
            },

            {
                'data': 'created_at'
            },

            {
                'data': 'updated_at'
            },

            {
                'data': 'CURP'
            },

            {
                "data": function (data) {
                    return `<td class="text-right py-0 align-middle">
                            <div class="btn-group btn-group-sm">
                                <button class="btn btn-warning btnEditEmpresa" data-toggle="modal" idEmpresa="${data.id}" data-target="#modalAddEmpresa">  <i class=" fa fa-edit"></i></button>
                                <button class="btn btn-danger btn-delete" data-id="${data.id}"><i class="fas fa-trash"></i></button>
                            </div>
                            </td>`
                }
            }
        ]
    });



    $(document).on('click', '#btnSaveEmpresa', function (e) {



        var idEmpresa = $("#idEmpresa").val();
        var nombre = $("#nombre").val();
        var direccion = $("#direccion").val();
        var telefono = $("#telefono").val();
        var correoElectronico = $("#correoElectronico").val();
        var razonSocial = $("#razonSocial").val();

        var rfc = $("#rfc").val();
        var CURP = $("#CURP").val();
        var regimenFiscal = $("#regimenFiscal").val();
        var codigoPostal = $("#codigoPostal").val();

        var certificado = $("#certificado").prop("files")[0];
        var archivoKey = $("#archivoKey").prop("files")[0];
        var contraCertificado = $("#contraCertificado").val();
        var logo = $("#logo").prop("files")[0];



        $("#btnSaveEmpresa").attr("disabled", true);

        var datos = new FormData();
        datos.append("idEmpresa", idEmpresa);
        datos.append("nombre", nombre);
        datos.append("direccion", direccion);
        datos.append("telefono", telefono);
        datos.append("correoElectronico", correoElectronico);
        datos.append("razonSocial", razonSocial);
        datos.append("codigoPostal", codigoPostal);

        datos.append("rfc", rfc);
        datos.append("CURP", CURP);
        datos.append("regimenFiscal", regimenFiscal);
        datos.append("certificado", certificado);
        datos.append("archivoKey", archivoKey);

        datos.append("contraCertificado", contraCertificado);
        datos.append("logo", logo);

        $.ajax({

            url: "<?= route_to('admin/empresas/save') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            //dataType:"json",
            success: function (respuesta) {


                if (respuesta.match(/Correctamente.*/)) {


                    Toast.fire({
                        icon: 'success',
                        title: "<?= lang('empresas..msg.msg_save') ?>"
                    });


                    tableEmpresas.ajax.reload();
                    $("#btnSaveEmpresa").removeAttr("disabled");


                    $('#modalAddEmpresa').modal('hide');
                } else {

                    Toast.fire({
                        icon: 'error',
                        title: respuesta
                    });

                    $("#btnSaveEmpresa").removeAttr("disabled");
                    //  $('#modalAgregarPaciente').modal('hide');

                }

            }

        }

        )




    });



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR PACIENTE
     =============================================*/
    $(".tableEmpresas").on("click", ".btnEditEmpresa", function () {

        var idEmpresa = $(this).attr("idEmpresa");



        var datos = new FormData();
        datos.append("idEmpresa", idEmpresa);

        $.ajax({

            url: "<?= base_url('admin/empresas/obtenerEmpresa') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            dataType: "json",
            success: function (respuesta) {
                console.log(respuesta);
                $("#idEmpresa").val(respuesta["id"]);
                $("#nombre").val(respuesta["nombre"]);
                $("#direccion").val(respuesta["direccion"]);
                $("#rfc").val(respuesta["rfc"]);
                $("#telefono").val(respuesta["telefono"]);
                $("#diasEntrega").val(respuesta["diasEntrega"]);
                $("#caja").val(respuesta["caja"]);
                $("#contraCertificado").val(respuesta["contraCertificado"]);
                $("#regimenFiscal").val(respuesta["regimenFiscal"]);
                $("#regimenFiscal").trigger("change");
                $("#razonSocial ").val(respuesta["razonSocial"]);
                $("#codigoPostal").val(respuesta["codigoPostal"]);
                $("#CURP").val(respuesta["CURP"]);
                $("#correoElectronico").val(respuesta["correoElectronico"]);

                if (respuesta["logo"] != "") {


                    $(".previsualizarLogo").attr('src', '<?= base_URL("images/logo") ?>' + '/' + respuesta["logo"]);

                }


            }

        })

    })


    /*=============================================
     ELIMINAR PACIENTE
     =============================================*/
    $(".tableEmpresas").on("click", ".btn-delete", function () {

        var idEmpresa = $(this).attr("data-id");


        console.log("eliminar");

        Swal.fire({
            title: '<?= lang('boilerplate.global.sweet.title') ?>',
            text: "<?= lang('boilerplate.global.sweet.text') ?>",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
        })
                .then((result) => {
                    if (result.value) {
                        $.ajax({
                            url: `<?= base_url('admin/empresas') ?>/` + idEmpresa,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });


                            tableEmpresas.ajax.reload();
                        }).fail((error) => {
                            Toast.fire({
                                icon: 'error',
                                title: error.responseJSON.messages.error,
                            });
                        })
                    }
                })
    });


    $('#regimenFiscal').select2();


    /*=============================================
     SUBIENDO LA FOTO DEL USUARIO
     =============================================*/
    $(".logo").change(function () {

        var imagen = this.files[0];

        /*=============================================
         VALIDAMOS EL FORMATO DE LA IMAGEN SEA JPG O PNG
         =============================================*/

        if (imagen["type"] != "image/png") {

            $(".logo").val("");

            Toast.fire({
                icon: 'error',
                title: "<?= lang('empresas.imagenesFormato') ?>",
            });


        } else if (imagen["size"] > 2000000) {

            $(".logo").val("");

            Toast.fire({
                icon: 'error',
                title: "<?= lang('empresas.imagenesPeso') ?>",
            });


        } else {

            var datosImagen = new FileReader;
            datosImagen.readAsDataURL(imagen);

            $(datosImagen).on("load", function (event) {

                var rutaImagen = event.target.result;

                $(".previsualizarLogo").attr("src", rutaImagen);

            })

        }
    })

</script>
<?= $this->endSection() ?>

Ahora los archivos de la vista creamos el archivo app/views/modulosEmpresas/facturacionEmpresa.php y dentro de el metemos el siguiente código

             
<p>
<h3>Datos Facturación</h3>

<div class="form-group row">
    <label for="inputName" class="col-sm-2 col-form-label"><?= lang('empresas.fields.razonSocial') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
            </div>
            <input type="text" name="razonSocial" id="razonSocial" class="form-control <?= session('error.razonSocial') ? 'is-invalid' : '' ?>" value="<?= old('razonSocial') ?>" placeholder="<?= lang('empresas.fields.razonSocial') ?>" autocomplete="off">
        </div>
    </div>
</div>

<div class="form-group row">
    <label for="codigoPostal" class="col-sm-2 col-form-label"><?= lang('empresas.fields.codigoPostal') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-location-arrow"></i></span>
            </div>
            <input type="text" name="codigoPostal"  id="codigoPostal"class="form-control <?= session('error.codigoPostal') ? 'is-invalid' : '' ?>" value="<?= old('codigoPostal') ?>" placeholder="<?= lang('empresas.fields.codigoPostal') ?>" autocomplete="off">
        </div>
    </div>
</div>

<div class="form-group row">
    <label for="rfc" class="col-sm-2 col-form-label"><?= lang('empresas.fields.rfc') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-credit-card"></i></span>
            </div>
            <input type="text" name="rfc"  id="rfc" class="form-control <?= session('error.rfc') ? 'is-invalid' : '' ?>" value="<?= old('rfc') ?>" placeholder="<?= lang('empresas.fields.rfc') ?>" autocomplete="off">
        </div>
    </div>
</div>


<div class="form-group row">
    <label for="CURP" class="col-sm-2 col-form-label"><?= lang('empresas.fields.CURP') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="far fa-id-card"></i></span>
            </div>
            <input type="text" name="CURP"  id="CURP" class="form-control <?= session('error.CURP') ? 'is-invalid' : '' ?>" value="<?= old('CURP') ?>" placeholder="<?= lang('empresas.fields.CURP') ?>" autocomplete="off">
        </div>
    </div>
</div>


<div class="form-group row">
    <label for="inputSkills" class="col-sm-2 col-form-label"><?= lang('empresas.fields.regimenFiscal') ?></label>
    <div class="col-sm-8">
        <select class="form-control select" name="regimenFiscal" id="regimenFiscal" style="width: 100%;">
            
            <option value="0"><?= lang('empresas.fields.regimenFiscalOpcion') ?></option>
            <?php
            foreach ($regimenesFiscales as $key => $value) {

                echo '<option value="' . $value->id() . '">' . $value->id() . ' - ' . $value->texto() . '</option>';
            }
            ?>
        </select>
    </div>
</div>

<div class="form-group row">
    <label for="certificado" class="col-sm-2 col-form-label"><?= lang('empresas.fields.certificado') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-award"></i></span>
            </div>
            <input type="file" name="certificado" id="certificado" class="form-control <?= session('error.certificado') ? 'is-invalid' : '' ?>" value="<?= old('certificado') ?>" placeholder="<?= lang('empresas.fields.correoElectronico') ?>" autocomplete="off">
        </div>
    </div>
</div>

<div class="form-group row">
    <label for="archivoKey" class="col-sm-2 col-form-label"><?= lang('empresas.fields.archivoKey') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-key"></i></span>
            </div>
            <input type="file" name="archivoKey" id="archivoKey" class="form-control <?= session('error.certificado') ? 'is-invalid' : '' ?>" value="<?= old('archivoKey') ?>" placeholder="<?= lang('empresas.fields.archivoKey') ?>" autocomplete="off">
        </div>
    </div>
</div>


<div class="form-group row">
    <label for="contraCertificado" class="col-sm-2 col-form-label"><?= lang('empresas.fields.contraCertificado') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-unlock"></i></span>
            </div>
            <input type="text" name="contraCertificado"  id="contraCertificado" class="form-control <?= session('error.contraCertificado') ? 'is-invalid' : '' ?>" value="<?= old('contraCertificado') ?>" placeholder="<?= lang('empresas.fields.contraCertificado') ?>" autocomplete="off">
        </div>
    </div>
</div>

</p>

Ahora los archivos de la vista creamos el archivo app/views/modulosEmpresas/generalesEmpresa.php y dentro de el metemos el siguiente código

             
<p>
<h3>Datos Generales</h3>

<div class="form-group row">
    <label for="inputName" class="col-sm-2 col-form-label"><?= lang('empresas.fields.nombre') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
            </div>
            <input type="hidden" name="idEmpresa" id="idEmpresa" value="0">
            <input type="text" name="nombre" id="nombre" class="form-control <?= session('error.nombre') ? 'is-invalid' : '' ?>" value="<?= old('nombre') ?>" placeholder="<?= lang('empresas.fields.nombre') ?>" autocomplete="off">
        </div>
    </div>
</div>

<div class="form-group row">
    <label for="inputName" class="col-sm-2 col-form-label"><?= lang('empresas.fields.direccion') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-location-arrow"></i></span>
            </div>
            <input type="text" name="direccion"  id="direccion"class="form-control <?= session('error.direccion') ? 'is-invalid' : '' ?>" value="<?= old('direccion') ?>" placeholder="<?= lang('empresas.fields.direccion') ?>" autocomplete="off">
        </div>
    </div>
</div>


<div class="form-group row">
    <label for="telefono" class="col-sm-2 col-form-label"><?= lang('empresas.fields.telefono') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="fas fa-mobile"></i></span>
            </div>
            <input type="text" name="telefono" id="telefono" class="form-control <?= session('error.telefono') ? 'is-invalid' : '' ?>" value="<?= old('telefono') ?>" placeholder="<?= lang('empresas.fields.telefono') ?>" autocomplete="off">
        </div>
    </div>
</div>


<div class="form-group row">
    <label for="correoElectronico" class="col-sm-2 col-form-label"><?= lang('empresas.fields.correoElectronico') ?></label>
    <div class="col-sm-10">
        <div class="input-group">
            <div class="input-group-prepend">
                <span class="input-group-text"><i class="far fa-envelope"></i></span>
            </div>
            <input type="text" name="correoElectronico" id="correoElectronico" class="form-control <?= session('error.correoElectronico') ? 'is-invalid' : '' ?>" value="<?= old('correoElectronico') ?>" placeholder="<?= lang('empresas.fields.correoElectronico') ?>" autocomplete="off">
        </div>
    </div>
</div>

</p>

Ahora los archivos de la vista creamos el archivo app/views/modulosEmpresas/logosEmpresa.php y dentro de el metemos el siguiente código

             
<p>
<h3>Imagenes</h3>

<div class="form-group">

    <div class="panel"><?= lang("empresas.fields.logo") ?></div>

    <input type="file" class="logo" name="logo" id="logo">

    <p class="help-block"><?= lang("empresas.imagenesPesoMaximo") ?></p>

    <img src="<?= base_url("images/logo/anonymous.png") ?>" class="img-thumbnail previsualizarLogo" width="100px">

    <input type="hidden" name="logoActual" id="logoActual">

</div>

</p>

Ahora los archivos de la vista creamos el archivo app/views/modulosEmpresas/modalCapturaEmpresas.php y dentro de el metemos el siguiente código

<!-- Modal Empresas -->
<div class="modal fade" id="modalAddEmpresa" tabindex="-1" role="dialog" aria-labelledby="modalAddEmpresa" aria-hidden="true">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><?= lang('empresas.createEdit') ?></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">

                <ul class="nav nav-tabs" id="myTab" role="tablist">
                    <li class="nav-item" role="presentation">
                        <button class="nav-link active" id="home-tab" data-toggle="tab" data-target="#generales" type="button" role="tab" aria-controls="home" aria-selected="true">Generales</button>
                    </li>
                    <li class="nav-item" role="presentation">
                        <button class="nav-link" id="profile-tab" data-toggle="tab" data-target="#datosFacturacion" type="button" role="tab" aria-controls="profile" aria-selected="false">Facturacion</button>
                    </li>
                    <li class="nav-item" role="presentation">
                        <button class="nav-link" id="contact-tab" data-toggle="tab" data-target="#contact" type="button" role="tab" aria-controls="contact" aria-selected="false">Logos / Imagenes</button>
                    </li>
                </ul>
                <form id="form-empresa" class="form-horizontal">  
                    <div class="tab-content" id="myTabContent">



                        <div class="tab-pane fade show active" id="generales" role="tabpanel" aria-labelledby="generales">

                            <?= $this->include('modulosEmpresas/generalesEmpresa') ?>

                        </div>
                        <div class="tab-pane fade" id="datosFacturacion" role="tabpanel" aria-labelledby="datosFacturacion">

                            <?= $this->include('modulosEmpresas/facturacionEmpresa') ?>

                        </div>
                        <div class="tab-pane fade" id="contact" role="tabpanel" aria-labelledby="contact-tab">
                            
                            <?= $this->include('modulosEmpresas/logosEmpresa') ?>
                        
                        </div>


                    </div>

                </form>


            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                <button type="button" class="btn btn-primary btn-sm" id="btnSaveEmpresa"><?= lang('boilerplate.global.save') ?></button>
            </div>
        </div>
    </div>
</div>

<?= $this->section('js') ?>


<script>

    $(document).on('click', '.btnAddEmpresa', function (e) {
        
        console.log("asd");
    
        $(".form-control").val("");

        $("#idEmpresa").val("0");

         $(".previsualizarLogo").attr('src','<?= base_URL("images/logo") ?>/anonymous.png'  );
        
        $("#btnSaveEmpresa").removeAttr("disabled");

    });

    /* 
     * AL hacer click al editar
     */



    $(document).on('click', '.btnEditarEmpresa', function (e) {


        var idEmpresa = $(this).attr("idEmpresa");

        //LIMPIAMOS CONTROLES
        $(".form-control").val("");

        $("#idEmpresa").val(idEmpresa);
        $("#btnGuardarEmpresa").removeAttr("disabled");

    });




</script>


<?= $this->endSection() ?>

En el archivo app/config/routes.php quedaria de la siguiente forma

$routes->group('admin', function ($routes) {

    $routes->resource('empresas', [
        'filter' => 'permission:empresas-permisos',
        'controller' => 'EmpresasController',
        'except' => 'show'
    ]);

    $routes->post('empresas/save', 'EmpresasController::save');
    $routes->post('empresas/obtenerEmpresa', 'EmpresasController::obtenerEmpresa');
});

En los archivos de lenguaje para ingles creamos el archivo app/Language/en/empresas.php con el siguiente contenido

<?php

$empresas["logDescription"] = "The companie was saved with the following data:";
$empresas["logUpdate"] = "The companie was updated  with the following data:";
$empresas["logDeleted"] = "The companie was deleted  with the following data:";
$empresas["msg_delete"] = "The companie was deleted  correctly:";

$empresas["add"] = "Add companie";
$empresas["edit"] = "Edit companie";
$empresas["createEdit"] = "Create / Edith";
$empresas["title"] = "companie management";
$empresas["subtitle"] = "companie list";
$empresas["fields"]["nombre"] = "Name";
$empresas["fields"]["direccion"] = "direction";
$empresas["fields"]["rfc"] = "RFC";
$empresas["fields"]["logo"] = "logo";
$empresas["fields"]["certificado"] = "certificate";
$empresas["fields"]["archivoKey"] = "KeyFile";
$empresas["fields"]["contraCertificado"] = "Password Certificate";
$empresas["fields"]["regimenFiscal"] = "Tax Regime";
$empresas["fields"]["regimenFiscalOpcion"] = "Select tax regime";
$empresas["fields"]["razonSocial "] = "Business name";
$empresas["fields"]["codigoPostal "] = "Postal Code ";
$empresas["fields"]["CURP"] = "CURP";



$empresas["fields"]["Created_at"] = "Created At";
$empresas["fields"]["Update_At"] = "Update At";

$empresas["msg"]["msg_save"] = "The companie has been correctly saved.";
$empresas["msg"]["msg_insert"] = "The companie has been correctly added.";
$empresas["msg"]["msg_update"] = "The companie has been correctly modified.";
$empresas["msg"]["msg_delete"] = "The companie has been correctly deleted.";
$empresas["msg"]["msg_get"] = "The companie has been successfully get.";
$empresas["msg"]["msg_get_fail"] = "The vehicle not found or already deleted.";

$empresas["imagenesFormato"] = "The image must be in PNG format!";
$empresas["imagenesPeso"] = "The image must not be larger than 2MB!";
$empresas["imagenesPesoMaximo"] = "Maximum weight of the photo 1MB";

$empresas["certExtensionIncorrect"] = "Extensión certificate incorrect";
$empresas["keyFileExtensionIncorrect"] = "Extensión key file incorrect";
$empresas["pngFileExtensionIncorrect"] = "This image not is PNG";


return $empresas;

En los archivos de lenguaje para ingles creamos el archivo app/Language/es/empresas.php con el siguiente contenido

<?php
$empresas["logDescription"] = "El Empresa ha sido guardado con los siguientes datos:";
$empresas["logUpdate"] = "EL Empresa ha sido actualizado con los siguientes datos";
$empresas["logDeleted"] = "El Empresa ha sido eliminado con los siguientes datos:";

$empresas["add"] = "Agregar Empresa";
$empresas["edit"] = "Editar Empresa";
$empresas["createEdit"] = "Crear / Editar";
$empresas["title"] = "Administración de Empresas";
$empresas["subtitle"] = "Lista de Empresas";
$empresas["fields"]["nombre"] = "Nombre";
$empresas["fields"]["direccion"] = "Direccion";
$empresas["fields"]["rfc"] = "RFC";
$empresas["fields"]["logo"] = "Logo";
$empresas["fields"]["certificado"] = "Certificado";
$empresas["fields"]["archivoKey"] = "Archivo Key";
$empresas["fields"]["telefono"] = "Telefono";
$empresas["fields"]["contraCertificado"] = "Contra Certificado";
$empresas["fields"]["regimenFiscal"] = "Regimen Fiscal";
$empresas["fields"]["regimenFiscalOpcion"] = "Seleccionar regimen fiscal";
$empresas["fields"]["razonSocial"] = "Razon Social";
$empresas["fields"]["codigoPostal"] = "Código Postal";
$empresas["fields"]["CURP"] = "CURP";
$empresas["fields"]["correoElectronico"] = "Correo Electronico";
$empresas["fields"]["acciones"] = "Acciones";

$empresas["msg"]["msg_save"] = "La Empresa ha sido correctamente guardada.";
$empresas["msg"]["msg_insert"] = "La Empresa ha sido correctamente agregado.";
$empresas["msg"]["msg_update"] = "La Empresa ha sido correctamente actualizado.";
$empresas["msg"]["msg_delete"] = "La Empresa ha sido correctamente eliminado.";
$empresas["msg"]["msg_get"] = "La Empresa ha sido correctamente obtenido.";
$empresas["msg"]["msg_get_fail"] = "No se encontro Empresa o fue eliminado.";

// Validations
$empresas["imagenesFormato"] = "¡La imagen debe estar en formato  PNG!";
$empresas["imagenesPeso"] = "¡La imagen no debe pesar más de 2MB!";
$empresas["imagenesPesoMaximo"] = "Peso máximo de la foto 1MB";

$empresas["certExtensionIncorrect"] = "Extensión del certificado incorrecto";
$empresas["keyFileExtensionIncorrect"] = "Extensión del archivo key incorrecto";
$empresas["pngFileExtensionIncorrect"] = "Solo se permiten imagenes con formato png";



return $empresas;

Creamos el siguiente directorio para las imagenes, public/images/logo

Creamos el archivo app/databe/migrations/2023-02-14001801_Empresas.php con el siguiente contenido, este nos servirá para crear la tabla

<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class Empresas extends Migration {

    public function up() {
        // Empresas
        $this->forge->addField([
            'id' => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
            'nombre' => ['type' => 'varchar', 'constraint' => 500, 'null' => false],
            'direccion' => ['type' => 'varchar', 'constraint' => 30, 'null' => true],
            'rfc' => ['type' => 'varchar', 'constraint' => 14, 'null' => false],
            'telefono' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
            'correoElectronico' => ['type' => 'varchar', 'constraint' => 256, 'null' => true],
            'diasEntrega' => ['type' => 'varchar', 'constraint' => 8, 'null' => true],
            'caja' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
            'logo' => ['type' => 'varchar', 'constraint' => 1024, 'null' => true],
            'certificado' => ['type' => 'varchar', 'constraint' => 1024, 'null' => true],
            'archivoKey' => ['type' => 'varchar', 'constraint' => 68, 'null' => true],
            'contraCertificado' => ['type' => 'varchar', 'constraint' => 68, 'null' => true],
            'regimenFiscal' => ['type' => 'varchar', 'constraint' => 68, 'null' => true],
            'razonSocial' => ['type' => 'varchar', 'constraint' => 68, 'null' => true],
            'codigoPostal' => ['type' => 'varchar', 'constraint' => 68, 'null' => true],
            'CURP' => ['type' => 'varchar', 'constraint' => 68, 'null' => true],
            'created_at' => ['type' => 'datetime', 'null' => true],
            'updated_at' => ['type' => 'datetime', 'null' => true],
            'deleted_at' => ['type' => 'datetime', 'null' => true],
        ]);

        $this->forge->addKey('id', true);

        $this->forge->createTable('empresas', true);
    }

    public function down() {
        $this->forge->dropTable('empresas', true);
    }

}

Creamos el modelo para la bitacora app/models/LogModel.php con el siguiente contenido

<?php

namespace App\Models;

use CodeIgniter\Model;

class LogModel extends Model
{
    protected $table      = 'log';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

    protected $returnType     = 'array';
    protected $useSoftDeletes = true;

    protected $allowedFields = ['id','description', 'user',"created_at","updated_at"];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules    = [];
    protected $validationMessages = [];
    protected $skipValidation     = false;

  
}

Creamos el archivo de migración para LogModel en app/database/migration/2023-02-07-165412_Log.php con el siguiente contenido

<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class Log extends Migration
{
    public function up()
    {
        $this->forge->addField([
            'id' => [
                'type' => 'INT',
                'constraint' => 5,
                'unsigned' => true,
                'auto_increment' => true,
            ],
            'description' => [
                'type' => 'VARCHAR',
                'constraint' => '256',
            ],
            'user' => [
                'type' => 'TEXT',
                'null' => true,
            ],
 
            'created_at' => ['type' => 'datetime', 'null' => true],
            'updated_at' => ['type' => 'datetime', 'null' => true],
            'deleted_at' => ['type' => 'datetime', 'null' => true],
        ]);

        $this->forge->addKey('id', true);
        $this->forge->createTable('log', true);
    }

    public function down()
    {
        $this->forge->dropTable('log');
    }
}

Por ultimo corremos el siguiente código en la terminal para correr la migración y así nos genere la tabla empresas y bitacora en la base de datos

php spark migrate
Y listo ya tenemos nuestro catalogo de empresas
Video Demostrativo

CodeIgniter 4 Aplicacion Boilerplate Plantilla

Que es boilerplate? normalmente en el ámbito de desarrollo de aplicaciones se le llama boilerplate a todo ese código repetitivo que usamos en un proyecto, o mas bien lo que siempre usamos en todos los proyectos, como login, menús, permisos.

A continuación les dejo un boilerplate para CodeIgniter el cual hice una bifurcación para adaptarlo a nuestras necesidades.

Este paquete para CodeIgniter 4 sirve como plataforma básica para crear rápidamente una aplicación administrativa. Incluye creación y gestión de perfiles, gestión de usuarios, roles, permisos y un menú generado dinámicamente.

Características

  • Tema backend configurable AdminLTE 3
  • CSS framework Bootstrap 4
  • Iconos de Font Awesome 5
  • Permisos basados en roles (RBAC) proporcionados porMyth/Auth
  • Generación de menús dinamicos
  • Traducciones en English / Indonesian / Spanish

Este proyecto aún está en sus primeras etapas de desarrollo… ¡no dude en contribuir!

El autor original es agungsugiarto/boilerplate https://github.com/agungsugiarto/boilerplate, solo hicimos un fork para adaptarlo a nuevas necesidades como traducirlo al español y hacerlo funcional en xampp/lampp

El fuente con los cambios mas recientes de fork estan en github https://github.com/julio101290/boilerplate

Instalación

  1. Obtener el modulo mediante composer
composer require julio101290/boilerplate

2. Establezca CI_ENVIRONMENT, baseURL, página de índice y configuración de la base de datos en su archivo .env según su base de datos existente (si no tiene un archivo .env, puede copiar primero desde el archivo env: cp env .env primero). Si la base de datos no existe, primero cree la base de datos.

# .env file
CI_ENVIRONMENT = development

app.baseURL = 'http://localhost:8080'
app.indexPage = ''

database.default.hostname = localhost
database.default.database = boilerplate
database.default.username = root
database.default.password =
database.default.DBDriver = MySQLi

3. Correr el comando para publicar de auth

php spark auth:publish

Publish Migration? [y, n]: y
  created: Database/Migrations/2017-11-20-223112_create_auth_tables.php
  Remember to run `spark migrate -all` to migrate the database.
Publish Models? [y, n]: n
Publish Entities? [y, n]: n
Publish Controller? [y, n]: n
Publish Views? [y, n]: n
Publish Filters? [y, n]: n
Publish Config file? [y, n]: y
  created: Config/Auth.php
Publish Language file? [y, n]: n

NOTA: Todo lo relacionado con cómo configurar la autenticación puede encontrar en Myth/Auth.

¿Ya está listo? ¡¡No tan rapido!! 😉 Después de publicar Config/Auth.php, debe cambiar las $views públicas con estas líneas a continuación:

public $views = [
    'login'           => 'julio101290\boilerplate\Views\Authentication\login',
    'register'        => 'julio101290\boilerplate\Views\Authentication\register',
    'forgot'          => 'julio101290\boilerplate\Views\Authentication\forgot',
    'reset'           => 'julio101290\boilerplate\Views\Authentication\reset',
    'emailForgot'     => 'julio101290\boilerplate\Views\Authentication\emails\forgot',
    'emailActivation' => 'julio101290\boilerplate\Views\Authentication\emails\activation',
];

Abra app\Config\Filters.php, busque $aliases y agregue estas líneas a continuación:

public $aliases = [
    'login'      => \Myth\Auth\Filters\LoginFilter::class,
    'role'       => \julio101290\boilerplate\Filters\RoleFilter::class,
    'permission' => \julio101290\boilerplate\Filters\PermissionFilter::class,
];

4. Ejecute publish, migrate y seed del boilerplate

php spark boilerplate:install

Abra app\Config\validation.php, busque $ruleSets y agregue estas líneas a continuación:

public $$ruleSets = [
    \Myth\Auth\Authentication\Passwords\ValidationRules::class,
];

Abra app\entities\Users.php, busque $casts y agregue estas líneas a continuación:


   protected $casts = [
        'username' => 'string',
        'email' => 'string',
        'firstname' => 'string',
        'lastname' => 'string',
        'active' => 'boolean',
        'force_pass_reset' => 'boolean',
    ];

Ejecute el servidor de desarrollo:

php spark serve

Abrir en el navegador http://localhost:8080/admin

Default user and password
+----+--------+-------------+
| No | User   | Password    |
+----+--------+-------------+
| 1  | admin  | super-admin |
| 2  | user   | super-user  |
+----+--------+-------------+

Configuraciones

Plantilla de configuración

Puede configurar el controlador de tablero predeterminado y el tema de back-end en app\Config\Boilerplate.php

class Boilerplate extends BaseConfig
{
    public $appName = 'Boilerplate';

    public $dashboard = [
        'namespace'  => 'julio101290\boilerplate\Controllers',
        'controller' => 'DashboardController::index',
        'filter'     => 'permission:back-office',
    ];
// App/Config/Boilerplate.php

Uso

Puede encontrar cómo funciona con las rutas de código de lectura, el controlador y las vistas, etc. Finalmente… ¡Feliz codificación!

Contribuciones

Las contribuciones son muy bienvenidas.

Licencia

Este paquete es software gratuito distribuido bajo los términos de la licencia MIT.

CI 4.0 MedicalSoft Modulo Para Cita y Creando nuestros Propios Helpers personalizados

En este apartado vamos a crear nuestro modulo de citas medicas que posteriormente podremos convertir en consulta, a su vez veremos como crear nuestros propios helpers y usarlos

Que es un helper en codeigniter, pues no es mas que funciones o utilerías que nos pueden servir en diferentes partes del sistema

Creamos la tabla para citas

-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 08-01-2023 a las 23:44:15
-- Versión del servidor: 10.4.17-MariaDB
-- Versión de PHP: 7.4.15

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Base de datos: `medicalsoft`
--

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `citas`
--

CREATE TABLE `citas` (
  `id` int(11) NOT NULL,
  `idPaciente` int(11) DEFAULT NULL,
  `fechaHora` datetime NOT NULL,
  `hastaFechaHora` datetime DEFAULT NULL,
  `observaciones` varchar(1024) COLLATE utf8_spanish2_ci NOT NULL,
  `created_at` datetime NOT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `estado` varchar(15) COLLATE utf8_spanish2_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `citas`
--
ALTER TABLE `citas`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `citas`
--
ALTER TABLE `citas`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Y como de costumbre creamos primeramente el archiv CitasModel.php en la carpeta app/models y le ponemos el siguiente código

<?php

namespace App\Models;

use CodeIgniter\Model;

class CitasModel extends Model {

    protected $table = 'citas';
    protected $primaryKey = 'id';
    protected $useAutoIncrement = true;
    protected $returnType = 'array';
    protected $useSoftDeletes = true;
    protected $allowedFields = ['id', 'idPaciente', 'fechaHora', 'hastaFechaHora', 'observaciones', 'created_at', 'updated_at'];
    protected $useTimestamps = true;
    protected $createdField = 'created_at';
    protected $deletedField = 'deleted_at';
    protected $validationRules = [
        'observaciones' => 'required|alpha_numeric_space|min_length[3]'
    ];
    protected $validationMessages = [];
    protected $skipValidation = false;

    public function mdlObtenerDatos() {

        $resultado = $this->db->table('citas a, pacientes b')
                ->select('a.id,concat(b.nombres,\' \',b.apellidos) as nombrePaciente,a.observaciones,a.fechaHora,a.hastaFechaHora,estado,a.created_at,a.updated_at')
                ->where('a.idPaciente', 'b.id', false)
                ->where('a.deleted_at', null);

        return $resultado;
    }

    public function mdlObtenerCita($idCita) {

        $resultado = $this->db->table('citas a, pacientes b')
                ->select('a.id,concat(b.nombres,\' \',b.apellidos) as nombrePaciente,a.observaciones,a.fechaHora,a.hastaFechaHora,estado,a.created_at,a.updated_at,a.idPaciente')
                ->where('a.idPaciente', 'b.id', false)
                ->where('a.deleted_at', null)
                ->where('a.id', $idCita,null)->get()->getResultArray();

        return $resultado[0];
    }

}

Posteriormente creamos el archivo CitasController.php en la carpeta app/controller y le insertamos el siguiente código

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use App\Models\CitasModel;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class CitasController extends BaseController {

    use ResponseTrait;

    protected $bitacora;
    protected $citas;

    public function __construct() {
        $this->citas = new CitasModel();
        $this->bitacora = new BitacoraModel();
        helper('menu');
      
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
              $start = $this->request->getGet('start');
              $length = $this->request->getGet('length');
              $search = $this->request->getGet('search[value]');
              $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
              $dir = $this->request->getGet('order[0][dir]');
             */

            $datos = $this->citas->mdlObtenerDatos();  
                    //where('deleted_at', null);
            // $resultado = $this->bitacora->findAll();
            // $this->bitacora->getResource()->countAllResults(),
            // $this->bitacora->getResource($search)->countAllResults()
            //      var_dump($datos);


            return \Hermawan\DataTables\DataTable::of($datos)->add('action', function ($row) {
                                return " <div class=\"btn-group\">
                          
                  <button class=\"btn btn-warning btnEditarCita\" data-toggle=\"modal\" idCita=\"$row->id\" data-target=\"#modalAgregarCitas\">  <i class=\" fa fa-edit \"></i></button>
                  <button class=\"btn btn-danger btnEliminarCita\" idCita=\"$row->id\"><i class=\"fa fa-times\"></i></button></div>";
                            }, 'last')
                            ->toJson();
        }

        
        $fechaActual =  fechaMySQLADateTimeHTML5(fechaHoraActual());
        
        $titulos["fecha"] =  $fechaActual;
        $titulos["title"] = lang('citas.title');
        $titulos["subtitle"] = lang('citas.subtitle');

        return view('citas', $titulos);
    }

    /*
     * Lee paciente
     */

    public function traeCita() {


        $idCita = $this->request->getPost("idCita");
        $datosCita = $this->citas->mdlObtenerCita($idCita);

        echo json_encode($datosCita);
    }

    /*
     * Guarda o actualiza paciente
     */

    public function guardar() {


        helper('auth');
        $userName = user()->username;
        $idUser = user()->id;

        $datos = $this->request->getPost();

        if ($datos["idCita"] == 0) {


            try {


                if ($this->citas->save($datos) === false) {

                    $errores = $this->citas->errors();

                    foreach ($errores as $field => $error) {

                        echo $error . " ";
                    }

                    return;
                }

                $datosBitacora["descripcion"] = "Se guardo la medicamento con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;

                $this->bitacora->save($datosBitacora);

                echo "Guardado Correctamente";
            } catch (\PHPUnit\Framework\Exception $ex) {


                echo "Error al guardar " . $ex->getMessage();
            }
        } else {


            if ($this->citas->update($datos["idCita"], $datos) == false) {
                
                $errores = $this->citas->errors();
                foreach ($errores as $field => $error) {

                    echo $error . " ";
                }
                
                return;
               
            } else {

                $datosBitacora["descripcion"] = "Se actualizo el medicamento con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;
                $this->bitacora->save($datosBitacora);
                echo "Actualizado Correctamente";

                return;
            }
        }

        return;


    }
    
    
    
     public function delete($id)
    {
        if (!$found = $this->citas->delete($id)) {
            return $this->failNotFound(lang('citas.msg.msg_get_fail'));
        }
        
        $infoCita = $this->citas->find($id);
        
        $datosBitacora["descripcion"] = "Se elimino la medicamento que contenia los siguientes datos ". json_encode($infoCita);
        
        $this->bitacora->save($datosBitacora);
        return $this->respondDeleted($found, lang('citas.msg.msg_delete'));
    }

}

Luego Creamos la vista citas.php en app/views

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

<!-- Section content -->
<?= $this->section('content') ?>

<?= $this->include('citasModulos\modalCaptura') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="float-right">
            <div class="btn-group">

                <button class="btn btn-primary btnAgregarCitas" data-toggle="modal" data-target="#modalAgregarCitas"><i class="fa fa-plus"></i>

                    <?= lang('citas.add') ?>

                </button>

            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaCitas" class="table table-striped table-hover va-middle tablaCitas">
                        <thead>
                            <tr>



                                <th>#</th>

                                <th><?= lang('citas.nombrePaciente') ?></th>
                                <th><?= lang('citas.observaciones') ?></th>
                                <th><?= lang('citas.fechaHora') ?></th>
                                <th><?= lang('citas.hastaFechaHora') ?></th>
                                <th><?= lang('citas.estado') ?></th>


                                <th><?= lang('citas.createdAt') ?></th>
                                <th><?= lang('citas.updateAt') ?></th>
                                <th><?= lang('citas.actions') ?> </th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->

<?= $this->endSection() ?>




<?= $this->section('js') ?>
<script>

    /**
     * Cargamos la tabla
     */


    function cargaTabla() {



        $('.tablaCitas').DataTable({
            "ajax": "<?= route_to('admin/citas') ?>",
            "deferRender": true,
            "serverSide": true,
            "retrieve": true,
            "processing": true

        });

    }


    cargaTabla();



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR PACIENTE
     =============================================*/
    $(".tablaCitas").on("click", ".btnEditarCita", function () {

        var idCita = $(this).attr("idCita");


        console.log("idCita ", idCita);

        var datos = new FormData();
        datos.append("idCita", idCita);

        $.ajax({

            url: "<?= route_to('admin/citas/traeCita') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            dataType: "json",
            success: function (respuesta) {
                console.log(respuesta);
                $("#idCita").val(respuesta["id"]);
                $("#observaciones").val(respuesta["observaciones"]);
                $("#fechaHora").val(respuesta["fechaHora"]);
                $("#hastaFechaHora").val(respuesta["hastaFechaHora"]);


                //$("#pacientes").val(respuesta["idPaciente"]);

                $("#pacientes").empty() //empty select
                        .append($("<option/>") //add option tag in select
                                .val(respuesta["idPaciente"]) //set value for option to post it
                                .text(respuesta["nombrePaciente"])) //set a text for show in select
                        .val(respuesta["idPaciente"]) //select option of select2
                        .trigger("change");


            }

        })

    })


    /*=============================================
     ELIMINAR PACIENTE
     =============================================*/
    $(".tablaCitas").on("click", ".btnEliminarCita", function () {

        var idCita = $(this).attr("idCita");




        Swal.fire({
            title: '<?= lang('boilerplate.global.sweet.title') ?>',
            text: "<?= lang('boilerplate.global.sweet.text') ?>",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
        })
                .then((result) => {
                    if (result.value) {
                        $.ajax({
                            url: `<?= route_to('admin/citas') ?>/` + idCita,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });

                            $(".tablaCitas").DataTable().destroy();
                            cargaTabla();
                            //tableUser.ajax.reload();
                        }).fail((error) => {
                            Toast.fire({
                                icon: 'error',
                                title: error.responseJSON.messages.error,
                            });
                        })
                    }
                })
    })




</script>
<?= $this->endSection() ?>

Luego en la carpeta app/views creamos la carpeta citasModulos, creamos el archivo modalCaptura.php y metemos el siguiente código

<!-- Modal Citas -->

<div class="modal fade" id="modalAgregarCitas" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><?= lang('citas.createEdit') ?></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form id="form-enfermedad" class="form-horizontal">
                    <!-- CSRF token --> 
                    <input type="hidden" class="txt_csrfname" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" />

                    <input type="hidden" id="idCita" name="idCita" value="0">




                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('citas.nombrePaciente') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>

                                <select id='pacientes'  name='pacientes' style='width: 80%;'>
                                    <option value='0'><?= lang('citas.seleccionePaciente') ?></option>
                                </select>


                            </div>
                        </div>
                    </div>


                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('citas.observaciones') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>

                                <textarea class="form-control <?= session('error.observaciones ') ? 'is-invalid' : '' ?>" rows="3" placeholder="<?= lang('citas.observaciones') ?>" id="observaciones" name="observaciones" autocomplete="off"></textarea>

                            </div>
                        </div>
                    </div>



                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('citas.fechaHora') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>


                                <input type="datetime-local" id="fechaHora" name="fechaHora" value="<?= $fecha ?>">


                            </div>
                        </div>
                    </div>


                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('citas.hastaFechaHora') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>


                                <input type="datetime-local" id="hastaFechaHora" name="hastaFechaHora" value="<?= $fecha ?>">


                            </div>
                        </div>
                    </div>



                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                <button type="button" class="btn btn-primary btn-sm" id="btnGuardarCita"><?= lang('boilerplate.global.save') ?></button>
            </div>
        </div>
    </div>
</div>

<?= $this->section('js') ?>


<script>

    $(document).on('click', '.btnAgregarCitas', function (e) {

        console.log("asdasd");
        $(".form-control").val("");

        $("#idCita").val("0");

        $("#btnGuardarCita").removeAttr("disabled");

    })

    /* 
     * AL hacer click al editar
     */



    $(document).on('click', '.btnGuardarCita', function (e) {


        var idCita = $(this).attr("idCita");

        //LIMPIAMOS CONTROLES
        $(".form-control").val("");

        $("#idCita").val(idCita);
        $("#btnGuardarCita").removeAttr("disabled");

    })

    /**
     * Guardar paciente
     */

    $(document).on('click', '#btnGuardarCita', function (e) {

        var idCita = $("#idCita").val();
        var observaciones = $("#observaciones").val();
        var idPaciente = $("#pacientes").val();
        var fechaHora = $("#fechaHora").val();
        var hastaFechaHora = $("#hastaFechaHora").val();
       

        console.log("OBSERVACIONES:",observaciones);

        $("#btnGuardarCita").attr("disabled", true);


        var datos = new FormData();
        datos.append("idCita", idCita);
        datos.append("observaciones", observaciones);
        datos.append("idPaciente", idPaciente);
        datos.append("fechaHora", fechaHora);
        datos.append("hastaFechaHora", hastaFechaHora);


        $.ajax({

            url: "<?= route_to('admin/citas/guardar') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            //dataType:"json",
            success: function (respuesta) {


                if (respuesta.match(/Correctamente.*/)) {


                    Toast.fire({
                        icon: 'success',
                        title: "Guardado Correctamente"
                    });


                    $('.tablaCitas').DataTable().destroy();
                    cargaTabla();
                    $("#btnGuardarCita").removeAttr("disabled");


                    $('#modalAgregarCitas').modal('hide');
                } else {

                    Toast.fire({
                        icon: 'error',
                        title: respuesta
                    });

                    $("#btnGuardarCita").removeAttr("disabled");
                    //  $('#modalAgregarCita').modal('hide');

                }

            }

        }

        )




    });



    // Initialize select2
    $("#pacientes").select2({
        ajax: {
            url: "<?= site_url('admin/pacientes/traerPacientesAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,
            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash

                return {
                    searchTerm: params.term, // search term
                    [csrfName]: csrfHash // CSRF Token
                };
            },
            processResults: function (response) {

                // Update CSRF Token
                $('.txt_csrfname').val(response.token);

                return {
                    results: response.data
                };
            },
            cache: true
        }
    });



</script>


<?= $this->endSection() ?>

En este modulo como se vieron el archivo CitasController.php se usaron unas funciones para el formato de las fechas dichas funciones estan en el helper, estas funciones nos sirvieron anteriormente en JCPOS2021, entonces en la carpeta app/Helpers creamos el archivo utilerias_helper.php y metemos el siguiente código

<?php

function strMenuActivo($strMenu1, $strMenu2) {
    if ($strMenu1 == $strMenu2) {
        $respuesta = 'class="active"';
    } else {
        $respuesta = "";
    }
    return $respuesta;
}

//SI LA VARIABLE ESTA VACIA O NO SETA DECLARADA MANDARA CERO SIEMPRE, ES COMO EL VAL DE VISUAL BASIC 6.0

function esCero($value) {

    if (empty($value)) {
        return "0";
    } else {
        return $value;
    }
}

//FECHA SQL PARA GUARDAR EN BASE DE DATOS

function fechaSQL($fecha) {

    return date("Ymd", strtotime($fecha));
}

// CONVIERTE FECHA MYSQLDATETIME A HTML5
function fechaMySQLADateTimeHTML5($fecha) {

    return date("Y-m-d", strtotime($fecha)) . "T" . date("H:i:s", strtotime($fecha));
}

function agregarMinutos($fecha, $minutos) {


    return date("Y/m/d h:i:s", strtotime($fecha . "+ $minutos minutes"));

    /*
      $fecha1= new DateTime($fecha);
      $fecha1->add(new DateInterval('PT10H30S'));
      return $date->format('Y-m-d H:i:s') . "\n";
     * 
     */
}

//CONVIERTE LA FECHA EN PERIODO
function fechaPeriodo($fecha) {

    return date("Ym", strtotime($fecha));
}

//OBTIENE FECHA ACTUAL
function fechaActual() {

    return date("Y/m/d");
}

//OBTIENE FECHA HORA ACTUAL

function fechaHoraActual() {

    return date("Y-m-d H:i:s ", time());
}

//DIFERENCIA ENTRE MINUTOS
function diferenciaMinutos($fecha_i, $fecha_f) {
    $minutos = (strtotime($fecha_i) - strtotime($fecha_f)) / 60;

    $minutos = abs($minutos);
    $minutos = floor($minutos);

    return $minutos;
}

function strSellar($llave, $password, $cadenaOriginal) {


    $archivoPem = "/tmp/llave.key.pem";
    $comando = "openssl pkcs8 -inform DER -in $llave -passin pass:$password -out $archivoPem";

    exec($comando);
    $sello = "ok";

    //Sellar
    $archivo = openssl_pkey_get_private(file_get_contents($archivoPem));
    $sig = "";
    openssl_sign($cadenaOriginal, $sig, $archivo, OPENSSL_ALGO_SHA256);

    $sello = base64_encode($sig);

    return $sello;
}

//SOLO DIA
function dia($fecha) {

    return date("d", strtotime($fecha));
}

//SOLO MES
function mes($fecha) {

    return date("m", strtotime($fecha));
}

//SOLO AÑO
function año($fecha) {

    return date("Y", strtotime($fecha));
}

Ahora en app/lenguajes/en/ creamos el archivo citas.php y metemos el siguiente archivo

<?php

$citas["observaciones"] = "Observations";
$citas["nombrePaciente"] = "Patient´s Name";
$citas["fechaHora"] = "Start Date";
$citas["hastaFechaHora"] = "End Date";

$citas["createdAt"] = "Date Creation";
$citas["updateAt"] = "Date Update";
$citas["add"] = "add appointment";
$citas["actions"] = "Acciones";
$citas["estado"] = "status";
$citas["createEdit"] = "add  / update appointment";
$citas["seleccionePaciente"] = "Select an Patient";
$citas["title"] = "appointment";
$citas["subtitle"] = "List of appointments";

$citas["msg_delete"] = "The quote has been removed .";
$citas["msg_get_fail"] = "The appointment does not exist or was deleted.";





return $citas;

Ahora en app/lenguajes/es/ creamos el archivo citas.php y metemos el siguiente archivo

<?php

$citas["observaciones"] = "Observaciones";
$citas["nombrePaciente"] = "Nombre Paciente";
$citas["fechaHora"] = "Fecha Inicio Cita";
$citas["obsevaciones"] = "Observaciones";
$citas["hastaFechaHora"] = "Fin Cita";

$citas["createdAt"] = "Fecha Creación";
$citas["updateAt"] = "Fecha de Modificación";
$citas["add"] = "Agregar Cita";
$citas["actions"] = "Acciones";
$citas["estado"] = "Estado";
$citas["createEdit"] = "Crear  / Editar Citas";
$citas["seleccionePaciente"] = "Seleccione un paciente";
$citas["title"] = "Citas";
$citas["subtitle"] = "Lista de Citas";

$citas["msg_delete"] = "La cita ha sido eliminada .";
$citas["msg_get_fail"] = "La cita no existe o fue eliminada.";

return $citas;

Luego ya por ultimo creamos las rutas en app/config/routes.php metemos el siguiente codigo

    $routes->resource('citas', [
        'filter' => 'permission:citas-permiso',
        'controller' => 'CitasController',
        'except' => 'show'
    ]);

    $routes->post('citas/guardar', 'CitasController::guardar');

    $routes->post('citas/traeCita', 'CitasController::traeCita');
Y listo ya tenemos nuestro modulo de citas y aprendimos como usar los Helpers que creamos

CI 4.0 MedicalSoft CRUD Para Pacientes

Ahora veremos como crear el CRUD de pacientes en el framework CodeIgniter 4.0 con los siguientes datos, Nombres, Apellidos, DNI, Correo Electrónico, para ello creamos la siguiente tabla.

Leer Mas: CI 4.0 MedicalSoft CRUD Para Pacientes
-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 04-01-2023 a las 18:20:08
-- Versión del servidor: 10.4.17-MariaDB
-- Versión de PHP: 7.4.15

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Base de datos: `medicalsoft`
--

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `pacientes`
--

CREATE TABLE `pacientes` (
  `id` int(11) NOT NULL,
  `nombres` varchar(256) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `apellidos` varchar(256) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `dni` varchar(32) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `telefono` varchar(16) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `correoElectronico` varchar(64) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `pacientes`
--
ALTER TABLE `pacientes`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `pacientes`
--
ALTER TABLE `pacientes`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Creamos el archivo PacientesModel.php en la carpeta model para el acceso a la tabla de la base de datos

<?php

namespace App\Models;

use CodeIgniter\Model;

class PacientesModel extends Model
{
    protected $table      = 'pacientes';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

    protected $returnType     = 'array';
    protected $useSoftDeletes = true;

    protected $allowedFields = ['id','nombres', 'apellidos','dni','telefono','correoElectronico','created_at','updated_at'];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules    =  [
        'nombres'     => 'required|alpha_numeric_space|min_length[3]',
        'apellidos'     => 'required|alpha_numeric_space|min_length[5]',
        'correoElectronico'        => 'required|valid_email|is_unique[users.email]'
        
    ];
    protected $validationMessages = [];
    protected $skipValidation     = false;
    
   
}

Creamos el archivo PacientesController.php alli tendremos la funciones para guardar, leer y modificar, creo que no falta decir que va en la carpeta controller

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use \App\Models\PacientesModel;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class PacientesController extends BaseController {

    use ResponseTrait;

    protected $bitacora;
    protected $pacientes;

    public function __construct() {
        $this->pacientes = new PacientesModel();
        $this->bitacora = new BitacoraModel();
        helper('menu');
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
              $start = $this->request->getGet('start');
              $length = $this->request->getGet('length');
              $search = $this->request->getGet('search[value]');
              $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
              $dir = $this->request->getGet('order[0][dir]');
             */

            $datos = $this->pacientes->select('id,nombres,apellidos,dni,telefono,correoElectronico,created_at,updated_at')->where('deleted_at', null);
// $resultado = $this->bitacora->findAll();
// $this->bitacora->getResource()->countAllResults(),
// $this->bitacora->getResource($search)->countAllResults()
//      var_dump($datos);


            return \Hermawan\DataTables\DataTable::of($datos)->add('action', function ($row) {
                                return " <div class=\"btn-group\">
                          
                  <button class=\"btn btn-warning btnEditarPaciente\" data-toggle=\"modal\" idPaciente=\"$row->id\" data-target=\"#modalAgregarPaciente\">  <i class=\" fa fa-edit \"></i></button>
                  <button class=\"btn btn-danger btnEliminarPaciente\" idPaciente=\"$row->id\"><i class=\"fa fa-times\"></i></button></div>";
                            }, 'last')
                            ->toJson();
        }

        $titulos["title"] = lang('patients.title');
        $titulos["subtitle"] = lang('patients.subtitle');
        
       
//$data["data"] = $datos;
        return view('pacientes', $titulos);
    }

    /*
     * Lee paciente
     */

    public function traePaciente() {


        $idPaciente = $this->request->getPost("idPaciente");
        $datosPaciente = $this->pacientes->find($idPaciente);

        echo json_encode($datosPaciente);
    }

    /*
     * Guarda o actualiza paciente
     */

    public function guardar() {


        helper('auth');
        $userName = user()->username;
        $idUser = user()->id;

        $datos = $this->request->getPost();

        if ($datos["idPaciente"] == 0) {


            try {


                if ($this->pacientes->save($datos) === false) {

                    $errores = $this->pacientes->errors();

                    foreach ($errores as $field => $error) {

                        echo $error . " ";
                    }

                    return;
                }

                $datosBitacora["descripcion"] = "Se guardo el paciente con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;

                $this->bitacora->save($datosBitacora);

                echo "Guardado Correctamente";
            } catch (\PHPUnit\Framework\Exception $ex) {


                echo "Error al guardar " . $ex->getMessage();
            }
        } else {


            if ($this->pacientes->update($datos["idPaciente"], $datos) == false) {

                $errores = $this->pacientes->errors();
                foreach ($errores as $field => $error) {

                    echo $error . " ";
                }

                return;
            } else {

                $datosBitacora["descripcion"] = "Se actualizo el paciente con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;
                $this->bitacora->save($datosBitacora);
                echo "Actualizado Correctamente";

                return;
            }
        }

        return;
    }

    public function delete($id) {
        if (!$found = $this->pacientes->delete($id)) {
            return $this->failNotFound(lang('patients.msg.msg_get_fail'));
        }

        $infoPaciente = $this->pacientes->find($id);

        $datosBitacora["descripcion"] = "Se elimino el paciente que contenia los siguientes datos " . json_encode($infoPaciente);

        $this->bitacora->save($datosBitacora);
        return $this->respondDeleted($found, lang('patients.msg.msg_delete'));
    }

    /**
     * Trae en formato JSON los pacientes para el select2
     * @return type
     */
    public function traerPacientesAjax() {
        
        $request = service('request');
        $postData = $request->getPost();

        $response = array();

        // Read new token and assign in $response['token']
        $response['token'] = csrf_hash();

        if (!isset($postData['searchTerm'])) {
            // Fetch record
            $pacientes = new PacientesModel();
            $listaPacientes = $pacientes->select('id,nombres,apellidos')
                    ->orderBy('nombres')
                    ->findAll(10);
        } else {
            $searchTerm = $postData['searchTerm'];

            // Fetch record
            $pacientes = new PacientesModel();
            $listaPacientes = $pacientes->select('id,nombres,apellidos')
                    ->where("deleted_at",null)
                    ->like('nombres', $searchTerm)
                    ->orLike('apellidos', $searchTerm)
                    ->orderBy('nombres')
                    ->findAll(10);
        }

        $data = array();
        foreach ($listaPacientes as $paciente) {
            $data[] = array(
                "id" => $paciente['id'],
                "text" => $paciente['nombres'].' '.$paciente['apellidos'],
            );
        }

        $response['data'] = $data;

        return $this->response->setJSON($response);
    }

}

En Views creamos el archivo de la vista

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

<!-- Section content -->
<?= $this->section('content') ?>

<?= $this->include('pacientesModulos\modalCaptura') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="float-right">
            <div class="btn-group">

                <button class="btn btn-primary btnAgregarPaciente" data-toggle="modal" data-target="#modalAgregarPaciente"><i class="fa fa-plus"></i>

                    <?= lang('patients.add') ?>

                </button>

            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaPacientes" class="table table-striped table-hover va-middle tablaPacientes">
                        <thead>
                            <tr>



                                <th>#</th>
                                <th><?= lang('patients.names') ?></th>
                                <th><?= lang('patients.lastName') ?></th>
                                <th><?= lang('patients.dni') ?></th>
                                <th><?= lang('patients.phone') ?></th>
                                <th><?= lang('patients.email') ?></th>
                                <th><?= lang('patients.createdAt') ?></th>
                                <th><?= lang('patients.updateAt') ?></th>
                                <th>Acciones </th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->

<?= $this->endSection() ?>




<?= $this->section('js') ?>
<script>

    /**
     * Cargamos la tabla
     */


    function cargaTabla() {



         $('.tablaPacientes').DataTable({
         "ajax": "<?= route_to('admin/pacientes') ?>",
         "deferRender": true,
         "serverSide": true,
         "retrieve": true,
         "processing": true
         
         });
        
    }


    cargaTabla();



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR PACIENTE
     =============================================*/
    $(".tablaPacientes").on("click", ".btnEditarPaciente", function () {

        var idPaciente = $(this).attr("idPaciente");


        console.log("paciente ", idPaciente);

        var datos = new FormData();
        datos.append("idPaciente", idPaciente);

        $.ajax({

            url: "<?= route_to('admin/pacientes/traerPaciente') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            dataType: "json",
            success: function (respuesta) {
                console.log(respuesta);
                $("#idPaciente").val(respuesta["id"]);
                $("#nombres").val(respuesta["nombres"]);
                $("#apellidos").val(respuesta["apellidos"]);
                $("#correoElectronico").val(respuesta["correoElectronico"]);
                $("#telefono").val(respuesta["telefono"]);
                $("#dni").val(respuesta["dni"]);

            }

        })

    })


    /*=============================================
     ELIMINAR PACIENTE
     =============================================*/
    $(".tablaPacientes").on("click", ".btnEliminarPaciente", function () {

        var idPaciente = $(this).attr("idPaciente");


        console.log("eliminar");

        Swal.fire({
            title: '<?= lang('boilerplate.global.sweet.title') ?>',
            text: "<?= lang('boilerplate.global.sweet.text') ?>",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
        })
                .then((result) => {
                    if (result.value) {
                        $.ajax({
                            url: `<?= route_to('admin/pacientes') ?>/` + idPaciente,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });

                           $(".tablaPacientes").DataTable().destroy();
                            cargaTabla();
                            //tableUser.ajax.reload();
                        }).fail((error) => {
                            Toast.fire({
                                icon: 'error',
                                title: error.responseJSON.messages.error,
                            });
                        })
                    }
                })
    })




</script>
<?= $this->endSection() ?>

En views creamos la carpeta pacientesModulos y dentro de esa capeta creamos el archivo modalCaptura.php con la siguiente código

<!-- Modal Pacientes -->
<div class="modal fade" id="modalAgregarPaciente" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><?= lang('patients.createEdit') ?></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form id="form-paciente" class="form-horizontal">
                    <input type="hidden" id="idPaciente" name="idPaciente" value="0">



                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('patients.names') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>
                                <input type="text" name="nombres" id="nombres" class="form-control <?= session('error.nombres') ? 'is-invalid' : '' ?>" value="<?= old('nombres') ?>" placeholder="<?= lang('patients.names') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('patients.lastName') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>
                                <input type="text" name="apellidos"  id="apellidos"class="form-control <?= session('error.apellidos') ? 'is-invalid' : '' ?>" value="<?= old('apellidos') ?>" placeholder="<?= lang('patients.lastName') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="dni" class="col-sm-2 col-form-label"><?= lang('patients.dni') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-credit-card"></i></span>
                                </div>
                                <input type="text" name="dni"  id="dni" class="form-control <?= session('error.dni') ? 'is-invalid' : '' ?>" value="<?= old('dni') ?>" placeholder="<?= lang('patients.dni') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="phone" class="col-sm-2 col-form-label"><?= lang('patients.phone') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-mobile"></i></span>
                                </div>
                                <input type="text" name="telefono" id="telefono" class="form-control <?= session('error.phone') ? 'is-invalid' : '' ?>" value="<?= old('telefono') ?>" placeholder="<?= lang('patients.phone') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="correoElectronico" class="col-sm-2 col-form-label"><?= lang('patients.email') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-envelope"></i></span>
                                </div>
                                <input type="text" name="correoElectronico" id="correoElectronico" class="form-control <?= session('error.correoElectronico') ? 'is-invalid' : '' ?>" value="<?= old('correoElectronico') ?>" placeholder="<?= lang('patients.email') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                <button type="button" class="btn btn-primary btn-sm" id="btnGuardarPaciente"><?= lang('boilerplate.global.save') ?></button>
            </div>
        </div>
    </div>
</div>

<?= $this->section('js') ?>


<script>

    $(document).on('click', '.btnAgregarPaciente', function (e) {

        console.log("asdasd");
        $(".form-control").val("");

        $("#idPaciente").val("0");
        
           $("#btnGuardarPaciente").removeAttr("disabled");

    })

    /* 
     * AL hacer click al editar
     */



    $(document).on('click', '.btnEditarPaciente', function (e) {


        var idPaciente = $(this).attr("idPaciente");

        //LIMPIAMOS CONTROLES
        $(".form-control").val("");

        $("#idPaciente").val(idPaciente);
        $("#btnGuardarPaciente").removeAttr("disabled");

    })

    /**
     * Guardar paciente
     */

    $(document).on('click', '#btnGuardarPaciente', function (e) {

        var idPaciente = $("#idPaciente").val();
        var nombres = $("#nombres").val();
        var apellidos = $("#apellidos").val();
        var telefono = $("#telefono").val();
        var dni = $("#dni").val();
        var correoElectronico = $("#correoElectronico").val();

        var ajaxPaciente = "ajaxPaciente";


        $("#btnGuardarPaciente").attr("disabled", true);


        var datos = new FormData();
        datos.append("idPaciente", idPaciente);
        datos.append("nombres", nombres);
        datos.append("apellidos", apellidos);
        datos.append("telefono", telefono);
        datos.append("dni", dni);
        datos.append("correoElectronico", correoElectronico);

        $.ajax({

            url: "<?= route_to('admin/pacientes/guardar') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            //dataType:"json",
            success: function (respuesta) {


                if (respuesta.match(/Correctamente.*/)) {


                    Toast.fire({
                        icon: 'success',
                        title: "Guardado Correctamente"
                    });


                    $('.tablaPacientes').DataTable().destroy();
                    cargaTabla();
                    $("#btnGuardarPaciente").removeAttr("disabled");

                   
                     $('#modalAgregarPaciente').modal('hide');
                } else {

                    Toast.fire({
                        icon: 'error',
                        title: respuesta
                    });

                    $("#btnGuardarPaciente").removeAttr("disabled");
                  //  $('#modalAgregarPaciente').modal('hide');

                }

            }

        }

        )




    });
</script>


<?= $this->endSection() ?>

Ahora configuramos las rutas en app/config/routes.php

    $routes->resource('pacientes', [
        'filter' => 'permission:pacientes-permiso',
        'controller' => 'PacientesController',
        'except' => 'show'
    ]);

    $routes->post('pacientes/guardar', 'PacientesController::guardar');

    $routes->post('pacientes/traerPaciente', 'PacientesController::traePaciente');

    $routes->post('pacientes/traerPacientesAjax', 'PacientesController::traerPacientesAjax');

En languaje en la carpeta es creamos el archivo patients con el siguiente codigo

<?php

$patients["names"] = "Firs Name";
$patients["lastName"] = "Last Name";
$patients["dni"] = "CARD ID";
$patients["phone"] = "Phone";
$patients["email"] = "E-Mail";
$patients["createdAt"] = "Date Creation";
$patients["updateAt"] = "Date Update";
$patients["add"] = "Add Patient";
$patients["actions"] = "Actions";
$patients["createEdit"] = "Create  / Edit Patient";
$patients["title"] = "Patient";
$patients["subtitle"] = "List of Patients";

$patients["msg_delete"] = "Patient has deleted .";
$patients["msg_get_fail"] = "The patient not exist or has deleted.";





return $patients;

Y en la carpeta es de español creamos el archivo patients con el siguiente código

<?php

$patients["names"] = "Nombres";
$patients["lastName"] = "Apellidos";
$patients["dni"] = "INE";
$patients["phone"] = "Telefono";
$patients["email"] = "Correo Electronico";
$patients["add"] = "Agregar Paciente";
$patients["updateAt"] = "Fecha Modificacion";
$patients["actions"] = "Acciones";
$patients["createEdit"] = "Crear / Editar Paciente";
$patients["title"] = "Pacientes";
$patients["subtitle"] = "Lista de Pacientes";

$patients["msg_delete"] = "El paciente ha sido eliminado correctamente.";
$patients["msg_get_fail"] = "El paciente no existe o fue eliminado.";




return $patients;
Y listo ya tenemos nuestro CRUD de pacientes

Página 9 de 13

Creado con WordPress & Tema de Anders Norén