La Carta Porte 3.0 es la versión más reciente del complemento que debe acompañar a los Comprobantes Fiscales Digitales por Internet (CFDI) que amparan el traslado de mercancías en México. Esta versión entró en vigor el 25 de noviembre de 2023 y es obligatoria a partir del 1 de enero de 2024.

La Carta Porte 3.0 tiene como objetivo mejorar la trazabilidad de las mercancías que se transportan en México, así como facilitar el cumplimiento de las obligaciones fiscales de los contribuyentes. Para ello, incluye nuevos campos y requisitos que deben ser proporcionados por los emisores de los CFDI.

Entre los principales cambios que introduce la Carta Porte 3.0 se encuentran los siguientes:

  • La inclusión de nuevos datos sobre el transporte de las mercancías, como el medio de transporte, la ruta y la fecha de salida y llegada.
  • La obligación de identificar al destinatario de las mercancías, incluso si es una persona física.
  • La posibilidad de utilizar un identificador único para el CFDI, lo que facilitará su consulta y verificación.

La Carta Porte 3.0 es un complemento obligatorio para todos los contribuyentes que realicen el traslado de mercancías en México, independientemente de su tamaño o actividad económica. Los contribuyentes que no cumplan con esta obligación podrán ser sancionados por el Servicio de Administración Tributaria (SAT).

A continuación, se presentan algunos ejemplos de los nuevos campos que deben ser proporcionados en la Carta Porte 3.0:

  • Medio de transporte: Se debe indicar el tipo de medio de transporte utilizado para el traslado de las mercancías, como camión, tren, barco o avión.
  • Ruta: Se debe indicar la ruta que se seguirá para el traslado de las mercancías, incluyendo los puntos de origen y destino.
  • Fecha de salida y llegada: Se debe indicar la fecha en que se iniciará y concluirá el traslado de las mercancías.
  • Destinatario: Se debe identificar al destinatario de las mercancías, incluyendo su nombre, RFC y domicilio.
  • Identificador único del CFDI: Se debe proporcionar un identificador único para el CFDI, el cual podrá ser generado por el SAT o por el emisor del CFDI.

Para obtener más información sobre la Carta Porte 3.0, se puede consultar el sitio web del SAT.

Para empezar el modulo de carta porte primero tenemos que tener los catálogos necesarios, en este caso mostraremos como crear el catalogo de ubicaciones para ello primero creamos el archivo de migración App/Databases/Migrations/2023-11-21114419_Ubicaciones.php con el siguiente código.

<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class Ubicaciones extends Migration {

    public function up() {
        // Ubicaciones
        $this->forge->addField([
            'id' => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
            'idEmpresa' => ['type' => 'bigint', 'constraint' => 20, 'null' => false],
            'calle' => ['type' => 'text', 'null' => true],
            'numInterior' => ['type' => 'varchar', 'constraint' => 32, 'null' => true],
            'numExterior' => ['type' => 'varchar', 'constraint' => 32, 'null' => true],
            'colonia' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
            'localidad' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
            'referencia' => ['type' => 'varchar', 'constraint' => 256, 'null' => true],
            'municipio' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
            'estado' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
            'pais' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
            'codigoPostal' => ['type' => 'varchar', 'constraint' => 16, '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('ubicaciones', true);
    }

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

}

Creamos el archivo modelo en App/Models/UbicacionesModel.php con el siguiente código

<?php

namespace App\Models;

use CodeIgniter\Model;

class UbicacionesModel extends Model {

    protected $table = 'ubicaciones';
    protected $primaryKey = 'id';
    protected $useAutoIncrement = true;
    protected $returnType = 'array';
    protected $useSoftDeletes = true;
    protected $allowedFields = ['id'
        , 'idEmpresa'
        , 'calle'
        , 'numInterior'
        , 'numExterior'
        , 'colonia'
        , 'localidad'
        , 'referencia'
        , 'municipio'
        , 'estado'
        , 'pais'
        , 'codigoPostal'
        , 'created_at'
        , 'updated_at'
        , 'deleted_at'];
    protected $useTimestamps = true;
    protected $createdField = 'created_at';
    protected $deletedField = 'deleted_at';
    protected $validationRules = [
        'codigoPostal' => 'required|regex_match[/^[0-9]{5}$/]',
        'colonia' => 'required|numeric',
        'localidad' => 'required|numeric',
        'municipio' => 'required|numeric',
        'pais' => 'required|regex_match[/^[a-zA-Z]{3}$/]',
        'estado' => 'required|regex_match[/^[a-zA-Z]{3}$/]',
    ];
    protected $validationMessages = [];
    protected $skipValidation = false;

    public function mdlGetUbicaciones($idEmpresas) {

        $result = $this->db->table('ubicaciones a, empresas b')
                ->select('a.id
                         ,a.idEmpresa
                         ,a.calle
                         ,a.numInterior
                         ,a.numExterior
                         ,a.colonia
                         ,a.localidad
                         ,a.referencia
                         ,a.municipio
                         ,a.estado
                         ,a.pais
                         ,a.codigoPostal
                         ,a.created_at
                         ,a.updated_at
                         ,a.deleted_at 
                         ,b.nombre as nombreEmpresa')
                ->where('a.idEmpresa', 'b.id', FALSE)
                ->whereIn('a.idEmpresa', $idEmpresas);

        return $result;
    }

}

Creamos el archivo controlador en App/Controllers/UbicacionesController.php con el siguiente código

<?php

namespace App\Models;

use CodeIgniter\Model;

class UbicacionesModel extends Model {

    protected $table = 'ubicaciones';
    protected $primaryKey = 'id';
    protected $useAutoIncrement = true;
    protected $returnType = 'array';
    protected $useSoftDeletes = true;
    protected $allowedFields = ['id'
        , 'idEmpresa'
        , 'calle'
        , 'numInterior'
        , 'numExterior'
        , 'colonia'
        , 'localidad'
        , 'referencia'
        , 'municipio'
        , 'estado'
        , 'pais'
        , 'codigoPostal'
        , 'created_at'
        , 'updated_at'
        , 'deleted_at'];
    protected $useTimestamps = true;
    protected $createdField = 'created_at';
    protected $deletedField = 'deleted_at';
    protected $validationRules = [
        'codigoPostal' => 'required|regex_match[/^[0-9]{5}$/]',
        'colonia' => 'required|numeric',
        'localidad' => 'required|numeric',
        'municipio' => 'required|numeric',
        'pais' => 'required|regex_match[/^[a-zA-Z]{3}$/]',
        'estado' => 'required|regex_match[/^[a-zA-Z]{3}$/]',
    ];
    protected $validationMessages = [];
    protected $skipValidation = false;

    public function mdlGetUbicaciones($idEmpresas) {

        $result = $this->db->table('ubicaciones a, empresas b')
                ->select('a.id
                         ,a.idEmpresa
                         ,a.calle
                         ,a.numInterior
                         ,a.numExterior
                         ,a.colonia
                         ,a.localidad
                         ,a.referencia
                         ,a.municipio
                         ,a.estado
                         ,a.pais
                         ,a.codigoPostal
                         ,a.created_at
                         ,a.updated_at
                         ,a.deleted_at 
                         ,b.nombre as nombreEmpresa')
                ->where('a.idEmpresa', 'b.id', FALSE)
                ->whereIn('a.idEmpresa', $idEmpresas);

        return $result;
    }

}

Creamos el archivo de la vista principal para ubicaciones en App/Views/ubicaciones.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('modulesUbicaciones/modalCaptureUbicaciones') ?>

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

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

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

                </button>

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

                                <th>#</th>
                                <th><?= lang('ubicaciones.fields.idEmpresa') ?></th>
                                <th><?= lang('ubicaciones.fields.calle') ?></th>
                                <th><?= lang('ubicaciones.fields.numInterior') ?></th>
                                <th><?= lang('ubicaciones.fields.numExterior') ?></th>
                                <th><?= lang('ubicaciones.fields.colonia') ?></th>
                                <th><?= lang('ubicaciones.fields.localidad') ?></th>
                                <th><?= lang('ubicaciones.fields.referencia') ?></th>
                                <th><?= lang('ubicaciones.fields.municipio') ?></th>
                                <th><?= lang('ubicaciones.fields.estado') ?></th>
                                <th><?= lang('ubicaciones.fields.pais') ?></th>
                                <th><?= lang('ubicaciones.fields.codigoPostal') ?></th>
                                <th><?= lang('ubicaciones.fields.created_at') ?></th>
                                <th><?= lang('ubicaciones.fields.updated_at') ?></th>
                                <th><?= lang('ubicaciones.fields.deleted_at') ?></th>

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

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

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


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

    /**
     * Cargamos la tabla
     */

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

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

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

            {
                'data': 'idEmpresa'
            },

            {
                'data': 'calle'
            },

            {
                'data': 'numInterior'
            },

            {
                'data': 'numExterior'
            },

            {
                'data': 'colonia'
            },

            {
                'data': 'localidad'
            },

            {
                'data': 'referencia'
            },

            {
                'data': 'municipio'
            },

            {
                'data': 'estado'
            },

            {
                'data': 'pais'
            },

            {
                'data': 'codigoPostal'
            },

            {
                '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 btnEditUbicaciones" data-toggle="modal" idUbicaciones="${data.id}" data-target="#modalAddUbicaciones">  <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', '#btnSaveUbicaciones', function (e) {


        var idUbicaciones = $("#idUbicaciones").val();
        var idEmpresa = $("#idEmpresa").val();
        var calle = $("#calle").val();
        var numInterior = $("#numInterior").val();
        var numExterior = $("#numExterior").val();
        var colonia = $("#colonia").val();
        var localidad = $("#localidad").val();
        var referencia = $("#referencia").val();
        var municipio = $("#municipio").val();
        var estado = $("#estado").val();
        var pais = $("#pais").val();
        var codigoPostal = $("#codigoPostal").val();

        if (idEmpresa == 0 || idEmpresa == null) {

            Toast.fire({
                icon: 'error',
                title: "Tiene que seleccionar la empresa"
            });
            return;
        }




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

        var datos = new FormData();
        datos.append("idUbicaciones", idUbicaciones);
        datos.append("idEmpresa", idEmpresa);
        datos.append("calle", calle);
        datos.append("numInterior", numInterior);
        datos.append("numExterior", numExterior);
        datos.append("colonia", colonia);
        datos.append("localidad", localidad);
        datos.append("referencia", referencia);
        datos.append("municipio", municipio);
        datos.append("estado", estado);
        datos.append("pais", pais);
        datos.append("codigoPostal", codigoPostal);


        $.ajax({

            url: "<?= base_url('admin/ubicaciones/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"
                    });

                    tableUbicaciones.ajax.reload();
                    $("#btnSaveUbicaciones").removeAttr("disabled");


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

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

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


                }

            }

        }

        )

    });



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR Ubicaciones
     =============================================*/
    $(".tableUbicaciones").on("click", ".btnEditUbicaciones", function () {

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

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

        $.ajax({

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

                $("#idEmpresa").val(respuesta["idEmpresa"]);
                $("#idEmpresa").trigger("change");

                $("#calle").val(respuesta["calle"]);
                $("#numInterior").val(respuesta["numInterior"]);
                $("#numExterior").val(respuesta["numExterior"]);
                $("#colonia").val(respuesta["colonia"]);
                $("#localidad").val(respuesta["localidad"]);
                $("#referencia").val(respuesta["referencia"]);
                $("#municipio").val(respuesta["municipio"]);
                $("#estado").val(respuesta["estado"]);

                var newOption = new Option(respuesta["pais"] + ' ' + respuesta["nombrePais"], respuesta["pais"], true, true);
                $('#pais').append(newOption).trigger('change');
                $("#pais").val(respuesta["pais"]);

                var newOption = new Option(respuesta["estado"] + ' ' + respuesta["nombreEstado"], respuesta["estado"], true, true);
                $('#estado').append(newOption).trigger('change');
                $("#estado").val(respuesta["estado"]);

                var newOption = new Option(respuesta["municipio"] + ' ' + respuesta["nombreMunicipio"], respuesta["municipio"], true, true);
                $('#municipio').append(newOption).trigger('change');
                $("#municipio").val(respuesta["municipio"]);

                var newOption = new Option(respuesta["localidad"] + ' ' + respuesta["nombreLocalidad"], respuesta["localidad"], true, true);
                $('#localidad').append(newOption).trigger('change');
                $("#localidad").val(respuesta["localidad"]);

                var newOption = new Option(respuesta["colonia"] + ' ' + respuesta["nombreColonia"], respuesta["colonia"], true, true);
                $('#colonia').append(newOption).trigger('change');
                $("#colonia").val(respuesta["colonia"]);

                $("#pais").val(respuesta["pais"]);
                $("#codigoPostal").val(respuesta["codigoPostal"]);

            }

        })

    })


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

        var idUbicaciones = $(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('admin/ubicaciones') ?>/` + idUbicaciones,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });


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

    $(function () {
        $("#modalAddUbicaciones").draggable();

    });



    /**
     * Categorias por empresa
     */

    $(".colonia").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getColoniaSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash
                var codigoPostal = $('.codigoPostal').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });



    /**
     * Categorias por empresa
     */

    $(".estado").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getEstadosSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash
                var pais = $('.pais').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });

    /**
     * Municipios
     */

    $(".municipio").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getMunicipiosSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash
                var estado = $('.estado').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });



    /**
     * Municipios
     */

    $(".localidad").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getLocalidadSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash
                var estado = $('.estado').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });

    /**
     * Categorias por empresa
     */

    $(".pais").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getPaisesSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });




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

Creamos el archivo de la vista modal para capturar las ubicaciones en App/Views/modulesUbicaciones/modalCaptureUbicaciones.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('modulesUbicaciones/modalCaptureUbicaciones') ?>

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

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

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

                </button>

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

                                <th>#</th>
                                <th><?= lang('ubicaciones.fields.idEmpresa') ?></th>
                                <th><?= lang('ubicaciones.fields.calle') ?></th>
                                <th><?= lang('ubicaciones.fields.numInterior') ?></th>
                                <th><?= lang('ubicaciones.fields.numExterior') ?></th>
                                <th><?= lang('ubicaciones.fields.colonia') ?></th>
                                <th><?= lang('ubicaciones.fields.localidad') ?></th>
                                <th><?= lang('ubicaciones.fields.referencia') ?></th>
                                <th><?= lang('ubicaciones.fields.municipio') ?></th>
                                <th><?= lang('ubicaciones.fields.estado') ?></th>
                                <th><?= lang('ubicaciones.fields.pais') ?></th>
                                <th><?= lang('ubicaciones.fields.codigoPostal') ?></th>
                                <th><?= lang('ubicaciones.fields.created_at') ?></th>
                                <th><?= lang('ubicaciones.fields.updated_at') ?></th>
                                <th><?= lang('ubicaciones.fields.deleted_at') ?></th>

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

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

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


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

    /**
     * Cargamos la tabla
     */

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

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

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

            {
                'data': 'idEmpresa'
            },

            {
                'data': 'calle'
            },

            {
                'data': 'numInterior'
            },

            {
                'data': 'numExterior'
            },

            {
                'data': 'colonia'
            },

            {
                'data': 'localidad'
            },

            {
                'data': 'referencia'
            },

            {
                'data': 'municipio'
            },

            {
                'data': 'estado'
            },

            {
                'data': 'pais'
            },

            {
                'data': 'codigoPostal'
            },

            {
                '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 btnEditUbicaciones" data-toggle="modal" idUbicaciones="${data.id}" data-target="#modalAddUbicaciones">  <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', '#btnSaveUbicaciones', function (e) {


        var idUbicaciones = $("#idUbicaciones").val();
        var idEmpresa = $("#idEmpresa").val();
        var calle = $("#calle").val();
        var numInterior = $("#numInterior").val();
        var numExterior = $("#numExterior").val();
        var colonia = $("#colonia").val();
        var localidad = $("#localidad").val();
        var referencia = $("#referencia").val();
        var municipio = $("#municipio").val();
        var estado = $("#estado").val();
        var pais = $("#pais").val();
        var codigoPostal = $("#codigoPostal").val();

        if (idEmpresa == 0 || idEmpresa == null) {

            Toast.fire({
                icon: 'error',
                title: "Tiene que seleccionar la empresa"
            });
            return;
        }




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

        var datos = new FormData();
        datos.append("idUbicaciones", idUbicaciones);
        datos.append("idEmpresa", idEmpresa);
        datos.append("calle", calle);
        datos.append("numInterior", numInterior);
        datos.append("numExterior", numExterior);
        datos.append("colonia", colonia);
        datos.append("localidad", localidad);
        datos.append("referencia", referencia);
        datos.append("municipio", municipio);
        datos.append("estado", estado);
        datos.append("pais", pais);
        datos.append("codigoPostal", codigoPostal);


        $.ajax({

            url: "<?= base_url('admin/ubicaciones/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"
                    });

                    tableUbicaciones.ajax.reload();
                    $("#btnSaveUbicaciones").removeAttr("disabled");


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

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

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


                }

            }

        }

        )

    });



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR Ubicaciones
     =============================================*/
    $(".tableUbicaciones").on("click", ".btnEditUbicaciones", function () {

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

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

        $.ajax({

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

                $("#idEmpresa").val(respuesta["idEmpresa"]);
                $("#idEmpresa").trigger("change");

                $("#calle").val(respuesta["calle"]);
                $("#numInterior").val(respuesta["numInterior"]);
                $("#numExterior").val(respuesta["numExterior"]);
                $("#colonia").val(respuesta["colonia"]);
                $("#localidad").val(respuesta["localidad"]);
                $("#referencia").val(respuesta["referencia"]);
                $("#municipio").val(respuesta["municipio"]);
                $("#estado").val(respuesta["estado"]);

                var newOption = new Option(respuesta["pais"] + ' ' + respuesta["nombrePais"], respuesta["pais"], true, true);
                $('#pais').append(newOption).trigger('change');
                $("#pais").val(respuesta["pais"]);

                var newOption = new Option(respuesta["estado"] + ' ' + respuesta["nombreEstado"], respuesta["estado"], true, true);
                $('#estado').append(newOption).trigger('change');
                $("#estado").val(respuesta["estado"]);

                var newOption = new Option(respuesta["municipio"] + ' ' + respuesta["nombreMunicipio"], respuesta["municipio"], true, true);
                $('#municipio').append(newOption).trigger('change');
                $("#municipio").val(respuesta["municipio"]);

                var newOption = new Option(respuesta["localidad"] + ' ' + respuesta["nombreLocalidad"], respuesta["localidad"], true, true);
                $('#localidad').append(newOption).trigger('change');
                $("#localidad").val(respuesta["localidad"]);

                var newOption = new Option(respuesta["colonia"] + ' ' + respuesta["nombreColonia"], respuesta["colonia"], true, true);
                $('#colonia').append(newOption).trigger('change');
                $("#colonia").val(respuesta["colonia"]);

                $("#pais").val(respuesta["pais"]);
                $("#codigoPostal").val(respuesta["codigoPostal"]);

            }

        })

    })


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

        var idUbicaciones = $(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('admin/ubicaciones') ?>/` + idUbicaciones,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });


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

    $(function () {
        $("#modalAddUbicaciones").draggable();

    });



    /**
     * Categorias por empresa
     */

    $(".colonia").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getColoniaSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash
                var codigoPostal = $('.codigoPostal').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });



    /**
     * Categorias por empresa
     */

    $(".estado").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getEstadosSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash
                var pais = $('.pais').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });

    /**
     * Municipios
     */

    $(".municipio").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getMunicipiosSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash
                var estado = $('.estado').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });



    /**
     * Municipios
     */

    $(".localidad").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getLocalidadSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash
                var estado = $('.estado').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });

    /**
     * Categorias por empresa
     */

    $(".pais").select2({
        ajax: {
            url: "<?= base_url('admin/ubicaciones/getPaisesSATAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,

            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash

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

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

                return {
                    results: response.data
                };
            },

            cache: true
        }
    });




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

Creamos el archivo de lenguaje para ingles en App/Language/en/ubicaciones.php con el siguiente código

<?php

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

$ubicaciones["add"] = "Add Ubicaciones";
$ubicaciones["edit"] = "Edit ubicaciones";
$ubicaciones["createEdit"] = "Create / Edit";
$ubicaciones["title"] = "ubicaciones management";
$ubicaciones["subtitle"] = "ubicaciones list";
$ubicaciones["fields"]["idEmpresa"] = "IdEmpresa";
$ubicaciones["fields"]["calle"] = "Calle";
$ubicaciones["fields"]["numInterior"] = "Num Interior";
$ubicaciones["fields"]["numExterior"] = "Num Exterior";
$ubicaciones["fields"]["colonia"] = "Colonia";
$ubicaciones["fields"]["localidad"] = "Localidad";
$ubicaciones["fields"]["referencia"] = "Referencia";
$ubicaciones["fields"]["municipio"] = "Municipio";
$ubicaciones["fields"]["estado"] = "Estado";
$ubicaciones["fields"]["pais"] = "Pais";
$ubicaciones["fields"]["codigoPostal"] = "Codigo Postal";
$ubicaciones["fields"]["created_at"] = "Created_at";
$ubicaciones["fields"]["updated_at"] = "Updated_at";
$ubicaciones["fields"]["deleted_at"] = "Deleted_at";

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

return $ubicaciones;

Creamos el archivo de lenguaje para español en App/Language/es/ubicaciones.php con el siguiente código

<?php

$ubicaciones["logDescription"] = "El registro en ubicaciones fue guardado con los siguientes datos:";
$ubicaciones["logUpdate"] = "El registro en ubicaciones fue actualizado con los siguientes datos:";
$ubicaciones["logDeleted"] = "El registro en ubicaciones fue eliminado con los siguientes datos:";
$ubicaciones["msg_delete"] = "El Registro en ubicaciones fue eliminado correctamente:";
$ubicaciones["add"] = "Agregar Ubicaciones";
$ubicaciones["edit"] = "Editar ubicaciones";
$ubicaciones["createEdit"] = "Crear / Editar";
$ubicaciones["title"] = "Admon. ubicaciones";
$ubicaciones["subtitle"] = "Lista ubicaciones";
$ubicaciones["fields"]["idEmpresa"] = "Empresa";
$ubicaciones["fields"]["calle"] = "Calle";
$ubicaciones["fields"]["numInterior"] = "Num Interior";
$ubicaciones["fields"]["numExterior"] = "Num Exterior";
$ubicaciones["fields"]["colonia"] = "Colonia";
$ubicaciones["fields"]["localidad"] = "Localidad";
$ubicaciones["fields"]["referencia"] = "Referencia";
$ubicaciones["fields"]["municipio"] = "Municipio";
$ubicaciones["fields"]["estado"] = "Estado";
$ubicaciones["fields"]["pais"] = "Pais";
$ubicaciones["fields"]["codigoPostal"] = "Codigo Postal";
$ubicaciones["fields"]["created_at"] = "Created_at";
$ubicaciones["fields"]["updated_at"] = "Updated_at";
$ubicaciones["fields"]["deleted_at"] = "Deleted_at";

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

Agregamos las rutas en App/Config/Routes.php en el grupo de admin el siguiente código

    /**
     * Ruta para las ubicaciones
     */
    $routes->resource('ubicaciones', [
        'filter' => 'permission:ubicaciones-permission',
        'controller' => 'ubicacionesController',
        'except' => 'show'
    ]);
    
    $routes->post('ubicaciones/save', 'UbicacionesController::save');
    $routes->post('ubicaciones/getUbicaciones', 'UbicacionesController::getUbicaciones');
    $routes->post('ubicaciones/getColoniaSATAjax', 'UbicacionesController::getColoniasSAT');
    $routes->post('ubicaciones/getLocalidadSATAjax', 'UbicacionesController::getLocalidadSAT');
    $routes->post('ubicaciones/getPaisesSATAjax', 'UbicacionesController::getPaisesSAT');
    $routes->post('ubicaciones/getEstadosSATAjax', 'UbicacionesController::getEstadosSAT');
    $routes->post('ubicaciones/getMunicipiosSATAjax', 'UbicacionesController::getMunicipiosSAT');
    

Creamos el permiso de ubicaciones con los siguientes datos

Creamos el menú de ubicaciones con los siguientes datos

Y listo ya con esto tenemos nuestro catalogo de ubicaciones para la carta porte