Ahora le toca al catalogo de choferes y debe contener los siguientes datos

- Empresa
- Nombre
- Apellido
Ya esta creado el catalogo de tipos de vehículos ahora crearemos el catalogo de vehículos que va a tener los siguientes datos
Creamos el archivo de migración en App/Database/Migrations/2023-09-02171027_Vehiculos.php con el siguiente codigo
Posteriormente vamos a necesitar el catalogo de tipo de vehículos y vehículos en esta publicación dejaremos la forma para crear el CRUD de tipos de vehículos con los siguientes datos.
Un módulo de ventas con facturación electrónica es un software que permite a las empresas realizar ventas y emitir facturas electrónicas de manera automatizada. Este tipo de módulos suele incluir las siguientes funcionalidades:
Los módulos de ventas con facturación electrónica ofrecen a las empresas una serie de ventajas, entre las que se incluyen:
En México, la facturación electrónica es obligatoria para todas las empresas que realicen ventas a clientes ubicados en el país. Por lo tanto, los módulos de ventas con facturación electrónica son una herramienta indispensable para las empresas mexicanas.
A continuación, se detallan algunas de las funcionalidades específicas que puede incluir un módulo de ventas con facturación electrónica:
Elegir un módulo de ventas con facturación electrónica adecuado para su empresa dependerá de una serie de factores, como el tamaño de su empresa, su volumen de ventas, sus necesidades específicas, etc. Es importante comparar diferentes módulos antes de tomar una decisión.
A continuación, se ofrecen algunos consejos para elegir un módulo de ventas con facturación electrónica:
Para registrar los movimientos requerimos especificar que tipo de movimiento es como puede ser entradas por compra, salidas por venta, entrada por devolución de cliente, saluda por devolución a proveedor
Para ello crearemos el siguiente catalogo de Tipos de movimiento con los siguientes campos
Primero creamos el archivo de migración App/Database/2023-08-17222335_Tipos_movimientos_inventario.php con el siguiente código
Es necesario para llevar el control correcto del inventario tener almacenes,
A continuación mostramos como crear el CRUD de almacenes
El CRUD tendrá los siguientes datos
Antes de empezar con el inventario necesitaremos los catálogos principales, uno de ellos es el catalogo de proveedores.
El catalogo es similar al de clientes
Para ello necesitamos en los siguientes campos:
Datos Generales
Datos Extra Facturación
Creamos el archivo de migración App/Database/Migrations/2023-04-21063336_Proveedores con el siguiente codigo
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Proveedores extends Migration {
public function up() {
// Custumers
$this->forge->addField([
'id' => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'idEmpresa' => ['type' => 'varchar', 'constraint' => 128, 'null' => true],
'firstname' => ['type' => 'varchar', 'constraint' => 128, 'null' => true],
'lastname' => ['type' => 'varchar', 'constraint' => 128, 'null' => true],
'razonSocial' => ['type' => 'varchar', 'constraint' => 512, '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],
'formaPago' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
'metodoPago' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
'usoCFDI' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
'codigoPostal' => ['type' => 'int', 'constraint' => 11, 'unsigned' => true],
'regimenFiscal' => ['type' => 'varchar', 'constraint' => 128, '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('proveedores', true);
}
public function down() {
$this->forge->dropTable('proveedores', true);
}
}
Creamos el archivo modelo en App/Models/ProveedoresModel.php con el siguiente código
<?php
namespace App\Models;
use CodeIgniter\Model;
class ProveedoresModel extends Model {
protected $table = 'proveedores';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = ['id'
, 'idEmpresa'
, 'firstname'
, 'lastname'
, 'taxID'
, 'email'
, 'direction'
, 'birthdate'
, 'created_at'
, 'updated_at'
, 'deleted_at'
, 'metodoPago'
, 'formaPago'
, 'codigoPostal'
, 'regimenFiscal'
, 'razonSocial'
, 'usoCFDI'];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $deletedField = 'deleted_at';
protected $validationRules = [
];
protected $validationMessages = [];
protected $skipValidation = false;
/**
* Obtener Clientes
*/
public function mdlGetProveedores($empresas) {
$resultado = $this->db->table('proveedores a, empresas b')
->select('a.id
,a.idEmpresa
,b.nombre as nombreEmpresa
,a.firstname
,a.lastname
,a.taxID
,a.email
,a.direction
,a.birthdate
,a.metodoPago
,a.formaPago
,a.usoCFDI
,a.created_at
,a.updated_at
,a.codigoPostal
,a.regimenFiscal
,a.razonSocial
,a.deleted_at')
->where('a.idEmpresa', 'b.id', FALSE)
->whereIn('a.idEmpresa', $empresas);
return $resultado;
}
}
Creamos el archivo controlador en App/Controllers/ProveedoresController.php con el siguiente código.
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use \App\Models\{
ProveedoresModel
};
use App\Models\LogModel;
use CodeIgniter\API\ResponseTrait;
use App\Models\EmpresasModel;
class ProveedoresController extends BaseController {
use ResponseTrait;
protected $log;
protected $proveedores;
protected $empresa;
public function __construct() {
$this->proveedores = new ProveedoresModel();
$this->log = new LogModel();
$this->empresa = new EmpresasModel();
helper('menu');
helper('utilerias');
}
public function index() {
helper('auth');
$idUser = user()->id;
$titulos["empresas"] = $this->empresa->mdlEmpresasPorUsuario($idUser);
if (count($titulos["empresas"]) == "0") {
$empresasID[0] = "0";
} else {
$empresasID = array_column($titulos["empresas"], "id");
}
if ($this->request->isAJAX()) {
$datos = $this->proveedores->mdlGetProveedores($empresasID);
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
$fechaActual = fechaMySQLADateTimeHTML5(fechaHoraActual());
$titulos["title"] = lang('proveedores.title');
$titulos["subtitle"] = lang('proveedores.subtitle');
$titulos["fecha"] = $fechaActual;
$titulos["formaPago"] = $this->catalogosSAT->formasDePago40()->searchByField("texto", "%%", 99999);
$titulos["usoCFDI"] = $this->catalogosSAT->usosCfdi40()->searchByField("texto", "%%", 99999);
$titulos["metodoPago"] = $this->catalogosSAT->metodosDePago40()->searchByField("texto", "%%", 99999);
$titulos["regimenFiscal"] = $this->catalogosSAT->regimenesFiscales40()->searchByField("texto", "%%", 99999);
return view('proveedores', $titulos);
}
/**
* Read Custumers
*/
public function getProveedores() {
$idProveedor = $this->request->getPost("idProveedor");
$datosProveedor = $this->proveedores->find($idProveedor);
echo json_encode($datosProveedor);
}
/**
* Get Custumers via AJax
*/
public function getProveedoresAjax() {
$request = service('request');
$postData = $request->getPost();
$response = array();
// Read new token and assign in $response['token']
$response['token'] = csrf_hash();
$proveedores = new ProveedoresModel();
$idEmpresa = $postData['idEmpresa'];
if (!isset($postData['searchTerm'])) {
// Fetch record
$listProveedores = $proveedores->select('id,firstname,lastname')->where("deleted_at", null)
->where('idEmpresa', $idEmpresa)
->orderBy('id')
->orderBy('firstname')
->orderBy('lastname')
->findAll(10);
} else {
$searchTerm = $postData['searchTerm'];
// Fetch record
$listProveedores = $proveedores->select('id,firstname,lastname')->where("deleted_at", null)
->where('idEmpresa', $idEmpresa)
->groupStart()
->like('firstname', $searchTerm)
->orLike('id', $searchTerm)
->orLike('lastname', $searchTerm)
->groupEnd()
->findAll(10);
}
$data = array();
foreach ($listProveedores as $proveedores) {
$data[] = array(
"id" => $proveedores['id'],
"text" => $proveedores['id'] . ' ' . $proveedores['firstname'] . ' ' . $proveedores['lastname'],
);
}
$response['data'] = $data;
return $this->response->setJSON($response);
}
/**
* Save or update Custumers
*/
public function save() {
helper('auth');
$userName = user()->username;
$idUser = user()->id;
$datos = $this->request->getPost();
if ($datos["idProveedor"] == 0) {
try {
if ($this->proveedores->save($datos) === false) {
$errores = $this->proveedores->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->proveedores->update($datos["idProveedor"], $datos) == false) {
$errores = $this->proveedores->errors();
foreach ($errores as $field => $error) {
echo $error . " ";
}
return;
} else {
$dateLog["description"] = lang("proveedores.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->proveedores->find($id);
helper('auth');
$userName = user()->username;
if (!$found = $this->proveedores->delete($id)) {
return $this->failNotFound(lang('custumers.msg.msg_get_fail'));
}
$this->proveedores->purgeDeleted();
$logData["description"] = lang("proveedores.logDeleted") . json_encode($infoCustumers);
$logData["user"] = $userName;
$this->log->save($logData);
return $this->respondDeleted($found, lang('custumers.msg_delete'));
}
}
Creamos el archivo principal de la vista en App/Views/proveedores.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('modulesProveedores/modalCaptureProveedores') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
<div class="card-header">
<div class="float-right">
<div class="btn-group">
<button class="btn btn-primary btnAddProveedores" data-toggle="modal" data-target="#modalAddProveedores"><i
class="fa fa-plus"></i>
<?= lang('proveedores.add') ?>
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="table-responsive">
<table id="tableProveedores" class="table table-striped table-hover va-middle tableProveedores">
<thead>
<tr>
<th>#</th>
<th>Empresa</th>
<th>
<?= lang('proveedores.fields.firstname') ?>
</th>
<th>
<?= lang('proveedores.fields.lastname') ?>
</th>
<th>
<?= lang('proveedores.fields.taxID') ?>
</th>
<th>
<?= lang('proveedores.fields.email') ?>
</th>
<th>
<?= lang('proveedores.fields.direction') ?>
</th>
<th>
<?= lang('proveedores.fields.birthdate') ?>
</th>
<th>
<?= lang('proveedores.fields.created_at') ?>
</th>
<th>
<?= lang('proveedores.fields.updated_at') ?>
</th>
<th>
<?= lang('proveedores.fields.deleted_at') ?>
</th>
<th>
<?= lang('proveedores.fields.actions') ?>
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- /.card -->
<?= $this->endSection() ?>
<?= $this->section('js') ?>
<script>
/**
* Cargamos la tabla
*/
var tableProveedores = $('#tableProveedores').DataTable({
processing: true,
serverSide: true,
responsive: true,
autoWidth: false,
order: [[1, 'asc']],
ajax: {
url: '<?= base_url('admin/proveedores') ?>',
method: 'GET',
dataType: "json"
},
columnDefs: [{
orderable: false,
targets: [11],
searchable: false,
targets: [11]
}],
columns: [{
'data': 'id'
},
{
'data': 'nombreEmpresa'
},
{
'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 btnEditProveedores" data-toggle="modal" idProveedor="${data.id}" data-target="#modalAddProveedores"> <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>`
}
}
]
});
/**
* Carga datos actualizar
*/
/*=============================================
EDITAR Proveedores
=============================================*/
$(".tableProveedores").on("click", ".btnEditProveedores", function () {
var idProveedor = $(this).attr("idProveedor");
var datos = new FormData();
datos.append("idProveedor", idProveedor);
$.ajax({
url: "<?= base_url('admin/proveedores/getProveedores') ?>",
method: "POST",
data: datos,
cache: false,
contentType: false,
processData: false,
dataType: "json",
success: function (respuesta) {
$("#idProveedor").val(respuesta["id"]);
$("#idEmpresaProveedor").val(respuesta["idEmpresa"]);
$("#idEmpresaProveedor").trigger("change");
$("#firstname").val(respuesta["firstname"]);
$("#razonSocial").val(respuesta["razonSocial"]);
$("#lastname").val(respuesta["lastname"]);
$("#taxID").val(respuesta["taxID"]);
$("#email").val(respuesta["email"]);
$("#direction").val(respuesta["direction"]);
$("#birthdate").val(respuesta["birthdate"]);
$("#formaPago").val(respuesta["formaPago"]);
$("#formaPago").trigger("change");
$("#metodoPago").val(respuesta["metodoPago"]);
$("#metodoPago").trigger("change");
$("#usoCFDI").val(respuesta["usoCFDI"]);
$("#usoCFDI").trigger("change");
$("#codigoPostal").val(respuesta["codigoPostal"]);
$("#regimenFiscal").val(respuesta["regimenFiscal"]);
$("#regimenFiscal").trigger("change");
}
})
})
$("#idEmpresaProveedor ").select2();
/*=============================================
ELIMINAR proveedores
=============================================*/
$(".tableProveedores").on("click", ".btn-delete", function () {
var idProveedores = $(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/proveedores') ?>/` + idProveedores,
method: 'DELETE',
}).done((data, textStatus, jqXHR) => {
Toast.fire({
icon: 'success',
title: jqXHR.statusText,
});
tableProveedores.ajax.reload();
}).fail((error) => {
Toast.fire({
icon: 'error',
title: error.responseJSON.messages.error,
});
})
}
})
})
$(function () {
$("#modalAddProveedores").draggable();
});
</script>
<?= $this->endSection() ?>
Creamos el archivo secundario que contiene el modal en App/Views/modulesProveedores/modalCaptureProveedores.php con el siguiente codigo.
<?= $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('modulesProveedores/modalCaptureProveedores') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
<div class="card-header">
<div class="float-right">
<div class="btn-group">
<button class="btn btn-primary btnAddProveedores" data-toggle="modal" data-target="#modalAddProveedores"><i
class="fa fa-plus"></i>
<?= lang('proveedores.add') ?>
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="table-responsive">
<table id="tableProveedores" class="table table-striped table-hover va-middle tableProveedores">
<thead>
<tr>
<th>#</th>
<th>Empresa</th>
<th>
<?= lang('proveedores.fields.firstname') ?>
</th>
<th>
<?= lang('proveedores.fields.lastname') ?>
</th>
<th>
<?= lang('proveedores.fields.taxID') ?>
</th>
<th>
<?= lang('proveedores.fields.email') ?>
</th>
<th>
<?= lang('proveedores.fields.direction') ?>
</th>
<th>
<?= lang('proveedores.fields.birthdate') ?>
</th>
<th>
<?= lang('proveedores.fields.created_at') ?>
</th>
<th>
<?= lang('proveedores.fields.updated_at') ?>
</th>
<th>
<?= lang('proveedores.fields.deleted_at') ?>
</th>
<th>
<?= lang('proveedores.fields.actions') ?>
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- /.card -->
<?= $this->endSection() ?>
<?= $this->section('js') ?>
<script>
/**
* Cargamos la tabla
*/
var tableProveedores = $('#tableProveedores').DataTable({
processing: true,
serverSide: true,
responsive: true,
autoWidth: false,
order: [[1, 'asc']],
ajax: {
url: '<?= base_url('admin/proveedores') ?>',
method: 'GET',
dataType: "json"
},
columnDefs: [{
orderable: false,
targets: [11],
searchable: false,
targets: [11]
}],
columns: [{
'data': 'id'
},
{
'data': 'nombreEmpresa'
},
{
'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 btnEditProveedores" data-toggle="modal" idProveedor="${data.id}" data-target="#modalAddProveedores"> <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>`
}
}
]
});
/**
* Carga datos actualizar
*/
/*=============================================
EDITAR Proveedores
=============================================*/
$(".tableProveedores").on("click", ".btnEditProveedores", function () {
var idProveedor = $(this).attr("idProveedor");
var datos = new FormData();
datos.append("idProveedor", idProveedor);
$.ajax({
url: "<?= base_url('admin/proveedores/getProveedores') ?>",
method: "POST",
data: datos,
cache: false,
contentType: false,
processData: false,
dataType: "json",
success: function (respuesta) {
$("#idProveedor").val(respuesta["id"]);
$("#idEmpresaProveedor").val(respuesta["idEmpresa"]);
$("#idEmpresaProveedor").trigger("change");
$("#firstname").val(respuesta["firstname"]);
$("#razonSocial").val(respuesta["razonSocial"]);
$("#lastname").val(respuesta["lastname"]);
$("#taxID").val(respuesta["taxID"]);
$("#email").val(respuesta["email"]);
$("#direction").val(respuesta["direction"]);
$("#birthdate").val(respuesta["birthdate"]);
$("#formaPago").val(respuesta["formaPago"]);
$("#formaPago").trigger("change");
$("#metodoPago").val(respuesta["metodoPago"]);
$("#metodoPago").trigger("change");
$("#usoCFDI").val(respuesta["usoCFDI"]);
$("#usoCFDI").trigger("change");
$("#codigoPostal").val(respuesta["codigoPostal"]);
$("#regimenFiscal").val(respuesta["regimenFiscal"]);
$("#regimenFiscal").trigger("change");
}
})
})
$("#idEmpresaProveedor ").select2();
/*=============================================
ELIMINAR proveedores
=============================================*/
$(".tableProveedores").on("click", ".btn-delete", function () {
var idProveedores = $(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/proveedores') ?>/` + idProveedores,
method: 'DELETE',
}).done((data, textStatus, jqXHR) => {
Toast.fire({
icon: 'success',
title: jqXHR.statusText,
});
tableProveedores.ajax.reload();
}).fail((error) => {
Toast.fire({
icon: 'error',
title: error.responseJSON.messages.error,
});
})
}
})
})
$(function () {
$("#modalAddProveedores").draggable();
});
</script>
<?= $this->endSection() ?>
Creamos el archivo de lenguaje en español en App/Languaje/es/proveedores.php con el siguiente código.
<?php
$proveedores["logDescription"] = "El registro en proveedores fue guardado con los siguientes datos:";
$proveedores["logUpdate"] = "El registro en proveedores fue actualizado con los siguientes datos:";
$proveedores["logDeleted"] = "El registro en proveedores fue eliminado con los siguientes datos:";
$proveedores["msg_delete"] = "El Registro en clieproveedoresntes fue eliminado correctamente:";
$proveedores["add"] = "Agregar Proveedor";
$proveedores["edit"] = "Editar Proveedor";
$proveedores["createEdit"] = "Crear / Editar";
$proveedores["title"] = "Admon. Proveedores";
$proveedores["subtitle"] = "Lista de Proveedores";
$proveedores["fields"]["firstname"] = "Nombre";
$proveedores["fields"]["lastname"] = "Apellido";
$proveedores["fields"]["taxID"] = "RFC";
$proveedores["fields"]["email"] = "Correo Electronico";
$proveedores["fields"]["direction"] = "Direccion";
$proveedores["fields"]["birthdate"] = "Fecha de nacimiento";
$cusproveedorestumers["fields"]["created_at"] = "Fecha de creacion";
$proveedores["fields"]["updated_at"] = "Ultima modificacion";
$proveedores["fields"]["deleted_at"] = "Fecha de eliminacion";
$proveedores["fields"]["actions"] = "Acciones";
$proveedores["msg"]["msg_insert"] = "Registro agregado correctamente.";
$proveedores["msg"]["msg_update"] = "Registro modificado correctamente.";
$proveedores["msg"]["msg_delete"] = "Registro eliminado correctamente.";
$proveedores["msg"]["msg_get"] = "Registro obtenido correctamente.";
$proveedores["msg"]["msg_get_fail"] = "Registro no encontrado o eliminado.";
return $proveedores;
Creamos el archivo de lenguaje en ingles en App/Languaje/en/proveedores.php con el siguiente código.
<?php
$proveedores["logDescription"] = "The custumers was saved with the following data:";
$proveedores["logUpdate"] = "The custumers was updated with the following data:";
$proveedores["logDeleted"] = "The custumers was deleted with the following data:";
$proveedores["msg_delete"] = "The custumers was deleted correctly:";
$proveedores["add"] = "Add Vendor";
$proveedores["edit"] = "Edit Vendor";
$proveedores["createEdit"] = "Create / Edit";
$proveedores["title"] = "Vendors management";
$proveedores["subtitle"] = "Vendors list";
$proveedores["fields"]["firstname"] = "Firstname";
$proveedores["fields"]["lastname"] = "Lastname";
$proveedores["fields"]["taxID"] = "TaxID";
$proveedores["fields"]["email"] = "Email";
$proveedores["fields"]["direction"] = "Direction";
$proveedores["fields"]["birthdate"] = "Birthdate";
$proveedores["fields"]["created_at"] = "Created_at";
$proveedores["fields"]["updated_at"] = "Updated_at";
$proveedores["fields"]["deleted_at"] = "Deleted_at";
$proveedores["fields"]["actions"] = "Actions";
$proveedores["msg"]["msg_insert"] = "The Vendor has been correctly added.";
$proveedores["msg"]["msg_update"] = "The Vendor has been correctly modified.";
$proveedores["msg"]["msg_delete"] = "The Vendor has been correctly deleted.";
$proveedores["msg"]["msg_get"] = "The Vendor has been successfully get.";
$proveedores["msg"]["msg_get_fail"] = "The Vendor not found or already deleted.";
return $proveedores;
En App/Config/Routes.php en el grupo admin agregamos las siguientes rutas
$routes->resource('proveedores', [
'filter' => 'permission:proveedores-permission',
'controller' => 'ProveedoresController',
'except' => 'show'
]);
$routes->post('proveedores/save', 'ProveedoresController::save');
$routes->post('proveedores/getProveedores', 'ProveedoresController::getProveedores');
$routes->post('proveedores/getProveedoresAjax', 'ProveedoresController::getProveedoresAjax');
Creamos el menú de proveedores tal cual se ve en la imagen
Creamos el permiso y lo asignamos al rol correspondiente
Y listo ya tenemos nuestro catalogo de proveedores
Bien como ya saben para hacer una venta se requiere saber de que sucursal es así como ciertas configuraciones particulares por sucursal como la configuración de las series electrónicas para el timbrado del CFDI, así como los datos que saldran en las impresiones de los reportes, como la dirección etc
Por esta ocasión agregaremos lo siguientes datos, posterior mente se pueden agregar mas si se necesitan
Primeramente creamos el archivo de migración app/database/migrations/2023-02-14110147_Branchoffices.php , se dan cuenta que dice Branchoffice, es por que en su momento lo quise empezar en ingles para practicar
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Branchoffices extends Migration {
public function up() {
// Branchoffices
$this->forge->addField([
'id' => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'key' => ['type' => 'varchar', 'constraint' => 8, 'null' => false],
'name' => ['type' => 'varchar', 'constraint' => 256, 'null' => true],
'cologne' => ['type' => 'varchar', 'constraint' => 64, 'null' => true],
'city' => ['type' => 'varchar', 'constraint' => 128, 'null' => true],
'postalCode' => ['type' => 'varchar', 'constraint' => 5, 'null' => true],
'timeDifference' => ['type' => 'varchar', 'constraint' => 4, 'null' => true],
'tax' => ['type' => 'varchar', 'constraint' => 4, 'null' => true],
'dateAp' => ['type' => 'date', 'null' => true],
'phone' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
'fax' => ['type' => 'varchar', 'constraint' => 16, 'null' => true],
'companie' => ['type' => 'varchar', 'constraint' => 8, 'null' => true],
'arqueoCaja' => ['type' => 'varchar', 'constraint' => 5, '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('branchoffices', true);
}
public function down() {
$this->forge->dropTable('branchoffices', true);
}
}
Una ve creado el archivo ejecutamos el comando que crea la tabla
también necesitamos la tabla de sucursales por usuario app/database/migrations/2023-02-14110147_Branchoffices.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class UsuariosSucursal extends Migration
{
public function up()
{
// Usuariosempresa
$this->forge->addField([
'id' => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'idEmpresa' => ['type' => 'int', 'constraint' => 11, 'null' => true],
'idSucursal' => ['type' => 'int', 'constraint' => 11, 'null' => true],
'idUsuario' => ['type' => 'int', 'constraint' => 11, 'null' => true],
'status' => ['type' => 'varchar', 'constraint' => 8, '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('usuarios_sucursal', true);
}
public function down()
{
$this->forge->dropTable('usuarios_sucursal', true);
}
}
php spark migrate
Creamos el archivo app/models/BranchofficeModel.php con los métodos necesarios de lectura y reglas de validación
<?php
namespace App\Models;
use CodeIgniter\Model;
class BranchofficesModel extends Model{
protected $table = 'branchoffices';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = ['id'
,'key'
,'name'
,'cologne'
,'city'
,'postalCode'
,'timeDifference'
,'tax'
,'dateAp'
,'phone'
,'fax'
,'companie'
,'arqueoCaja'
,'created_at
','deleted_at'
,'updated_at'];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $deletedField = 'deleted_at';
protected $validationRules = [
'key ' => 'is_unique[branchoffices.key]',
];
protected $validationMessages = [];
protected $skipValidation = false;
public function mdlSucursalesPorUsuario($usuario){
$resultado =$this->db->table('branchoffices a, usuarios_sucursal b')
->select('a.id,a.name,key,a.created_at,a.updated_at,a.deleted_at')
->where('a.id', 'b.idSucursal', FALSE)
->where('b.status', 'on')
->where('b.idUsuario', $usuario)->get()->getResultArray();
return $resultado;
}
}
Creamos el archivo del modelo usuarios por sucursal app/model/UsuariosSucursalModel.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class UsuariosSucursalModel extends Model
{
protected $table = 'usuarios_sucursal';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = ['id', 'idEmpresa', 'idSucursal', 'idUsuario', 'status', 'created_at', 'updated_at', 'deleted_at'];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $deletedField = 'deleted_at';
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
public function mdlSucursalesPorUsuario($sucursal, $empresasID)
{
$result = $this->db->table('users a, usuariosempresa b')
->select(
'ifnull(a.id,0) as id
,a.username
,b.idEmpresa
,' . $sucursal . ' as idSucursal
,ifnull((select status
from usuarios_sucursal z
where z.idUsuario = a.id
and z.idEmpresa=b.idEmpresa
and z.idSucursal=' . $sucursal . '
),\'off\') as status
,ifnull((select id
from usuarios_sucursal z
where z.idUsuario = a.id
and z.idEmpresa=b.idEmpresa
and z.idSucursal=' . $sucursal . '
),0) as idSucursalUsuario
'
)
->where('a.id', 'b.idUsuario', FALSE)
->where('b.idEmpresa', $empresasID);
return $result;
}
}
Creamos el archivo app/controller/BranchofficesController.php
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use \App\Models\{
BranchofficesModel
};
use App\Models\LogModel;
use CodeIgniter\API\ResponseTrait;
use App\Models\EmpresasModel;
use App\Models\UsuariosSucursalModel;
class BranchofficesController extends BaseController {
use ResponseTrait;
protected $log;
protected $branchoffices;
protected $empresas;
protected $usuariosPorSucursal;
public function __construct() {
$this->branchoffices = new BranchofficesModel();
$this->log = new LogModel();
$this->empresas = new EmpresasModel();
$this->usuariosPorSucursal = new UsuariosSucursalModel();
helper('menu');
}
public function index() {
helper('auth');
$idUser = user()->id;
$titulos["empresas"] = $this->empresas->mdlEmpresasPorUsuario($idUser);
if (count($titulos["empresas"]) == "0") {
$empresasID[0] = "0";
} else {
$empresasID = array_column($titulos["empresas"], "id");
}
if ($this->request->isAJAX()) {
$datos = $this->branchoffices->select('id
,key
,name
,cologne
,city
,postalCode
,timeDifference
,tax,dateAp
,phone
,fax
,companie
,created_at
,deleted_at
,updated_at')->where('deleted_at', null)
->whereIn('companie', $empresasID);;
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
// $empresas = $this->empresas->select("id,nombre")->asObject()->findAll();
// $titulos["empresas"] = $empresas;
$titulos["title"] = lang('branchoffices.title');
$titulos["subtitle"] = lang('branchoffices.subtitle');
return view('branchoffices', $titulos);
}
/**
* Read Branchoffices
*/
public function getBranchoffices() {
$idBranchoffices = $this->request->getPost("idBranchoffices");
$datosBranchoffices = $this->branchoffices->find($idBranchoffices);
echo json_encode($datosBranchoffices);
}
/**
* Save or update Branchoffices
*/
public function save() {
helper('auth');
$userName = user()->username;
$idUser = user()->id;
$datos = $this->request->getPost();
if ($datos["idBranchoffices"] == 0) {
try {
if ($this->branchoffices->save($datos) === false) {
$errores = $this->branchoffices->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->branchoffices->update($datos["idBranchoffices"], $datos) == false) {
$errores = $this->branchoffices->errors();
foreach ($errores as $field => $error) {
echo $error . " ";
}
return;
} else {
$dateLog["description"] = lang("branchoffices.logUpdated") . json_encode($datos);
$dateLog["user"] = $userName;
$this->log->save($dateLog);
echo "Actualizado Correctamente";
return;
}
}
return;
}
/**
* Delete Branchoffices
* @param type $id
* @return type
*/
public function delete($id) {
$infoBranchoffices = $this->branchoffices->find($id);
helper('auth');
$userName = user()->username;
if (!$found = $this->branchoffices->delete($id)) {
return $this->failNotFound(lang('branchoffices.msg.msg_get_fail'));
}
$logData["description"] = lang("branchoffices.logDeleted") . json_encode($infoBranchoffices);
$logData["user"] = $userName;
$this->log->save($logData);
return $this->respondDeleted($found, lang('branchoffices.msg_delete'));
}
public function usuariosPorSucursal($sucursal) {
helper('auth');
$idUser = user()->id;
$datosSucursal = $this->branchoffices->select("companie as empresa")->where("id",$sucursal)->first();
if(isset($datosSucursal["empresa"])){
$idEmpresa = $datosSucursal["empresa"];
}else{
$idEmpresa = -1;
}
$usuarios = $this->usuariosPorSucursal->mdlSucursalesPorUsuario($sucursal, $idEmpresa);
return \Hermawan\DataTables\DataTable::of($usuarios)->toJson(true);
}
/**
* Activar Desactivar Usuario Por Empresa
*/
public function ActivarDesactivar() {
$datos = $this->request->getPost();
if ($datos["id"] > 0) {
//ACTUALIZA SI EXISTE
if ($this->usuariosPorSucursal->update($datos["id"], $datos) === false) {
$errores = $this->usuariosPorSucursal->errors();
foreach ($errores as $field => $error) {
echo $error . " ";
}
return;
}
echo "ok";
} else {
//INSERTA SI NO EXISTE
if ($this->usuariosPorSucursal->save($datos) === false) {
$errores = $this->usuariosPorSucursal->errors();
foreach ($errores as $key => $error) {
echo $error . " ";
}
return;
}
echo "ok";
}
}
/**
* Get Storages via AJax
*/
public function getSucursalesAjax() {
$request = service('request');
$postData = $request->getPost();
$response = array();
// Read new token and assign in $response['token']
$response['token'] = csrf_hash();
helper('auth');
$userName = user()->username;
$idUser = user()->id;
$sucursalesPorUsuario = $this->usuariosPorSucursal->select("*")
->where("idUsuario", $idUser)
->where("status", "on")->findAll();
$sucursalesPorUsuario = array_column($sucursalesPorUsuario, "idSucursal");
if (!isset($postData['searchTerm'])) {
// Fetch record
$sucursales = new BranchofficesModel();
$listSucursales = $sucursales->select('id,key,name')->where("deleted_at", null)
->whereIn("id", $sucursalesPorUsuario)
->where("companie", $postData["idEmpresa"])
->orderBy('id')
->orderBy('key')
->orderBy('name')
->findAll();
} else {
$searchTerm = $postData['searchTerm'];
// Fetch record
$sucursales = new BranchofficesModel();
$listSucursales = $sucursales->select('id,key,name')
->where("deleted_at", null)
->whereIn("id", $sucursalesPorUsuario)
->where("companie", $postData["idEmpresa"])
->like('name', $searchTerm)
->orLike('id', $searchTerm)
->orLike('key', $searchTerm)
->findAll();
}
$data = array();
$data[] = array(
"id" => 0,
"text" => "0 Todas las sucursales",
);
foreach ($listSucursales as $sucursal) {
$data[] = array(
"id" => $sucursal['id'],
"text" => $sucursal['key'] . ' ' . $sucursal['name'],
);
}
$response['data'] = $data;
return $this->response->setJSON($response);
}
}
Creamos el archivo del controlador para usuarios por sucursal app/controller/UsuariosSucursalController.php
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use \App\Models\{
UsuariosSucursalModel
};
use App\Models\LogModel;
use CodeIgniter\API\ResponseTrait;
class UsuariosSucursalController extends BaseController {
use ResponseTrait;
protected $log;
protected $usuariosSucursal;
public function __construct() {
$this->usuariosSucursal = new UsuariosSucursalModel();
$this->log = new LogModel();
helper('menu');
}
public function index() {
if ($this->request->isAJAX()) {
$datos = $this->usuariosSucursal>select('id,idEmpresa,idSucursal,idUsuario,status,created_at,updated_at,deleted_at')->where('deleted_at', null);
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
$titulos["title"] = "Usuarios Sucursal";
$titulos["subtitle"] = "Usuarios Por Sucursal";
return view('usuariosAlmacen', $titulos);
}
/**
* Read Usuariosempresa
*/
public function getUsuariosAlmacen() {
$idUsuariosAlmacen = $this->request->getPost("idUsuariosSucursal");
$datosUsuariosAlmacen = $this->usuariosAlmacen->find($idUsuariosAlmacen);
echo json_encode($datosUsuariosAlmacen);
}
/**
* Save or update Usuariosempresa
*/
public function save() {
helper('auth');
$userName = user()->username;
$idUser = user()->id;
$datos = $this->request->getPost();
if ($datos["idUsuariosSucursal"] == 0) {
try {
if ($this->usuariosSucursal->save($datos) === false) {
$errores = $this->usuariosSucursal->errors();
foreach ($errores as $field => $error) {
echo $error . " ";
}
return;
}
$dateLog["description"] = "Usuarios Por Sucursal" . 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->usuariosSucursal->update($datos["idUsuariossucursal"], $datos) == false) {
$errores = $this->usuariosSucursal->errors();
foreach ($errores as $field => $error) {
echo $error . " ";
}
return;
} else {
$dateLog["description"] = lang("usuariosSucursal.logUpdated") . json_encode($datos);
$dateLog["user"] = $userName;
$this->log->save($dateLog);
echo "Actualizado Correctamente";
return;
}
}
return;
}
/**
* Delete Usuariosempresa
* @param type $id
* @return type
*/
public function delete($id) {
$infoUsuariosSucursal = $this->usuariosSucursal->find($id);
helper('auth');
$userName = user()->username;
if (!$found = $this->usuariosSucursal->delete($id)) {
return $this->failNotFound(lang('usuariosempresa.msg.msg_get_fail'));
}
$this->usuariosSucursal->purgeDeleted();
$logData["description"] = "Datos Anteriores Usuarios Por Sucursal" . json_encode($infoUsuariosSucursal);
$logData["user"] = $userName;
$this->log->save($logData);
return $this->respondDeleted($found, lang('usuariossucursal.msg_delete'));
}
}
Creamos el archivo principal de la vista de sucursales app/views/branchoffice.php este archivo hará una inclusión a los modales de usuarios por sucursal y captura de sucursales
<?= $this->include('load/toggle') ?>
<?= $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('modulesBranchoffices/modalCaptureBranchoffices') ?>
<?= $this->include('modulesBranchoffices/usuariosSucursalModal') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
<div class="card-header">
<div class="float-right">
<div class="btn-group">
<button class="btn btn-primary btnAddBranchoffices" data-toggle="modal" data-target="#modalAddBranchoffices"><i class="fa fa-plus"></i>
<?= lang('branchoffices.add') ?>
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="table-responsive">
<table id="tableBranchoffices" class="table table-striped table-hover va-middle tableBranchoffices">
<thead>
<tr>
<th>#</th>
<th><?= lang('branchoffices.fields.key') ?></th>
<th><?= lang('branchoffices.fields.name') ?></th>
<th><?= lang('branchoffices.fields.cologne') ?></th>
<th><?= lang('branchoffices.fields.city') ?></th>
<th><?= lang('branchoffices.fields.postalCode') ?></th>
<th><?= lang('branchoffices.fields.timeDifference') ?></th>
<th><?= lang('branchoffices.fields.tax') ?></th>
<th><?= lang('branchoffices.fields.dateAp') ?></th>
<th><?= lang('branchoffices.fields.phone') ?></th>
<th><?= lang('branchoffices.fields.fax') ?></th>
<th><?= lang('branchoffices.fields.companie') ?></th>
<th><?= lang('branchoffices.fields.created_at') ?></th>
<th><?= lang('branchoffices.fields.deleted_at') ?></th>
<th><?= lang('branchoffices.fields.updated_at') ?></th>
<th><?= lang('branchoffices.fields.actions') ?> </th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- /.card -->
<?= $this->endSection() ?>
<?= $this->section('js') ?>
<script>
/**
* Cargamos la tabla
*/
var tableBranchoffices = $('#tableBranchoffices').DataTable({
processing: true,
serverSide: true,
autoWidth: false,
responsive: true,
order: [
[1, 'asc']
],
ajax: {
url: '<?= base_url('admin/branchoffices') ?>',
method: 'GET',
dataType: "json"
},
columnDefs: [{
orderable: false,
targets: [15],
searchable: false,
targets: [15]
}],
columns: [{
'data': 'id'
},
{
'data': 'key'
},
{
'data': 'name'
},
{
'data': 'cologne'
},
{
'data': 'city'
},
{
'data': 'postalCode'
},
{
'data': 'timeDifference'
},
{
'data': 'tax'
},
{
'data': 'dateAp'
},
{
'data': 'phone'
},
{
'data': 'fax'
},
{
'data': 'companie'
},
{
'data': 'created_at'
},
{
'data': 'deleted_at'
},
{
'data': 'updated_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 btnEditBranchoffices" data-toggle="modal" idBranchoffices="${data.id}" data-target="#modalAddBranchoffices"> <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>
<button class="btn btn-success btn-users" data-id="${data.id}" data-toggle="modal" data-target="#modalUsuariosSucursal"><i class="fas fa-users"></i></button>
</div>
</td>`
}
}
]
});
$(document).on('click', '#btnSaveBranchoffices', function (e) {
var idBranchoffices = $("#idBranchoffices").val();
var key = $("#key").val();
var name = $("#name").val();
var cologne = $("#cologne").val();
var city = $("#city").val();
var postalCode = $("#postalCode").val();
var timeDifference = $("#timeDifference").val();
var tax = $("#tax").val();
var dateAp = $("#dateAp").val();
var phone = $("#phone").val();
var fax = $("#fax").val();
var companie = $("#companie").val();
if ($("#arqueoCaja").is(':checked')) {
var arqueoCaja = "on";
} else {
var arqueoCaja = "off";
}
$("#btnSaveBranchoffices").attr("disabled", true);
var datos = new FormData();
datos.append("idBranchoffices", idBranchoffices);
datos.append("key", key);
datos.append("name", name);
datos.append("cologne", cologne);
datos.append("city", city);
datos.append("postalCode", postalCode);
datos.append("timeDifference", timeDifference);
datos.append("tax", tax);
datos.append("dateAp", dateAp);
datos.append("phone", phone);
datos.append("fax", fax);
datos.append("companie", companie);
datos.append("arqueoCaja", arqueoCaja);
$.ajax({
url: "<?= base_url('admin/branchoffices/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"
});
tableBranchoffices.ajax.reload();
$("#btnSaveBranchoffices").removeAttr("disabled");
$('#modalAddBranchoffices').modal('hide');
} else {
Toast.fire({
icon: 'error',
title: respuesta
});
$("#btnSaveBranchoffices").removeAttr("disabled");
}
}
}
)
});
/**
* Carga datos actualizar
*/
/*=============================================
EDITAR Branchoffices
=============================================*/
$(".tableBranchoffices").on("click", ".btnEditBranchoffices", function () {
var idBranchoffices = $(this).attr("idBranchoffices");
var datos = new FormData();
datos.append("idBranchoffices", idBranchoffices);
$.ajax({
url: "<?= base_url('admin/branchoffices/getBranchoffices') ?>",
method: "POST",
data: datos,
cache: false,
contentType: false,
processData: false,
dataType: "json",
success: function (respuesta) {
console.log(respuesta["dateAp"]);
$("#idBranchoffices").val(respuesta["id"]);
$("#key").val(respuesta["key"]);
$("#name").val(respuesta["name"]);
$("#cologne").val(respuesta["cologne"]);
$("#city").val(respuesta["city"]);
$("#postalCode").val(respuesta["postalCode"]);
$("#timeDifference").val(respuesta["timeDifference"]);
$("#tax").val(respuesta["tax"]);
$("#dateAp").val(respuesta["dateAp"]);
$("#phone").val(respuesta["phone"]);
$("#fax").val(respuesta["fax"]);
$("#companie").val(respuesta["companie"]);
$("#companie").trigger("change");
var arqueoCaja = respuesta["arqueoCaja"];
if (arqueoCaja == "null" || arqueoCaja == "NULL") {
arqueoCaja = respuesta["arqueoCaja"];
}
$("#arqueoCaja").bootstrapToggle(arqueoCaja);
}
})
})
/*=============================================
ELIMINAR branchoffices
=============================================*/
$(".tableBranchoffices").on("click", ".btn-delete", function () {
var idBranchoffices = $(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/branchoffices') ?>/` + idBranchoffices,
method: 'DELETE',
}).done((data, textStatus, jqXHR) => {
Toast.fire({
icon: 'success',
title: jqXHR.statusText,
});
tableBranchoffices.ajax.reload();
}).fail((error) => {
Toast.fire({
icon: 'error',
title: error.responseJSON.messages.error,
});
})
}
})
})
</script>
<?= $this->endSection() ?>
ahora creamos el archivo app/modulosBranchoffices/modalCaptureBranchoffice.php con el siguiente código
<?= $this->include('load/toggle') ?>
<?= $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('modulesBranchoffices/modalCaptureBranchoffices') ?>
<?= $this->include('modulesBranchoffices/usuariosSucursalModal') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
<div class="card-header">
<div class="float-right">
<div class="btn-group">
<button class="btn btn-primary btnAddBranchoffices" data-toggle="modal" data-target="#modalAddBranchoffices"><i class="fa fa-plus"></i>
<?= lang('branchoffices.add') ?>
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="table-responsive">
<table id="tableBranchoffices" class="table table-striped table-hover va-middle tableBranchoffices">
<thead>
<tr>
<th>#</th>
<th><?= lang('branchoffices.fields.key') ?></th>
<th><?= lang('branchoffices.fields.name') ?></th>
<th><?= lang('branchoffices.fields.cologne') ?></th>
<th><?= lang('branchoffices.fields.city') ?></th>
<th><?= lang('branchoffices.fields.postalCode') ?></th>
<th><?= lang('branchoffices.fields.timeDifference') ?></th>
<th><?= lang('branchoffices.fields.tax') ?></th>
<th><?= lang('branchoffices.fields.dateAp') ?></th>
<th><?= lang('branchoffices.fields.phone') ?></th>
<th><?= lang('branchoffices.fields.fax') ?></th>
<th><?= lang('branchoffices.fields.companie') ?></th>
<th><?= lang('branchoffices.fields.created_at') ?></th>
<th><?= lang('branchoffices.fields.deleted_at') ?></th>
<th><?= lang('branchoffices.fields.updated_at') ?></th>
<th><?= lang('branchoffices.fields.actions') ?> </th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- /.card -->
<?= $this->endSection() ?>
<?= $this->section('js') ?>
<script>
/**
* Cargamos la tabla
*/
var tableBranchoffices = $('#tableBranchoffices').DataTable({
processing: true,
serverSide: true,
autoWidth: false,
responsive: true,
order: [
[1, 'asc']
],
ajax: {
url: '<?= base_url('admin/branchoffices') ?>',
method: 'GET',
dataType: "json"
},
columnDefs: [{
orderable: false,
targets: [15],
searchable: false,
targets: [15]
}],
columns: [{
'data': 'id'
},
{
'data': 'key'
},
{
'data': 'name'
},
{
'data': 'cologne'
},
{
'data': 'city'
},
{
'data': 'postalCode'
},
{
'data': 'timeDifference'
},
{
'data': 'tax'
},
{
'data': 'dateAp'
},
{
'data': 'phone'
},
{
'data': 'fax'
},
{
'data': 'companie'
},
{
'data': 'created_at'
},
{
'data': 'deleted_at'
},
{
'data': 'updated_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 btnEditBranchoffices" data-toggle="modal" idBranchoffices="${data.id}" data-target="#modalAddBranchoffices"> <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>
<button class="btn btn-success btn-users" data-id="${data.id}" data-toggle="modal" data-target="#modalUsuariosSucursal"><i class="fas fa-users"></i></button>
</div>
</td>`
}
}
]
});
$(document).on('click', '#btnSaveBranchoffices', function (e) {
var idBranchoffices = $("#idBranchoffices").val();
var key = $("#key").val();
var name = $("#name").val();
var cologne = $("#cologne").val();
var city = $("#city").val();
var postalCode = $("#postalCode").val();
var timeDifference = $("#timeDifference").val();
var tax = $("#tax").val();
var dateAp = $("#dateAp").val();
var phone = $("#phone").val();
var fax = $("#fax").val();
var companie = $("#companie").val();
if ($("#arqueoCaja").is(':checked')) {
var arqueoCaja = "on";
} else {
var arqueoCaja = "off";
}
$("#btnSaveBranchoffices").attr("disabled", true);
var datos = new FormData();
datos.append("idBranchoffices", idBranchoffices);
datos.append("key", key);
datos.append("name", name);
datos.append("cologne", cologne);
datos.append("city", city);
datos.append("postalCode", postalCode);
datos.append("timeDifference", timeDifference);
datos.append("tax", tax);
datos.append("dateAp", dateAp);
datos.append("phone", phone);
datos.append("fax", fax);
datos.append("companie", companie);
datos.append("arqueoCaja", arqueoCaja);
$.ajax({
url: "<?= base_url('admin/branchoffices/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"
});
tableBranchoffices.ajax.reload();
$("#btnSaveBranchoffices").removeAttr("disabled");
$('#modalAddBranchoffices').modal('hide');
} else {
Toast.fire({
icon: 'error',
title: respuesta
});
$("#btnSaveBranchoffices").removeAttr("disabled");
}
}
}
)
});
/**
* Carga datos actualizar
*/
/*=============================================
EDITAR Branchoffices
=============================================*/
$(".tableBranchoffices").on("click", ".btnEditBranchoffices", function () {
var idBranchoffices = $(this).attr("idBranchoffices");
var datos = new FormData();
datos.append("idBranchoffices", idBranchoffices);
$.ajax({
url: "<?= base_url('admin/branchoffices/getBranchoffices') ?>",
method: "POST",
data: datos,
cache: false,
contentType: false,
processData: false,
dataType: "json",
success: function (respuesta) {
console.log(respuesta["dateAp"]);
$("#idBranchoffices").val(respuesta["id"]);
$("#key").val(respuesta["key"]);
$("#name").val(respuesta["name"]);
$("#cologne").val(respuesta["cologne"]);
$("#city").val(respuesta["city"]);
$("#postalCode").val(respuesta["postalCode"]);
$("#timeDifference").val(respuesta["timeDifference"]);
$("#tax").val(respuesta["tax"]);
$("#dateAp").val(respuesta["dateAp"]);
$("#phone").val(respuesta["phone"]);
$("#fax").val(respuesta["fax"]);
$("#companie").val(respuesta["companie"]);
$("#companie").trigger("change");
var arqueoCaja = respuesta["arqueoCaja"];
if (arqueoCaja == "null" || arqueoCaja == "NULL") {
arqueoCaja = respuesta["arqueoCaja"];
}
$("#arqueoCaja").bootstrapToggle(arqueoCaja);
}
})
})
/*=============================================
ELIMINAR branchoffices
=============================================*/
$(".tableBranchoffices").on("click", ".btn-delete", function () {
var idBranchoffices = $(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/branchoffices') ?>/` + idBranchoffices,
method: 'DELETE',
}).done((data, textStatus, jqXHR) => {
Toast.fire({
icon: 'success',
title: jqXHR.statusText,
});
tableBranchoffices.ajax.reload();
}).fail((error) => {
Toast.fire({
icon: 'error',
title: error.responseJSON.messages.error,
});
})
}
})
})
</script>
<?= $this->endSection() ?>
Ahora sigue el código del modal para capturar/editar los datos de la sucursal
<?= $this->include('load/toggle') ?>
<?= $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('modulesBranchoffices/modalCaptureBranchoffices') ?>
<?= $this->include('modulesBranchoffices/usuariosSucursalModal') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
<div class="card-header">
<div class="float-right">
<div class="btn-group">
<button class="btn btn-primary btnAddBranchoffices" data-toggle="modal" data-target="#modalAddBranchoffices"><i class="fa fa-plus"></i>
<?= lang('branchoffices.add') ?>
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="table-responsive">
<table id="tableBranchoffices" class="table table-striped table-hover va-middle tableBranchoffices">
<thead>
<tr>
<th>#</th>
<th><?= lang('branchoffices.fields.key') ?></th>
<th><?= lang('branchoffices.fields.name') ?></th>
<th><?= lang('branchoffices.fields.cologne') ?></th>
<th><?= lang('branchoffices.fields.city') ?></th>
<th><?= lang('branchoffices.fields.postalCode') ?></th>
<th><?= lang('branchoffices.fields.timeDifference') ?></th>
<th><?= lang('branchoffices.fields.tax') ?></th>
<th><?= lang('branchoffices.fields.dateAp') ?></th>
<th><?= lang('branchoffices.fields.phone') ?></th>
<th><?= lang('branchoffices.fields.fax') ?></th>
<th><?= lang('branchoffices.fields.companie') ?></th>
<th><?= lang('branchoffices.fields.created_at') ?></th>
<th><?= lang('branchoffices.fields.deleted_at') ?></th>
<th><?= lang('branchoffices.fields.updated_at') ?></th>
<th><?= lang('branchoffices.fields.actions') ?> </th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- /.card -->
<?= $this->endSection() ?>
<?= $this->section('js') ?>
<script>
/**
* Cargamos la tabla
*/
var tableBranchoffices = $('#tableBranchoffices').DataTable({
processing: true,
serverSide: true,
autoWidth: false,
responsive: true,
order: [
[1, 'asc']
],
ajax: {
url: '<?= base_url('admin/branchoffices') ?>',
method: 'GET',
dataType: "json"
},
columnDefs: [{
orderable: false,
targets: [15],
searchable: false,
targets: [15]
}],
columns: [{
'data': 'id'
},
{
'data': 'key'
},
{
'data': 'name'
},
{
'data': 'cologne'
},
{
'data': 'city'
},
{
'data': 'postalCode'
},
{
'data': 'timeDifference'
},
{
'data': 'tax'
},
{
'data': 'dateAp'
},
{
'data': 'phone'
},
{
'data': 'fax'
},
{
'data': 'companie'
},
{
'data': 'created_at'
},
{
'data': 'deleted_at'
},
{
'data': 'updated_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 btnEditBranchoffices" data-toggle="modal" idBranchoffices="${data.id}" data-target="#modalAddBranchoffices"> <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>
<button class="btn btn-success btn-users" data-id="${data.id}" data-toggle="modal" data-target="#modalUsuariosSucursal"><i class="fas fa-users"></i></button>
</div>
</td>`
}
}
]
});
$(document).on('click', '#btnSaveBranchoffices', function (e) {
var idBranchoffices = $("#idBranchoffices").val();
var key = $("#key").val();
var name = $("#name").val();
var cologne = $("#cologne").val();
var city = $("#city").val();
var postalCode = $("#postalCode").val();
var timeDifference = $("#timeDifference").val();
var tax = $("#tax").val();
var dateAp = $("#dateAp").val();
var phone = $("#phone").val();
var fax = $("#fax").val();
var companie = $("#companie").val();
if ($("#arqueoCaja").is(':checked')) {
var arqueoCaja = "on";
} else {
var arqueoCaja = "off";
}
$("#btnSaveBranchoffices").attr("disabled", true);
var datos = new FormData();
datos.append("idBranchoffices", idBranchoffices);
datos.append("key", key);
datos.append("name", name);
datos.append("cologne", cologne);
datos.append("city", city);
datos.append("postalCode", postalCode);
datos.append("timeDifference", timeDifference);
datos.append("tax", tax);
datos.append("dateAp", dateAp);
datos.append("phone", phone);
datos.append("fax", fax);
datos.append("companie", companie);
datos.append("arqueoCaja", arqueoCaja);
$.ajax({
url: "<?= base_url('admin/branchoffices/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"
});
tableBranchoffices.ajax.reload();
$("#btnSaveBranchoffices").removeAttr("disabled");
$('#modalAddBranchoffices').modal('hide');
} else {
Toast.fire({
icon: 'error',
title: respuesta
});
$("#btnSaveBranchoffices").removeAttr("disabled");
}
}
}
)
});
/**
* Carga datos actualizar
*/
/*=============================================
EDITAR Branchoffices
=============================================*/
$(".tableBranchoffices").on("click", ".btnEditBranchoffices", function () {
var idBranchoffices = $(this).attr("idBranchoffices");
var datos = new FormData();
datos.append("idBranchoffices", idBranchoffices);
$.ajax({
url: "<?= base_url('admin/branchoffices/getBranchoffices') ?>",
method: "POST",
data: datos,
cache: false,
contentType: false,
processData: false,
dataType: "json",
success: function (respuesta) {
console.log(respuesta["dateAp"]);
$("#idBranchoffices").val(respuesta["id"]);
$("#key").val(respuesta["key"]);
$("#name").val(respuesta["name"]);
$("#cologne").val(respuesta["cologne"]);
$("#city").val(respuesta["city"]);
$("#postalCode").val(respuesta["postalCode"]);
$("#timeDifference").val(respuesta["timeDifference"]);
$("#tax").val(respuesta["tax"]);
$("#dateAp").val(respuesta["dateAp"]);
$("#phone").val(respuesta["phone"]);
$("#fax").val(respuesta["fax"]);
$("#companie").val(respuesta["companie"]);
$("#companie").trigger("change");
var arqueoCaja = respuesta["arqueoCaja"];
if (arqueoCaja == "null" || arqueoCaja == "NULL") {
arqueoCaja = respuesta["arqueoCaja"];
}
$("#arqueoCaja").bootstrapToggle(arqueoCaja);
}
})
})
/*=============================================
ELIMINAR branchoffices
=============================================*/
$(".tableBranchoffices").on("click", ".btn-delete", function () {
var idBranchoffices = $(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/branchoffices') ?>/` + idBranchoffices,
method: 'DELETE',
}).done((data, textStatus, jqXHR) => {
Toast.fire({
icon: 'success',
title: jqXHR.statusText,
});
tableBranchoffices.ajax.reload();
}).fail((error) => {
Toast.fire({
icon: 'error',
title: error.responseJSON.messages.error,
});
})
}
})
})
</script>
<?= $this->endSection() ?>
Bien ahora en app/config/routes.php agregamos las siguientes rutas dentro del grupo admin
$routes->resource('branchoffices', [
'filter' => 'permission:branchoffices-permission',
'controller' => 'branchofficesController',
'except' => 'show'
]);
$routes->post('branchoffices/save', 'BranchofficesController::save');
$routes->post('branchoffices/getBranchoffices', 'BranchofficesController::getBranchoffices');
$routes->get('sucursales/usuariosPorSucursal/(:any)', 'BranchofficesController::usuariosPorSucursal/$1');
$routes->post('sucursales/activarDesactivar', 'BranchofficesController::activarDesactivar');
$routes->post('sucursales/getSucursalesAjax', 'BranchofficesController::getSucursalesAjax');
Creamos el menú de sucursales con los siguientes datos
Y listo ya tenemos nuestro modulo hecho
Bien pues para hacer una venta necesitamos un catalogo de los productos los cuales vamos a vender.
En el catalogo de productos necesitamos los siguientes datos en datos generales:
Para los datos de control de inventario necesitaremos los siguientes datos:
Para los datos del calculo de precios agregaremos los siguientes datos:
También vamos necesitar los datos de facturación:
Bien primero que nada necesitamos crear la tabla en la base de datos, normalmente creamos la tabla directamente en la base de datos, pero como estamos trabajando en CodeIgniter 4 creamos el archivo de migración en app/Database/Migrations/2023-04-24060002_Products.php
El código quedaría de la siguiente manera
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Products extends Migration
{
public function up()
{
// Products
$this->forge->addField([
'id' => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'idEmpresa' => ['type' => 'bigint', 'null' => true],
'idCategory' => ['type' => 'int', 'constraint' => 11, 'null' => true],
'code' => ['type' => 'varchar', 'constraint' => 64, 'null' => true],
'barcode' => ['type' => 'varchar', 'constraint' => 64, 'null' => true],
'unidad' => ['type' => 'varchar', 'constraint' => 64, 'null' => true],
'description' => ['type' => 'varchar', 'constraint' => 512, 'null' => true],
'stock' => ['type' => 'decimal', 'constraint' => '18,2', 'null' => true],
'validateStock' => ['type' => 'varchar', 'constraint' => 4, 'null' => true],
'inventarioRiguroso' => ['type' => 'varchar', 'constraint' => 4, 'null' => true],
'buyPrice' => ['type' => 'decimal', 'constraint' => '18,2', 'null' => true],
'salePrice' => ['type' => 'decimal', 'constraint' => '18,2', 'null' => true],
'porcentSale' => ['type' => 'int', 'constraint' => 11, 'null' => true],
'porcentTax' => ['type' => 'int', 'constraint' => 11, 'null' => true],
'unidadSAT' => ['type' => 'varchar', 'constraint' => 64, 'null' => true],
'claveProductoSAT' => ['type' => 'varchar', 'constraint' => 64, 'null' => true],
'nombreUnidadSAT' => ['type' => 'varchar', 'constraint' => 256, 'null' => true],
'nombreClaveProducto' => ['type' => 'varchar', 'constraint' => 256, 'null' => true],
'porcentIVARetenido' => ['type' => 'decimal', 'constraint' => '18,4', 'null' => true],
'porcentISRRetenido' => ['type' => 'decimal', 'constraint' => '18,4', 'null' => true],
'routeImage' => ['type' => 'varchar', 'constraint' => 256, '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('products', true);
}
public function down()
{
$this->forge->dropTable('products', true);
}
}
Una vez creado ejecutan el comando para generar la tabla
php spark migrate
Ahora creamos el archivo del modelo con las funciones necesarias para el manejo de la tabla en app/models/productosModel.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class ProductsModel extends Model {
protected $table = 'products';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = [
'id'
, 'idEmpresa'
, 'code'
, 'idCategory'
, 'description'
, 'stock'
, 'validateStock'
, 'inventarioRiguroso'
, 'buyPrice'
, 'salePrice'
, 'porcentSale'
, 'porcentTax'
, 'porcentIVARetenido'
, 'porcentISRRetenido'
, 'routeImage'
, 'created_at'
, 'deleted_at'
, 'updated_at'
, 'unidadSAT'
, 'claveProductoSAT'
, 'unidad'
, 'nombreUnidadSAT'
, 'nombreClaveProducto'
, 'barcode'
];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $deletedField = 'deleted_at';
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
public function mdlProductos($empresas) {
$resultado = $this->db->table('products a, empresas b')
->select('a.id
,a.code
,a.idCategory
,a.validateStock
,a.inventarioRiguroso
,a.description
,a.stock
,a.buyPrice
,a.salePrice
,a.porcentSale
,a.porcentTax
,a.routeImage
,a.created_at
,a.deleted_at
,a.updated_at
,a.barcode
,a.unidad
,b.nombre as nombreEmpresa
,a.porcentIVARetenido
,a.porcentISRRetenido
,a.nombreUnidadSAT
,a.nombreClaveProducto
,a.unidadSAT
,a.claveProductoSAT')
->where('a.idEmpresa', 'b.id', FALSE)
->whereIn('idEmpresa', $empresas)
->where('a.deleted_at', null);
return $resultado;
}
public function mdlProductosEmpresa($empresas, $empresa) {
$resultado2 = $this->db->table('products a, empresas b, saldos c, storages d')
->select('a.id,a.code
,a.idCategory
,a.validateStock
,a.inventarioRiguroso
,a.description
,c.cantidad as stock
,a.buyPrice
,a.salePrice
,a.porcentSale
,a.porcentTax
,a.routeImage
,a.created_at
,a.deleted_at
,a.updated_at
,a.barcode
,a.unidad
,b.nombre as nombreEmpresa
,a.porcentIVARetenido
,a.porcentISRRetenido
,a.nombreUnidadSAT
,a.nombreClaveProducto
,a.unidadSAT
,c.lote as lote
,c.idAlmacen
,d.name as almacen
,a.claveProductoSAT')
->where('c.idProducto', 'a.id', FALSE)
->where('a.idEmpresa', 'b.id', FALSE)
->where('c.cantidad >', '0')
->where('a.idEmpresa', 'c.idEmpresa', FALSE)
->where('c.idAlmacen', 'd.id', FALSE)
->where('a.idEmpresa', $empresa)
->where('a.deleted_at', null)
->where('a.inventarioRiguroso', 'on')
->where('a.validateStock', 'on')
->whereIn('c.idEmpresa', $empresas);
$resultado = $this->db->table('products a, empresas b')
->select('a.id,a.code
,a.idCategory
,a.validateStock
,a.inventarioRiguroso
,a.description
,a.stock as stock
,a.buyPrice
,a.salePrice
,a.porcentSale
,a.porcentTax
,a.routeImage
,a.created_at
,a.deleted_at
,a.updated_at
,a.barcode
,a.unidad
,b.nombre as nombreEmpresa
,a.porcentIVARetenido
,a.porcentISRRetenido
,a.nombreUnidadSAT
,a.nombreClaveProducto
,a.unidadSAT
,\'\' as lote
, 0 as idAlmacen
,\'\' as almacen
,a.claveProductoSAT')
->where('a.idEmpresa', 'b.id', FALSE)
->where('a.idEmpresa', $empresa)
->groupStart()
->where('a.inventarioRiguroso', "off")
->orWhere("a.inventarioRiguroso","NULL")
->orWhere("a.inventarioRiguroso",NULL)
->groupEnd()
->where('a.deleted_at', null)
->whereIn('idEmpresa', $empresas);
$resultado->union($resultado2);
$this->db->query("DROP TABLE IF EXISTS tempProducts");
$this->db->query("create table tempProducts " . $resultado->getCompiledSelect());
return $this->db->table('tempProducts');
}
/**
* Lista Para inventario Riguroso
* @param type $empresas
* @param type $empresa
* @return type
*/
public function mdlProductosEmpresaInventarioEntrada($empresas, $empresa) {
$resultado = $this->db->table('products a, empresas b')
->select('a.id,a.code
,a.idCategory
,a.validateStock
,a.inventarioRiguroso
,a.description
,a.stock
,a.buyPrice
,a.salePrice
,a.porcentSale
,a.porcentTax
,a.routeImage
,a.created_at
,a.deleted_at
,a.updated_at
,a.barcode
,a.unidad
, b.nombre as nombreEmpresa
,a.porcentIVARetenido
,a.porcentISRRetenido
,a.nombreUnidadSAT
,a.nombreClaveProducto
,a.unidadSAT
,"" as lote
,"" as almacen
,a.claveProductoSAT')
->where('a.idEmpresa', 'b.id', FALSE)
->where('a.idEmpresa', $empresa)
->where('a.deleted_at', null)
->where('a.inventarioRiguroso', 'on')
->where('a.validateStock', 'on')
->whereIn('idEmpresa', $empresas);
return $resultado;
}
public function mdlProductosEmpresaInventarioSalida($empresas, $empresa) {
$resultado = $this->db->table('products a, empresas b, saldos c, storages d')
->select('a.id,a.code
,a.idCategory
,a.validateStock
,a.inventarioRiguroso
,a.description
,c.cantidad as stock
,a.buyPrice
,a.salePrice
,a.porcentSale
,a.porcentTax
,a.routeImage
,a.created_at
,a.deleted_at
,a.updated_at
,a.barcode
,a.unidad
,b.nombre as nombreEmpresa
,a.porcentIVARetenido
,a.porcentISRRetenido
,a.nombreUnidadSAT
,a.nombreClaveProducto
,a.unidadSAT
,c.lote as lote
,c.idAlmacen
,d.name as almacen
,a.claveProductoSAT')
->where('c.idProducto', 'a.id', FALSE)
->where('a.idEmpresa', 'b.id', FALSE)
->where('a.idEmpresa', 'c.idEmpresa', FALSE)
->where('c.idAlmacen', 'd.id', FALSE)
->where('a.idEmpresa', $empresa)
->where('a.deleted_at', null)
->where('a.inventarioRiguroso', 'on')
->where('a.validateStock', 'on')
->whereIn('c.idEmpresa', $empresas);
return $resultado;
}
public function mdlGetProductoEmpresa($empresas, $idProducto) {
$resultado = $this->db->table('products a, empresas b, categorias c ')
->select('a.id
,a.code
,a.idEmpresa
,a.validateStock
,a.inventarioRiguroso
,a.idCategory
,c.clave
,c.descripcion as descripcionCategoria
,a.description
,a.stock
,a.buyPrice
,a.salePrice,a.porcentSale
,a.porcentTax,a.routeImage
,a.created_at,a.deleted_at
,a.updated_at
,a.barcode
,a.unidad
, b.nombre as nombreEmpresa
,a.porcentIVARetenido
,a.porcentISRRetenido
,a.nombreUnidadSAT
,a.nombreClaveProducto
,a.unidadSAT
,a.claveProductoSAT')
->where('a.idEmpresa', 'b.id', FALSE)
->where('a.idCategory', 'c.id', FALSE)
->where('a.id', $idProducto)
->where('a.deleted_at', null)
->whereIn('a.idEmpresa', $empresas)->get()->getFirstRow();
return $resultado;
}
}
Agregamos el archivo del controlador para las operaciones de validacion altas bajas y cambios en app/controllers/ProductsController.php
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use App\Models\ProductsModel;
use App\Models\LogModel;
use CodeIgniter\API\ResponseTrait;
use App\Models\CategoriasModel;
use App\Models\EmpresasModel;
use App\Models\QuotesDetailsModel;
use App\Models\SellsDetailsModel;
use App\Models\Tipos_movimientos_inventarioModel;
class ProductsController extends BaseController {
use ResponseTrait;
protected $log;
protected $products;
protected $empresa;
protected $categorias;
protected $sellsDetails;
protected $quoteDetails;
protected $tiposMovimientoInventario;
public function __construct() {
$this->products = new ProductsModel();
$this->log = new LogModel();
$this->categorias = new CategoriasModel();
$this->empresa = new EmpresasModel();
$this->sellsDetails = new SellsDetailsModel();
$this->quoteDetails = new QuotesDetailsModel();
$this->tiposMovimientoInventario = new Tipos_movimientos_inventarioModel();
helper('menu');
}
public function index() {
helper('auth');
$idUser = user()->id;
$titulos["empresas"] = $this->empresa->mdlEmpresasPorUsuario($idUser);
if (count($titulos["empresas"]) == "0") {
$empresasID[0] = "0";
} else {
$empresasID = array_column($titulos["empresas"], "id");
}
if ($this->request->isAJAX()) {
$datos = $this->products->mdlProductos($empresasID);
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
$titulos["categorias"] = $this->categorias->select("*")->where("deleted_at", null)->asArray()->findAll();
$titulos["title"] = lang('products.title');
$titulos["subtitle"] = lang('products.subtitle');
return view('products', $titulos);
}
public function getAllProducts($empresa) {
helper('auth');
$idUser = user()->id;
$titulos["empresas"] = $this->empresa->mdlEmpresasPorUsuario($idUser);
if (count($titulos["empresas"]) == "0") {
$empresasID[0] = "0";
} else {
$empresasID = array_column($titulos["empresas"], "id");
}
if ($this->request->isAJAX()) {
$datos = $this->products->mdlProductosEmpresa($empresasID, $empresa);
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
}
public function getAllProductsInventory($empresa, $idStorage, $idTipoMovimiento) {
helper('auth');
$idUser = user()->id;
$titulos["empresas"] = $this->empresa->mdlEmpresasPorUsuario($idUser);
if (count($titulos["empresas"]) == "0") {
$empresasID[0] = "0";
} else {
$empresasID = array_column($titulos["empresas"], "id");
}
//BUSCAMOS EL TIPO DE MOVIMIENTO SI ES ENTRADA O SALIDA
$tiposMovimiento = $this->tiposMovimientoInventario->select("*")
->wherein("idEmpresa", $empresasID)
->where("id", $idTipoMovimiento)->first();
if ($tiposMovimiento == null) {
$datos = $this->products->mdlProductosEmpresaInventarioEntrada($empresasID, $empresa);
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
if ($tiposMovimiento["tipo"] == "ENT") {
if ($this->request->isAJAX()) {
$datos = $this->products->mdlProductosEmpresaInventarioEntrada($empresasID, $empresa);
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
}
if ($tiposMovimiento["tipo"] == "SAL") {
if ($this->request->isAJAX()) {
$datos = $this->products->mdlProductosEmpresaInventarioSalida($empresasID, $empresa);
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
}
$datos = $this->products->mdlProductosEmpresaInventarioEntrada($empresasID, $empresa);
return \Hermawan\DataTables\DataTable::of($datos)->toJson(true);
}
/**
* Get Unidad SAT via AJax
*/
public function getUnidadSATAjax() {
$request = service('request');
$postData = $request->getPost();
$response = array();
// Read new token and assign in $response['token']
$response['token'] = csrf_hash();
if (!isset($postData['searchTerm'])) {
// Fetch record
$listUnidadesSAT = $this->catalogosSAT->clavesUnidades40()->searchByField("texto", "%$%", 100);
} else {
$searchTerm = $postData['searchTerm'];
// Fetch record
$listUnidadesSAT = $this->catalogosSAT->clavesUnidades40()->searchByField("texto", "%$searchTerm%", 100);
}
$data = array();
foreach ($listUnidadesSAT as $unidadSAT => $value) {
$data[] = array(
"id" => $value->id(),
"text" => $value->id() . ' ' . $value->texto(),
);
}
$response['data'] = $data;
return $this->response->setJSON($response);
}
/**
* Get Unidad SAT via AJax
*/
public function getProductosSATAjax() {
$request = service('request');
$postData = $request->getPost();
$response = array();
// Read new token and assign in $response['token']
$response['token'] = csrf_hash();
if (!isset($postData['searchTerm'])) {
// Fetch record
$listProducts = $this->catalogosSAT->productosServicios40()->searchByField("texto", "%$searchTerm%", 50);
} else {
$searchTerm = $postData['searchTerm'];
// Fetch record
$listProducts = $this->catalogosSAT->productosServicios40()->searchByField("texto", "%$searchTerm %", 50);
}
$data = array();
foreach ($listProducts as $productosSAT => $value) {
$data[] = array(
"id" => $value->id(),
"text" => $value->id() . ' ' . $value->texto(),
);
}
$response['data'] = $data;
return $this->response->setJSON($response);
}
/**
* Get Products via AJax
*/
public function getProductsAjaxSelect2() {
$request = service('request');
$postData = $request->getPost();
$response = array();
// Read new token and assign in $response['token']
$response['token'] = csrf_hash();
$products = new ProductsModel();
$idEmpresa = $postData['idEmpresa'];
if (!isset($postData['searchTerm'])) {
// Fetch record
$listProducts = $products->select('id,code,description')->where("deleted_at", null)
->where('idEmpresa', $idEmpresa)
->orderBy('id')
->orderBy('code')
->orderBy('description')
->findAll(1000);
} else {
$searchTerm = $postData['searchTerm'];
// Fetch record
$listProducts = $products->select('id,code,description')->where("deleted_at", null)
->where('idEmpresa', $idEmpresa)
->groupStart()
->like('description', $searchTerm)
->orLike('id', $searchTerm)
->orLike('code', $searchTerm)
->groupEnd()
->findAll(1000);
}
$data = array();
$data[] = array(
"id" => 0,
"text" => "0 Todos Los Productos",
);
foreach ($listProducts as $product) {
$data[] = array(
"id" => $product['id'],
"text" => $product['id'] . ' ' . $product['id'] . ' ' . $product['code'] . ' ' . $product['description'],
);
}
$response['data'] = $data;
return $this->response->setJSON($response);
}
/**
* Read Products
*/
public function getProducts() {
helper('auth');
$idUser = user()->id;
$titulos["empresas"] = $this->empresa->mdlEmpresasPorUsuario($idUser);
if (count($titulos["empresas"]) == "0") {
$empresasID[0] = "0";
} else {
$empresasID = array_column($titulos["empresas"], "id");
}
$idProducts = $this->request->getPost("idProducts");
$datosProducts = $this->products->mdlGetProductoEmpresa($empresasID, $idProducts);
echo json_encode($datosProducts);
}
/**
* Save or update Products
*/
public function save() {
helper('auth');
$userName = user()->username;
$idUser = user()->id;
$datos = $this->request->getPost();
var_dump($datos);
$imagenProducto = $this->request->getFile("imagenProducto");
$datos["routeImage"] = "";
if ($imagenProducto) {
if ($imagenProducto->getClientExtension() <> "png") {
return lang("empresas.pngFileExtensionIncorrect");
}
$datos["routeImage"] = $imagenProducto->getRandomName();
}
if ($datos["idProducts"] == 0) {
try {
if ($this->products->save($datos) === false) {
$errores = $this->products->errors();
foreach ($errores as $field => $error) {
echo $error . " ";
}
return;
}
$dateLog["description"] = lang("vehicles.logDescription") . json_encode($datos);
$dateLog["user"] = $userName;
$this->log->save($dateLog);
if ($imagenProducto <> null) {
$imagenProducto->move("images/products", $datos["routeImage"]);
}
echo "Guardado Correctamente";
} catch (\PHPUnit\Framework\Exception $ex) {
echo "Error al guardar " . $ex->getMessage();
}
} else {
$dataPrevious = $this->products->find($datos["idProducts"]);
if ($this->products->update($datos["idProducts"], $datos) == false) {
$errores = $this->products->errors();
foreach ($errores as $field => $error) {
echo $error . " ";
}
return;
} else {
$dateLog["description"] = lang("products.logUpdated") . json_encode($datos);
$dateLog["user"] = $userName;
$this->log->save($dateLog);
if ($imagenProducto <> null) {
if (file_exists("images/products/" . $dataPrevious["routeImage"])) {
unlink("images/products/" . $dataPrevious["routeImage"]);
}
$imagenProducto->move("images/products", $datos["routeImage"]);
}
echo "Actualizado Correctamente";
return;
}
}
return;
}
/**
* Delete Products
* @param type $id
* @return type
*/
public function delete($id) {
if ($this->sellsDetails->select("id")->where("idProduct", $id)->countAllResults() > 0) {
$this->products->db->transRollback();
return $this->failValidationError("No se puede borrar ya que hay ventas con este producto");
}
if ($this->quoteDetails->select("id")->where("idProduct", $id)->countAllResults() > 0) {
$this->products->db->transRollback();
return $this->failValidationError("No se puede borrar ya que hay cotizaciones con este producto");
}
$infoProducts = $this->products->find($id);
helper('auth');
$userName = user()->username;
if (!$found = $this->products->delete($id)) {
$this->products->db->transRollback();
return $this->failNotFound(lang('products.msg.msg_get_fail'));
}
if ($infoProducts["routeImage"] != "") {
if (file_exists("images/products/" . $infoProducts["routeImage"])) {
unlink("images/products/" . $infoProducts["routeImage"]);
}
}
$this->products->purgeDeleted();
$logData["description"] = lang("products.logDeleted") . json_encode($infoProducts);
$logData["user"] = $userName;
$this->log->save($logData);
$this->products->db->transCommit();
return $this->respondDeleted($found, lang('products.msg_delete'));
}
/**
* Get Vehiculos via AJax
*/
public function getProductsAjax() {
$request = service('request');
$postData = $request->getPost();
$response = array();
// Read new token and assign in $response['token']
$response['token'] = csrf_hash();
$custumers = new VehiculosModel();
$idEmpresa = $postData['idEmpresa'];
if (!isset($postData['searchTerm'])) {
// Fetch record
$listProducts = $products->select('id,description')->where("deleted_at", null)
->where('idEmpresa', $idEmpresa)
->orderBy('id')
->orderBy('descripcion')
->findAll(1000);
} else {
$searchTerm = $postData['searchTerm'];
// Fetch record
$listProducts = $products->select('id,description')->where("deleted_at", null)
->where('idEmpresa', $idEmpresa)
->groupStart()
->like('descripcion', $searchTerm)
->orLike('id', $searchTerm)
->groupEnd()
->findAll(1000);
}
$data = array();
foreach ($listProducts as $product) {
$data[] = array(
"id" => $custumers['id'],
"text" => $custumers['id'] . ' ' . $product['description'],
);
}
$response['data'] = $data;
return $this->response->setJSON($response);
}
}
Ahora creamos la interfaz es decir la vista, para ello tendremos el archivo principal app/views/products.php y este incluirá los archivos secundarios, una forma que a mi parecer se organiza mejor para no tener todo en un solo archivo.
En el diseño estaran los bloques separados en pestañas “TABS”
Los archivos secundarios serán los siguientes
El archivo principal app/views/products.php contiene el siguiente código, incluye a los demas y genera el datable
<?= $this->include('load/toggle') ?>
<?= $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('modulesProducts/modalCaptureProducts') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
<div class="card-header">
<div class="float-right">
<div class="btn-group">
<button class="btn btn-primary btnAddProducts" data-toggle="modal" data-target="#modalAddProducts"><i class="fa fa-plus"></i>
<?= lang('products.add') ?>
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="table-responsive">
<table id="tableProducts" class="table table-striped table-hover va-middle tableProducts">
<thead>
<tr>
<th>#</th>
<th>
Empresa
</th>
<th>
Clave
</th>
<th>
<?= lang('products.fields.idCategory') ?>
</th>
<th>
<?= lang('products.fields.barcode') ?>
</th>
<th>
<?= lang('products.fields.description') ?>
</th>
<th>
<?= lang('products.fields.stock') ?>
</th>
<th>
<?= lang('products.fields.buyPrice') ?>
</th>
<th>
<?= lang('products.fields.salePrice') ?>
</th>
<th>
<?= lang('products.fields.porcentSale') ?>
</th>
<th>
<?= lang('products.fields.porcentTax') ?>
</th>
<th>
<?= lang('products.fields.routeImage') ?>
</th>
<th>
<?= lang('products.fields.created_at') ?>
</th>
<th>
<?= lang('products.fields.deleted_at') ?>
</th>
<th>
<?= lang('products.fields.updated_at') ?>
</th>
<th>
<?= lang('products.fields.actions') ?>
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- /.card -->
<?= $this->endSection() ?>
<?= $this->section('js') ?>
<script>
/**
* Cargamos la tabla
*/
var tableProducts = $('#tableProducts').DataTable({
processing: true,
serverSide: true,
responsive: true,
autoWidth: false,
order: [
[1, 'asc']
],
ajax: {
url: '<?= base_url('admin/products') ?>',
method: 'GET',
dataType: "json"
},
columnDefs: [{
orderable: false,
targets: [11, 15],
searchable: false,
targets: [11, 15]
}],
columns: [{
'data': 'id'
},
{
'data': 'nombreEmpresa'
},
{
'data': 'code'
},
{
'data': 'idCategory'
},
{
'data': 'barcode'
},
{
'data': 'description'
},
{
'data': 'stock'
},
{
'data': 'buyPrice'
},
{
'data': 'salePrice'
},
{
'data': 'porcentSale'
},
{
'data': 'porcentTax'
},
{
"data": function (data) {
if (data.routeImage == "") {
data.routeImage = "anonymous.png";
}
return `<td class="text-right py-0 align-middle">
<div class="btn-group btn-group-sm">
<img src="<?= base_URL("images/products") ?>/${data.routeImage}" data-action="zoom" width="40px" class="" style="">
</div>
</td>`
}
},
{
'data': 'created_at'
},
{
'data': 'deleted_at'
},
{
'data': 'updated_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 btnEditProducts" data-toggle="modal" idProducts="${data.id}" data-target="#modalAddProducts"> <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>`
}
}
]
});
/**
* Carga datos actualizar
*/
/*=============================================
EDITAR Products
=============================================*/
$(".tableProducts").on("click", ".btnEditProducts", function () {
var idProducts = $(this).attr("idProducts");
var datos = new FormData();
datos.append("idProducts", idProducts);
if (idEmpresa == 0) {
Toast.fire({
icon: 'error',
title: "Tiene que seleccionar la empresa"
});
}
$.ajax({
url: "<?= base_url('admin/products/getProducts') ?>",
method: "POST",
data: datos,
cache: false,
contentType: false,
processData: false,
dataType: "json",
success: function (respuesta) {
$("#idProducts").val(respuesta["id"]);
$("#idEmpresa").val(respuesta["idEmpresa"]);
$("#idEmpresa").trigger("change");
var newOption = new Option(respuesta["clave"] + ' ' + respuesta["descripcionCategoria"], respuesta["idCategory"], true, true);
$('#idCategory').append(newOption).trigger('change');
$("#idCategory").val(respuesta["idCategory"]);
var newOptionUnidad = new Option(respuesta["nombreUnidadSAT"], respuesta["unidadSAT"], true, true);
$('#unidadSAT').append(newOptionUnidad).trigger('change');
$("#unidadSAT").val(respuesta["unidadSAT"]);
$("#unidad").val(respuesta["unidad"]);
var newOptionClaveProducto = new Option(respuesta["nombreClaveProducto"], respuesta["claveProductoSAT"], true, true);
$('#claveProductoSAT').append(newOptionClaveProducto).trigger('change');
$("#claveProductoSAT").val(respuesta["claveProductoSAT"]);
$("#clave").val(respuesta["clave"]);
$("#description").val(respuesta["description"]);
$("#stock").val(respuesta["stock"]);
$("#buyPrice").val(respuesta["buyPrice"]);
$("#salePrice").val(respuesta["salePrice"]);
$("#porcentSale").val(respuesta["porcentSale"]);
$("#porcentTax").val(respuesta["porcentTax"]);
$("#porcentIVARetenido").val(respuesta["porcentIVARetenido"]);
$("#porcentISRRetenido").val(respuesta["porcentISRRetenido"]);
$("#barcode").val(respuesta["barcode"]);
$("#validateStock").bootstrapToggle(respuesta["validateStock"]);
$("#inventarioRiguroso").bootstrapToggle(respuesta["inventarioRiguroso"]);
//$("#routeImage").val(respuesta["routeImage"]);
if (respuesta["routeImage"] == "") {
$(".previsualizarLogo").attr("src", '<?= base_URL("images/products/") ?>anonymous.png');
} else {
$(".previsualizarLogo").attr("src", '<?= base_URL("images/products") ?>/' + respuesta["routeImage"]);
}
$("#code").val(respuesta["code"]);
}
})
})
/*=============================================
ELIMINAR products
=============================================*/
$(".tableProducts").on("click", ".btn-delete", function () {
var idProducts = $(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/products') ?>/` + idProducts,
method: 'DELETE',
}).done((data, textStatus, jqXHR) => {
Toast.fire({
icon: 'success',
title: jqXHR.statusText,
});
tableProducts.ajax.reload();
}).fail((error) => {
Toast.fire({
icon: 'error',
title: error.responseJSON.messages.error,
});
})
}
})
})
$(function () {
$("#modalAddProducts").draggable();
});
/*=============================================
SUBIENDO LA FOTO DEL USUARIO
=============================================*/
$(".imagenProducto").change(function () {
var imagen = this.files[0];
/*=============================================
VALIDAMOS EL FORMATO DE LA IMAGEN SEA JPG O PNG
=============================================*/
if (imagen["type"] != "image/png") {
$(".imagenProducto").val("");
Toast.fire({
icon: 'error',
title: "<?= lang('empresas.imagenesFormato') ?>",
});
} else if (imagen["size"] > 2000000) {
$(".imagenProducto").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() ?>
Luego tenemos app/views/modulesProducts/generalsProducts.php que son los datos generales del producto
<p>
<h3>Datos Generales</h3>
<div class="form-group row">
<label for="emitidoRecibido" class="col-sm-2 col-form-label">Empresa</label>
<div class="col-sm-10">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
</div>
<select class="form-control idEmpresa form-controlProducts" name="idEmpresa" id="idEmpresa" style="width:80%;">
<option value="0">Seleccione empresa</option>
<?php
foreach ($empresas as $key => $value) {
echo "<option value='$value[id]'>$value[id] - $value[nombre] </option> ";
}
?>
</select>
</div>
</div>
</div>
<div class="form-group row">
<label for="idCategory" class="col-sm-2 col-form-label">
<?= lang('products.fields.idCategory') ?>
</label>
<div class="col-sm-10">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
</div>
<select name="idCategory" id="idCategory" style="width: 90%;" class="form-control idCategory form-controlProducts">
<option value="0" selected>
<?= lang('products.fields.idSelectCategory') ?>
</option>
</select>
</div>
</div>
</div>
<div class="form-group row">
<label for="code" class="col-sm-2 col-form-label">
Clave
</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="code" id="code" class="form-control form-controlProducts <?= session('error.code') ? 'is-invalid' : '' ?>" value="" placeholder="Clave" autocomplete="off" readonly>
</div>
</div>
</div>
<div class="form-group row">
<label for="barcode" class="col-sm-2 col-form-label">
<?= lang('products.fields.barcode') ?>
</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="barcode" id="barcode" class="form-control form-controlProducts <?= session('error.barcode') ? 'is-invalid' : '' ?>" value="<?= old('barcode') ?>" placeholder="<?= lang('products.fields.barcode') ?>" autocomplete="off">
</div>
</div>
</div>
<div class="form-group row">
<label for="description" class="col-sm-2 col-form-label">
<?= lang('products.fields.description') ?>
</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="description" id="description" class="form-control form-controlProducts <?= session('error.description') ? 'is-invalid' : '' ?>" value="<?= old('description') ?>" placeholder="<?= lang('products.fields.description') ?>" autocomplete="off">
</div>
</div>
</div>
<div class="form-group row">
<label for="unidad" class="col-sm-2 col-form-label">
Unidad
</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="unidad" id="unidad" class="form-control form-controlProducts <?= session('error.unidad') ? 'is-invalid' : '' ?>" value="<?= old('unidad') ?>" placeholder="Unidad" autocomplete="off">
</div>
</div>
</div>
</p>
Agregamos el código donde capturamos los datos del inventario app/views/modulesProducts/inventoryProducts.php
<p>
<h3>Datos Inventario</h3>
<div class="form-group row">
<label for="stock" class="col-sm-2 col-form-label">
<?= lang('products.fields.stock') ?>
</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="number" name="stock" id="stock" class="form-control form-controlProducts <?= session('error.stock') ? 'is-invalid' : '' ?>" value="<?= old('stock') ?>" placeholder="<?= lang('products.fields.stock') ?>" autocomplete="off" min="0">
</div>
</div>
</div>
<div class="form-group row">
<label for="stock" class="col-sm-2 col-form-label">
Valida Stock
</label>
<div class="col-sm-10">
<div class="input-group">
<div class="input-group-prepend">
</div>
<input type="checkbox" id="validateStock" name="validateStock" class="validateStock" data-width="250" data-height="40" checked data-toggle="toggle" data-on="Valida Stock" data-off="No valida stock" data-onstyle="success" data-offstyle="danger">
</div>
</div>
</div>
<div class="form-group row">
<label for="stock" class="col-sm-2 col-form-label">
Inventario Riguroso
</label>
<div class="col-sm-10">
<div class="input-group">
<div class="input-group-prepend">
</div>
<input type="checkbox" id="inventarioRiguroso" name="inventarioRiguroso" class="inventarioRiguroso" data-width="250" data-height="40" checked data-toggle="toggle" data-on="Inventario Riguroso" data-off="Inventario básico" data-onstyle="success" data-offstyle="danger">
</div>
</div>
</div>
</p>
Este es el código para capturar la imagen del producto app/views/modulesProducts/imageProduct.php
<p>
<h3>Imagenes</h3>
<div class="form-group ">
<input type="file" class="imagenProducto" name="imagenProducto" id="imagenProducto">
<p class="help-block">
<?= lang("empresas.imagenesPesoMaximo") ?>
</p>
<img src="<?= base_url("images/products/anonymous.png") ?>" class="img-thumbnail previsualizarLogo" width="100px">
<input type="hidden" name="imagenActual" id="imagenActual">
</div>
</p>
Capturamos el archivo base del modal aquí van configuradas las pestañas “TABS” app/views/modulesProducts/modalCaptureProducts.php
<!-- Modal Products -->
<div class="modal fade" id="modalAddProducts" tabindex="-1" role="dialog" aria-labelledby="modalAddProducts" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<?= lang('products.createEdit') ?>
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</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="#inventoryProducts" type="button" role="tab" aria-controls="profile" aria-selected="false">Inventario</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="emailsetting-tab" data-toggle="tab" data-target="#precios" type="button" role="tab" aria-controls="emailSettings" aria-selected="false">Precios</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="contact-tab" data-toggle="tab" data-target="#imageProduct" type="button" role="tab" aria-controls="contact" aria-selected="false">Logos / Imagenes</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="contact-tab" data-toggle="tab" data-target="#productsCFDIV4" type="button" role="tab" aria-controls="contact" aria-selected="false">Datos CFDI V4 </button>
</li>
</ul>
<form id="form-products" 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('modulesProducts/generalsProducts') ?>
</div>
<div class="tab-pane fade" id="inventoryProducts" role="tabpanel" aria-labelledby="datosFacturacion">
<?= $this->include('modulesProducts/inventoryProducts') ?>
</div>
<div class="tab-pane fade" id="precios" role="tabpanel" aria-labelledby="contact-tab">
<?= $this->include('modulesProducts/priceProducts') ?>
</div>
<div class="tab-pane fade" id="imageProduct" role="tabpanel" aria-labelledby="contact-tab">
<?= $this->include('modulesProducts/imageProduct') ?>
</div>
<div class="tab-pane fade" id="productsCFDIV4" role="tabpanel" aria-labelledby="contact-tab">
<?= $this->include('modulesProducts/productsCFDIV4') ?>
</div>
</div>
<input type="hidden" id="idProducts" name="idProducts" value="0">
</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="btnSaveProducts">
<?= lang('boilerplate.global.save') ?>
</button>
</div>
</div>
</div>
</div>
<?= $this->section('js') ?>
<script>
$("#idEmpresa").select2();
/**
* Categorias por empresa
*/
$(".unidadSAT").select2({
ajax: {
url: "<?= base_url('admin/products/getUnidadSATAjax') ?>",
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 idEmpresa = $('.idEmpresa').val(); // CSRF hash
return {
searchTerm: params.term, // search term
[csrfName]: csrfHash, // CSRF Token
idEmpresa: idEmpresa // search term
};
},
processResults: function(response) {
// Update CSRF Token
$('.txt_csrfname').val(response.token);
return {
results: response.data
};
},
cache: true
}
});
/**
* Categorias por empresa
*/
$(".claveProductoSAT").select2({
ajax: {
url: "<?= base_url('admin/products/getProductosSATAjax') ?>",
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 idEmpresa = $('.idEmpresa').val(); // CSRF hash
return {
searchTerm: params.term, // search term
[csrfName]: csrfHash, // CSRF Token
idEmpresa: idEmpresa // search term
};
},
processResults: function(response) {
// Update CSRF Token
$('.txt_csrfname').val(response.token);
return {
results: response.data
};
},
cache: true
}
});
/**
* Categorias por empresa
*/
$(".idCategory").select2({
ajax: {
url: "<?= base_url('admin/categorias/getCategoriasAjax') ?>",
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 idEmpresa = $('.idEmpresa').val(); // CSRF hash
return {
searchTerm: params.term, // search term
[csrfName]: csrfHash, // CSRF Token
idEmpresa: idEmpresa // search term
};
},
processResults: function(response) {
// Update CSRF Token
$('.txt_csrfname').val(response.token);
return {
results: response.data
};
},
cache: true
}
});
/**
* When change id Control
*/
$("#idCategory").on("change", function() {
var idCategoria = $(this).val();
var idEmpresa = $(".idEmpresa").val();
var idProduct = $("#idProducts").val();
var datos = new FormData();
datos.append("idCategoria", idCategoria);
datos.append("idEmpresa", idEmpresa);
$.ajax({
url: "<?= base_url('admin/categorias/buscarFolio') ?>",
method: "POST",
data: datos,
cache: false,
contentType: false,
processData: false,
success: function(respuesta) {
if (idProduct == 0) {
$("#code").val(respuesta);
}
}
}
)
});
$("#porcentSale").on("keyup", function() {
var porcentaje = $(this).val() / 100;
var precioCompra = $("#buyPrice").val()
var precioVenta = $("#salePrice").val()
if (!(precioCompra > 0)) {
return;
}
precioVenta = precioCompra * (porcentaje + 1)
$("#salePrice").val(precioVenta);
});
$(document).on('click', '.btnAddProducts', function(e) {
$(".form-controlProducts").val("");
$("#idProducts").val("0");
$("#btnSaveProducts").removeAttr("disabled");
$("#idCategory").val("0");
$("#idCategory").trigger("change");
$("#idEmpresa").val("0");
$("#idEmpresa").trigger("change");
$("#porcentSale").val("40");
$("#porcentTax").val("0");
$("#porcentIVARetenido").val("0");
$("#porcentISRRetenido").val("0");
$("#unidadSAT").val("0");
$("#unidadSAT").trigger("change");
$("#claveProductoSAT").val("0");
$("#claveProductoSAT").trigger("change");
$("#facturacionRD").bootstrapToggle("off");
});
/*
* AL hacer click al editar
*/
$(document).on('click', '.btnEditProducts', function(e) {
var idProducts = $(this).attr("idProducts");
//LIMPIAMOS CONTROLES
$(".form-control").val("");
$("#idProducts").val(idProducts);
$("#btnGuardarProducts").removeAttr("disabled");
});
$(document).on('click', '#btnSaveProducts', function(e) {
var idEmpresa = $("#idEmpresa").val();
var idProducts = $("#idProducts").val();
var clave = $("#code").val();
var idCategory = $("#idCategory").val();
var barcode = $("#barcode").val();
var description = $("#description").val();
var stock = $("#stock").val();
var buyPrice = $("#buyPrice").val();
var salePrice = $("#salePrice").val();
var porcentSale = $("#porcentSale").val();
var porcentTax = $("#porcentTax").val();
var porcentIVARetenido = $("#porcentIVARetenido").val();
var porcentISRRetenido = $("#porcentISRRetenido").val();
var routeImage = $("#routeImage").val();
var unidadSAT = $("#unidadSAT").val();
var unidad = $("#unidad").val();
var claveProductoSAT = $("#claveProductoSAT").val();
var nombreUnidadSAT = $("#unidadSAT option:selected").text();
var nombreClaveProducto = $("#claveProductoSAT option:selected").text();
var imagenProducto = $("#imagenProducto").prop("files")[0];
if ($("#validateStock").is(':checked')) {
var validateStock = "on";
} else {
var validateStock = "off";
}
if ($("#inventarioRiguroso").is(':checked')) {
var inventarioRiguroso = "on";
} else {
var inventarioRiguroso = "off";
}
if (idEmpresa == 0 || idEmpresa == "") {
Toast.fire({
icon: 'error',
title: "Tiene que seleccionar la empresa"
});
return;
}
if (idCategory == 0 || idCategory == "") {
Toast.fire({
icon: 'error',
title: "Tiene que seleccionar la categoria"
});
return;
}
if (porcentTax == "") {
Toast.fire({
icon: 'error',
title: "Tiene que ingregar el porcentaje de impuesto"
});
return;
}
$("#btnSaveProducts").attr("disabled", true);
var datos = new FormData();
datos.append("idEmpresa", idEmpresa);
datos.append("idProducts", idProducts);
datos.append("code", clave);
datos.append("idCategory", idCategory);
datos.append("description", description);
datos.append("stock", stock);
datos.append("buyPrice", buyPrice);
datos.append("salePrice", salePrice);
datos.append("porcentSale", porcentSale);
datos.append("porcentTax", porcentTax);
datos.append("porcentIVARetenido", porcentIVARetenido);
datos.append("porcentISRRetenido", porcentISRRetenido);
datos.append("imagenProducto", imagenProducto);
datos.append("barcode", barcode);
datos.append("validateStock", validateStock);
datos.append("inventarioRiguroso", inventarioRiguroso);
datos.append("unidadSAT", unidadSAT);
datos.append("unidad", unidad);
datos.append("claveProductoSAT", claveProductoSAT);
datos.append("nombreUnidadSAT", nombreUnidadSAT);
datos.append("nombreClaveProducto", nombreClaveProducto);
$.ajax({
url: "<?= base_url('admin/products/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"
});
tableProducts.ajax.reload();
$("#btnSaveProducts").removeAttr("disabled");
$('#modalAddProducts').modal('hide');
} else {
Toast.fire({
icon: 'error',
title: respuesta
});
$("#btnSaveProducts").removeAttr("disabled");
}
}
}
)
});
</script>
<?= $this->endSection() ?>
Este es el codigo para capturar el precio de los productos app/views/modulesProducts/priceProducts.php
<p>
<h3>Precios e Impuestos</h3>
<div class="form-group row ">
<div class="col-sm-6 ">
<label for="buyPrice" class="col-sm-12 col-form-label">
<?= lang('products.fields.buyPrice') ?>
</label>
<div class="col-sm-12">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-arrow-down"></i></span>
</div>
<input type="number" name="buyPrice" id="buyPrice" class="form-control form-controlProducts <?= session('error.buyPrice') ? 'is-invalid' : '' ?>" value="<?= old('buyPrice') ?>" step=".01" placeholder="<?= lang('products.fields.buyPrice') ?>" autocomplete="off">
</div>
</div>
</div>
<div class="col-sm-6 ">
<label for="salePrice" class="col-sm-12 col-form-label">
<?= lang('products.fields.salePrice') ?>
</label>
<div class="col-sm-12">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-arrow-up"></i></span>
</div>
<input type="number" name="salePrice" id="salePrice" class="form-control form-controlProducts <?= session('error.salePrice') ? 'is-invalid' : '' ?>" value="<?= old('salePrice') ?>" step=".01" pattern="^\d*(\.\d{0,2})?$" placeholder="<?= lang('products.fields.salePrice') ?>" autocomplete="off">
</div>
</div>
</div>
</div>
<div class="form-group row ">
<div class="col-sm-6"></div>
<div class="col-sm-6 ">
<label for="porcentSale" class="col-sm-12 col-form-label">
<?= lang('products.fields.porcentSale') ?>
</label>
<div class="col-sm-12">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-percent"></i></span>
</div>
<input type="number" name="porcentSale" id="porcentSale" class="form-control form-controlProducts <?= session('error.porcentSale') ? 'is-invalid' : '' ?>" value="<?= old('porcentSale') ?>" placeholder="<?= lang('products.fields.porcentSale') ?>" autocomplete="off" min="0" value="40">
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="porcentTax" class="col-sm-2 col-form-label">
<?= lang('products.fields.porcentTax') ?>
</label>
<div class="col-sm-10">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-percent"></i></span>
</div>
<input type="number" name="porcentTax" id="porcentTax" class="form-control form-controlProducts <?= session('error.porcentTax') ? 'is-invalid' : '' ?>" value="<?= old('porcentTax') ?>" placeholder="<?= lang('products.fields.porcentTax') ?>" autocomplete="off">
</div>
</div>
</div>
<div class="form-group row">
<label for="porcentTax" class="col-sm-2 col-form-label">
Iva Retenido
</label>
<div class="col-sm-10">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-percent"></i></span>
</div>
<input type="number" name="porcentIVARetenido" id="porcentIVARetenido" class="form-control form-controlProducts <?= session('error.porcentIVARetenido') ? 'is-invalid' : '' ?>" value="<?= old('porcentIVARetenido') ?>" placeholder="Iva Retenido" autocomplete="off">
</div>
</div>
</div>
<div class="form-group row">
<label for="porcentTax" class="col-sm-2 col-form-label">
ISR Retenido
</label>
<div class="col-sm-10">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-percent"></i></span>
</div>
<input type="number" name="porcentISRRetenido" id="porcentISRRetenido" class="form-control form-controlProducts <?= session('error.porcentISRRetenido') ? 'is-invalid' : '' ?>" value="<?= old('porcentISRRetenido') ?>" placeholder="ISR Retenido" autocomplete="off">
</div>
</div>
</div>
</p>
Con este código generamos los controles para capturar los datos del SAT del producto app/views/modulesProducts/productsCFDIV4.php
Creamos las rutas necesarias en app/Config/routes.php
$routes->post('products/save', 'ProductsController::save');
$routes->post('products/getProducts', 'ProductsController::getProducts');
$routes->get('products/getAllProducts/(:any)', 'ProductsController::getAllProducts/$1');
$routes->get('products/getAllProductsInventory/(:any)/(:any)/(:any)', 'ProductsController::getAllProductsInventory/$1/$2/$3');
$routes->post('products/getUnidadSATAjax', 'ProductsController::getUnidadSATAjax');
$routes->post('products/getProductosSATAjax', 'ProductsController::getProductosSATAjax');
$routes->post('products/getProductsAjax', 'ProductsController::getProductsAjaxSelect2');
Creamos el menú para entrar a productos
Y listo ya tenemos nuestro modulo hecho
Como el tutorial se hizo ya con el fuente avanzado algunos catálogos necesarios como sucursales puede que no estén a la fecha, pero se agregaran
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">×</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
Creado con WordPress & Tema de Anders Norén