Herramientas Informaticas

Autor: juliocesar20200413 Página 13 de 139

Desarrollador web apasionado y gusto por la buena musica

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 modulo de configuración de correo electrónico #04

Ya creamos el catalogo de empresas, ahora vamos a ir creando un modulo de configuración de correo electrónico

Primero creamos el archivo del modelo en app/Models/SettingsMailModel.php con el siguiente contenido

<?php

namespace App\Models;

use CodeIgniter\Model;

class SettingsMailModel extends Model
{
    protected $table      = 'mailsettings';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

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

    protected $allowedFields = ['id', 'email', 'host', 'smtpDebug', 'SMTPAuth', 'port', 'created_at', 'deleted_at', 'updated_at', 'smptSecurity', 'pass'];
}

Para la tabla donde se guardara la información corremos el siguiente código SQL en PHPMYADMIN en nuestra base de datos, en publicaciones posteriores les dejare los archivos de migración para que no tengan la necesidad de correr archivos SQL y sirva para diferentes motores de base de datos

-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 18-04-2023 a las 00:56:17
-- 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: `wheelsoft`
--

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

--
-- Estructura de tabla para la tabla `mailsettings`
--

CREATE TABLE `mailsettings` (
  `id` int(11) NOT NULL,
  `email` varchar(512) DEFAULT NULL,
  `host` varchar(128) DEFAULT NULL,
  `smtpDebug` varchar(16) DEFAULT NULL,
  `SMTPAuth` varchar(16) DEFAULT NULL,
  `port` varchar(16) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `smptSecurity` varchar(64) DEFAULT NULL,
  `pass` varchar(128) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

--
-- Volcado de datos para la tabla `mailsettings`
--

INSERT INTO `mailsettings` (`id`, `email`, `host`, `smtpDebug`, `SMTPAuth`, `port`, `created_at`, `deleted_at`, `updated_at`, `smptSecurity`, `pass`) VALUES
(1, 'mail@domain.com', 'mail.host5.com', '1', '1', '587', NULL, NULL, NULL, 'tls', 'password');

--
-- Índices para tablas volcadas
--

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

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `mailsettings`
--
ALTER TABLE `mailsettings`
  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 */;

Creamos el archivo app/controllers/SettingsMailController.php con el siguiente contenido

<?php

namespace julio101290\boilerplate\Controllers\Users;

namespace App\Controllers;

use CodeIgniter\API\ResponseTrait;
use CodeIgniter\Controller;
use App\Models\SettingsMailModel;
use App\Models\LogModel;
use julio101290\boilerplate\Controllers\BaseController;
use julio101290\boilerplate\Entities\Collection;
use julio101290\boilerplate\Models\GroupModel;
use CodeIgniter\Config\Services;
use App\Models\RegisterModel;
use App\Controllers\RegisterController;
use PHPMailer\PHPMailer;

/**
 * Class UserController.
 */
class SettingsMailController extends BaseController
{

    use ResponseTrait;

    /** @var \agungsugiarto\boilerplate\Models\GroupModel */
    protected $group;
    protected $settingsMail;
    protected $log;
    protected $register;
    protected $registerController;
    protected $custumer;

    /** @var \agungsugiarto\boilerplate\Models\UserModel */
    protected $users;

    public function __construct()
    {
        $this->group = new GroupModel();
        $this->log = new LogModel();
        $this->settingsMail = new SettingsMailModel();
        $autorize = $this->authorize = Services::authorization();
        helper('menu');
    }

    public function index()
    {




        $datos = $this->settingsMail->where("id", 1)->first();

        $data["title"] = "Correo Electronicos";
        $data["subtitle"] = "Configuraciones de Correo Electronico";
        $data["data"] = $datos;

        return view('mailSettings', $data);
    }

    /** 
    public function sendMailPDF($uuid)
    {

        //DATOS CORREO
        $datos = $this->settingsMail->where("id", 1)->first();

        //DATOS  REGISTRO

        $register = $this->register->select("*")->where("uuid", $uuid)->first();

        $custumer = $this->custumer->select("*")->where("id", $register["custumer"])->first();

        $mailsTarjets = "";

        $correo = $datos["email"];
        $SMTPDebug = $datos["smtpDebug"];
        $host = $datos["host"];

        if ($datos["SMTPAuth"] == 1) {
            $SMTPAuth = true;
        } else {
            $SMTPAuth = false;
        }

        $puerto = $datos["port"];
        $clave = $datos["pass"];

        $SMTPSeguridad = $datos["smptSecurity"];

        // Load Composer's autoloader
        // Instantiation and passing `true` enables exceptions
        $mail = new PHPMailer\PHPMailer();

        try {


            //Server settings
            $mail->SMTPDebug = $SMTPDebug;                      // Enable verbose debug output
            $mail->isSMTP();                                            // Send using SMTP
            $mail->Host = $host;                    // Set the SMTP server to send through
            $mail->SMTPAuth = $SMTPAuth;                                   // Enable SMTP authentication
            $mail->Username = $correo;                     // SMTP username
            $mail->Password = $clave;                               // SMTP password
            $mail->SMTPSecure = $SMTPSeguridad;         // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` also accepted
            $mail->Port = $puerto;

            $nombreEmpresa = "";
            // TCP port to connect to
            //Recipients
            $mail->setFrom($correo, $nombreEmpresa);

            if ($custumer["email1"] != "") {
                try {
                    $mailAddress = $mail->addAddress($custumer["email1"], '');

                    if (!$mailAddress) {

                        echo "Error con el correo Electronico";
                        return;
                    }
                } catch (Exception $ex) {

                    echo $ex->getMessage();
                }
            }

            if ($custumer["email2"] != "") {

                try {
                    $mailAddress = $mail->addAddress($custumer["email2"], '');

                    if (!$mailAddress) {

                        echo "Error con el correo Electronico";
                        return;
                    }
                } catch (Exception $ex) {

                    echo $ex->getMessage();
                }
            }

            if ($custumer["email3"] != "") {

                try {
                    $mailAddress = $mail->addAddress($custumer["email3"], '');

                    if (!$mailAddress) {

                        echo "Error con el correo Electronico";
                        return;
                    }

                    if (!$mailAddress) {

                        echo "Error con el correo Electronico";
                        return;
                    }
                } catch (Exception $ex) {

                    echo $ex->getMessage();
                }
            }

            if ($custumer["email4"] != "") {

                try {
                    $mailAddress = $mail->addAddress($custumer["email4"], '');

                    if (!$mailAddress) {

                        echo "Error con el correo Electronico";
                        return;
                    }
                } catch (Exception $ex) {

                    echo $ex->getMessage();
                }
            }

            if ($custumer["email5"] != "") {

                try {
                    $mailAddress = $mail->addAddress($custumer["email5"], '');

                    if (!$mailAddress) {

                        echo "Error con el correo Electronico";
                        return;
                    }
                } catch (Exception $ex) {

                    echo $ex->getMessage();
                }
            }
            // Add a recipient
            //$mail->addReplyTo('info@example.com', 'Information');
            //mail->addCC('cc@example.com');
            //$mail->addBCC('bcc@example.com');
            // Attachments
            $attachment = $this->registerController->report($uuid, 1);
            $mail->AddStringAttachment($attachment, 'registro' . $register["codeCustumer"] . '.pdf', 'base64', 'application/pdf');

            // Add attachments
            //$mail->addAttachment('/tmp/image.jpg', 'new.jpg');    // Optional name
            // Content
            $mail->isHTML(true);                                  // Set email format to HTML
            $mail->Subject = "Envio de Registro";
            $mail->Body = "Adjuntamos el registro de verificación de neumaticos";
            $mail->AltBody = 'This is the body in plain text for non-HTML mail clients';

            try {
                $send = $mail->send();
            } catch (Exception $ex) {

                echo $ex->getMessage();
                return;
            }

            if ($send) {
                echo 'Correo Enviado Correctamente';
            } else {
                echo 'Error al enviar el correo';
            }
        } catch (Exception $e) {
            echo "Error al enviar el correo: {$e->ErrorInfo}";
        }
    }
*/
    public function guardar()
    {


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

        //GUARDA CONFIGURACIONES
        $this->settingsMail->update(1, $_POST);

        //  return redirect()->to("/admin/hospital");
        return redirect()->back()->with('sweet-success', 'Actualizado Correctamente');
        // return redirect()->back()->with('sweet-success','Guardado Correctamente');
    }
}

Creamos el archivo app/views/mailSettings.php con el siguiente contenido

<?= $this->extend('julio101290\boilerplate\Views\layout\index') ?>

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



<section class="content">
    <div class="container-fluid">
        <div class="row">

            <div class="col-md-12">

                <div class="card card-primary">
                    <div class="card-header">
                        <h3 class="card-title">Configuración Correo Electrónico</h3>
                    </div>



                    <form action="<?= base_url('admin/mailSettings') ?>/save" method="post">
                        <?= csrf_field() ?>

                        <div class="card-body">


                            <div class="form-group">
                                <label for="nombreEmpresa">E-Mail</label>
                                <input type="text" class="form-control" id="email" value="<?= $data["email"] ?>" name="email" placeholder="Inserte el Email">
                            </div>

                            <div class="form-group">
                                <label for="correoElectronico">Host</label>
                                <input type="text" class="form-control" value="<?= $data["host"] ?>" id="host" name="host" placeholder="Host">
                            </div>

                            <div class="form-group">
                                <label for="smtpDebug">Debug Client</label>
                                <select id="smtpDebug" name="smtpDebug">

                                    <option value="0">DEBUG_OFF</option>

                                    <option value="1">DEBUG_CLIENT</option>

                                    <option value="2">DEBUG_SERVER</option>

                                </select>

                            </div>

                            <div class="form-group">
                                <label for="SMTPAuth">SMTP Auth</label>
                                <select id="SMTPAuth" name="SMTPAuth">


                                    <option value=""></option>

                                    <option value="0">Sin autentificación</option>

                                    <option value="1">Con autentificación</option>


                                </select>

                            </div>


                            <div class="form-group">
                                <label for="smptSecurity">Seguridad</label>
                                <select id="smptSecurity" name="smptSecurity">


                                    <option value="">Sin Seguridad</option>
                                    <option value="ssl">Seguridad SSL</option>
                                    <option value="tls">Seguridad TLS</option>

                                </select>

                            </div>


                            <div class="form-group">
                                <label for="port">Puerto</label>
                                <input type="number" class="form-control" value="<?= $data["port"] ?>" id="port" name="port" placeholder="Puerto">
                            </div>

                            <div class="form-group">
                                <label for="port">Contraseña</label>
                                <input type="password" class="form-control" value="<?= $data["pass"] ?>" id="pass" name="pass" placeholder="Contraseña">
                            </div>


                        </div>

                        <div class="card-footer">
                            <button type="submit" class="btn btn-primary btnGuardar">Guardar</button>
                        </div>
                    </form>
                </div>





            </div>



        </div>

    </div>
</section>


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


<?= $this->section('js') ?>
<script>
    $("#smptSecurity").val("<?= $data["smptSecurity"] ?>");
    $("#SMTPAuth").val("<?= $data["SMTPAuth"] ?>");
    $("#smtpDebug").val("<?= $data["smtpDebug"] ?>");
</script>
<?= $this->endSection() ?>

en app/config/routes.php agregamos el siguiente codigo

 $routes->resource('emailSettings', [
        'filter' => 'permission:email-permiso',
        'controller' => 'SettingsMailController',
    ]);
    
    $routes->post('mailSettings/save', 'SettingsMailController::guardar');
    
    //Para futuros envios
    //$routes->get('mailSettings/sendMail/(:any)', 'SettingsMailController::sendMailPDF/$1');
Y listo ya tenemos nuestro modulo de configuración solo tenemos que ajustar unos permisos y roles
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

CI4JCPOS Construyendo Menú #2

Ya tenemos la base del proyecto, ahora lo que sigue es darle forma creando los menús

Realmente aquí no hay nada complejo, el menú soporta 2 niveles, podemos escoger nuestro icono de font awesome le pones la ruta y que roles tendrán acceso a ese nivel, para acomodar el menú solo basta con arrastrar y soltar los items
Realmente aquí no hay nada complejo, el menú soporta 2 niveles, podemos escoger nuestro icono de font awesome le pones la ruta y que roles tendrán acceso a ese nivel, para acomodar el menú solo basta con arrastrar y soltar los items
Video Demostrativo

CURSO CREANDO UN SISTEMA DE PUNTO DE VENTA EN CODEIGNITER 4

Dejamos este tutorial en el cual veremos como hacer de la manera mas fácil un sistema de punto de venta similar al JCPOS

Lo que tendrá el sistema será lo siguiente

  1. Administrador de usuarios
  2. Catalogo de categorias
  3. Catalogo de productos
  4. Catalogo de clientes
  5. Cotizaciones
  6. Ventas
  7. Control de inventarios
  1. Introducción – Creando Proyecto #01
  2. Construyendo Menú #02
  3. Creando catalogo de empresas #03
  4. Creando modulo de configuración de correo electrónico #04
  5. Creando CRUD de categorías usando AutoCrud #05
  6. Creando CRUD de clientes #06
  7. Creando CRUD de productos #07
  8. Descargar catálogos del SAT CFDI en SQLITE
  9. Creando CRUD de sucursales #09
  10. Creando CRUD de Series Electrónicas #10
  11. Creando CRUD de Proveedores #11
  12. Cómo crear un módulo de kardex de inventario en CodeIgniter 4 #12
  13. Cómo crear un módulo de ventas con factura electrónica en CodeIgniter 4 #13
  14. Creando el CRUD de Tipos de Vehículos #14
  15. Creando CRUD de Vehículos #15
  16. Creando CRUD de choferes en CodeIgniter 4 #16
  17. Descarga Masiva en PHP CodeIgniter 4 #17
  18. Modulo para complemento de pago en CodeIgniter 4 PHP 8 #18
  19. Creando CRUD de ubicaciones para la carta porte 3.0 en CodeIgniter 4 #19
  20. Agregando campos nuevos para vehículos y usarlo en carta porte 3.0 en CodeIgniter 4 #20
  21. Agregando campos nuevos para choferes y usarlo en carta porte 3.0 en CodeIgniter 4 #21
  22. Creando CRUD de remolques para la carta porte 3.0 en CodeIgniter 4 #22

CI4JCPOS – INTRODUCCION #01

En esta serie de publicaciones vamos a hacer un sistema de punto de venta con el Framework de PHP CodeIgniter 4

El sistema será igual al JCPOS que ya veníamos haciendo pero con todas las ventajas que ofrece CodeIgniter

Primero crearemos el proyecto con Composer

composer create-project codeigniter4/appstarter ci4jcpos

Creamos nuestra base de datos en phpmyadmin con el nombre ci4jcpos

CREATE SCHEMA ci4jcpos DEFAULT CHARACTER SET utf8 COLLATE utf8_spanish2_ci

Ahora instalamos la plantilla boilerplate el cual nos instalara la plantilla adminlte3 con el administrador de usuario auth, lo instalamos corriendo el siguiente comando dentro del proyecto

En mi caso yo deje en así app/config/database.php

    public array $default = [
        'DSN'      => '',
        'hostname' => '127.0.0.1',
        'username' => 'root',
        'password' => '',
        'database' => 'ci4jcpos',
        'DBDriver' => 'MySQLi',
        'DBPrefix' => '',
        'pConnect' => false,
        'DBDebug'  => true,
        'charset'  => 'utf8',
        'DBCollat' => 'utf8_general_ci',
        'swapPre'  => '',
        'encrypt'  => false,
        'compress' => false,
        'strictOn' => false,
        'failover' => [],
        'port'     => 3306,
    ];
composer require julio101290/boilerplate

Ahora siguiente las instrucciones del paquete https://packagist.org/packages/julio101290/boilerplate corremos el comando migrate para 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

En app/config/auth.php modifcamos la matriz/arreglo de $views y la dejamos asi

       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',
    ];

En app/config/filters.php en la matriz aliases agregamos los siguientes valores, ojo, no quitar los que estaban, solo agregar los que hacen falta

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

Instalamos boilerplate con el siguiente comando

php spark boilerplate:install

En App/config/validation.php agregamos lo siguiente en la matriz de $$rulesSet

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

En App/entities/users.php en la matriz $cast lo dejamos así

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

En app/controller/home.php lo dejamos así para que entre directamente al administrador

<?php

namespace App\Controllers;

class Home extends BaseController
{
    public function index()
    {
        return redirect()->to("/admin");
    }
}

En app/config/constants.php al final del archivo le agregamos la siguiente constante para que nos permita debugear, una vez que este en producción se tiene que quitar


define("ENVIRONMENT","development");

En app/config/app.php le quita a la variable $indexPage el valor dejandolo en blanco, la variable defaultLocale y supportedLocales la dejamos con el valor “es”

public string $indexPage = '';
public string $defaultLocale = 'es';
public array $supportedLocales = ['en'];

En el archivo app/config/boilerplate.php asignamos el nombre de la app y en $i18n que es para el lenguaje de los datatables lo dejamos como Spanish

Y listo ya tenemos nuestro login y adminlte3 listo para agregarle los módulos
Video Demostrativo

CI 4.0 JCPOS Punto de Venta En CodeIgniter4 Introducción #1

JCPOS es un sistema de punto de venta (POS) de código abierto desarrollado en PHP. Recientemente, JCPOS se ha migrado a CodeIgniter 4, lo que ha mejorado significativamente su rendimiento y escalabilidad. En este artículo, exploraremos los detalles de la migración de JCPOS a CodeIgniter 4 y sus beneficios.

Antes de profundizar en la migración de JCPOS a CodeIgniter 4, es importante comprender qué es CodeIgniter y por qué es una opción popular para desarrolladores de PHP.

CodeIgniter es un framework PHP de código abierto que se utiliza para desarrollar aplicaciones web dinámicas y sitios web. Es popular debido a su facilidad de uso, velocidad y seguridad. Además, CodeIgniter tiene una gran comunidad de desarrolladores que proporciona soporte y actualizaciones regulares.

La migración de JCPOS a CodeIgniter 4 implica actualizar el código de JCPOS para que sea compatible con la última versión del framework. CodeIgniter 4 presenta varias mejoras y cambios significativos en comparación con su versión anterior, CodeIgniter 3. Algunas de estas mejoras incluyen:

  • Mayor rendimiento y velocidad gracias a la utilización de características avanzadas de PHP 7
  • Soporte mejorado para la creación de APIs RESTful
  • Mejoras en la seguridad y en la gestión de sesiones
  • Uso de clases y métodos modernos para mejorar la legibilidad y mantenibilidad del código
  • Flexibilidad y escalabilidad mejoradas gracias a su arquitectura modular

Para migrar JCPOS a CodeIgniter 4, los desarrolladores tuvieron que actualizar el código de JCPOS para utilizar las nuevas características y métodos de CodeIgniter 4. Esto incluyó cambios en la estructura del directorio, en la configuración del archivo y en el uso de nuevos métodos y características de CodeIgniter 4.

Una vez completada la migración, JCPOS obtuvo varios beneficios significativos. En primer lugar, el rendimiento y la velocidad de JCPOS se mejoraron considerablemente gracias a la utilización de características avanzadas de PHP 7 y a la optimización de la arquitectura de CodeIgniter 4. Esto significa que el sistema puede manejar más transacciones y usuarios simultáneamente, lo que es especialmente importante en entornos de punto de venta de alta demanda.

Además, la migración a CodeIgniter 4 proporcionó una mayor flexibilidad y escalabilidad a JCPOS. La arquitectura modular de CodeIgniter 4 permite que los desarrolladores de JCPOS agreguen fácilmente nuevas funcionalidades y características al sistema sin afectar negativamente al rendimiento o la estabilidad. Esto significa que JCPOS puede adaptarse y crecer para satisfacer las necesidades cambiantes de sus usuarios.

En conclusión, la migración de JCPOS a CodeIgniter 4 ha mejorado significativamente el rendimiento, la escalabilidad y la flexibilidad del sistema. Los desarrolladores de JCPOS han aprovechado las características avanzadas y la arquitectura modular de CodeIgniter 4 para optimizar el sistema y garantizar su compatibilidad con las últimas tecnologías y tendencias en el desarrollo web.

Página 13 de 139

Creado con WordPress & Tema de Anders Norén