falta ordenar por cajas

This commit is contained in:
2025-05-04 13:05:37 +02:00
parent 3f90665c39
commit 39639d9ff8
10 changed files with 519 additions and 60 deletions

View File

@ -851,6 +851,11 @@ $routes->group('etiquetasTitulos', ['namespace' => 'App\Controllers\Logistica'],
$routes->post('delete', 'EtiquetasTitulosController::deleteEtiqueta');
$routes->get('edit/(:num)', 'EtiquetasTitulosController::edit/$1');
$routes->get('datatableLineas/(:num)', 'EtiquetasTitulosController::datatableLineasEtiquetas/$1');
$routes->get('findOts', 'EtiquetasTitulosController::findOtsWithAddress');
$routes->post('addLineas', 'EtiquetasTitulosController::addLineasEtiqueta');
$routes->post('deleteLineas', 'EtiquetasTitulosController::deleteLineasEtiqueta');
$routes->post('updateLineas', 'EtiquetasTitulosController::updateLineasEtiqueta');
$routes->post('updateComentarios', 'EtiquetasTitulosController::updateComentarios');
});
/*

View File

@ -206,6 +206,47 @@ class EtiquetasTitulosController extends BaseController
return $result->toJson(returnAsObject: true);
}
public function findOtsWithAddress()
{
if ($this->request->isAJAX()) {
$id = $this->request->getGet('id') ?? null;
$query = EtiquetasTitulosService::findOTsWithAddress($id);
if ($this->request->getGet("q")) {
$query->groupStart()
->orLike("name", $this->request->getGet("q"))
->groupEnd();
}
$result = $query->orderBy("id", "DESC")->get()->getResultObject();
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function addLineasEtiqueta(){
if($this->request->isAJAX()){
$etiqueta_id = $this->request->getPost('etiqueta_id') ?? null;
$ot_id = $this->request->getPost('ot_id') ?? null;
$unidades = $this->request->getPost('unidades') ?? null;
$cajas = $this->request->getPost('cajas') ?? null;
$result = EtiquetasTitulosService::addLineasEtiqueta($etiqueta_id, $ot_id, $unidades, $cajas);
return $this->response->setJSON($result);
}
else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function datatableLineasEtiquetas($id = null){
$model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
@ -222,29 +263,122 @@ class EtiquetasTitulosController extends BaseController
callback: function ($q) {
return '<input type="checkbox" class="form-check-input checkbox-linea-envio" name="row_selected[]" value="' . $q->id . '">';
}
);
/*->edit(
"pedido",
)
->edit(
"ot",
function ($row, $meta) {
return '<a href="' . base_url('pedidos/edit/' . $row->pedido) . '" target="_blank">' . $row->pedido . '</a>';
return '<a href="' . base_url('produccion/ordentrabajo/edit/' . $row->ot) . '" target="_blank">' . $row->ot . '</a>';
}
)
->edit(
"presupuesto",
"unidades",
function ($row, $meta) {
return '<a href="' . base_url('presupuestoadmin/edit/' . $row->presupuesto) . '" target="_blank">' . $row->presupuesto . '</a>';
}
)->edit(
"unidadesEnvio",
function ($row, $meta) {
if($row->finalizado == 1 || $row->tipo_envio == 'ferro_prototipo'){
return $row->unidadesEnvio;
}
return '<input type="number" class="form-control input-lineas input-unidades text-center"
data-id="'. $row->id.'" data-name="unidades_envio" value="' . $row->unidadesEnvio . '">';
data-id="'. $row->id.'" data-name="unidades" value="' . $row->unidades . '">';
}
);*/
)
->edit(
"numero_caja",
function ($row, $meta) {
return '<input type="number" class="form-control input-lineas input-cajas text-center"
data-id="'. $row->id.'" data-name="numero_caja" value="' . $row->numero_caja . '">';
}
)
->add(
"action",
callback: function ($q) {
return '
<div class="btn-group btn-group-sm">
<a href="javascript:void(0);"><i class="ti ti-trash ti-sm btn-delete" data-id="' . $q->id . '"></i></a>
</div>
';
}
);
return $result->toJson(returnAsObject: true);
}
public function deleteLineasEtiqueta()
{
if ($this->request->isAJAX()) {
$ids = $this->request->getPost('ids') ?? [];
if ($ids == [] || $ids == null) {
return [
'status' => false,
'message' => lang('Logistica.errors.errorMissingData')
];
}
$model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
for( $i = 0; $i < count($ids); $i++){
$model->delete($ids[$i]);
}
$result = [
'status' => true,
'message' => lang('Logistica.success.successDeleteLines'),
];
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function updateLineasEtiqueta(){
if ($this->request->isAJAX()) {
$id = $this->request->getPost('id') ?? null;
$name = $this->request->getPost('name') ?? null;
$value = $this->request->getPost('value') ?? null;
if ($id == null || $name == null || $value == null) {
return [
'status' => false,
'message' => lang('Logistica.errors.errorMissingData')
];
}
$model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
$model->update($id, [$name => $value]);
$result = [
'status' => true,
'message' => lang('Logistica.success.successUpdateLine'),
];
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function updateComentarios(){
if ($this->request->isAJAX()) {
$id = $this->request->getPost('id') ?? null;
$comentarios = $this->request->getPost('comentarios') ?? null;
if ($id == null || $comentarios == null) {
return [
'status' => false,
'message' => lang('Logistica.errors.errorMissingData')
];
}
$model = model('App\Models\Etiquetas\EtiquetasTitulosModel');
$model->update($id, ['comentarios' => $comentarios]);
$result = [
'status' => true,
'message' => lang('Logistica.success.comentariosUpdated'),
];
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
}

View File

@ -753,7 +753,7 @@ return [
"menu_informes" => "Informes",
"menu_pedidos" => "Pedidos",
"menu_pedidos_validacion" => "Validación",
"menu_pedidos_validacion" => "Aprobación",
"menu_pedidos_activos" => "Producción",
"menu_pedidos_finalizados" => "Finalizados",
"menu_pedidos_cancelados" => "Cancelados",

View File

@ -93,6 +93,10 @@ return [
'finalizado' => 'El envío se ha finalizado correctamente',
'finalizadoOTs' => 'El envío se ha finalizado correctamente.\nSe han creado las OTs siguientes OTs: {ots}',
'successDeleteEtiqueta' => 'Etiqueta eliminada correctamente',
'successInsertLines' => 'Lineas de etiqueta insertadas correctamente',
'successDeleteLines' => 'Lineas de etiqueta eliminadas correctamente',
'successUpdateLine' => 'Linea de etiqueta actualizadas correctamente',
'comentariosUpdated' => 'Comentarios actualizados correctamente',
],
];

View File

@ -34,6 +34,7 @@ class EtiquetasTitulosLineasModel extends Model
protected $skipValidation = false;
protected $beforeDelete = ['addUserDeleted'];
protected $beforeUpdate = ['addUserUpdated'];
protected function addUserDeleted(array $data)
{
@ -51,31 +52,55 @@ class EtiquetasTitulosLineasModel extends Model
return $data;
}
public function getDatatableQuery($etiqueta_id, $direccion = null){
protected function addUserUpdated(array $data)
{
$userId = auth()->user()->id;
if (!isset($data['data'])) {
$data['data'] = [];
}
if (!empty($data['id'])) {
$builder = $this->builder();
$builder->whereIn($this->primaryKey, (array) $data['id'])
->update(['user_updated_at' => $userId]);
}
return $data;
}
public function getDatatableQuery($etiqueta_id, $direccion = null)
{
$direccionNormalizada = str_replace(' ', '', strtolower(trim($direccion)));
$direccionSQL = $this->db->escape($direccionNormalizada);
// Subconsulta: suma de pesos por presupuesto
$subPeso = $this->db->table('presupuesto_linea')
->select('presupuesto_id, ROUND(SUM(peso)/1000, 2) as pesoUnidad')
->groupBy('presupuesto_id');
$builder = $this->db->table('etiquetas_titulos_lineas etl')
->select('etl.ot_id as ot, pr.titulo as titulo, etl.unidades as unidades, etl.numero_caja as numero_caja,
pd.cantidad as unidadesTotal, etl.id as id, etl.unidades as unidadesRaw,
(SELECT ROUND(SUM(peso)/1000, 2)
FROM presupuesto_linea
WHERE presupuesto_id = pr.id) AS pesoUnidad')
->select('
etl.ot_id as ot,
pr.titulo as titulo,
etl.unidades as unidades,
etl.numero_caja as numero_caja,
pd.cantidad as unidadesTotal,
etl.id as id,
etl.unidades as unidadesRaw,
peso_sub.pesoUnidad
')
->join('etiquetas_titulos et', 'et.id = etl.etiqueta_titulos_id')
->join('ordenes_trabajo ot', 'ot.id = etl.ot_id')
->join('pedidos p', 'p.id = ot.pedido_id')
->join('pedidos_linea pl', 'pl.pedido_id = p.id')
->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
->join('presupuesto_linea plinea', 'pr.id = plinea.presupuesto_id')
->join("({$subPeso->getCompiledSelect()}) as peso_sub", 'peso_sub.presupuesto_id = pr.id', 'left')
->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id')
->where('etl.deleted_at IS NULL')
->where('et.id', $etiqueta_id)
->where("REPLACE(LOWER(TRIM(pd.direccion)), ' ', '') = $direccionSQL", null, false)
->groupBy('etl.numero_caja');
->where("REPLACE(LOWER(TRIM(pd.direccion)), ' ', '') = '{$direccionNormalizada}'", null, false)
->orderBy('etl.numero_caja');
return $builder;
}
}

View File

@ -35,6 +35,7 @@ class EtiquetasTitulosModel extends Model
protected $beforeDelete = ['addUserDeleted'];
protected $beforeUpdate = ['addUserUpdated'];
protected function addUserDeleted(array $data)
{
@ -52,6 +53,22 @@ class EtiquetasTitulosModel extends Model
return $data;
}
protected function addUserUpdated(array $data)
{
$userId = auth()->user()->id;
if (!isset($data['data'])) {
$data['data'] = [];
}
if (!empty($data['id'])) {
$builder = $this->builder();
$builder->whereIn($this->primaryKey, (array) $data['id'])
->update(['user_updated_at' => $userId]);
}
return $data;
}
public function getEtiquetasTitulos()
{
return $this->db->table('etiquetas_titulos et')

View File

@ -21,13 +21,13 @@ class EtiquetasTitulosService
->join('pedidos_linea pl', 'p.id = pl.pedido_id')
->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
->whereIn('p.estado', ['finalizado', 'produccion'])
->where('ot_dates.embalaje_at IS NOT NULL');
->whereIn('p.estado', ['finalizado', 'produccion']);
return $builder;
}
public static function getDireccionesOT($ot_id){
public static function getDireccionesOT($ot_id)
{
$db = \Config\Database::connect();
@ -42,7 +42,6 @@ class EtiquetasTitulosService
->join('ordenes_trabajo ot', 'ot.pedido_id = p.id')
->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
->whereIn('p.estado', ['finalizado', 'produccion'])
->where('ot_dates.embalaje_at IS NOT NULL')
->where('ot.id', $ot_id);
return $builder;
@ -76,7 +75,7 @@ class EtiquetasTitulosService
]);
$etiquetaId = $modelEtiquetasTitulos->getInsertID();
if($etiquetaId == null){
if ($etiquetaId == null) {
return [
'status' => false,
'message' => lang('Logistica.errorInsertarEtiqueta'),
@ -85,7 +84,7 @@ class EtiquetasTitulosService
$cantidad_restante = intval($data['cantidad']);
$numero_caja = 1;
while($cantidad_restante > 0){
while ($cantidad_restante > 0) {
$modelEtiquetasTitulosLineas = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
$modelEtiquetasTitulosLineas->insert([
'etiqueta_titulos_id' => $etiquetaId,
@ -104,4 +103,89 @@ class EtiquetasTitulosService
'etiqueta' => $etiquetaId,
];
}
public static function findOTsWithAddress(int $etiqueta_id)
{
$db = \Config\Database::connect();
// 1. Dirección del envío actual
$etiqueta = $db->table('etiquetas_titulos')->select('direccion')->where('id', $etiqueta_id)->get()->getRow();
if (!$etiqueta) {
return $db->table('(SELECT NULL AS id, NULL AS name, NULL as unidades) AS empty')->where('1 = 0');
}
$direccionNormalizada = str_replace(' ', '', strtolower(trim($etiqueta->direccion)));
$direccionSQL = $db->escape($direccionNormalizada);
// 2. Obtener presupuestos con esa dirección
$presupuestosConEsaDireccion = $db->table('presupuesto_direcciones')
->select('presupuesto_id')
->where("REPLACE(LOWER(TRIM(direccion)), ' ', '') = $direccionSQL", null, false)
->get()
->getResultArray();
$presupuestoIds = array_column($presupuestosConEsaDireccion, 'presupuesto_id');
if (empty($presupuestoIds)) {
return $db->table('(SELECT NULL AS id, NULL AS name, NULL as unidades) AS empty')->where('1 = 0');
}
// 3. Subconsulta principal
$subBuilder = $db->table('pedidos_linea pl')
->select("
ot.id AS id,
CONCAT('[', ot.id, '] - ', pr.titulo) AS name,
pd.cantidad AS description
")
->join('pedidos p', 'p.id = pl.pedido_id')
->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id')
->join('ordenes_trabajo ot', 'ot.pedido_id = p.id')
->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
->whereIn('pr.id', $presupuestoIds)
->whereIn('p.estado', ['finalizado', 'produccion'])
->groupBy('pl.id');
// 4. Envolver y filtrar por unidades pendientes
$builder = $db->table("({$subBuilder->getCompiledSelect(false)}) AS sub");
$builder->select('id, name, description');
return $builder;
}
public static function addLineasEtiqueta($etiqueta_id, $ot_id, $unidades, $cajas)
{
$unidades_caja = intdiv($unidades, $cajas);
$unidades_caja = $unidades_caja == 0 ? $unidades : $unidades_caja;
$unidades_restantes = $unidades;
$modelEtitquetaLinea = model('\App\Models\Etiquetas\EtiquetasTitulosLineasModel');
$next_caja = $modelEtitquetaLinea
->selectMax('numero_caja')
->where('etiqueta_titulos_id', $etiqueta_id)
->first();
$next_caja = $next_caja->numero_caja ?? 0;
$next_caja++;
$user_id = auth()->user()->id;
while($unidades_restantes > 0){
$modelEtitquetaLinea->insert([
'etiqueta_titulos_id' => $etiqueta_id,
'ot_id' => $ot_id,
'unidades' => $unidades_restantes - $unidades_caja < 0 ? $unidades_restantes : intval($unidades_caja),
'numero_caja' => $next_caja,
'user_created_at' => $user_id,
]);
$unidades_restantes -= $unidades_caja;
$next_caja++;
}
return [
'status' => true,
'message' => lang('Logistica.success.successInsertLines'),
];
}
}

View File

@ -166,9 +166,9 @@
<thead>
<tr>
<th></th>
<th><?= lang("Logistica.otId") ?></th>
<th><?= lang("Logistica.titulo") ?></th>
<th><?= lang("Logistica.num_caja") ?></th>
<th style="max-width: 10%;"><?= lang("Logistica.otId") ?></th>
<th style="max-width: 70%;"><?= lang("Logistica.titulo") ?></th>
<th style="max-width: 10%;"><?= lang("Logistica.num_caja") ?></th>
<th class="text-center" style="width: 10%;">
<?= lang("Logistica.unidadesEnCaja") ?>
</th>
@ -178,13 +178,14 @@
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<th colspan="9">
<th colspan="10">
<div class="text-end">
<?= lang("Logistica.unidadesTotalesFooter") ?>
<span id="footer-unidades-envio"></span>
@ -192,7 +193,7 @@
</th>
</tr>
<tr>
<th colspan="9">
<th colspan="10">
<div class="text-end">
<?= lang("Logistica.peso") ?>
<span id="footer-peso"></span>

View File

@ -88,6 +88,9 @@ let ClassSelect = function (domItem, url, placeholder, allowClear = false, param
this.getText = () => {
return this.item.find(":selected").text();
};
this.getDesc = () => {
return this.item.find(":selected").data("desc");
};
this.onChange = function (callback) {
this.item.on('change', callback);
};

View File

@ -15,19 +15,35 @@ class EtiquetaEdit {
{ data: "unidadesTotal" },
{ data: "id" },
{ data: "pesoUnidad" },
{ data: "unidadesRaw" }
{ data: "unidadesRaw" },
{ data: "action" }
];
this.table = null;
this.direccion = $('#direccion');
this.addLineas = $('#btnAddLinea');
this.btnEliminarLineas = $('#btnEliminarLineas');
this.btbnGuardarComentarios = $('#guardarComentarios');
this.buscador = new ClassSelect($('#buscadorPedidos'), '/etiquetasTitulos/findOts', '', true, { 'id': $('#id').val() });
}
init() {
this.initDatatable();
const self = this;
this._initDatatable();
this.buscador.init();
this.addLineas.on('click', this._addLineas.bind(this));
$(document).on('click', '.btn-delete', this._deleteLinea.bind(this));
$(document).on('change', '.input-lineas', this._updateLinea.bind(this));
this.btnEliminarLineas.on('click', this._deleteLinea.bind(this));
this.btbnGuardarComentarios.on('click', this._guardarComentarios.bind(this));
}
initDatatable() {
_initDatatable() {
this.table = $('#tableLineasEtiqueta').DataTable({
processing: true,
@ -69,7 +85,7 @@ class EtiquetaEdit {
},
"columnDefs": [
{
"targets": [0],
"targets": [0, 9],
"className": "text-center",
"orderable": false,
"searchable": false,
@ -84,8 +100,178 @@ class EtiquetaEdit {
}
]
});
}
_addLineas() {
if (this.buscador.item.select2('data').length > 0) {
let maxUnidades = 0;
if (this.buscador.item.select2('data')[0].desc) {
maxUnidades = parseInt(this.buscador.item.select2('data')[0].desc);
Swal.fire({
title: 'Unidades',
html: `
<div class="mb-2">
<label for="inputUnidades">Unidades:</label>
<input type="number" id="inputUnidades" class="swal2-input" value="${maxUnidades}">
</div>
<div>
<label for="inputCajas">Cajas:</label>
<input type="number" id="inputCajas" class="swal2-input" value="1">
</div>
`,
icon: 'info',
showCancelButton: true,
confirmButtonColor: '#3085d6',
confirmButtonText: 'Aceptar',
cancelButtonText: 'Cancelar',
customClass: {
confirmButton: 'btn btn-primary me-1',
cancelButton: 'btn btn-secondary'
},
buttonsStyling: false,
preConfirm: () => {
const unidades = parseInt(document.getElementById('inputUnidades').value);
const cajas = parseInt(document.getElementById('inputCajas').value);
if (isNaN(unidades) || isNaN(cajas)) {
Swal.showValidationMessage('Debe completar ambos campos');
}
return { unidades, cajas };
}
}).then((result) => {
if (result.isConfirmed) {
const { unidades, cajas } = result.value;
const url = '/etiquetasTitulos/addLineas';
const data = {
etiqueta_id: $('#id').val(),
ot_id: this.buscador.getVal(),
unidades: unidades,
cajas: cajas
};
$.post(
url,
data,
function (response) {
if (response.status) {
self.table.ajax.reload();
popSuccessAlert(response.message);
} else {
popErrorAlert('Error en la respuesta');
}
}
).fail(function (xhr, status, error) {
popErrorAlert(error);
});
}
});
}
}
}
_deleteLinea(e) {
e.preventDefault();
const self = this;
let ids = [];
if (e.currentTarget.id == "btnEliminarLineas") {
this.table.rows().every(function (rowIdx, tableLoop, rowLoop) {
const node = this.node(); // DOM del tr
const checkbox = $(node).find('.checkbox-linea-envio');
if (checkbox.is(':checked')) {
const data = this.data();
ids.push(data.id);
}
});
}
else{
ids.push($(e.currentTarget).attr('data-id'));
}
Swal.fire({
title: '¿Está seguro de eliminar la linea?',
text: "No podrá revertir esta acción",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí',
cancelButtonText: 'Cancelar',
customClass: {
confirmButton: 'btn btn-danger me-1',
cancelButton: 'btn btn-secondary'
},
buttonsStyling: false
}).then((result) => {
if (result.isConfirmed) {
$.post(
'/etiquetasTitulos/deleteLineas',
{
ids: ids
},
function (response) {
if (response.status) {
self.table.ajax.reload();
popSuccessAlert(response.message);
} else {
popErrorAlert('Error borrando la etiqueta');
}
}
).fail(function (xhr, status, error) {
popErrorAlert(error);
});
}
})
}
_updateLinea(e) {
e.preventDefault();
const self = this;
const id = $(e.currentTarget).attr('data-id');
const name = $(e.currentTarget).attr('data-name');
const value = $(e.currentTarget).val();
const url = '/etiquetasTitulos/updateLineas';
const data = {
id: id,
name: name,
value: value
};
$.post(
url,
data,
function (response) {
if (!response.status) {
popErrorAlert('Error actualizando la linea');
}
}
).fail(function (xhr, status, error) {
popErrorAlert(error);
});
}
_guardarComentarios(){
const self = this;
const id = $('#id').val();
const comentarios = $('#comentarios').val();
const url = '/etiquetasTitulos/updateComentarios';
const data = {
id: id,
comentarios: comentarios
};
$.post(
url,
data,
function (response) {
if (!response.status) {
popErrorAlert('Error actualizando comentarios');
}
else{
popSuccessAlert(response.message);
}
}
).fail(function (xhr, status, error) {
popErrorAlert(error);
});
}
}
document.addEventListener('DOMContentLoaded', function () {