terminado

This commit is contained in:
2025-05-04 18:35:08 +02:00
parent 39639d9ff8
commit 7b645539e3
9 changed files with 589 additions and 74 deletions

View File

@ -856,6 +856,9 @@ $routes->group('etiquetasTitulos', ['namespace' => 'App\Controllers\Logistica'],
$routes->post('deleteLineas', 'EtiquetasTitulosController::deleteLineasEtiqueta'); $routes->post('deleteLineas', 'EtiquetasTitulosController::deleteLineasEtiqueta');
$routes->post('updateLineas', 'EtiquetasTitulosController::updateLineasEtiqueta'); $routes->post('updateLineas', 'EtiquetasTitulosController::updateLineasEtiqueta');
$routes->post('updateComentarios', 'EtiquetasTitulosController::updateComentarios'); $routes->post('updateComentarios', 'EtiquetasTitulosController::updateComentarios');
$routes->post('updateOrdenCajas', 'EtiquetasTitulosController::updateOrdenCajas');
$routes->post('renumber', 'EtiquetasTitulosController::renumberCajas');
$routes->post('imprimirEtiquetas', 'EtiquetasTitulosController::imprimirEtiquetas');
}); });
/* /*

View File

@ -42,7 +42,7 @@ class EtiquetasTitulosController extends BaseController
if ($this->request->isAJAX()) { if ($this->request->isAJAX()) {
$query = EtiquetasTitulosService::getOtsWithTitulos(); $query = EtiquetasTitulosService::getOtsWithTitulos();
if ($this->request->getGet("q")) { if ($this->request->getGet("q")) {
$query->groupStart() $query->groupStart()
->orLike("ot.id", $this->request->getGet("q")) ->orLike("ot.id", $this->request->getGet("q"))
@ -66,7 +66,7 @@ class EtiquetasTitulosController extends BaseController
$ot_id = $this->request->getGet("ot_id"); $ot_id = $this->request->getGet("ot_id");
$query = EtiquetasTitulosService::getDireccionesOT($ot_id); $query = EtiquetasTitulosService::getDireccionesOT($ot_id);
if ($this->request->getGet("q")) { if ($this->request->getGet("q")) {
$query->groupStart() $query->groupStart()
->orLike("pd.direccion", $this->request->getGet("q")) ->orLike("pd.direccion", $this->request->getGet("q"))
@ -92,11 +92,11 @@ class EtiquetasTitulosController extends BaseController
$data['direccion'] = $this->request->getPost('direccion') ?? null; $data['direccion'] = $this->request->getPost('direccion') ?? null;
$data['unidades_caja'] = $this->request->getPost('unidades_caja') ?? null; $data['unidades_caja'] = $this->request->getPost('unidades_caja') ?? null;
if( if (
$this->request->getPost('ot_id') == null || $this->request->getPost('ot_id') == null ||
$this->request->getPost('direccion') == null || $this->request->getPost('direccion') == null ||
$this->request->getPost('unidades_caja') == null $this->request->getPost('unidades_caja') == null
){ ) {
return [ return [
'status' => false, 'status' => false,
'message' => lang('Logistica.errorMissingData') 'message' => lang('Logistica.errorMissingData')
@ -125,12 +125,12 @@ class EtiquetasTitulosController extends BaseController
$modelLineas = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel'); $modelLineas = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
$ids = $modelLineas->where('etiqueta_titulos_id', $id)->findColumn('id'); $ids = $modelLineas->where('etiqueta_titulos_id', $id)->findColumn('id');
if($ids){ if ($ids) {
$modelLineas->delete($ids); $modelLineas->delete($ids);
} }
$model = model('App\Models\Etiquetas\EtiquetasTitulosModel'); $model = model('App\Models\Etiquetas\EtiquetasTitulosModel');
$id = $model->where('id', $id)->findColumn('id'); $id = $model->where('id', $id)->findColumn('id');
if($id){ if ($id) {
$model->delete($id); $model->delete($id);
} }
$result = [ $result = [
@ -142,7 +142,7 @@ class EtiquetasTitulosController extends BaseController
} else { } else {
return $this->failUnauthorized('Invalid request', 403); return $this->failUnauthorized('Invalid request', 403);
} }
} }
@ -160,8 +160,8 @@ class EtiquetasTitulosController extends BaseController
if (empty($etiquetaEntity)) { if (empty($etiquetaEntity)) {
return redirect()->to(base_url('logistica/etiquetasLogistica'))->with('error', lang('Logistica.errors.noEnvio')); return redirect()->to(base_url('logistica/etiquetasLogistica'))->with('error', lang('Logistica.errors.noEnvio'));
} }
$modelImpresora = model('App\Models\Configuracion\ImpresoraEtiquetaModel'); $modelImpresora = model('App\Models\Configuracion\ImpresoraEtiquetaModel');
$impresoras = $modelImpresora->select('id, name') $impresoras = $modelImpresora->select('id, name')
->where('deleted_at', null) ->where('deleted_at', null)
@ -193,7 +193,7 @@ class EtiquetasTitulosController extends BaseController
$q->groupEnd(); $q->groupEnd();
} }
$result = DataTable::of($q) $result = DataTable::of($q)
->add("action", callback: function ($q) { ->add("action", callback: function ($q) {
return ' return '
<div class="btn-group btn-group-sm"> <div class="btn-group btn-group-sm">
@ -202,7 +202,7 @@ class EtiquetasTitulosController extends BaseController
</div> </div>
'; ';
}); });
return $result->toJson(returnAsObject: true); return $result->toJson(returnAsObject: true);
} }
@ -212,7 +212,7 @@ class EtiquetasTitulosController extends BaseController
$id = $this->request->getGet('id') ?? null; $id = $this->request->getGet('id') ?? null;
$query = EtiquetasTitulosService::findOTsWithAddress($id); $query = EtiquetasTitulosService::findOTsWithAddress($id);
if ($this->request->getGet("q")) { if ($this->request->getGet("q")) {
$query->groupStart() $query->groupStart()
->orLike("name", $this->request->getGet("q")) ->orLike("name", $this->request->getGet("q"))
@ -229,9 +229,10 @@ class EtiquetasTitulosController extends BaseController
} }
} }
public function addLineasEtiqueta(){ public function addLineasEtiqueta()
{
if($this->request->isAJAX()){ if ($this->request->isAJAX()) {
$etiqueta_id = $this->request->getPost('etiqueta_id') ?? null; $etiqueta_id = $this->request->getPost('etiqueta_id') ?? null;
$ot_id = $this->request->getPost('ot_id') ?? null; $ot_id = $this->request->getPost('ot_id') ?? null;
@ -240,14 +241,14 @@ class EtiquetasTitulosController extends BaseController
$result = EtiquetasTitulosService::addLineasEtiqueta($etiqueta_id, $ot_id, $unidades, $cajas); $result = EtiquetasTitulosService::addLineasEtiqueta($etiqueta_id, $ot_id, $unidades, $cajas);
return $this->response->setJSON($result); return $this->response->setJSON($result);
} } else {
else {
return $this->failUnauthorized('Invalid request', 403); return $this->failUnauthorized('Invalid request', 403);
} }
} }
public function datatableLineasEtiquetas($id = null){ public function datatableLineasEtiquetas($id = null)
{
$model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel'); $model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
@ -270,20 +271,22 @@ class EtiquetasTitulosController extends BaseController
return '<a href="' . base_url('produccion/ordentrabajo/edit/' . $row->ot) . '" target="_blank">' . $row->ot . '</a>'; return '<a href="' . base_url('produccion/ordentrabajo/edit/' . $row->ot) . '" target="_blank">' . $row->ot . '</a>';
} }
) )
->edit( ->edit(
"unidades", "unidades",
function ($row, $meta) { function ($row, $meta) {
return '<input type="number" class="form-control input-lineas input-unidades text-center" return '<input type="number" class="form-control input-lineas input-unidades text-center"
data-id="'. $row->id.'" data-name="unidades" value="' . $row->unidades . '">'; data-id="' . $row->id . '" data-name="unidades" value="' . $row->unidades . '">';
} }
) )
->edit( ->edit(
"numero_caja", "numero_caja",
function ($row, $meta) { function ($row, $meta) {
return '<input type="number" class="form-control input-lineas input-cajas text-center" 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 . '">'; data-id="' . $row->id . '" data-name="numero_caja" value="' . $row->numero_caja . '">';
} }
) )
->add("unidades_raw", fn($row) => $row->unidades)
->add("pesoUnidad_raw", fn($row) => $row->pesoUnidad)
->add( ->add(
"action", "action",
callback: function ($q) { callback: function ($q) {
@ -311,10 +314,10 @@ class EtiquetasTitulosController extends BaseController
} }
$model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel'); $model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
for( $i = 0; $i < count($ids); $i++){ for ($i = 0; $i < count($ids); $i++) {
$model->delete($ids[$i]); $model->delete($ids[$i]);
} }
$result = [ $result = [
'status' => true, 'status' => true,
@ -327,7 +330,8 @@ class EtiquetasTitulosController extends BaseController
} }
} }
public function updateLineasEtiqueta(){ public function updateLineasEtiqueta()
{
if ($this->request->isAJAX()) { if ($this->request->isAJAX()) {
$id = $this->request->getPost('id') ?? null; $id = $this->request->getPost('id') ?? null;
@ -355,7 +359,8 @@ class EtiquetasTitulosController extends BaseController
} }
} }
public function updateComentarios(){ public function updateComentarios()
{
if ($this->request->isAJAX()) { if ($this->request->isAJAX()) {
$id = $this->request->getPost('id') ?? null; $id = $this->request->getPost('id') ?? null;
@ -381,4 +386,82 @@ class EtiquetasTitulosController extends BaseController
return $this->failUnauthorized('Invalid request', 403); return $this->failUnauthorized('Invalid request', 403);
} }
} }
public function updateOrdenCajas()
{
$rawInput = $this->request->getBody();
$data = json_decode($rawInput, true);
$orden = $data['orden'] ?? [];
if (!is_array($orden)) {
return $this->response->setJSON([
'status' => false,
'message' => 'Datos inválidos'
]);
}
$model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
foreach ($orden as $item) {
if (isset($item['id'], $item['numero_caja'])) {
$model->update($item['id'], [
'numero_caja' => $item['numero_caja'],
'updated_at' => date('Y-m-d H:i:s') // opcional
]);
}
}
return $this->response->setJSON([
'status' => true,
'message' => 'Orden de cajas actualizado correctamente'
]);
}
public function renumberCajas()
{
$id = $this->request->getPost('id') ?? null;
if ($id == null) {
return [
'status' => false,
'message' => lang('Logistica.errors.errorMissingData')
];
}
$result = EtiquetasTitulosService::reordenarCajas($id);
return $this->response->setJSON($result);
}
public function imprimirEtiquetas()
{
$etiqueta_id = $this->request->getPost('etiqueta_id') ?? null;
$ids = $this->request->getPost('ids') ?? [];
$impresora_id = $this->request->getPost('impresora_id') ?? null;
$modelImpresora = model('App\Models\Configuracion\ImpresoraEtiquetaModel');
$impresora = $modelImpresora->select('id, name, ip, port, user, pass')
->where('deleted_at', null)
->where('id', $impresora_id)
->orderBy('name', 'asc')
->first();
if ($impresora == null) {
return $this->response->setJSON([
'status' => false,
'message' => 'Impresora no válida'
]);
}
if ($etiqueta_id == null || $ids == []) {
return [
'status' => false,
'message' => lang('Logistica.errors.errorMissingData')
];
}
$result = EtiquetasTitulosService::imprimirEtiquetas($etiqueta_id, $ids, $impresora);
return $this->response->setJSON($result);
}
} }

View File

@ -78,7 +78,7 @@ return [
'cliente' => 'Cliente', 'cliente' => 'Cliente',
'comentariosEtiqueta' => 'Comentarios etiqueta', 'comentariosEtiqueta' => 'Comentarios etiqueta',
'addLineaEtiqueta' => 'Añadir líneas a la etiqueta', 'addLineaEtiqueta' => 'Añadir líneas a la etiqueta',
'renumber' => 'Renumerar etiquetas',
'errors' => [ 'errors' => [
'noEnvio' => 'No se ha encontrado el envio', 'noEnvio' => 'No se ha encontrado el envio',
@ -88,6 +88,8 @@ return [
'noAddresses' => 'El pedido no tiene direcciones de envío', 'noAddresses' => 'El pedido no tiene direcciones de envío',
'errorMissingData' => 'Faltan datos para crear la etiqueta', 'errorMissingData' => 'Faltan datos para crear la etiqueta',
'errorInsertarEtiqueta' => 'Error al insertar la etiqueta', 'errorInsertarEtiqueta' => 'Error al insertar la etiqueta',
'noEtiqueta' => 'No se ha encontrado la etiqueta',
'noEtiquetaLineas' => 'No se han encontrado líneas de etiqueta',
], ],
'success' => [ 'success' => [
'finalizado' => 'El envío se ha finalizado correctamente', 'finalizado' => 'El envío se ha finalizado correctamente',
@ -97,6 +99,8 @@ return [
'successDeleteLines' => 'Lineas de etiqueta eliminadas correctamente', 'successDeleteLines' => 'Lineas de etiqueta eliminadas correctamente',
'successUpdateLine' => 'Linea de etiqueta actualizadas correctamente', 'successUpdateLine' => 'Linea de etiqueta actualizadas correctamente',
'comentariosUpdated' => 'Comentarios actualizados correctamente', 'comentariosUpdated' => 'Comentarios actualizados correctamente',
'successReordenarCajas' => 'Cajas reordenadas correctamente',
'imprimirEtiquetas' => 'Etiquetas impresas correctamente',
], ],
]; ];

View File

@ -79,10 +79,12 @@ class EtiquetasTitulosLineasModel extends Model
$builder = $this->db->table('etiquetas_titulos_lineas etl') $builder = $this->db->table('etiquetas_titulos_lineas etl')
->select(' ->select('
etl.id as id,
etl.ot_id as ot, etl.ot_id as ot,
pr.titulo as titulo, pr.titulo as titulo,
etl.unidades as unidades, etl.unidades as unidades,
etl.numero_caja as numero_caja, etl.numero_caja as numero_caja,
etl.numero_caja as numero_caja_raw,
pd.cantidad as unidadesTotal, pd.cantidad as unidadesTotal,
etl.id as id, etl.id as id,
etl.unidades as unidadesRaw, etl.unidades as unidadesRaw,

View File

@ -165,12 +165,12 @@ class EtiquetasTitulosService
->selectMax('numero_caja') ->selectMax('numero_caja')
->where('etiqueta_titulos_id', $etiqueta_id) ->where('etiqueta_titulos_id', $etiqueta_id)
->first(); ->first();
$next_caja = $next_caja->numero_caja ?? 0; $next_caja = $next_caja->numero_caja ?? 0;
$next_caja++; $next_caja++;
$user_id = auth()->user()->id; $user_id = auth()->user()->id;
while($unidades_restantes > 0){ while ($unidades_restantes > 0) {
$modelEtitquetaLinea->insert([ $modelEtitquetaLinea->insert([
'etiqueta_titulos_id' => $etiqueta_id, 'etiqueta_titulos_id' => $etiqueta_id,
'ot_id' => $ot_id, 'ot_id' => $ot_id,
@ -188,4 +188,177 @@ class EtiquetasTitulosService
]; ];
} }
public static function reordenarCajas($etiqueta_id)
{
$model = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
// 1. Obtener todas las líneas ordenadas por numero_caja e id
$lineas = $model
->where('etiqueta_titulos_id', $etiqueta_id)
->orderBy('numero_caja ASC')
->orderBy('id ASC')
->findAll();
// 2. Agrupar por caja
$grupos = [];
foreach ($lineas as $linea) {
$grupos[$linea->numero_caja][] = $linea;
}
// 3. Reasignar números de caja de forma consecutiva
$nuevoNumeroCaja = 1;
foreach ($grupos as $grupo) {
foreach ($grupo as $linea) {
$model->update($linea->id, ['numero_caja' => $nuevoNumeroCaja]);
}
$nuevoNumeroCaja++;
}
return [
'status' => true,
'message' => lang('Logistica.success.successReordenarCajas'),
];
}
public static function imprimirEtiquetas($etiqueta_id = null, $ids = [], $printer = null){
$model = model('App\Models\Etiquetas\EtiquetasTitulosModel');
$modelLineas = model('App\Models\Etiquetas\EtiquetasTitulosLineasModel');
if ($etiqueta_id) {
$etiquetas = $model->where('id', $etiqueta_id)->first();
$etiquetas_lineas = $modelLineas->whereIn('id', $ids)->orderBy('numero_caja', 'ASC')->findAll();
}
// buscar el maximo numero_cajas en el array de objetos etiquetas_lineas
$max_num_cajas = 0;
foreach ($etiquetas_lineas as $linea) {
if ($linea->numero_caja > $max_num_cajas) {
$max_num_cajas = $linea->numero_caja;
}
}
// se obtienen los numero_caja diferentes en un array
$numero_cajas = [];
foreach ($etiquetas_lineas as $linea) {
if (!in_array($linea->numero_caja, $numero_cajas)) {
$numero_cajas[] = $linea->numero_caja;
}
}
if (empty($etiquetas)) {
return [
'status' => false,
'message' => lang('Logistica.errors.noEtiqueta'),
];
}
if (empty($etiquetas_lineas)) {
return [
'status' => false,
'message' => lang('Logistica.errors.noEtiquetaLineas'),
];
}
$data = [
"printer" => $printer->name,
"header" => [
"_FORMAT" => "E:MULTI.ZPL",
"_QUANTITY" => 1,
"_PRINBTERNAME" => $printer->name,
"_JOBNAME" => "LBL101"
],
'direccion' => $etiquetas->direccion,
'grupos' => [],
'notas' => $etiquetas->comentarios,
'nombre' => $etiquetas->att,
];
$index_etiqueta = 0;
foreach ($numero_cajas as $caja) {
array_push($data['grupos'], [
]);
$prefix = 1;
$lineas = array_filter($etiquetas_lineas, function ($linea) use ($caja) {
return $linea->numero_caja == $caja;
});
$lineaCounter = 0;
foreach($lineas as $linea){
if($lineaCounter >= 5){
$lineaCounter = 0;
$index_etiqueta++;
array_push($data['grupos'], []);
}
$modelPresupuestos = model('App\Models\OrdenTrabajo\OrdenTrabajoModel');
$datos_etiqueta = $modelPresupuestos->select('
pr.titulo as titulo,
pr.isbn as isbn,
pr.referencia_cliente as referencia_cliente,
p.id as id_pedido,
ordenes_trabajo.total_tirada as total_tirada,')
->join('pedidos p', 'p.id = ordenes_trabajo.pedido_id')
->join('pedidos_linea pl', 'pl.pedido_id = p.id')
->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
->where('ordenes_trabajo.id',$linea->ot_id)->first();
$data['grupos'][$index_etiqueta][] = [
'prefix' => $caja,
'titulo' => mb_substr($datos_etiqueta->titulo, 0, 40),
'cantidad' => $linea->unidades,
'tirada' => $datos_etiqueta->total_tirada,
'ean' => str_replace('-', '', $datos_etiqueta->isbn),
'npedido' => $datos_etiqueta->id,
'refcliente' => $datos_etiqueta->referencia_cliente,
];
$lineaCounter++;
}
$index_etiqueta++;
}
$servicioImpresora = new ImpresoraEtiquetaService();
$xml = $servicioImpresora->createEtiquetaTitulos($data);
if ($xml == null) {
return [
'status' => false,
'message' => lang('Logistica.errors.noEtiquetas'),
];
}
$sk_environment = getenv('SK_ENVIRONMENT');
if ($sk_environment == 'production') {
$status = $servicioImpresora->sendToImpresoraEtiqueta("ETIQUETA", $xml, $printer);
if ($status) {
return [
'status' => true,
'message' => lang('Logistica.success.imprimirEtiquetas'),
];
} else {
return [
'status' => false,
'message' => lang('Logistica.errors.noEtiquetas'),
];
}
} else {
return [
'status' => true,
'message' => lang('Logistica.success.imprimirEtiquetas'),
'data' => $xml
];
}
}
} }

View File

@ -24,9 +24,9 @@ class ImpresoraEtiquetaService extends BaseService
"labels" => [ "labels" => [
[ [
"cliente" => "Cliente Potencial", "cliente" => "Cliente Potencial",
"titulo" => "[1234] TEST OLIVEROS", "titulo" => "[1234] TEST OLIVEROS",
"cantidad" => 100, "cantidad" => 100,
"tirada" => 50, "tirada" => 50,
"cajas" => 1, "cajas" => 1,
"ean" => null, "ean" => null,
"nombre" => "___Nombre___", "nombre" => "___Nombre___",
@ -76,6 +76,66 @@ class ImpresoraEtiquetaService extends BaseService
$xml->appendChild($labels); $xml->appendChild($labels);
return $xml->saveXML(); return $xml->saveXML();
} }
public function createEtiquetaTitulos(array $data_label = []): ?string
{
$xml = new DOMDocument('1.0', 'utf-8');
// Crear nodo raíz "labels"
$labels = $xml->createElement("labels");
// Establecer atributos de "labels" desde "header"
foreach ($data_label["header"] as $key => $value) {
$labels->setAttribute($key, $value);
}
// Recorrer grupos de etiquetas
foreach ($data_label["grupos"] as $grupo) {
$labelChild = $xml->createElement('label');
// Crear variables específicas del grupo
foreach ($grupo as $libro) {
$prefix = $libro['prefix'];
$variables = [
"titulo$prefix" => $libro['titulo'],
"tirada$prefix" => $libro['tirada'],
"ean$prefix" => $libro['ean'],
"npedido$prefix" => $libro['npedido'],
"refcliente$prefix" => $libro['refcliente'],
"textpedido$prefix" => 'Pedido',
"textcantidad$prefix" => 'Cantidad:',
"textref$prefix" => 'Ref:',
"textean$prefix" => 'ISBN',
];
foreach ($variables as $varName => $varValue) {
$variableChild = $xml->createElement('variable');
$variableChild->setAttribute("name", $varName);
$variableChild->appendChild($xml->createTextNode((string) $varValue));
$labelChild->appendChild($variableChild);
}
}
// Variables comunes del grupo
$nombreVariable = $xml->createElement('variable', htmlspecialchars($data_label['nombre']));
$nombreVariable->setAttribute('name', 'nombre');
$labelChild->appendChild($nombreVariable);
$direccionVariable = $xml->createElement('variable', htmlspecialchars($data_label['direccion']));
$direccionVariable->setAttribute('name', 'direccion');
$labelChild->appendChild($direccionVariable);
$labels->appendChild($labelChild);
}
$xml->appendChild($labels);
return $xml->saveXML();
}
public function sendToImpresoraEtiqueta(string $name, string $content, ImpresoraEtiquetaEntity $impresoraEtiqueta): bool public function sendToImpresoraEtiqueta(string $name, string $content, ImpresoraEtiquetaEntity $impresoraEtiqueta): bool
{ {
$tmpFile = tmpfile(); $tmpFile = tmpfile();
@ -84,8 +144,8 @@ class ImpresoraEtiquetaService extends BaseService
rewind($tmpFile); rewind($tmpFile);
$tmpMetaData = stream_get_meta_data($tmpFile); $tmpMetaData = stream_get_meta_data($tmpFile);
$conn = @ftp_connect($impresoraEtiqueta->ip,$impresoraEtiqueta->port); $conn = @ftp_connect($impresoraEtiqueta->ip, $impresoraEtiqueta->port);
if(!$conn){ if (!$conn) {
throw new Exception('Error al establecer conexión FTP'); throw new Exception('Error al establecer conexión FTP');
} }
$isLoginSuccess = @ftp_login($conn, $impresoraEtiqueta->user, $impresoraEtiqueta->pass); $isLoginSuccess = @ftp_login($conn, $impresoraEtiqueta->user, $impresoraEtiqueta->pass);
@ -99,10 +159,10 @@ class ImpresoraEtiquetaService extends BaseService
if (ftp_put($conn, $name, $tmpMetaData['uri'], FTP_ASCII) === FALSE) { if (ftp_put($conn, $name, $tmpMetaData['uri'], FTP_ASCII) === FALSE) {
$status = false; $status = false;
ftp_close($conn); ftp_close($conn);
}else{ } else {
$status = true; $status = true;
} }
return $status; return $status;
} }
} }

View File

@ -143,6 +143,21 @@
</button> </button>
</div> </div>
<div class="col-sm-2 px-3">
<button id="btnRenumber" name="btnRenumber" tabindex="1"
class="btn btn-success w-100">
<?= lang("Logistica.renumber") ?>
<i class="ti ti-box"></i>
</button>
</div>
<div class="col-sm-2 px-3">
<button id="btnImprimirEtiquetas" name="btnImprimirEtiquetas" tabindex="1"
class="btn btn-info w-100">
<?= lang("Logistica.imprimirEtiquetas") ?>
<i class="ti ti-printer"></i>
</button>
</div>
<div class="col-sm-2 px-3 d-flex flex-column justify-content-end"> <div class="col-sm-2 px-3 d-flex flex-column justify-content-end">
<div class="d-flex flex-column justify-content-end h-100"> <div class="d-flex flex-column justify-content-end h-100">
<label for="impresoraEtiquetas" class="form-label"> <label for="impresoraEtiquetas" class="form-label">
@ -179,13 +194,14 @@
<th></th> <th></th>
<th></th> <th></th>
<th></th> <th></th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<th colspan="10"> <th colspan="11">
<div class="text-end"> <div class="text-end">
<?= lang("Logistica.unidadesTotalesFooter") ?> <?= lang("Logistica.unidadesTotalesFooter") ?>
<span id="footer-unidades-envio"></span> <span id="footer-unidades-envio"></span>
@ -193,7 +209,7 @@
</th> </th>
</tr> </tr>
<tr> <tr>
<th colspan="10"> <th colspan="11">
<div class="text-end"> <div class="text-end">
<?= lang("Logistica.peso") ?> <?= lang("Logistica.peso") ?>
<span id="footer-peso"></span> <span id="footer-peso"></span>
@ -221,12 +237,13 @@
<link rel="stylesheet" href="https://cdn.datatables.net/rowreorder/1.4.1/css/rowReorder.dataTables.min.css"> <link rel="stylesheet" href="https://cdn.datatables.net/rowreorder/1.4.1/css/rowReorder.dataTables.min.css">
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css"> <link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
<link rel="stylesheet" href="<?= site_url("/themes/vuexy/vendor/libs/flatpickr/flatpickr.css") ?>"> <link rel="stylesheet" href="<?= site_url("/themes/vuexy/vendor/libs/flatpickr/flatpickr.css") ?>">
<link rel="stylesheet" href="https://cdn.datatables.net/rowreorder/1.4.1/css/rowReorder.dataTables.min.css">
<?= $this->endSection() ?> <?= $this->endSection() ?>
<?= $this->section('additionalExternalJs') ?> <?= $this->section('additionalExternalJs') ?>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script> <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.js') ?>"></script> <script src="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.js') ?>"></script>
<script src="https://cdn.datatables.net/rowgroup/1.3.1/js/dataTables.rowGroup.min.js"></script> <script src="https://cdn.datatables.net/rowgroup/1.3.1/js/dataTables.rowGroup.min.js"></script>
<script src="https://cdn.datatables.net/rowreorder/1.4.1/js/dataTables.rowReorder.min.js"></script>
<script type="module" src="<?= site_url("assets/js/safekat/pages/logistica/etiquetaEdit.js") ?>"></script> <script type="module" src="<?= site_url("assets/js/safekat/pages/logistica/etiquetaEdit.js") ?>"></script>
<?= $this->endSection() ?> <?= $this->endSection() ?>

View File

@ -16,7 +16,9 @@ class EtiquetaEdit {
{ data: "id" }, { data: "id" },
{ data: "pesoUnidad" }, { data: "pesoUnidad" },
{ data: "unidadesRaw" }, { data: "unidadesRaw" },
{ data: "action" } { data: "action" },
{ data: "numero_caja_raw" }
]; ];
this.table = null; this.table = null;
@ -25,6 +27,9 @@ class EtiquetaEdit {
this.addLineas = $('#btnAddLinea'); this.addLineas = $('#btnAddLinea');
this.btnEliminarLineas = $('#btnEliminarLineas'); this.btnEliminarLineas = $('#btnEliminarLineas');
this.btbnGuardarComentarios = $('#guardarComentarios'); this.btbnGuardarComentarios = $('#guardarComentarios');
this.btnSelectAll = $('#btnSelectAll');
this.btnRenumber = $('#btnRenumber');
this.btnImprimirEtiquetas = $('#btnImprimirEtiquetas');
this.buscador = new ClassSelect($('#buscadorPedidos'), '/etiquetasTitulos/findOts', '', true, { 'id': $('#id').val() }); this.buscador = new ClassSelect($('#buscadorPedidos'), '/etiquetasTitulos/findOts', '', true, { 'id': $('#id').val() });
} }
@ -41,10 +46,88 @@ class EtiquetaEdit {
$(document).on('change', '.input-lineas', this._updateLinea.bind(this)); $(document).on('change', '.input-lineas', this._updateLinea.bind(this));
this.btnEliminarLineas.on('click', this._deleteLinea.bind(this)); this.btnEliminarLineas.on('click', this._deleteLinea.bind(this));
this.btbnGuardarComentarios.on('click', this._guardarComentarios.bind(this)); this.btbnGuardarComentarios.on('click', this._guardarComentarios.bind(this));
this.btnSelectAll.on('click', this._selectAll.bind(this));
this.btnRenumber.on('click', this._renumber.bind(this));
this.btnImprimirEtiquetas.on('click', this._imprimirEtiquetas.bind(this));
} }
_imprimirEtiquetas() {
const self = this;
const ids = [];
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);
}
});
$.post(
'/etiquetasTitulos/imprimirEtiquetas',
{
etiqueta_id: $('#id').val(),
ids: ids,
impresora_id: $('#impresoraEtiquetas').val()
},
function (response) {
if (response.status) {
popSuccessAlert(response.message);
if(response.data) {
// show xml in new tab
const blob = new Blob([response.data], { type: 'application/xml' });
const url = URL.createObjectURL(blob);
const newTab = window.open(url, '_blank');
if (newTab) {
newTab.onload = function () {
// Revoke the object URL after the new tab has loaded
URL.revokeObjectURL(url);
};
} else {
popErrorAlert('Error abriendo la pestaña');
}
}
} else {
popErrorAlert('Error imprimiendo las etiquetas');
}
}
).fail(function (xhr, status, error) {
popErrorAlert(error);
});
}
_renumber() {
const self = this;
$.post(
'/etiquetasTitulos/renumber',
{
id: $('#id').val()
},
function (response) {
if (response.status) {
self.table.ajax.reload();
popSuccessAlert(response.message);
} else {
popErrorAlert('Error renumerando las lineas');
}
}
).fail(function (xhr, status, error) {
popErrorAlert(error);
});
}
_selectAll() {
const checkboxes = this.table.$('input[type="checkbox"]');
const allChecked = checkboxes.length === checkboxes.filter(':checked').length;
checkboxes.prop('checked', !allChecked);
}
_initDatatable() { _initDatatable() {
const self = this;
this.table = $('#tableLineasEtiqueta').DataTable({ this.table = $('#tableLineasEtiqueta').DataTable({
processing: true, processing: true,
serverSide: true, serverSide: true,
@ -52,54 +135,137 @@ class EtiquetaEdit {
responsive: true, responsive: true,
scrollX: true, scrollX: true,
orderCellsTop: true, orderCellsTop: true,
orderable: false, rowId: 'id',
order: [[7, 'asc']], order: [[10, 'asc']],
lengthMenu: [5, 10, 25, 50, 75, 100, 250, 500, 1000, 2500], rowGroup: {
dataSrc: 'numero_caja_raw',
startRender: function (rows, group) {
let totalUnidades = 0;
let totalPeso = 0;
rows.data().each(function (row) {
const unidades = parseFloat(row.unidadesRaw) || 0;
const pesoUnidad = parseFloat(row.pesoUnidad) || 0;
totalUnidades += unidades;
totalPeso += unidades * pesoUnidad;
});
const groupId = 'grupo-caja-' + group;
return $('<tr/>')
.attr('data-group', groupId)
.addClass('group-header bg-light fw-bold')
.css('cursor', 'pointer')
.append(
`<td colspan="11">
📦 CAJA ${group} UNIDADES: ${totalUnidades} PESO: ${totalPeso.toFixed(2)} kg
</td>`
);
}
},
rowReorder: {
dataSrc: 'numero_caja_raw',
update: false,
selector: 'td:not(.dt-no-reorder)' // evita inputs
},
lengthMenu: [5, 10, 25, 50, 100],
pageLength: 50, pageLength: 50,
"dom": 'lrtip', ajax: {
"ajax": { url: "/etiquetasTitulos/datatableLineas/" + $('#id').val(),
"url": "/etiquetasTitulos/datatableLineas/" + $('#id').val(), data: function (d) {
"data": function (d) {
d.direccion = $('#direccion').val(); d.direccion = $('#direccion').val();
d.id = $('#id').val(); d.id = $('#id').val();
}, },
}, },
"columns": this.tableCols, columns: this.tableCols,
"language": { language: {
url: "/themes/vuexy/vendor/libs/datatables-sk/plugins/i18n/es-ES.json" url: "/themes/vuexy/vendor/libs/datatables-sk/plugins/i18n/es-ES.json"
}, },
footerCallback: function (row, data, start, end, display) { columnDefs: [
let totalUnidades = 0;
let totalPeso = 0;
data.forEach(row => {
const unidades = parseFloat(row.unidadesEnvioRaw) || 0;
const pesoUnidad = parseFloat(row.pesoUnidad) || 0;
totalUnidades += unidades;
totalPeso += unidades * pesoUnidad;
});
// Mostrar en spans personalizados del <tfoot>
$('#footer-unidades-envio').text(totalUnidades);
$('#footer-peso').text(totalPeso.toFixed(2));
},
"columnDefs": [
{ {
"targets": [0, 9], targets: [0, 9],
"className": "text-center", className: "text-center dt-no-reorder",
"orderable": false, orderable: false,
"searchable": false, searchable: false
}, },
{ {
"targets": [1, 2, 4, 5, 6], targets: [3, 4],
"className": "text-center", className: "text-center dt-no-reorder"
}, },
{ {
targets: [6, 7, 8], targets: [1, 2, 4, 5, 6],
className: "text-center"
},
{
targets: [6, 7, 8, 10],
visible: false visible: false
} }
] ]
}); });
$('#tableLineasEtiqueta tbody').on('click', 'tr.group-header', function () {
const group = $(this).data('group');
const table = $('#tableLineasEtiqueta').DataTable();
const icon = $(this).find('.group-toggle-icon');
let visible = true;
table.rows().every(function () {
const row = this.node();
const data = this.data();
if ('numero_caja' in data && ('grupo-caja-' + data.numero_caja) === group) {
if (!$(row).hasClass('group-header')) {
$(row).toggle();
visible = !$(row).is(':visible');
}
}
});
// Cambiar el icono
icon.text(visible ? '▼' : '►');
});
this.table.on('row-reorder', function (e, diff, edit) {
if (!diff.length || !edit.triggerRow) return;
const table = self.table;
const movedRowData = table.row(edit.triggerRow).data();
if (!movedRowData?.id) return;
const movedItem = diff.find(d => {
const rowData = table.row(d.node).data();
return rowData?.id === movedRowData.id;
});
if (!movedItem) return;
const payload = {
id: movedRowData.id,
numero_caja: movedItem.newData
};
$.ajax({
url: '/etiquetasTitulos/updateOrdenCajas',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ orden: [payload] }),
success: function (response) {
if (response.status) {
setTimeout(() => {
self.table.ajax.reload(null, false);
}, 100);
} else {
popErrorAlert('Error actualizando el orden');
}
},
error: function () {
popErrorAlert('Error en la solicitud AJAX');
}
});
});
} }
_addLineas() { _addLineas() {
@ -185,7 +351,7 @@ class EtiquetaEdit {
} }
}); });
} }
else{ else {
ids.push($(e.currentTarget).attr('data-id')); ids.push($(e.currentTarget).attr('data-id'));
} }
Swal.fire({ Swal.fire({
@ -241,13 +407,16 @@ class EtiquetaEdit {
if (!response.status) { if (!response.status) {
popErrorAlert('Error actualizando la linea'); popErrorAlert('Error actualizando la linea');
} }
else {
self.table.ajax.reload();
}
} }
).fail(function (xhr, status, error) { ).fail(function (xhr, status, error) {
popErrorAlert(error); popErrorAlert(error);
}); });
} }
_guardarComentarios(){ _guardarComentarios() {
const self = this; const self = this;
const id = $('#id').val(); const id = $('#id').val();
const comentarios = $('#comentarios').val(); const comentarios = $('#comentarios').val();
@ -263,7 +432,7 @@ class EtiquetaEdit {
if (!response.status) { if (!response.status) {
popErrorAlert('Error actualizando comentarios'); popErrorAlert('Error actualizando comentarios');
} }
else{ else {
popSuccessAlert(response.message); popSuccessAlert(response.message);
} }
} }

View File

@ -139,4 +139,8 @@
.dtrg-group.ui-droppable-hover { .dtrg-group.ui-droppable-hover {
background-color: #d1ecf1 !important; background-color: #d1ecf1 !important;
cursor: move; cursor: move;
}
.dt-no-reorder {
cursor: auto !important;
} }