Merge branch 'main' into feat/view-maquinista

This commit is contained in:
amazuecos
2025-04-25 07:41:25 +02:00
52 changed files with 5300 additions and 469 deletions

BIN
ci4/app/Config/RBAC.zip Normal file

Binary file not shown.

View File

@ -548,6 +548,7 @@ $routes->group('facturas', ['namespace' => 'App\Controllers\Facturacion'], funct
$routes->post('updateCabecera/(:any)', 'Facturas::updateCabecera/$1', ['as' => 'updateCabecera']);
$routes->post('datatablePagos/(:any)', 'FacturasPagos::datatable/$1', ['as' => 'dataTableOfPagosFacturas']);
$routes->post('editorPagos', 'FacturasPagos::datatable_editor', ['as' => 'editorOfPagosFacturas']);
$routes->post('deleteFacturaLineaPago', 'Facturas::deleteLineaPago', ['as' => 'deleteLineaPago']);
$routes->post('datatablePedidos', 'Facturas::datatablePedidos', ['as' => 'dataTableOfFacturasPedido']);
$routes->get('getdatoscliente/(:any)', 'Facturas::getDatosFacturacionClienteForm/$1');
});
@ -799,8 +800,7 @@ $routes->group('logistica', ['namespace' => 'App\Controllers\Logistica'], functi
$routes->get('print/label/test', 'LogisticaController::print_test_label');
$routes->get('panel', 'LogisticaController::panel', ['as' => 'LogisticaPanel']);
$routes->get('selectEnvios/(:any)', 'LogisticaController::selectorEnvios/$1', ['as' => 'selectEnvios']);
$routes->get('buscar/(:any)', 'LogisticaController::searchPedidoOrISBN/$1', ['as' => 'buscarPedidoOrISBN']);
$routes->get('envios', 'LogisticaController::gestionEnvios', ['as' => 'gestionEnvios']);
$routes->get('datatableEnvios', 'LogisticaController::datatable_envios');
$routes->get('datatableLineasEnvios/(:num)', 'LogisticaController::datatable_enviosEdit/$1');
$routes->get('envio/(:num)', 'LogisticaController::editEnvio/$1');
@ -811,6 +811,12 @@ $routes->group('logistica', ['namespace' => 'App\Controllers\Logistica'], functi
$routes->post('updateLineaEnvio', 'LogisticaController::updateLineaEnvio');
$routes->post('updateComentariosEnvio', 'LogisticaController::saveComments');
$routes->post('updateCajasEnvio', 'LogisticaController::updateCajasEnvio');
$routes->post('updateCodigoSeguimiento', 'LogisticaController::updateCodigoSeguimiento');
$routes->post('updateProveedorEnvio', 'LogisticaController::updateProveedorEnvio');
$routes->post('finalizarEnvio', 'LogisticaController::finalizarEnvio');
$routes->post('generateEnvio', 'LogisticaController::generarEnvio');
$routes->get('selectPedidosForEnvio', 'LogisticaController::findPedidosNewEnvio');
$routes->get('selectDireccionForEnvio', 'LogisticaController::selectDireccionForEnvio');
});
/*

View File

@ -0,0 +1,29 @@
<?php
use CodeIgniter\Router\RouteCollection;
/** @var RouteCollection $routes */
/* Rutas para tarifas */
$routes->group('catalogo', ['namespace' => 'App\Controllers\Catalogo'], function ($routes) {
/* Libros */
$routes->group('libros', ['namespace' => 'App\Controllers\Catalogo'], function ($routes) {
/**======================
* CRUD
*========================**/
$routes->get('', 'CatalogoLibros::index', ['as' => 'catalogoLibrosList']);
$routes->get('gettarifas', 'CatalogoLibros::getSelect2');
$routes->match(['get', 'post'], 'add', 'CatalogoLibros::add', ['as' => 'catalogoLibrosAdd']);
$routes->match(['get', 'post'], 'edit/(:num)', 'CatalogoLibros::edit/$1', ['as' => 'catalogoLibrosEdit']);
$routes->get('delete/(:num)', 'CatalogoLibros::delete/$1', ['as' => 'catalogoLibrosDelete']);
$routes->get('datatable', 'CatalogoLibros::datatable', ['as' => 'catalogoLibrosDT']);
/**======================
* AJAX
*========================**/
$routes->get('clientlist', 'CatalogoLibros::getClientList', ['as' => 'catalogoLibrosClientList']);
});
});

View File

@ -19,6 +19,7 @@ $routes->group('compras', ['namespace' => 'App\Controllers\Compras'], function (
$routes->get('delete/(:num)', 'Proveedores::delete/$1', ['as' => 'deleteProveedores']);
$routes->post('allmenuitems', 'Proveedores::allItemsSelect', ['as' => 'select2ItemsOfProveedores']);
$routes->post('menuitems', 'Proveedores::menuItems', ['as' => 'menuItemsOfProveedores']);
$routes->get('getProveedores', 'Proveedores::getForSelect');
});
});

View File

@ -0,0 +1,239 @@
<?php
namespace App\Controllers\Catalogo;
use App\Controllers\BaseResourceController;
use App\Entities\Catalogo\CatalogoLibroEntity;
use App\Models\Catalogo\CatalogoLibroModel;
use App\Models\Clientes\ClienteModel;
use Hermawan\DataTables\DataTable;
class CatalogoLibros extends BaseResourceController
{
protected $modelName = CatalogoLibroModel::class;
protected $format = 'json';
protected static $singularObjectName = 'Catalogo';
protected static $singularObjectNameCc = 'CatalogoLibros';
protected static $pluralObjectName = 'Catalogos';
protected static $pluralObjectNameCc = 'catalogos';
protected static $controllerSlug = 'catalogo';
protected static $viewPath = 'themes/vuexy/form/catalogo/';
protected $indexRoute = 'CatalogoLibrosList';
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
$this->viewData['pageTitle'] = lang('Catalogo.listingPage');
$this->viewData['usingSweetAlert'] = true;
// Breadcrumbs (IMN)
$this->viewData['breadcrumb'] = [
['title' => lang("App.menu_catalogo"), 'route' => "javascript:void(0);", 'active' => false],
['title' => lang("App.menu_catalogo_libros"), 'route' => route_to('catalogoLibrosList'), 'active' => true]
];
parent::initController($request, $response, $logger);
}
public function index()
{
$viewData = [
'currentModule' => static::$controllerSlug,
'pageSubTitle' => lang('Basic.global.ManageAllRecords', [lang('Catalogo.catalogo')]),
'catalogoLibrosEntity' => new CatalogoLibroEntity(),
'usingServerSideDataTable' => true,
];
$viewData = array_merge($this->viewData, $viewData); // merge any possible values from the parent controller class
return view(static::$viewPath . 'viewCatalogoLibrosList', $viewData);
}
public function add()
{
if ($this->request->getPost()):
$postData = $this->request->getPost();
$sanitizedData = $this->sanitized($postData, true);
$sanitizedData['user_created_id'] = auth()->user()->id;
unset($sanitizedData['isk']);
$noException = true;
if ($successfulResult = $this->canValidate()):
if ($this->canValidate()):
try {
$successfulResult = $this->model->skipValidation(true)->save($sanitizedData);
} catch (\Exception $e) {
$noException = false;
$this->dealWithException($e);
}
else:
$this->viewData['errorMessage'] = lang('Basic.global.formErr1', [lang('Basic.global.record')]);
$this->session->setFlashdata('formErrors', $this->model->errors());
endif;
$thenRedirect = true; // Change this to false if you want your user to stay on the form after submission
endif;
if ($noException && $successfulResult):
$id = $this->model->db->insertID();
$message = lang('Basic.global.saveSuccess', [lang('Basic.global.record')]) . '.';
if ($thenRedirect):
if (!empty($this->indexRoute)):
return redirect()->to(route_to($this->indexRoute))->with('sweet-success', $message);
else:
return $this->redirect2listView('sweet-success', $message);
endif;
else:
$this->session->setFlashData('sweet-success', $message);
endif;
endif; // $noException && $successfulResult
endif; // ($requestMethod === 'post')
$this->viewData['catalogoLibrosEntity'] = isset($sanitizedData) ? new CatalogoLibroEntity($sanitizedData) : new CatalogoLibroEntity();
$this->viewData['formAction'] = route_to('catalogoLibrosAdd');
$this->viewData['boxTitle'] = lang('Basic.global.addNew') . ' ' . lang('Catalogo.moduleTitle') . ' ' . lang('Basic.global.addNewSuffix');
return $this->displayForm(__METHOD__);
} // end function add()
public function edit($requestedId = null)
{
if ($requestedId == null):
return $this->redirect2listView();
endif;
$id = filter_var($requestedId, FILTER_SANITIZE_URL);
$catalogoLibrosEntity = $this->model->find($id);
if ($catalogoLibrosEntity == false):
$message = lang('Basic.global.notFoundWithIdErr', [mb_strtolower(lang('Catalogo.pais')), $id]);
return $this->redirect2listView('sweet-error', $message);
endif;
if ($this->request->getPost()):
$postData = $this->request->getPost();
$sanitizedData = $this->sanitized($postData, true);
unset($sanitizedData['isk']);
$sanitizedData['user_update_id'] = auth()->user()->id;
$noException = true;
if ($successfulResult = $this->canValidate()): // if ($successfulResult = $this->validate($this->formValidationRules) ) :
if ($this->canValidate()):
try {
$successfulResult = $this->model->skipValidation(true)->update($id, $sanitizedData);
} catch (\Exception $e) {
$noException = false;
$this->dealWithException($e);
}
else:
$this->viewData['warningMessage'] = lang('Basic.global.formErr1', [mb_strtolower(lang('Catalogo.catalogo'))]);
$this->session->setFlashdata('formErrors', $this->model->errors());
endif;
$catalogoLibrosEntity->fill($sanitizedData);
$thenRedirect = false;
endif;
if ($noException && $successfulResult):
$id = $catalogoLibrosEntity->id ?? $id;
$message = lang('Basic.global.updateSuccess', [lang('Basic.global.record')]) . '.';
if ($thenRedirect):
if (!empty($this->indexRoute)):
return redirect()->to(route_to($this->indexRoute))->with('sweet-success', $message);
else:
return $this->redirect2listView('sweet-success', $message);
endif;
else:
$this->session->setFlashData('sweet-success', $message);
endif;
endif; // $noException && $successfulResult
endif; // ($requestMethod === 'post')
$this->viewData['catalogoLibrosEntity'] = $catalogoLibrosEntity;
$this->viewData['formAction'] = route_to('catalogoLibrosEdit', $id);
$this->viewData['boxTitle'] = lang('Basic.global.edit2') . ' ' . lang('Catalogo.moduleTitle') . ' ' . lang('Basic.global.edit3');
return $this->displayForm(__METHOD__, $id);
} // end function edit(...)
public function datatable()
{
$reqData = $this->request->getGet();
$start = $reqData['start'] ?? 0;
$length = $reqData['length'] ?? 5;
$q = $this->model->getDatatableQuery()->limit($length, $start);
$result = DataTable::of($q)
->edit(
"portada",
function ($row, $meta) {
if (is_null($row->cubierta_archivo)) {
return '<img class="img-thumbnail" src="' . $row->portada . '" alt="Portada" style="max-height: 80px;">';
} else {
return '';
}
}
)
->add("actionBtns", callback: function ($q) {
$actions = '';
if (auth()->user()->can('catalogo.edit')) {
$actions .= '
<div class="btn-group btn-group-sm">
<a href="javascript:void(0);"><i class="ti ti-pencil ti-sm btn-edit mx-2" data-id="' . $q->id . '"></i></a>
</div>';
}
if (auth()->user()->can('catalogo.delete')) {
$actions .= '
<div class="btn-group btn-group-sm">
<a href="javascript:void(0);"><i class="ti ti-trash ti-sm btn-delete mx-2" data-id="' . $q->id . '"></i></a>
</div>';
}
return $actions;
});
return $result->toJson(returnAsObject: true);
}
/* IMN */
public function getClientList()
{
$search = $this->request->getGet("q") ?? "";
$data = (new ClienteModel())->getIdName($search);
return $this->response->setJSON($data);
}
}

View File

@ -0,0 +1,2 @@
Portada Id Cliente Título Edición Autor Archivo ISBN EAN Páginas Acciones
Lo que hay que listar

View File

@ -383,4 +383,26 @@ class Proveedores extends \App\Controllers\BaseResourceController {
}
}
public function getForSelect(){
if ($this->request->isAJAX()) {
$tipo_id = $this->request->getGet("tipo_id");
$query = $this->model->builder()->select(
[
"id",
"nombre as name"
]
)->where('tipo_id', $tipo_id)->where('deleted_at', null)->orderBy("nombre", "asc");
if ($this->request->getGet("q")) {
$query->groupStart()
->orLike("lg_proveedores.nombre", $this->request->getGet("q"))
->groupEnd();
}
return $this->response->setJSON($query->get()->getResultObject());
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
}

View File

@ -787,6 +787,32 @@ class Facturas extends \App\Controllers\BaseResourceController
}
}
public function deleteLineaPago()
{
if ($this->request->isAJAX()) {
$postData = $this->request->getPost();
$factura_id = $postData['factura_id'] ?? 0;
$pago_id = $postData['pago_id'] ?? 0;
$model_factura_pago = model('\App\Models\Facturas\FacturaPagoModel');
$model_factura_pago->update($pago_id, [
'deleted_at' => date('Y-m-d H:i:s'),
'user_updated_id' => auth()->user()->id,
]);
$newTokenHash = csrf_hash();
$csrfTokenName = csrf_token();
$data = [
$csrfTokenName => $newTokenHash
];
return $this->respond($data);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function validar($factura_id)
{

View File

@ -16,14 +16,15 @@ class FacturasLineas extends \App\Controllers\BaseResourceController
protected static $controllerSlug = 'factura-lineas';
public function datatable($factura_id = null){
public function datatable($factura_id = null)
{
if ($this->request->isAJAX() && $factura_id != null) {
$reqData = $this->request->getPost();
if (!isset($reqData['draw']) || !isset($reqData['columns']) ) {
if (!isset($reqData['draw']) || !isset($reqData['columns'])) {
$errstr = 'No data available in response to this specific request.';
$response = $this->respond(Collection::datatable( [], 0, 0, $errstr ), 400, $errstr);
$response = $this->respond(Collection::datatable([], 0, 0, $errstr), 400, $errstr);
return $response;
}
$start = $reqData['start'] ?? 0;
@ -46,31 +47,32 @@ class FacturasLineas extends \App\Controllers\BaseResourceController
}
public function deleteLinea($factura_linea_id = 0){
public function deleteLinea($factura_linea_id = 0)
{
if (!empty(static::$pluralObjectNameCc) && !empty(static::$singularObjectNameCc)) {
$objName = mb_strtolower(lang(ucfirst(static::$pluralObjectNameCc).'.'.static::$singularObjectNameCc));
$objName = mb_strtolower(lang(ucfirst(static::$pluralObjectNameCc) . '.' . static::$singularObjectNameCc));
} else {
$objName = lang('Basic.global.record');
}
if($factura_linea_id == 0){
if ($factura_linea_id == 0) {
return $this->failNotFound(lang('Basic.global.deleteError', [$objName]));
}
$facturaLinea = $this->model->find($factura_linea_id);
if($facturaLinea == null){
if ($facturaLinea == null) {
return $this->failNotFound(lang('Basic.global.deleteError', [$objName]));
}
if($facturaLinea->pedido_linea_impresion_id != null){
$this->model->deleteFacturasLineasPedido($facturaLinea->factura_id, $facturaLinea->pedido_linea_impresion_id, $facturaLinea->cantidad);
if ($facturaLinea->pedido_linea_impresion_id != null) {
$this->model->deleteFacturasLineasPedido($facturaLinea->factura_id, $facturaLinea->pedido_linea_impresion_id, $facturaLinea->cantidad);
}
if($facturaLinea->pedido_maquetacion_id != null){
if ($facturaLinea->pedido_maquetacion_id != null) {
//$this->model->deleteFacturasLineasPedido($facturaLinea->factura_id, $facturaLinea->pedido_maquetacion_id, $facturaLinea->cantidad);
}
$facturaLinea = $this->model->delete($factura_linea_id);
$message = lang('Basic.global.deleteSuccess', [lang('Basic.global.record')]);
$response = $this->respondDeleted(['id' => $factura_linea_id, 'msg' => $message]);
@ -78,51 +80,86 @@ class FacturasLineas extends \App\Controllers\BaseResourceController
}
public function datatable_editor() {
public function datatable_editor()
{
if ($this->request->isAJAX()) {
include(APPPATH . "ThirdParty/DatatablesEditor/DataTables.php");
// Build our Editor instance and process the data coming from _POST
$response = Editor::inst( $db, 'facturas_lineas' )
$response = Editor::inst($db, 'facturas_lineas')
->fields(
Field::inst( 'id' ),
Field::inst( 'base' ),
Field::inst( 'total_iva' ),
Field::inst( 'total' ),
Field::inst( 'cantidad' )
->validator('Validate::numeric', array(
'message' => lang('Facturas.validation.numerico'))
Field::inst('id'),
Field::inst('base'),
Field::inst('total_iva'),
Field::inst('total'),
Field::inst('cantidad')
->validator(
'Validate::numeric',
array(
'message' => lang('Facturas.validation.numerico')
)
)
->validator('Validate::notEmpty', array(
'message' => lang('Facturas.validation.requerido'))
->validator(
'Validate::notEmpty',
array(
'message' => lang('Facturas.validation.requerido')
)
),
Field::inst( 'descripcion' )
->validator('Validate::notEmpty', array(
'message' => lang('Facturas.validation.requerido'))
Field::inst('descripcion')
->validator(
'Validate::notEmpty',
array(
'message' => lang('Facturas.validation.requerido')
)
),
Field::inst( 'iva' )
->validator('Validate::numeric', array(
'message' => lang('Facturas.validation.numerico'))
Field::inst('iva')
->validator(
'Validate::numeric',
array(
'message' => lang('Facturas.validation.numerico')
)
)
->validator('Validate::notEmpty', array(
'message' => lang('Facturas.validation.requerido'))
->validator(
'Validate::notEmpty',
array(
'message' => lang('Facturas.validation.requerido')
)
),
Field::inst( 'pedido_linea_impresion_id' )
->setFormatter(function($val, $data, $opts) {
Field::inst('total')
->validator(
'Validate::numeric',
array(
'message' => lang('Facturas.validation.numerico')
)
)
->validator(
'Validate::notEmpty',
array(
'message' => lang('Facturas.validation.requerido')
)
),
Field::inst('pedido_linea_impresion_id')
->setFormatter(function ($val, $data, $opts) {
return $val === '' ? null : $val;
}),
Field::inst( 'factura_id' ),
Field::inst( 'user_updated_id' ),
Field::inst('pedido_maquetacion_id')
->setFormatter(function ($val, $data, $opts) {
return $val === '' ? null : $val;
}),
Field::inst('factura_id'),
Field::inst('user_updated_id'),
)
->on('preCreate', function ($editor, &$values) {
$totales = $this->generate_totales(
$values['factura_id'],
$values['pedido_linea_impresion_id'],
$values['total'],
$values['iva'],
$values['factura_id'],
$values['pedido_linea_impresion_id'],
$values['total'],
$values['iva'],
$values['cantidad'],
$values['old_cantidad']);
$values['old_cantidad'],
$values['base']
);
$editor
->field('user_updated_id')
->setValue(auth()->user()->id);
@ -141,12 +178,14 @@ class FacturasLineas extends \App\Controllers\BaseResourceController
})
->on('preEdit', function ($editor, $id, &$values) {
$totales = $this->generate_totales(
$values['factura_id'],
$values['pedido_linea_impresion_id'],
$values['total'],
$values['iva'],
$values['factura_id'],
$values['pedido_linea_impresion_id'],
$values['total'],
$values['iva'],
$values['cantidad'],
$values['old_cantidad']);
$values['old_cantidad'],
$values['base']
);
$editor
->field('factura_id')
->setValue($values['factura_id']);
@ -185,8 +224,9 @@ class FacturasLineas extends \App\Controllers\BaseResourceController
}
}
public function updateTotalesFactura($factura_id = 0){
if($factura_id == 0){
public function updateTotalesFactura($factura_id = 0)
{
if ($factura_id == 0) {
return;
}
@ -194,26 +234,42 @@ class FacturasLineas extends \App\Controllers\BaseResourceController
$model->updateTotales($factura_id);
}
private function generate_totales($factura_id, $pedido_linea_id, $total, $iva, $cantidad, $old_cantidad)
private function generate_totales($factura_id, $pedido_linea_id, $total, $iva, $cantidad, $old_cantidad, $base_input)
{
// si es una linea que se refiere a pedido
if ($pedido_linea_id != null && $factura_id != null) {
// se actualiza la cantidad de la linea de pedido en la tabla pivote facturas_pedidos_lineas
$this->model->updateFacturaPedidoLinea($factura_id, $pedido_linea_id, $old_cantidad, $cantidad);
}
// se calcula y se actualiza el subtotal, total_iva y total
// redondeando a 4 decimales el precio_unidad y a dos el resto
$precio_unidad = round($total / $old_cantidad, 4);
$base = round($precio_unidad * $cantidad, 2);
$total_iva = round($base * $iva / 100, 2);
$total = round($base + $total_iva, 2);
$values = [];
$values['base'] = $base;
$values['total_iva'] = $total_iva;
$values['total'] = $total;
// Si es una linea añadida a mano, el total y el iva tienen que venir metidos a mano
if ($pedido_linea_id != null) {
// se calcula y se actualiza el subtotal, total_iva y total
// redondeando a 4 decimales el precio_unidad y a dos el resto
$precio_unidad = round($total / $old_cantidad, 4);
$base = round($precio_unidad * $cantidad, 2);
$total_iva = round($base * $iva / 100, 2);
$total = round($base + $total_iva, 2);
$values = [];
$values['base'] = $base;
$values['total_iva'] = $total_iva;
$values['total'] = $total;
}
else{
// se pasa la base y el iva
$total_iva = round($base_input * $iva / 100, 2);
$total = round($base_input + $total_iva, 2);
$values = [];
$values['base'] = $base_input;
$values['total_iva'] = $total_iva;
$values['total'] = $total;
}
return $values;
}
}

View File

@ -29,7 +29,7 @@ class LogisticaController extends BaseController
// Breadcrumbs
$this->viewData['breadcrumb'] = [
['title' => lang("App.menu_logistica"), 'route' => "javascript:void(0);", 'active' => false],
['title' => lang("App.menu_logistica"), 'route' => route_to("LogisticaPanel"), 'active' => false],
];
@ -56,13 +56,12 @@ class LogisticaController extends BaseController
return view(static::$viewPath . 'viewPanelLogistica', $viewData);
}
public function selectorEnvios($tipoEnvio = null)
public function gestionEnvios()
{
$viewData = [
'currentModule' => static::$controllerSlug,
'boxTitle' => lang('Logistica.envioSimpleMultiple'),
'boxTitle' => lang('Logistica.gestionEnvios'),
'usingServerSideDataTable' => true,
'tipoEnvio' => $tipoEnvio,
];
$viewData = array_merge($this->viewData, $viewData); // merge any possible values from the parent controller class
@ -70,6 +69,46 @@ class LogisticaController extends BaseController
return view(static::$viewPath . 'viewLogisticaSelectEnvios', $viewData);
}
public function findPedidosNewEnvio()
{
if ($this->request->isAJAX()) {
$query = LogisticaService::findPedidosNewEnvio();
if ($this->request->getGet("q")) {
$query->groupStart()
->orLike("id", $this->request->getGet("q"))
->orLike("name", $this->request->getGet("q"))
->groupEnd();
}
$result = $query->orderBy("name", "asc")->get()->getResultObject();
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function selectDireccionForEnvio(){
if ($this->request->isAJAX()) {
$pedido_id = $this->request->getGet('pedido_id');
if($pedido_id == null || $pedido_id == 0){
return [];
}
$searchVal = $this->request->getGet("q") ?? "";
$result = LogisticaService::findDireccionesNewEnvio($pedido_id, $searchVal);
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function searchPedidoOrISBN($search = "", $envio_id = null)
{
@ -84,6 +123,20 @@ class LogisticaController extends BaseController
return $this->response->setJSON($result);
}
public function generarEnvio()
{
if ($this->request->isAJAX()) {
$pedido_id = $this->request->getPost('pedido_id');
$direccion = $this->request->getPost('direccion');
$result = LogisticaService::generateEnvio($pedido_id, $direccion);
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function selectAddEnvioLinea()
{
@ -91,8 +144,8 @@ class LogisticaController extends BaseController
$query = LogisticaService::findLineaEnvioPorEnvio($this->request->getGet('envio'));
if ($this->request->getGet("q")) {
$query->groupStart()
->orLike("p.id", $this->request->getGet("q"))
->orLike("pr.titulo", $this->request->getGet("q"))
->orLike("id", $this->request->getGet("q"))
->orLike("name", $this->request->getGet("q"))
->groupEnd();
}
@ -164,6 +217,16 @@ class LogisticaController extends BaseController
return redirect()->to(base_url('logistica/selectEnvios/simple'))->with('error', lang('Logistica.errors.noEnvio'));
}
$modelProveedor = model('App\Models\Compras\ProveedorModel');
$proveedor = $modelProveedor->select('id, nombre')
->where('deleted_at', null)
->where('id', $envioEntity->proveedor_id)
->orderBy('nombre', 'asc')
->first();
if(!empty($proveedor)){
$envioEntity->proveedor_nombre = $proveedor->nombre;
}
$viewData = [
'currentModule' => static::$controllerSlug,
'boxTitle' => '<i class="ti ti-truck ti-xl"></i>' . ' ' . lang('Logistica.envio') . ' [' . $envioEntity->id . ']: ' . $envioEntity->direccion,
@ -193,6 +256,21 @@ class LogisticaController extends BaseController
}
}
public function finalizarEnvio()
{
if ($this->request->isAJAX()) {
$id = $this->request->getPost('id') ?? null;
$finalizar_ots = $this->request->getPost('finalizar_ots') ?? false;
$result = LogisticaService::finalizarEnvio($id, $finalizar_ots);
return $this->response->setJSON($result);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function datatable_enviosEdit($idEnvio)
{
$model = model('App\Models\Logistica\EnvioLineaModel');
@ -217,23 +295,16 @@ class LogisticaController extends BaseController
function ($row, $meta) {
return '<a href="' . base_url('presupuestoadmin/edit/' . $row->presupuesto) . '" target="_blank">' . $row->presupuesto . '</a>';
}
)
->edit(
"cajas",
function ($row, $meta) {
return '<input type="number" class="form-control input-lineas input-cajas text-center"
data-id="'. $row->id.'" data-name="cajas" value="' . $row->cajas . '">';
}
)->edit(
"unidadesEnvio",
function ($row, $meta) {
if($row->finalizado == 1){
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 . '">';
}
)
->edit('cajasRaw', function ($row) {
return is_null($row->cajas) ? '__SIN__ASIGNAR__' : $row->cajas;
});
);
return $result->toJson(returnAsObject: true);
}
@ -296,6 +367,52 @@ class LogisticaController extends BaseController
]);
}
public function updateCodigoSeguimiento()
{
$id = $this->request->getPost('id');
$fieldValue = $this->request->getPost('codigo_seguimiento');
if (!$id) {
return $this->response->setJSON([
'status' => false,
'message' => 'Datos inválidos'
]);
}
$model = model('App\Models\Logistica\EnvioModel');
$updated = $model->update($id, [
"codigo_seguimiento" => $fieldValue==""? null: $fieldValue,
]);
return $this->response->setJSON([
'status' => $updated,
'message' => $updated ? 'Actualizado' : 'Error al actualizar'
]);
}
public function updateProveedorEnvio()
{
$id = $this->request->getPost('id');
$fieldValue = $this->request->getPost('proveedor_id');
if (!$id) {
return $this->response->setJSON([
'status' => false,
'message' => 'Datos inválidos'
]);
}
$model = model('App\Models\Logistica\EnvioModel');
$updated = $model->update($id, [
"proveedor_id" => $fieldValue==""? null: $fieldValue,
]);
return $this->response->setJSON([
'status' => $updated,
'message' => $updated ? 'Actualizado' : 'Error al actualizar'
]);
}
public function saveComments()
{
$id = $this->request->getPost('id');

View File

@ -56,10 +56,33 @@ class Intranet extends Controller
}
}
function orden_trabajo($ot_id,$resource_name)
function orden_trabajo($ot_id, $resource_name)
{
helper('file');
$resource_path = WRITEPATH . 'uploads/orden_trabajo/'.$ot_id. '/' . $resource_name;
$resource_path = WRITEPATH . 'uploads/orden_trabajo/' . $ot_id . '/' . $resource_name;
if (file_exists($resource_path)) {
// Get the mime type of the file
$mime_type = mime_content_type($resource_path);
// Get an instance of the Response class
$response = service('response');
// Set the content type
$response->setContentType($mime_type);
// Set the output
$response->setBody(file_get_contents($resource_path));
// Send the response to the browser
$response->send();
}
}
function catalogo($catalogo_id, $resource_name)
{
helper('file');
$resource_path = WRITEPATH . 'uploads/catalogo/' . $catalogo_id . '/' . $resource_name;
if (file_exists($resource_path)) {
// Get the mime type of the file
$mime_type = mime_content_type($resource_path);

View File

@ -11,7 +11,8 @@ use App\Models\Configuracion\MaquinaModel;
use App\Models\Presupuestos\ImportadorModel;
use App\Models\Presupuestos\PresupuestoModel;
use App\Models\Usuarios\GroupModel;
use App\Models\Usuarios\PermisosModel;
use App\Models\Catalogo\CatalogoLibroModel;
use App\Models\Catalogo\IdentificadorIskModel;
use App\Services\PresupuestoService;
use CodeIgniter\Shield\Entities\User;
@ -22,33 +23,48 @@ class Test extends BaseController
{
}
public function echo(){
public function echo()
{
echo "echo";
}
public function index()
private function index()
{
$emailService = service('emailService');
return $emailService->send("Hola mundo", "Hola mundo", "imnavajas@coit.es");
$modelCL = new CatalogoLibroModel();
$modelISK = new IdentificadorIskModel();
// Obtener todos los registros sin isk
$registros = $modelCL->where('isk', null)->findAll();
$i = 0;
foreach ($registros as $registro) {
$isk = $modelISK->newIsk();
$modelCL->update($registro->id, ['isk' => $isk]);
echo "[" . $i++ . "]Asignado ISK {$isk} a ID {$registro->id}<br>";
}
}
private function clonar_tarifa_encuadernacion($teOrigen, $teDestino){
private function clonar_tarifa_encuadernacion($teOrigen, $teDestino)
{
$tet_model = model('App\Models\Tarifas\TarifaEncuadernacionTiradaModel');
$tel_model = model('App\Models\Tarifas\TarifaEncuadernacionLineaModel');
$tarifasTiradas = $tet_model->asObject()->where('tarifa_encuadernacion_id',$teOrigen)->findAll();
$tarifasTiradas = $tet_model->asObject()->where('tarifa_encuadernacion_id', $teOrigen)->findAll();
foreach ($tarifasTiradas as $tarifasTirada){
foreach ($tarifasTiradas as $tarifasTirada) {
echo "--->" . $tarifasTirada->id . "<br>";
$tarifasLineas = $tel_model->asObject()->where('tirada_encuadernacion_id',$tarifasTirada->id)->findAll();
$tarifasLineas = $tel_model->asObject()->where('tirada_encuadernacion_id', $tarifasTirada->id)->findAll();
// Prepare the data
unset($tarifasTirada->id);
@ -61,7 +77,7 @@ class Test extends BaseController
$tet_model->insert($tarifasTirada);
$inserted_id = $tet_model->insertID();
foreach ($tarifasLineas as $tarifasLinea){
foreach ($tarifasLineas as $tarifasLinea) {
echo "------>" . $tarifasLinea->id . "<br>";
@ -82,10 +98,20 @@ class Test extends BaseController
private function test_get_tirada_alt($tirada, $merma, $tipo_impresion_id,
$json_data, $cliente_id, $ancho, $alto,
$solapas_cubierta, $solapas_ancho_cubierta, $solapas_sobrecubierta, $solapas_ancho_sobrecubierta, $lomo)
{
private function test_get_tirada_alt(
$tirada,
$merma,
$tipo_impresion_id,
$json_data,
$cliente_id,
$ancho,
$alto,
$solapas_cubierta,
$solapas_ancho_cubierta,
$solapas_sobrecubierta,
$solapas_ancho_sobrecubierta,
$lomo
) {
$values = [];
if ($json_data) {
@ -96,7 +122,7 @@ class Test extends BaseController
echo '------------------------------------<br>';
var_dump($linea);
// Se obtienen los valores de cada linea para el calculo del precio
$datosPedido = (object)array(
$datosPedido = (object) array(
'paginas' => intval($linea['paginas']) ?? 0,
'tirada' => intval($tirada) ?? 0,
'merma' => intval($merma) ?? 0,
@ -174,7 +200,7 @@ class Test extends BaseController
$datosTipolog = $linea['gotaNegro'] ?? null;
if (!is_null($datosTipolog)) {
$datosTipolog = [];
$data = (object)array(
$data = (object) array(
'negro' => intval($linea['cobNegro']) ?? 0,
'cyan' => intval($linea['cobCyan']) ?? 0,
'magenta' => intval($linea['cobMagenta']) ?? 0,
@ -193,7 +219,7 @@ class Test extends BaseController
$data['papel'] = $papel;
$data['opciones_papel'] = $opciones_papel;
$data['maquina'] = $maquina;
$data['papel_generico'] = (array)$papel_generico;
$data['papel_generico'] = (array) $papel_generico;
$data['isColor'] = $isColor;
$data['a_favor_fibra'] = $linea['aFavorFibra'] ?? null;
$data['datosTipolog'] = $datosTipolog;
@ -222,7 +248,7 @@ class Test extends BaseController
// Previo a ejecutar, vaciar la tabla clientes_precios (ojo si hay customizaciones)
$db = \Config\Database::connect();
$db = \Config\Database::connect();
$builder = $db->table('cliente_precios');
$plantillaDefectoId = 5;
@ -394,14 +420,14 @@ class Test extends BaseController
{
$paginas = 240;
$papel_impresion = (object)array(
$papel_impresion = (object) array(
'id' => 198,
'gramaje' => 90,
'precio_tonelada' => 1600
);
$maquina = (object)array(
$maquina = (object) array(
//'id' => 48,
'alto' => 800,
'ancho' => 520,
@ -430,7 +456,7 @@ class Test extends BaseController
{
$uso = 'interior';
$tipo = 'negro';
$datosPedido = (object)array(
$datosPedido = (object) array(
'paginas' => 200,
'tirada' => 500,
'merma' => 10,
@ -482,7 +508,7 @@ class Test extends BaseController
echo '<pre>';
$uso = 'cubierta';
$tipo = 'color';
$datosPedido = (object)array(
$datosPedido = (object) array(
'paginas' => 200,
'tirada' => 500,
'merma' => 10,
@ -542,7 +568,7 @@ class Test extends BaseController
$datosPedido = (object)array(
$datosPedido = (object) array(
'paginas' => 240,
'tirada' => 100,
'merma' => 10,
@ -552,10 +578,10 @@ class Test extends BaseController
'isCosido' => true,
);
$parametrosRotativa = (object)array(
$parametrosRotativa = (object) array(
'a_favor_fibra' => 0,
'bnPages' => 240,
'colorPages' => 0,
'bnPages' => 240,
'colorPages' => 0,
'rotativa_gota_negro' => 0,
'rotativa_gota_color' => 0,
);
@ -587,12 +613,12 @@ class Test extends BaseController
var_dump($datosTipologias);
echo '</pre>';
$parametrosRotativa->rotativa_gota_negro = $datosTipologias[0]->gota_negro;
$parametrosRotativa->rotativa_gota_color = $datosTipologias[0]->gota_color;
$parametrosRotativa->rotativa_negro = $datosTipologias[0]->negro;
$parametrosRotativa->rotativa_cyan = $datosTipologias[0]->cyan;
$parametrosRotativa->rotativa_magenta = $datosTipologias[0]->magenta;
$parametrosRotativa->rotativa_amarillo = $datosTipologias[0]->amarillo;
$parametrosRotativa->rotativa_gota_negro = $datosTipologias[0]->gota_negro;
$parametrosRotativa->rotativa_gota_color = $datosTipologias[0]->gota_color;
$parametrosRotativa->rotativa_negro = $datosTipologias[0]->negro;
$parametrosRotativa->rotativa_cyan = $datosTipologias[0]->cyan;
$parametrosRotativa->rotativa_magenta = $datosTipologias[0]->magenta;
$parametrosRotativa->rotativa_amarillo = $datosTipologias[0]->amarillo;
echo '-------------------------------';
$maquinas = $maquina_model->getMaquinaImpresionForPresupuesto(
@ -624,7 +650,7 @@ class Test extends BaseController
$uso = 'cubierta';
$tipo = 'color';
$datosPedido = (object)array(
$datosPedido = (object) array(
'paginas' => 240,
'tirada' => 100,
'merma' => 10,
@ -714,7 +740,7 @@ class Test extends BaseController
$uso = 'sobrecubierta';
$tipo = 'colorhq';
$datosPedido = (object)array(
$datosPedido = (object) array(
'paginas' => 240,
'tirada' => 100,
'merma' => 10,

View File

@ -0,0 +1,44 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateIdentificadoresIskTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
'auto_increment' => true,
],
'isk' => [
'type' => 'VARCHAR',
'constraint' => 64,
'null' => false,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
'default' => 'current_timestamp()',
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true); // primary key
$this->forge->addUniqueKey('isk'); // unique index
$this->forge->createTable('identificadores_isk', true);
}
public function down()
{
$this->forge->dropTable('identificadores_isk', true);
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateCatalogoLibros extends Migration
{
public function up()
{
$this->db->query('SET foreign_key_checks = 0');
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'auto_increment' => true],
'cliente_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'proveedor_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'user_created_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'default' => 1],
'user_update_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'default' => 1],
'cubierta_archivo' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'cubierta_url' => ['type' => 'VARCHAR', 'constraint' => 500, 'null' => true],
'ancho' => ['type' => 'DOUBLE', 'constraint' => '8,2'],
'alto' => ['type' => 'DOUBLE', 'constraint' => '8,2'],
'peso' => ['type' => 'DOUBLE', 'constraint' => '8,2', 'null' => true],
'titulo' => ['type' => 'VARCHAR', 'constraint' => 300],
'autor' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'autor_entidad' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'traductor' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'ilustrador' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'idioma' => ['type' => 'VARCHAR', 'constraint' => 3, 'default' => 'spa'],
'num_edic' => ['type' => 'INT', 'default' => 1, 'null' => true],
'fecha_disponibilidad' => ['type' => 'DATE', 'null' => true],
'fecha_public' => ['type' => 'DATE', 'null' => true],
'num_fotos' => ['type' => 'INT', 'default' => 0],
'num_ilustr' => ['type' => 'INT', 'default' => 0],
'num_ilustr_color' => ['type' => 'INT', 'default' => 0],
'num_ilustr_bn' => ['type' => 'INT', 'default' => 0],
'coleccion' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'isk' => ['type' => 'VARCHAR', 'constraint' => 64, 'null' => true],
'isbn' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'ean' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'editorial' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'resumen' => ['type' => 'TEXT', 'null' => true],
'resumen_breve' => ['type' => 'TEXT', 'null' => true],
'sello' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'paginas' => ['type' => 'INT'],
'tipo_impresion' => ['type' => 'ENUM', 'constraint' => ['negro','negrohq','color','colorhq'], 'null' => true],
'comentarios' => ['type' => 'TEXT', 'null' => true],
'negro_paginas' => ['type' => 'INT', 'null' => true],
'negro_papel_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'negro_gramaje' => ['type' => 'DOUBLE', 'null' => true],
'negro_pod_papel_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'negro_pod_gramaje' => ['type' => 'DOUBLE', 'null' => true],
'color_paginas' => ['type' => 'INT', 'null' => true],
'color_papel_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'color_gramaje' => ['type' => 'DOUBLE', 'null' => true],
'color_pod_papel_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'color_pod_gramaje' => ['type' => 'DOUBLE', 'null' => true],
'cubierta_paginas' => ['type' => 'INT', 'null' => true],
'cubierta_papel_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'cubierta_gramaje' => ['type' => 'DOUBLE', 'null' => true],
'cubierta_acabado_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'cubierta_ancho_solapas' => ['type' => 'DOUBLE', 'constraint' => '8,2', 'default' => 0.00, 'unsigned' => true],
'cubierta_pod_papel_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'cubierta_pod_gramaje' => ['type' => 'DOUBLE', 'null' => true],
'sobrecubierta_paginas' => ['type' => 'INT', 'null' => true],
'sobrecubierta_papel_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'sobrecubierta_gramaje' => ['type' => 'DOUBLE', 'null' => true],
'sobrecubierta_acabado_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'sobrecubierta_ancho_solapas' => ['type' => 'DOUBLE', 'constraint' => '8,2', 'default' => 0.00, 'unsigned' => true],
'sobrecubierta_pod_papel_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'sobrecubierta_pod_gramaje' => ['type' => 'DOUBLE', 'null' => true],
'encuadernacion_id' => ['type' => 'INT', 'constraint' => 10, 'unsigned' => true, 'null' => true],
'ubicacion' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'created_at' => ['type' => 'TIMESTAMP', 'default' => 'CURRENT_TIMESTAMP'],
'updated_at' => ['type' => 'TIMESTAMP', 'null' => true],
'deleted_at' => ['type' => 'TIMESTAMP', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('isk');
$this->forge->addForeignKey('cliente_id', 'clientes', 'id');
$this->forge->createTable('catalogo_libros');
$this->db->query('SET foreign_key_checks = 1');
}
public function down()
{
$this->forge->dropTable('catalogo_libros');
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,265 @@
<?php
namespace App\Entities\Catalogo;
use CodeIgniter\Entity\Entity;
use App\Models\Configuracion\PapelGenericoModel;
use App\Models\Configuracion\TipoPresupuestoModel;
use App\Models\Usuarios\UserModel;
use App\Models\Tarifas\Acabados\ServicioAcabadoModel;
use App\Models\Clientes\ClienteModel;
class CatalogoLibroEntity extends Entity
{
protected $attributes = [
'id' => null,
'cliente_id' => null,
'proveedor_id' => null,
'user_created_id' => 1,
'user_update_id' => 1,
'cubierta_archivo' => null,
'cubierta_url' => null,
'ancho' => 0.00,
'alto' => 0.00,
'peso' => null,
'titulo' => '',
'autor' => '',
'autor_entidad' => null,
'traductor' => null,
'ilustrador' => null,
'idioma' => 'spa',
'num_edic' => 1,
'fecha_disponibilidad' => null,
'fecha_public' => null,
'num_fotos' => 0,
'num_ilustr' => 0,
'num_ilustr_color' => 0,
'num_ilustr_bn' => 0,
'coleccion' => '',
'isk' => null,
'isbn' => null,
'ean' => null,
'editorial' => '',
'resumen' => null,
'resumen_breve' => null,
'sello' => null,
'paginas' => 0,
'tipo_impresion' => null,
'solapas_ancho' => 0.00,
'cubiertas_ancho' => 0.00,
'comentarios' => '',
'negro_paginas' => null,
'negro_papel' => null,
'negro_papel_id' => null,
'negro_gramaje' => null,
'negro_pod_papel_id' => null,
'negro_pod_gramaje' => null,
'color_paginas' => null,
'color_papel' => null,
'color_papel_id' => null,
'color_gramaje' => null,
'color_pod_papel_id' => null,
'color_pod_gramaje' => null,
'cubierta_paginas' => null,
'cubierta_papel' => null,
'cubierta_papel_id' => null,
'cubierta_gramaje' => null,
'cubierta_acabado' => null,
'cubierta_pod_papel_id' => null,
'cubierta_pod_gramaje' => null,
'sobrecubierta_paginas' => null,
'sobrecubierta_papel' => null,
'sobrecubierta_papel_id' => null,
'sobrecubierta_gramaje' => null,
'sobrecubierta_acabado' => null,
'sobrecubierta_pod_papel_id' => null,
'sobrecubierta_pod_gramaje' => null,
'encuardenacion_id' => 'null',
'ubicacion' => null,
'created_at' => null,
'updated_at' => null,
'deleted_at' => null,
];
protected $dates = ['created_at', 'updated_at', 'deleted_at', 'fecha_disponibilidad', 'fecha_public'];
protected $casts = [
'id' => 'int',
'cliente_id' => '?int',
'proveedor_id' => '?int',
'user_created_id' => 'int',
'user_update_id' => 'int',
'ancho' => 'float',
'alto' => 'float',
'peso' => '?float',
'num_edic' => '?int',
'num_fotos' => '?int',
'num_ilustr' => '?int',
'num_ilustr_color' => '?int',
'num_ilustr_bn' => '?int',
'paginas' => 'int',
'solapas_ancho' => 'float',
'cubiertas_ancho' => 'float',
'negro_paginas' => '?int',
'negro_gramaje' => '?float',
'negro_papel_id' => '?int',
'negro_pod_papel_id' => '?int',
'negro_pod_gramaje' => '?float',
'color_paginas' => '?int',
'color_gramaje' => '?float',
'color_papel_id' => '?int',
'color_pod_papel_id' => '?int',
'color_pod_gramaje' => '?float',
'cubierta_paginas' => '?int',
'cubierta_gramaje' => '?float',
'cubierta_papel_id' => '?int',
'cubierta_pod_papel_id' => '?int',
'cubierta_pod_gramaje' => '?float',
'sobrecubierta_paginas' => '?int',
'sobrecubierta_gramaje' => '?float',
'sobrecubierta_papel_id' => '?int',
'sobrecubierta_pod_papel_id' => '?int',
'sobrecubierta_pod_gramaje' => '?float',
'fecha_disponibilidad' => 'datetime',
'fecha_public' => 'datetime',
];
public function getClienteName()
{
if (!$this->cliente_id) {
return null;
}
$cliente = model(ClienteModel::class)->find($this->cliente_id);
return $cliente->nombre ?? null;
}
public function getEncuadernacionName()
{
if (!$this->encuadernacion_id) {
return null;
}
$enc = model(TipoPresupuestoModel::class)->find($this->encuadernacion_id);
return $enc->encuadernacion ?? null;
}
public function getCreatedUser()
{
if (!$this->user_created_id) {
return null;
}
return model(UserModel::class)->getFullName($this->user_created_id);
}
public function getUpdatedUser()
{
if (!$this->user_update_id) {
return null;
}
return model(UserModel::class)->getFullName($this->user_update_id);
}
public function getNegroPapelName()
{
if (!$this->negro_papel_id) {
return null;
}
$papel = model(PapelGenericoModel::class)->asObject()->find($this->negro_papel_id);
return $papel?->nombre ?? null;
}
public function getNegroPodPapelName()
{
if (!$this->negro_pod_papel_id) {
return null;
}
$papel = model(PapelGenericoModel::class)->asObject()->find($this->negro_pod_papel_id);
return $papel?->nombre ?? null;
}
public function getColorPapelName()
{
if (!$this->color_papel_id) {
return null;
}
$papel = model(PapelGenericoModel::class)->asObject()->find($this->color_papel_id);
return $papel?->nombre ?? null;
}
public function getColorPodPapelName()
{
if (!$this->color_pod_papel_id) {
return null;
}
$papel = model(PapelGenericoModel::class)->asObject()->find($this->color_pod_papel_id);
return $papel?->nombre ?? null;
}
public function getCubiertaPapelName()
{
if (!$this->cubierta_papel_id) {
return null;
}
$papel = model(PapelGenericoModel::class)->asObject()->find($this->cubierta_papel_id);
return $papel?->nombre ?? null;
}
public function getCubiertaPodPapelName()
{
if (!$this->cubierta_pod_papel_id) {
return null;
}
$papel = model(PapelGenericoModel::class)->asObject()->find($this->cubierta_pod_papel_id);
return $papel?->nombre ?? null;
}
public function getSobrecubiertaPapelName()
{
if (!$this->sobrecubierta_papel_id) {
return null;
}
$papel = model(PapelGenericoModel::class)->asObject()->find($this->sobrecubierta_papel_id);
return $papel?->nombre ?? null;
}
public function getSobrecubiertaPodPapelName()
{
if (!$this->sobrecubierta_pod_papel_id) {
return null;
}
$papel = model(PapelGenericoModel::class)->asObject()->find($this->sobrecubierta_pod_papel_id);
return $papel?->nombre ?? null;
}
public function getCubiertaAcabadoName()
{
if (!$this->cubierta_acabado_id) {
return null;
}
$servicioAcabado = model(ServicioAcabadoModel::class)->asObject()->find($this->cubierta_acabado_id);
return $servicioAcabado?->nombre ?? null;
}
public function getSobrecubiertaAcabadoName()
{
if (!$this->sobrecubierta_acabado_id) {
return null;
}
$servicioAcabado = model(ServicioAcabadoModel::class)->asObject()->find($this->sobrecubierta_acabado_id);
return $servicioAcabado?->nombre ?? null;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Entities\Catalogo;
use CodeIgniter\Entity\Entity;
class IdentificadorIsk extends Entity
{
protected $dates = ['created_at', 'updated_at'];
}

View File

@ -0,0 +1,75 @@
<?php
return [
'moduleTitle' => 'Catálogo de libros',
'listingPage' => 'Listado de libros',
'catalogo' => 'catálogo',
'libro' => 'libro',
'id' => 'ID',
'clienteId' => 'Cliente',
'cliente' => 'Cliente',
'proveedorId' => 'Proveedor',
'userCreatedId' => 'Usuario Creador',
'userUpdateId' => 'Usuario Actualizador',
'cubiertaArchivo' => 'Archivo de Cubierta',
'cubiertaUrl' => 'URL de Cubierta',
'portada' => 'Portada',
'ancho' => 'Ancho',
'alto' => 'Alto',
'peso' => 'Peso',
'titulo' => 'Título',
'autor' => 'Autor',
'autorEntidad' => 'Entidad del Autor',
'traductor' => 'Traductor',
'ilustrador' => 'Ilustrador',
'idioma' => 'Idioma',
'numEdic' => 'Número de Edición',
'edicion' => 'Edición',
'fechaDisponibilidad' => 'Fecha de Disponibilidad',
'fechaPublic' => 'Fecha de Publicación',
'numFotos' => 'Número de Fotos',
'numIlustr' => 'Número de Ilustraciones',
'numIlustrColor' => 'Ilustraciones a Color',
'numIlustrBn' => 'Ilustraciones en Blanco y Negro',
'coleccion' => 'Colección',
'isbn' => 'ISBN',
'ean' => 'EAN',
'editorial' => 'Editorial',
'resumen' => 'Resumen',
'resumenBreve' => 'Resumen Breve',
'sello' => 'Sello',
'paginas' => 'Páginas',
'tipoImpresion' => 'Tipo de Impresión',
'seleccionarTipoImpresion' => 'Seleccionar tipo de impresión',
'solapasAncho' => 'Ancho de Solapas',
'cubiertasAncho' => 'Ancho de Cubiertas',
'comentarios' => 'Comentarios',
'negroPaginas' => 'Páginas Negras',
'negroPapel' => 'Papel Negro',
'negroGramaje' => 'Gramaje Negro',
'colorPaginas' => 'Páginas a Color',
'colorPapel' => 'Papel a Color',
'colorGramaje' => 'Gramaje Color',
'portadaPaginas' => 'Páginas de Portada',
'portadaPapel' => 'Papel de Portada',
'portadaGramaje' => 'Gramaje Portada',
'portadaAcabado' => 'Acabado Portada',
'cubiertaPaginas' => 'Páginas de Cubierta',
'cubiertaPapel' => 'Papel de Cubierta',
'cubiertaGramaje' => 'Gramaje Cubierta',
'cubiertaAcabado' => 'Acabado Cubierta',
'encuardenacion' => 'Encuadernación',
'ubicacion' => 'Ubicación',
'createdAt' => 'Fecha de Creación',
'updatedAt' => 'Fecha de Actualización',
'deletedAt' => 'Fecha de Eliminación',
'catalogoLibro' => 'Libro',
'catalogoLibroList' => 'Lista de Libros',
'datosGenerales' => 'Datos generales del libro',
'otrosDatosLibro' => 'Otros datos del libro',
'configuracionLibro' => 'Configuración del libro',
'ficherosLibro' => 'Ficheros',
'created_by_at' => 'Creado:',
'updated_by_at' => 'Actualizado:',
];

View File

@ -2,17 +2,16 @@
return [
'logistica' => 'Logística',
'panel' => 'Panel',
'envioSimple' => 'Envío simple',
'envioMultiple' => 'Envío múltiple',
'envioConjunto' => 'Envío conjunto',
'gestionEnvios' => 'Gestión de Envíos de pedidos',
'etiquetasTitulos' => 'Etiquetas de títulos',
'etiquetasEnvio' => 'Etiquetas de envío',
'envioFerros' => 'Envío de ferros',
'cerrarOTauto' => 'Cerrar OT automáticamente',
'envioFerros' => 'Gestión de Envíos de ferros/prototipos',
'albaranes' => 'Albaranes',
'envioSimpleMultiple' => 'Envío simple/múltiple',
'nuevoEnvio' => 'Nuevo envío',
'buscadorPedidosTitle' => 'Código Pedido o ISBN',
'buscadorPedidosTitle2' => 'Código Pedido o título',
'selectDirecciones' => 'Dirección de envio',
'listadoEnvios' => 'Listado de envíos',
'idEnvio' => 'ID Envío',
'numeroPedidos' => 'Nº Pedidos',
@ -58,11 +57,21 @@ return [
'peso' => 'Peso (kg): ',
'unidadesTotalesFooter' => 'Unidades:',
'codigoSeguimiento' => 'Código de seguimiento',
'empresaMensajería' => 'Empresa de mensajería',
'finalizarEnvio' => 'Finalizar envío',
'finalizarEnvioYOTs' => 'Finalizar envío y OTS',
'errors' => [
'noEnvio' => 'No se ha encontrado el envio',
'noEnvioLineas' => 'No se han encontrado líneas de envío',
'noDataToFind' => 'No se ha introducido ningún dato para buscar',
'notFound' => 'No se encuentra el pedido o ISBN, el pedido aún no se ha finalizado o no tiene envíos pendientes',
'notFound' => 'No se encuentra el pedido o ISBN, el pedido no está en producción o finalizado o no tiene envíos pendientes',
'noAddresses' => 'El pedido no tiene direcciones de envío',
],
'success' => [
'finalizado' => 'El envío se ha finalizado correctamente',
'finalizadoOTs' => 'El envío se ha finalizado correctamente.\nSe han creado las OTs siguientes OTs: {ots}',
],
];

View File

@ -23,9 +23,13 @@ return [
'margen' => 'Margen',
'interior' => 'Interior',
'cubierta' => 'Cubierta',
'cubierta_pod' => 'Cubierta (POD)',
'sobrecubierta' => 'Sobrecubierta',
'sobrecubierta_pod' => 'Sobrecubierta (POD)',
'negro' => 'Negro',
'negro_pod' => 'Negro (POD)',
'color' => 'Color',
'color_pod' => 'Color (POD)',
'negrohq' => 'Negro HQ',
'colorhq' => 'Color HQ',
'bicolor' => 'Bicolor',

View File

@ -10,8 +10,10 @@ return [
'color' => 'Color',
'createdAt' => 'Creado en',
'cubierta' => 'Cubierta',
'cubierta_pod' => 'Cubierta (POD)',
'use_for_tapa_dura' => 'Papel tapa dura',
'sobrecubierta' => 'Sobrecubierta',
'sobrecubierta_pod' => 'Sobrecubierta (POD)',
'guardas' => 'Guardas',
'defecto' => 'Por defecto',
'deletedAt' => 'Borrado en',

View File

@ -89,7 +89,11 @@ return [
'colorPageInstructions' => 'Introduzca la posición de las páginas a color dentro del libro. Ej: 3,5,7 ó 4-10,20,155',
'numeroPaginas' => 'Nº Páginas',
'papel' => 'Papel',
'papelPod' => 'Papel (POD)',
'gramaje' => 'Gramaje',
'gramajePod' => 'Gramaje (POD)',
'solapas' => 'Solapas',
'acabados' => 'Acabados',
'opcionesPresupuesto' => 'Opciones presupuesto',
'retractilado' => 'Retractilado individual',
'retractilado5' => 'Retractilado de 5',

View File

@ -0,0 +1,214 @@
<?php
namespace App\Models\Catalogo;
use CodeIgniter\Model;
use App\Entities\Catalogo\CatalogoLibroEntity;
use App\Models\Clientes\ClienteModel;
class CatalogoLibroModel extends Model
{
protected $table = 'catalogo_libros';
protected $primaryKey = 'id';
protected $returnType = CatalogoLibroEntity::class;
protected $useSoftDeletes = true;
protected $useTimestamps = true;
protected $allowedFields = [
'cliente_id',
'proveedor_id',
'cubierta_archivo',
'ancho',
'alto',
'peso',
'titulo',
'autor',
'autor_entidad',
'traductor',
'ilustrador',
'idioma',
'num_edic',
'fecha_disponibilidad',
'fecha_public',
'num_fotos',
'num_ilustr',
'num_ilustr_color',
'num_ilustr_bn',
'coleccion',
'isbn',
'editorial',
'resumen',
'resumen_breve',
'sello',
'paginas',
'tipo_impresion',
'comentarios',
'negro_paginas',
'negro_papel_id',
'negro_gramaje',
'negro_pod_papel_id',
'negro_pod_gramaje',
'color_paginas',
'color_papel_id',
'color_gramaje',
'color_pod_papel_id',
'color_pod_gramaje',
'cubierta_paginas',
'cubierta_papel_id',
'cubierta_gramaje',
'cubierta_acabado_id',
'cubierta_ancho_solapas',
'cubierta_pod_papel_id',
'cubierta_pod_gramaje',
'sobrecubierta_paginas',
'sobrecubierta_papel_id',
'sobrecubierta_gramaje',
'sobrecubierta_acabado_id',
'sobrecubierta_ancho_solapas',
'sobrecubierta_pod_papel_id',
'sobrecubierta_pod_gramaje',
'encuadernacion_id',
'ubicacion',
];
protected $useAutoIncrement = true;
protected $protectFields = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Opcional: reglas de validación
protected $validationRules = [
'cliente_id' => 'required|is_natural_no_zero',
'titulo' => 'required|string|min_length[2]|max_length[300]',
'paginas' => 'required|integer|greater_than[0]',
'ancho' => 'required|decimal|greater_than[0]',
'alto' => 'required|decimal|greater_than[0]',
'tipo_impresion' => 'required|in_list[negro,negrohq,color,colorhq]',
'isbn' => 'required|regex_match[/^[\d-]+$/]',
'encuadernacion_id' => 'required|is_natural_no_zero',
];
protected $validationMessages = [];
protected $skipValidation = false;
protected $beforeInsert = ['asignarIsk', 'asignarEan', 'asignarCubiertaUrl'];
protected $beforeUpdate = ['asignarEan', 'asignarCubiertaUrl'];
protected function asignarIsk(array $data): array
{
$data['data']['isk'] = model('App\Models\Catalogo\IdentificadorIskModel')->newIsk();
return $data;
}
protected function asignarEan(array $data): array
{
if (!empty($data['data']['isbn'])) {
$ean = $this->generarEanDesdeIsbn($data['data']['isbn']);
if ($ean !== null) {
$data['data']['ean'] = $ean;
}
}
return $data;
}
private function generarEanDesdeIsbn(string $isbn): ?string
{
// Elimina guiones o espacios y convierte a mayúsculas
$isbn = preg_replace('/[^0-9X]/', '', strtoupper($isbn));
// Si ya es un ISBN-13 válido
if (strlen($isbn) === 13 && preg_match('/^97[89][0-9]{10}$/', $isbn)) {
return $isbn;
}
// Si es un ISBN-10, lo convertimos a EAN-13
if (strlen($isbn) === 10) {
$isbnSinDigito = substr($isbn, 0, 9);
$eanBase = '978' . $isbnSinDigito;
$digitoControl = $this->calcularDigitoControlEan($eanBase);
return $eanBase . $digitoControl;
}
return null; // Formato inválido
}
protected function asignarCubiertaUrl(array $data): array
{
// No sobreescribir si ya hay un archivo manual
if (!empty($data['data']['cubierta_archivo'] ?? null)) {
return $data;
}
// Usamos el EAN generado
$ean = $data['data']['ean'] ?? null;
if ($ean && preg_match('/^\d{13}$/', $ean)) {
$ean0 = substr($ean, 0, 7);
$ean12 = substr($ean, 0, 12);
$data['data']['cubierta_url'] = "https://static.cegal.es/imagenes/marcadas/$ean0/$ean12.gif";
}
return $data;
}
private function calcularDigitoControlEan(string $eanBase): int
{
$suma = 0;
for ($i = 0; $i < 12; $i++) {
$digito = (int) $eanBase[$i];
$suma += ($i % 2 === 0) ? $digito : $digito * 3;
}
$modulo = $suma % 10;
return ($modulo === 0) ? 0 : 10 - $modulo;
}
/**
* Get resource data.
*
* @return \CodeIgniter\Database\BaseBuilder
*/
public function getDatatableQuery()
{
$builder = $this->db
->table($this->table . " t1")
->select("
t1.id AS id,
t1.titulo AS titulo,
t2.nombre AS cliente,
t1.num_edic AS edicion,
t1.autor AS autor,
t1.isbn AS isbn,
t1.ean AS ean,
t1.paginas AS paginas,
t1.cubierta_archivo AS cubierta_archivo,
t1.cubierta_url AS portada
")
->join('clientes t2', 't1.cliente_id = t2.id')
->where('t1.deleted_at', null);
return $builder;
}
public function getClientList($search = "")
{
$clienteModel = new ClienteModel();
$query = $clienteModel->builder()
->select('id, nombre as name') // O el campo que quieras usar como "name"
->where('deleted_at', null);
if ($search != "") {
$query->groupStart()
->orLike("nombre", $search)
->groupEnd();
}
return $query->get()->getResultObject();
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace App\Models\Catalogo;
use CodeIgniter\Model;
use RuntimeException;
class IdentificadorIskModel extends Model
{
protected $table = 'identificadores_isk';
protected $primaryKey = 'id';
protected $returnType = \App\Entities\Catalogo\IdentificadorIsk::class;
protected $useSoftDeletes = false; // No soft delete
protected $useTimestamps = true;
protected $allowedFields = ['isk'];
protected $beforeInsert = ['agregarIsk'];
/**
* Crea un nuevo registro con un ISK único y lo devuelve.
*/
public function newIsk(string $contexto = 'libro'): string
{
$isk = $this->generarIskUnico($contexto);
$this->insert(['isk' => $isk]);
return $isk;
}
/**
* Genera un ISK único validado contra la base de datos.
*/
private function generarIskUnico(string $contexto): string
{
do {
$isk = $this->generarIsk($contexto);
} while ($this->where('isk', $isk)->countAllResults() > 0);
return $isk;
}
/**
* Formato legible de ISK, ejemplo: isk_libro_20250419_ab12c
*/
private function generarIsk(string $contexto): string
{
$fecha = date('Ymd');
$random = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 5);
return "isk_{$contexto}_{$fecha}_{$random}";
}
/**
* Hook para generar el ISK automáticamente al insertar.
*/
protected function agregarIsk(array $data): array
{
if (!isset($data['data']['isk']) || empty($data['data']['isk'])) {
$data['data']['isk'] = $this->generarIskUnico('registro');
}
return $data;
}
// Bloqueo total de eliminaciones
public function delete($id = null, bool $purge = false)
{
throw new RuntimeException('La eliminación de registros está deshabilitada.');
}
public function deleteWhere($where)
{
throw new RuntimeException('La eliminación de registros está deshabilitada.');
}
public function deleteBatch($where)
{
throw new RuntimeException('La eliminación de registros está deshabilitada.');
}
}

View File

@ -39,14 +39,14 @@ class FacturaLineaModel extends \App\Models\BaseModel {
t1.pedido_linea_impresion_id AS pedido_linea_impresion_id, t1.pedido_maquetacion_id AS pedido_maquetacion_id,
t1.descripcion AS descripcion, t1.cantidad as cantidad, t1.iva AS iva,
t1.base AS base, t1.total_iva AS total_iva, t1.total AS total, t1.data AS data, t2.pedido_id AS pedido_id,
t3.total_aceptado_revisado AS total_aceptado, t4.tirada_flexible AS tirada_flexible, t4.descuento_tirada_flexible AS descuento_tirada_flexible,
t3.total_aceptado AS total_aceptado, t3.total_aceptado_revisado AS total_aceptado_revisado, t4.tirada_flexible AS tirada_flexible, t4.descuento_tirada_flexible AS descuento_tirada_flexible,
t6.cantidad AS cantidad_albaran"
)
->join("pedidos_linea t2", "t2.id = t1.pedido_linea_impresion_id", "left")
->join("presupuestos t3", "t3.id = t2.presupuesto_id", "left")
->join("clientes t4", "t4.id = t3.cliente_id", "left")
->join("albaranes t5", "t5.pedido_id = t2.pedido_id", "left")
->join("albaranes_lineas t6", "t6.albaran_id = t5.id", "left")
->join("pedidos t5", "t5.id = t2.pedido_id", "left")
->join("albaranes_lineas t6", "t6.pedido_linea_id = t2.id", "left")
->where("t1.factura_id", $factura_id)
->where("t1.deleted_at", null)
->groupBy('t1.id');

View File

@ -40,19 +40,24 @@ class EnvioLineaModel extends Model
t1.unidades_total as unidadesTotal,
IFNULL((
SELECT SUM(t_sub.unidades_envio)
FROM " . $this->table . " t_sub
FROM envios_lineas t_sub
JOIN envios e ON e.id = t_sub.envio_id
JOIN presupuesto_direcciones d ON d.presupuesto_id = t_sub.presupuesto_id
WHERE e.finalizado = 1
AND t_sub.pedido_id = t1.pedido_id
AND e.direccion = d.direccion COLLATE utf8mb3_general_ci
), 0) as unidadesEnviadas,
AND e.direccion = t2.direccion COLLATE utf8mb3_general_ci
AND (
t_sub.envio_id <> t1.envio_id
OR (t_sub.envio_id = t1.envio_id AND e.finalizado = 1)
)
), 0) AS unidadesEnviadas,
IFNULL((
SELECT ROUND(SUM(peso) / 1000, 1)
FROM presupuesto_linea
WHERE presupuesto_id = t3.id
), 0) AS pesoUnidad"
), 0) AS pesoUnidad,
t2.finalizado as finalizado,"
);
$builder->join("envios t2", "t1.envio_id = t2.id", "left");
$builder->join("presupuestos t3", "t1.presupuesto_id = t3.id", "left");
$builder->where("t1.envio_id", $envio_id);
@ -60,5 +65,5 @@ class EnvioLineaModel extends Model
return $builder;
}
}

View File

@ -1,6 +1,7 @@
<?php
namespace App\Services;
use App\Services\ProductionService;
use Config\Services;
@ -9,6 +10,7 @@ class LogisticaService
public static function findPedidoOrISBN($search)
{
$multienvio = false;
$direcciones = [];
$modelPedido = model('App\Models\Pedidos\PedidoModel');
@ -31,13 +33,14 @@ class LogisticaService
$builder->groupStart()
->where('pedidos.id', $search)
->whereIn('pedidos.estado', ['finalizado'])
->whereIn('pedidos.estado', ['finalizado', 'produccion'])
->orWhere("REPLACE(presupuestos.isbn, '-', '')", $searchClean)
->groupEnd();
$builder->groupBy('pedidos_linea.id');
$builder->having('IFNULL(SUM(envios_lineas.unidades_envio), 0) < cantidad_linea', null, false);
$result = $builder->get()->getResult();
if (empty($result)) {
@ -59,6 +62,21 @@ class LogisticaService
return $response;
} else if ($numDirecciones > 1) {
$multienvio = true;
$dirs = $PresupuestoDireccionesModel->select('direccion')->where('presupuesto_id', $result[0]->presupuesto_id)
->findAll();
foreach ($dirs as $key => $direccion) {
$modelEnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
$unidades_en_direccion = $modelEnvioLineasModel->select('SUM(envios_lineas.unidades_envio) as unidades_enviadas,
envios_lineas.unidades_total')
->join('envios', 'envios.id = envios_lineas.envio_id')
->where('pedido_id', $result[0]->pedido_id)
->where('envios.direccion', $direccion->direccion)
->where('envios.finalizado', 1)
->groupBy('pedido_id')->get()->getResult();
if (count($unidades_en_direccion) == 0 || $unidades_en_direccion[0]->unidades_enviadas < $unidades_en_direccion[0]->unidades_total) {
array_push($direcciones, $direccion->direccion);
}
}
}
$response = [
@ -66,8 +84,20 @@ class LogisticaService
'data' => $result[0],
];
if ($multienvio) {
$response_envio = [
'status' => true,
'multienvio' => true,
'direcciones' => $direcciones,
'pedido_id' => $result[0]->pedido_id,
];
return $response_envio;
}
$response_envio = LogisticaService::generateEnvio($result[0]->pedido_id, $multienvio);
$direccion = $PresupuestoDireccionesModel->select('direccion')->where('presupuesto_id', $result[0]->presupuesto_id)
->first()->direccion;
$response_envio = LogisticaService::generateEnvio($result[0]->pedido_id, $direccion);
if ($response_envio['status'] == false) {
$response = [
'status' => false,
@ -76,91 +106,205 @@ class LogisticaService
return $response;
} else {
$response['data']->id_envio = $response_envio['data']['id_envio'];
$response['data']->multienvio = $response_envio['data']['multienvio'];
$response['data']->multienvio = false;
}
return $response;
}
public static function findLineaEnvioPorEnvio(int $envio_id)
{
$db = \Config\Database::connect();
$subCliente = $db->table('envios')
->select('cliente_id')
->where('id', $envio_id)
->getCompiledSelect();
$builder = $db->table('envios e_main');
// 1. Dirección del envío actual
$envio = $db->table('envios')->select('direccion')->where('id', $envio_id)->get()->getRow();
if (!$envio) {
return $db->table('(SELECT NULL AS id, NULL AS name) AS empty')->where('1 = 0');
}
$builder->select("
CONCAT('[', p.id, '] - ', pr.titulo) AS name,
pl.id AS id,
pl.cantidad,
(
SELECT IFNULL(SUM(el.unidades_envio), 0)
$direccionNormalizada = str_replace(' ', '', strtolower(trim($envio->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) AS empty')->where('1 = 0');
}
// 3. Subconsulta principal
$subBuilder = $db->table('pedidos_linea pl')
->select("
pl.id AS id,
CONCAT('[', p.id, '] - ', pr.titulo) AS name,
(
SELECT IFNULL(SUM(el.unidades_envio), 0)
FROM envios_lineas el
JOIN envios e ON e.id = el.envio_id
WHERE el.pedido_id = p.id
AND el.presupuesto_id = pr.id
AND REPLACE(LOWER(TRIM(e.direccion)), ' ', '') = $direccionSQL
AND (e.finalizado = 1 OR e.id = $envio_id)
) AS unidades_enviadas,
pd.cantidad AS cantidad
")
->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'])
->where('ot_dates.embalaje_at IS NOT NULL')
->where("NOT EXISTS (
SELECT 1
FROM envios_lineas el
JOIN envios e ON e.id = el.envio_id
WHERE el.pedido_id = p.id
AND e.direccion = e_main.direccion
AND pr.cliente_id = ($subCliente)
) AS unidades_enviadas,
(
pl.cantidad - (
SELECT IFNULL(SUM(el2.unidades_envio), 0)
FROM envios_lineas el2
JOIN envios e2 ON e2.id = el2.envio_id
WHERE el2.pedido_id = p.id
AND e2.direccion = e_main.direccion
AND pr.cliente_id = ($subCliente)
)
) AS unidades_pendientes
");
WHERE el.envio_id = $envio_id
AND el.pedido_id = p.id
AND el.presupuesto_id = pr.id
GROUP BY el.pedido_id, el.presupuesto_id
HAVING SUM(el.unidades_envio) >= pd.cantidad
)", null, false)
->groupBy('pl.id');
$builder->join('pedidos_linea pl', '1=1'); // para incluir líneas sin envío aún
$builder->join('pedidos p', 'p.id = pl.pedido_id');
$builder->join('presupuestos pr', 'pr.id = pl.presupuesto_id');
$builder->where('e_main.id', $envio_id);
$builder->where('p.estado', 'finalizado');
$builder->where("pr.cliente_id = ($subCliente)", null, false);
$builder->having('unidades_pendientes >', 0);
// 4. Envolver y filtrar por unidades pendientes
$builder = $db->table("({$subBuilder->getCompiledSelect(false)}) AS sub");
$builder->select('id, name');
$builder->where('cantidad > unidades_enviadas');
$builder->orderBy('name', 'ASC');
return $builder;
}
public static function addLineaEnvio($envio_id = null, $pedido_id = null, $direccion = null)
public static function findPedidosNewEnvio()
{
$modelPedido = model('App\Models\Pedidos\PedidoModel');
$db = \Config\Database::connect();
$builder = $modelPedido->builder();
$builder->select("
pedidos.id as pedido_id,
pedidos_linea.id as linea_id,
pedidos_linea.cantidad as total_unidades,
// 3. Subconsulta principal
$subBuilder = $db->table('pedidos_linea pl')
->select("
pl.id AS id,
CONCAT('[', p.id, '] - ', pr.titulo) AS name,
(
SELECT IFNULL(SUM(el.unidades_envio), 0)
FROM envios_lineas el
JOIN envios e ON e.id = el.envio_id
WHERE el.pedido_id = pedidos.id
AND e.finalizado = 1
AND TRIM(e.direccion) = '" . addslashes(trim($direccion)) . "'
) as unidades_enviadas,
pedidos_linea.cantidad - IFNULL(SUM(envios_lineas.unidades_envio), 0) as unidades_envio,
presupuestos.id as presupuesto_id
");
WHERE el.pedido_id = p.id
AND el.presupuesto_id = pr.id
AND e.finalizado = 1
) AS unidades_enviadas,
pd.cantidad AS cantidad
")
->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('p.estado', ['finalizado', 'produccion'])
->where('ot_dates.embalaje_at IS NOT NULL')
->groupBy('pl.id');
$builder->join('pedidos_linea', 'pedidos_linea.pedido_id = pedidos.id', 'left');
$builder->join('presupuestos', 'presupuestos.id = pedidos_linea.presupuesto_id', 'left');
$builder->join('envios_lineas', 'envios_lineas.pedido_id = pedidos_linea.pedido_id', 'left');
// 4. Envolver y filtrar por unidades pendientes
$builder = $db->table("({$subBuilder->getCompiledSelect(false)}) AS sub");
$builder->select('id, name');
$builder->where('cantidad > unidades_enviadas');
$builder->orderBy('name', 'ASC');
$builder->groupBy('pedidos_linea.id');
$builder->having('IFNULL(SUM(envios_lineas.unidades_envio), 0) < pedidos_linea.cantidad', null, false);
$builder->where('pedidos.estado', 'finalizado');
$builder->where('pedidos.id', $pedido_id);
return $builder;
}
public static function findDireccionesNewEnvio($pedido_id, $searchVal = "")
{
$direcciones = [];
$counter = 1;
$PresupuestoDireccionesModel = model('App\Models\Presupuestos\PresupuestoDireccionesModel');
$dirs = $PresupuestoDireccionesModel->select('direccion')
->join('presupuestos pr', 'pr.id=presupuesto_direcciones.presupuesto_id')
->join('pedidos_linea pl', 'pl.presupuesto_id = pr.id')
->join('pedidos p', 'pl.pedido_id=p.id')
->where('p.id', $pedido_id);
if ($searchVal != "") {
$dirs = $dirs->groupStart()
->Like("id", $searchVal)
->groupEnd();
}
$dirs = $dirs->orderBy('direccion', 'asc')->findAll();
foreach ($dirs as $key => $direccion) {
$modelEnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
$unidades_en_direccion = $modelEnvioLineasModel->select('SUM(envios_lineas.unidades_envio) as unidades_enviadas,
envios_lineas.unidades_total')
->join('envios', 'envios.id = envios_lineas.envio_id')
->where('pedido_id', $pedido_id)
->where('envios.direccion', $direccion->direccion)
->where('envios.finalizado', 1)
->groupBy('pedido_id')->get()->getResult();
if (count($unidades_en_direccion) == 0 || $unidades_en_direccion[0]->unidades_enviadas < $unidades_en_direccion[0]->unidades_total) {
array_push(
$direcciones,
(object) [
"id" => $counter,
'name' => $direccion->direccion
]
);
$counter++;
}
}
return $direcciones;
}
public static function addLineaEnvio($envio_id = null, $pedido_id = null, $direccion = null)
{
$db = \Config\Database::connect();
$direccionNormalizada = strtolower(trim($direccion));
// 1. Obtener presupuesto_id y cantidad desde presupuesto_direcciones
$builder = $db->table('pedidos_linea pl')
->select("
p.id AS pedido_id,
pl.id AS linea_id,
pr.id AS presupuesto_id,
pd.cantidad AS total_unidades,
(
SELECT IFNULL(SUM(el.unidades_envio), 0)
FROM envios_lineas el
JOIN envios e ON e.id = el.envio_id
WHERE el.pedido_id = p.id
AND el.presupuesto_id = pr.id
AND TRIM(LOWER(e.direccion)) = '$direccionNormalizada'
AND (e.finalizado = 1 OR e.id = $envio_id)
) AS unidades_enviadas,
(
pd.cantidad - (
SELECT IFNULL(SUM(el2.unidades_envio), 0)
FROM envios_lineas el2
JOIN envios e2 ON e2.id = el2.envio_id
WHERE el2.pedido_id = p.id
AND el2.presupuesto_id = pr.id
AND TRIM(LOWER(e2.direccion)) = '$direccionNormalizada'
AND (e2.finalizado = 1 OR e2.id = $envio_id)
)
) AS unidades_envio
")
->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')
->where('p.id', $pedido_id)
->whereIn('p.estado', ['finalizado', 'produccion'])
->where("TRIM(LOWER(pd.direccion)) = '$direccionNormalizada'", null, false)
->groupBy('pl.id');
$result = $builder->get()->getResultObject();
@ -169,119 +313,215 @@ class LogisticaService
'status' => false,
'message' => lang('Logistica.errors.notFound'),
];
} else {
}
$r = $result[0];
// 2. Insertar solo si hay unidades a enviar
if ($r->unidades_envio > 0) {
$EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
$EnvioLineasModel->save([
'envio_id' => $envio_id,
'pedido_id' => $result[0]->pedido_id,
'unidades_envio' => $result[0]->unidades_envio,
'unidades_total' => $result[0]->total_unidades,
'pedido_id' => $r->pedido_id,
'presupuesto_id' => $r->presupuesto_id,
'unidades_envio' => $r->unidades_envio,
'unidades_total' => $r->total_unidades,
'cajas' => null,
'unidades_cajas' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'created_by' => auth()->user()->id,
'updated_by' => auth()->user()->id,
'presupuesto_id' => $result[0]->presupuesto_id,
]);
}
return [
'status' => true,
'data' => [
'unidades_envio' => $result[0]->unidades_envio,
'unidades_enviadas' => $result[0]->unidades_enviadas,
'total_unidades' => $result[0]->total_unidades,
'unidades_envio' => $r->unidades_envio,
'unidades_enviadas' => $r->unidades_enviadas,
'total_unidades' => $r->total_unidades,
]
];
}
private static function generateEnvio($pedido_id, $multienvio = false)
public static function generateEnvio($pedido_id, $direccion = null)
{
$presupuestoDireccionesModel = model('App\Models\Presupuestos\PresupuestoDireccionesModel');
$direccionNormalizada = strtolower(trim($direccion));
if (!$multienvio) {
// solo hay una dirección, se obtiene de los albaranes
$datosEnvio = $presupuestoDireccionesModel
->select("
presupuestos.id as presupuesto_id,
presupuesto_direcciones.att,
presupuesto_direcciones.direccion,
presupuesto_direcciones.provincia as ciudad,
presupuesto_direcciones.cp,
presupuesto_direcciones.telefono,
presupuesto_direcciones.email,
presupuesto_direcciones.pais_id,
presupuesto_direcciones.cantidad as cantidad_total,
presupuestos.cliente_id as cliente_id,
$datosEnvio = $presupuestoDireccionesModel
->select('
presupuestos.id as presupuesto_id,
presupuesto_direcciones.att,
presupuesto_direcciones.direccion,
presupuesto_direcciones.provincia as ciudad,
presupuesto_direcciones.cp,
presupuesto_direcciones.telefono,
presupuesto_direcciones.email,
presupuesto_direcciones.pais_id,
presupuesto_direcciones.cantidad - IFNULL(SUM(envios_lineas.unidades_envio), 0) as cantidad,
presupuesto_direcciones.cantidad as cantidad_total,
presupuestos.cliente_id as cliente_id
')
->join('pedidos_linea', 'pedidos_linea.presupuesto_id = presupuesto_direcciones.presupuesto_id')
->join('pedidos', 'pedidos.id = pedidos_linea.pedido_id')
->join('presupuestos', 'pedidos_linea.presupuesto_id = presupuestos.id')
->join('envios_lineas', 'envios_lineas.pedido_id = pedidos.id', 'left')
->join('envios', 'envios.id = envios_lineas.envio_id', 'left')
->where('pedidos.id', $pedido_id)
->groupBy('presupuesto_direcciones.id') // Necesario por el uso de SUM
->first();
(
presupuesto_direcciones.cantidad - IFNULL((
SELECT SUM(el.unidades_envio)
FROM envios_lineas el
JOIN envios e ON e.id = el.envio_id
WHERE el.pedido_id = pedidos.id
AND el.presupuesto_id = presupuestos.id
AND e.direccion LIKE " . $presupuestoDireccionesModel->db->escape("%$direccion%") . "
AND e.finalizado = 1
), 0)
) as cantidad
")
->join('pedidos_linea', 'pedidos_linea.presupuesto_id = presupuesto_direcciones.presupuesto_id')
->join('pedidos', 'pedidos.id = pedidos_linea.pedido_id')
->join('presupuestos', 'pedidos_linea.presupuesto_id = presupuestos.id')
->where('pedidos.id', $pedido_id)
->like('presupuesto_direcciones.direccion', $direccion)
->groupBy('presupuesto_direcciones.id')
->first();
// se genera un nuevo envio con estos datos
$EnvioModel = model('App\Models\Logistica\EnvioModel');
$EnvioModel->set('cliente_id', $datosEnvio->cliente_id);
$EnvioModel->set('att', $datosEnvio->att);
$EnvioModel->set('direccion', $datosEnvio->direccion);
$EnvioModel->set('ciudad', $datosEnvio->ciudad);
$EnvioModel->set('cp', $datosEnvio->cp);
$EnvioModel->set('telefono', $datosEnvio->telefono);
$EnvioModel->set('email', $datosEnvio->email);
$EnvioModel->set('pais_id', $datosEnvio->pais_id);
$EnvioModel->set('cantidad', $datosEnvio->cantidad);
$EnvioModel->set('cajas', 1);
$EnvioModel->set('multienvio', $multienvio ? 1 : 0);
$EnvioModel->set('created_at', date('Y-m-d H:i:s'));
$EnvioModel->set('updated_at', date('Y-m-d H:i:s'));
$EnvioModel->insert();
$idEnvio = $EnvioModel->insertID();
// se genera la linea de envio
$EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
$EnvioLineasModel->save([
'envio_id' => $idEnvio,
'pedido_id' => $pedido_id,
'unidades_envio' => $datosEnvio->cantidad,
'unidades_total' => $datosEnvio->cantidad_total,
'cajas' => 1,
'unidades_cajas' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'created_by' => auth()->user()->id,
'updated_by' => auth()->user()->id,
'presupuesto_id' => (int) $datosEnvio->presupuesto_id
]);
return [
'status' => true,
'data' => [
'id_envio' => $idEnvio,
'multienvio' => false,
],
];
}
if (empty($datosEnvio)) {
// Validación si no hay datos o no quedan unidades
if (empty($datosEnvio) || $datosEnvio->cantidad <= 0) {
return [
'status' => false,
'message' => lang('Logistica.errors.noAddresses'),
'message' => lang('Logistica.errors.noAddresses') . ' o no hay unidades pendientes.',
];
}
// Crear envío
$EnvioModel = model('App\Models\Logistica\EnvioModel');
$EnvioModel->set([
'cliente_id' => $datosEnvio->cliente_id,
'att' => $datosEnvio->att,
'direccion' => $datosEnvio->direccion,
'ciudad' => $datosEnvio->ciudad,
'cp' => $datosEnvio->cp,
'telefono' => $datosEnvio->telefono,
'email' => $datosEnvio->email,
'pais_id' => $datosEnvio->pais_id,
'cantidad' => $datosEnvio->cantidad,
'cajas' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
$EnvioModel->insert();
$idEnvio = $EnvioModel->insertID();
// Crear línea de envío
$EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
$EnvioLineasModel->save([
'envio_id' => $idEnvio,
'pedido_id' => $pedido_id,
'unidades_envio' => $datosEnvio->cantidad,
'unidades_total' => $datosEnvio->cantidad_total,
'cajas' => 1,
'unidades_cajas' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'created_by' => auth()->user()->id,
'updated_by' => auth()->user()->id,
'presupuesto_id' => (int) $datosEnvio->presupuesto_id
]);
return [
'status' => true,
'data' => [
'id_envio' => $idEnvio,
],
];
}
public static function finalizarEnvio($envio_id, $finalizar_ot = false)
{
// hay que comprobar que para todas las lineas de envio de este envio
// se ha enviado toda la cantidad teniendo en cuenta otros envios
$EnvioModel = model('App\Models\Logistica\EnvioModel');
$EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
$ots = [];
$envio = $EnvioModel->find($envio_id);
if (empty($envio)) {
return [
'status' => false,
'message' => lang('Logistica.errors.noEnvio'),
];
}
$lineasEnvio = $EnvioLineasModel->where('envio_id', $envio_id)
->findAll();
if (empty($lineasEnvio)) {
return [
'status' => false,
'message' => lang('Logistica.errors.noEnvioLineas'),
];
}
foreach ($lineasEnvio as $linea) {
$pedido = model('App\Models\Pedidos\PedidoModel')->find($linea->pedido_id);
if (empty($pedido)) {
return [
'status' => false,
'message' => lang('Logistica.errors.notFound'),
];
}
$cantidad_enviada = $EnvioLineasModel->select('SUM(envios_lineas.unidades_envio) as unidades_enviadas')
->where('envios_lineas.pedido_id', $linea->pedido_id)
->where('envios_lineas.envio_id !=', $envio_id)
->where('envios.finalizado', 1)
->join('envios', 'envios.id = envios_lineas.envio_id')
->get()->getResult();
if (
count($cantidad_enviada) == 0 ||
empty($cantidad_enviada[0]->unidades_enviadas) ||
$cantidad_enviada[0]->unidades_enviadas == null
) {
$cantidad_enviada = 0;
} else {
$cantidad_enviada = $cantidad_enviada[0]->unidades_enviadas;
}
if ($cantidad_enviada + $linea->unidades_envio == $pedido->total_tirada) {
$otModel = model('App\Models\OrdenTrabajo\OrdenTrabajoModel');
$ot = $otModel->where('pedido_id', $linea->pedido_id)
->first();
$ps = (new ProductionService())->init($ot->id);
$ps->updateOrdenTrabajoDate([
"name" => "envio_at",
"envio_at" => date('Y-m-d H:i:s')
]);
if ($finalizar_ot) {
$ps->updateOrdenTrabajo(
[
"estado" => 'F'
]
);
array_push($ots, $ot->id);
}
}
}
$EnvioModel->update($envio_id, ['finalizado' => 1]);
$data_return = [
'status' => true,
'message' => lang('Logistica.success.finalizado'),
];
if ($finalizar_ot) {
$data_return['message'] = lang('Logistica.success.finalizadoOTs', [
'ots' => implode(', ', $ots)
]);
$data_return['ots'] = $ots;
}
return $data_return;
}
}

View File

@ -144,7 +144,7 @@ class PresupuestoService extends BaseService
$maquina->velocidad
);
$tiempo = round($tiempo, 2);
[$precio_hora, $margen_precio_hora] = $clientePreciosModel->get_precio_hora($cliente_id, $config, $tiempo);
[$precio_hora, $margen_precio_hora] = $clientePreciosModel->get_precio_hora($cliente_id, $config, round($tiempo, 2));
if (is_null($precio_hora)) {
@ -309,7 +309,7 @@ class PresupuestoService extends BaseService
$linea['fields']['precio_click'],
$maquina->velocidad
);
[$precio_hora, $margen_precio_hora] = $clientePreciosModel->get_precio_hora($cliente_id, $config, $tiempo);
[$precio_hora, $margen_precio_hora] = $clientePreciosModel->get_precio_hora($cliente_id, $config, round($tiempo, 2));
if (is_null($precio_hora)) {
return [];

View File

@ -262,13 +262,13 @@ class ProductionService extends BaseService
{
$fecha_encuadernado = Time::now()->addDays(2)->format("Y-m-d");
$fecha_entrega_real = Time::now()->addDays(5)->format("Y-m-d");
$fecha_embalaje_at = Time::now()->addDays(4)->format("Y-m-d");
//$fecha_embalaje_at = Time::now()->addDays(4)->format("Y-m-d");
$data = [
"orden_trabajo_id" => $this->ot->id,
"fecha_encuadernado_at" => $fecha_encuadernado,
"fecha_entrega_real_at" => $fecha_entrega_real,
"fecha_impresion_at" => Time::now()->format("Y-m-d"),
"embalaje_at" => $fecha_embalaje_at,
//"embalaje_at" => $fecha_embalaje_at,
"fecha_entrega_externo" => $this->pedido->fecha_entrega_externo,
];
$otDateId = $this->otDate->insert($data);

View File

@ -0,0 +1,146 @@
<!-- File: app/Views/catalogo_configuracion_libro.php -->
<div class="accordion accordion-bordered mt-3" id="accordionConfiguracionLibro">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingConfiguracionLibro">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#collapseConfiguracionLibro" aria-expanded="true"
aria-controls="collapseConfiguracionLibro">
<h5 class="mb-0"><?= lang('Catalogo.configuracionLibro') ?: 'Configuración del libro' ?></h5>
</button>
</h2>
<div id="collapseConfiguracionLibro" class="accordion-collapse collapse show"
data-bs-parent="#accordionConfiguracionLibro">
<div class="accordion-body">
<?php
// 1) TIPO DE IMPRESIÓN + ENCUADERNACIÓN
$tipos = ['negro', 'negrohq', 'color', 'colorhq'];
?>
<div class="divider divider-dark text-start mb-1">
<div class="divider-text">
<h5><?= lang('Presupuestos.tipoImpresion') ?></h5>
</div>
</div>
<div class="row px-4 mt-1">
<div class="col-lg-3 col-md-12">
<select id="tipo_impresion" name="tipo_impresion" class="form-control select2bs2 warning-change"
style="width:100%" data-placeholder="<?= lang('Catalogo.seleccionarTipoImpresion') ?>">
<option></option>
<?php foreach ($tipos as $t): ?>
<option value="<?= $t ?>" <?= $catalogoLibrosEntity->tipo_impresion === $t ? 'selected' : '' ?>>
<?= lang("MaquinasTarifasImpresions.$t") ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-lg-3 col-md-12">
<select id="encuadernacion_id" name="encuadernacion_id"
class="form-control select2bs2 warning-change" style="width:100%">
<option value="<?= $catalogoLibrosEntity->encuadernacion_id ?>" selected>
<?= esc($catalogoLibrosEntity->encuadernacionName) ?>
</option>
</select>
</div>
</div>
<?php
// 2) COLUMNAS Y ETIQUETAS COMPARTIDAS
$cols = [2, 2, 2, 2, 2, 2];
$labels = ['', 'numeroPaginas', 'papel', 'gramaje', 'solapas', 'acabados'];
// Función única para renderizar cualquier fila, con clase opcional
$renderRow = function (array $cellsHtml, string $rowClass = '') use ($cols) {
$classAttr = $rowClass ? " {$rowClass}-line" : '';
echo "<div class=\"row{$classAttr} mb-0\">";
foreach ($cellsHtml as $i => $cell) {
$w = $cols[$i] ?? 2;
echo "<div class=\"col-lg-{$w} col-md-12 px-4 mb-0\">{$cell}</div>";
}
echo '<div><hr class="my-1"></div></div>';
};
?>
<div class="divider divider-dark text-start mb-1">
<div class="divider-text">
<h5><?= lang('Presupuestos.papelesComparadorPresupuestoAdmin') ?></h5>
</div>
</div>
<!-- 2a) Encabezado -->
<?php
$hdr = [];
foreach ($labels as $lbl) {
$hdr[] = '<p class="mb-0">' . ($lbl ? lang("Presupuestos.$lbl") : '') . '</p>';
}
echo '<div class="row mb-1">';
foreach ($hdr as $i => $html) {
echo '<div class="col-lg-' . $cols[$i] . ' col-md-12 px-4 mb-0">' . $html . '</div>';
}
echo '<div><hr class="my-1"><hr class="my-1"></div></div>';
?>
<!-- 3) Filas dinámicas: negro y color -->
<?php foreach (['negro', 'negro_pod', 'color', 'color_pod'] as $tipo):
$pag = old("{$tipo}_paginas", $catalogoLibrosEntity->{"{$tipo}_paginas"});
$papId = $catalogoLibrosEntity->{"{$tipo}_papel_id"};
$papNm = $catalogoLibrosEntity->{"{$tipo}PapelName"};
$gram = $catalogoLibrosEntity->{"{$tipo}_gramaje"};
$cells = [
'<p>' . lang("MaquinasTarifasImpresions.$tipo") . '</p>',
"<input type=\"text\" id=\"{$tipo}_paginas\" name=\"{$tipo}_paginas\" placeholder=\"0\" maxlength=\"5\" class=\"form-control {$tipo}_items\" value=\"" . esc($pag) . "\">",
"<select id=\"{$tipo}_papel_id\" name=\"{$tipo}_papel_id\" class=\"form-control select2bs2 {$tipo}_items\" style=\"width:100%\">"
. ($papNm ? "<option value=\"" . esc($papId) . "\" selected>" . esc($papNm) . "</option>" : "")
. "</select>",
"<select id=\"{$tipo}_gramaje\" name=\"{$tipo}_gramaje\" class=\"form-control select2bs2 {$tipo}_items\" style=\"width:100%\">"
. "<option value=\"" . esc($gram) . "\" selected>" . esc($gram) . "</option></select>",
'', // solapas
'' // acabados
];
$renderRow($cells, $tipo);
endforeach; ?>
<!-- 4) Cubierta y Sobrecubierta -->
<?php
$especiales = [
'cubierta' => ['faces' => [2 => 'unaCara', 4 => 'dosCaras'], 'disable' => false],
'cubierta_pod' => ['faces' => [2 => 'unaCara', 4 => 'dosCaras'], 'disable' => false],
'sobrecubierta' => ['faces' => [0 => 'no', 1 => 'si'], 'disable' => in_array($catalogoLibrosEntity->tipo_impresion_id, [5, 6, 7, 8, 21])],
'sobrecubierta_pod' => ['faces' => [0 => 'no', 1 => 'si'], 'disable' => in_array($catalogoLibrosEntity->tipo_impresion_id, [5, 6, 7, 8, 21])]
];
foreach ($especiales as $tipo => $cfg):
$pag = $catalogoLibrosEntity->{"{$tipo}_paginas"};
$papId = $catalogoLibrosEntity->{"{$tipo}_papel_id"};
$papNm = $catalogoLibrosEntity->{"{$tipo}PapelName"};
$gram = $catalogoLibrosEntity->{"{$tipo}_gramaje"};
$sol = old("{$tipo}_solapas_ancho", $catalogoLibrosEntity->{"{$tipo}_ancho_solapas"});
$acId = $catalogoLibrosEntity->{"{$tipo}_acabado_id"};
$acNm = $catalogoLibrosEntity->{"{$tipo}AcabadoName"};
$optPag = '';
foreach ($cfg['faces'] as $v => $lbl) {
$sel = $pag == $v ? ' selected' : '';
$optPag .= "<option value=\"$v\"$sel>" . lang("Presupuestos.$lbl") . "</option>";
}
$cells = [
'<p>' . lang("PapelImpresion.$tipo") . '</p>',
"<select id=\"{$tipo}_paginas\" name=\"{$tipo}_paginas\" class=\"form-control select2bs2\" style=\"width:100%\"" . ($cfg['disable'] ? ' disabled' : '') . ">{$optPag}</select>",
"<select id=\"{$tipo}_papel_id\" name=\"{$tipo}_papel_id\" class=\"form-control select2bs2 {$tipo}_items\" style=\"width:100%\"" . ($cfg['disable'] ? ' disabled' : '') . ">"
. ($papNm ? "<option value=\"" . esc($papId) . "\" selected>" . esc($papNm) . "</option>" : "")
. "</select>",
"<select id=\"{$tipo}_gramaje\" name=\"{$tipo}_gramaje\" class=\"form-control select2bs2 {$tipo}_items\" style=\"width:100%\"" . ($cfg['disable'] ? ' disabled' : '') . ">"
. "<option value=\"" . esc($gram) . "\" selected>" . esc($gram) . "</option></select>",
"<input type=\"text\" id=\"{$tipo}_ancho_solapas\" name=\"{$tipo}_ancho_solapas\" placeholder=\"0\" maxlength=\"5\" class=\"form-control {$tipo}_items\" value=\"" . esc($sol) . "\"" . ($cfg['disable'] ? ' disabled' : '') . ">",
"<select id=\"{$tipo}_acabado_id\" name=\"{$tipo}_acabado_id\" class=\"form-control select2bs2 {$tipo}_items\" style=\"width:100%\"" . ($cfg['disable'] ? ' disabled' : '') . ">"
. "<option value=\"" . esc($acId) . "\" selected>" . esc($acNm) . "</option></select>",
];
$renderRow($cells, $tipo);
endforeach;
?>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,127 @@
<div class="accordion accordion-bordered mt-3" id="accordionLibro">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingLibroDatos">
<button type="button" class="accordion-button" data-bs-toggle="collapse"
data-bs-target="#collapseLibroDatos" aria-expanded="true" aria-controls="collapseLibroDatos">
<h5 class="mb-0"><?= lang("Catalogo.datosGenerales") ?? 'Datos generales del libro' ?></h5>
</button>
</h2>
<div id="collapseLibroDatos" class="accordion-collapse collapse show" data-bs-parent="#accordionLibro">
<div class="accordion-body">
<div class="row">
<!-- COLUMNA IZQUIERDA: Imagen -->
<div class="col-md-12 col-lg-2 px-4 d-flex flex-column justify-content-center align-items-center"
style="min-height: 100%;">
<div class="mb-3 text-center w-100">
<?php if (!empty($catalogoLibrosEntity->cubierta_url)): ?>
<img src="<?= esc($catalogoLibrosEntity->cubierta_url) ?>" class="img-fluid mb-2"
alt="Portada">
<?php else: ?>
<img src="https://static.cegal.es/imagenes/marcadas/9788415/978841f45711.gif" class="img-fluid mb-2"
alt="Sin portada">
<?php endif; ?>
</div>
</div>
<!-- COLUMNA DERECHA: Datos -->
<div class="col-md-12 col-lg-10 px-4">
<div class="row">
<div class="col-md-4 mb-3">
<label for="cliente_id" class="form-label">Cliente *</label>
<select id="cliente_id" name="cliente_id" class="form-select select2bs5">
<option value="<?= $catalogoLibrosEntity->cliente_id ?>" selected>
<?= $catalogoLibrosEntity->clienteName ?>
</option>
</select>
</div>
<div class="col-md-8 mb-3">
<label for="titulo" class="form-label">Título *</label>
<input type="text" id="titulo" name="titulo" class="form-control"
value="<?= old('titulo', $catalogoLibrosEntity->titulo) ?>" required
maxlength="300">
</div>
<div class="col-md-3 mb-3">
<label for="autor" class="form-label">Autor</label>
<input type="text" id="autor" name="autor" class="form-control"
value="<?= old('autor', $catalogoLibrosEntity->autor) ?>" maxlength="255">
</div>
<div class="col-md-3 mb-3">
<label for="coleccion" class="form-label">Colección</label>
<input type="text" id="coleccion" name="coleccion" class="form-control"
value="<?= old('coleccion', $catalogoLibrosEntity->coleccion) ?>" maxlength="255">
</div>
<div class="col-md-3 mb-3">
<label for="editorial" class="form-label">Editorial</label>
<input type="text" id="editorial" name="editorial" class="form-control"
value="<?= old('editorial', $catalogoLibrosEntity->editorial) ?>" maxlength="255">
</div>
<div class="col-md-3 mb-3">
<label for="sello" class="form-label">Sello</label>
<input type="text" id="sello" name="sello" class="form-control"
value="<?= old('sello', $catalogoLibrosEntity->sello) ?>" maxlength="255">
</div>
<div class="col-md-3 mb-3">
<label for="isk" class="form-label">Identificador ISK</label>
<input type="text" id="isk" name="isk" class="form-control" readonly
value="<?= old('isk', $catalogoLibrosEntity->isk) ?>" maxlength="64"
style="background: #E8E8E8;">
</div>
<div class="col-md-3 mb-3">
<label for="isbn" class="form-label">ISBN *</label>
<input type="text" id="isbn" name="isbn" class="form-control"
value="<?= old('isbn', $catalogoLibrosEntity->isbn) ?>" maxlength="255">
</div>
<div class="col-md-3 mb-3">
<label for="num_edic" class="form-label">Edición</label>
<input type="number" id="num_edic" name="num_edic" class="form-control"
value="<?= old('num_edic', $catalogoLibrosEntity->num_edic ?? 1) ?>">
</div>
<div class="col-md-3 mb-3">
<label for="ean" class="form-label">EAN</label>
<input type="text" id="ean" name="ean" class="form-control" readonly
style="background: #E8E8E8;" value="<?= old('ean', $catalogoLibrosEntity->ean) ?>">
</div>
<div class="col-md-3 mb-3">
<label for="ubicacion" class="form-label">Ubicación</label>
<input type="text" id="ubicacion" name="ubicacion" class="form-control"
value="<?= old('ubicacion', $catalogoLibrosEntity->ubicacion) ?>">
</div>
<div class="col-md-3 mb-3">
<label for="ancho" class="form-label">Ancho *</label>
<input type="number" step="0.01" id="ancho" name="ancho" class="form-control"
value="<?= old('ancho', $catalogoLibrosEntity->ancho) ?>">
</div>
<div class="col-md-3 mb-3">
<label for="alto" class="form-label">Alto *</label>
<input type="number" step="0.01" id="alto" name="alto" class="form-control"
value="<?= old('alto', $catalogoLibrosEntity->alto) ?>">
</div>
<div class="col-md-3 mb-3">
<label for="paginas" class="form-label">Nº Páginas *</label>
<input type="number" id="paginas" name="paginas" class="form-control"
value="<?= old('paginas', $catalogoLibrosEntity->paginas) ?>">
</div>
</div>
</div>
</div> <!-- //.row -->
</div> <!-- //.accordion-body -->
</div> <!-- //.collapse -->
</div> <!-- //.accordion-item -->
</div> <!-- //.accordion -->

View File

@ -0,0 +1,99 @@
<div class="accordion accordion-bordered mt-3" id="accordionLibroOtros">
<div class="card accordion-item">
<h2 class="accordion-header" id="headingOtrosDatos">
<button type="button" class="accordion-button collapsed" data-bs-toggle="collapse"
data-bs-target="#collapseOtrosDatos" aria-expanded="false" aria-controls="collapseOtrosDatos">
<h5 class="mb-0"><?= lang("Catalogo.otrosDatosLibro") ?? 'Otros datos del libro' ?></h5>
</button>
</h2>
<div id="collapseOtrosDatos" class="accordion-collapse collapse" data-bs-parent="#accordionLibroOtros">
<div class="accordion-body">
<div class="row">
<div class="col-md-2 mb-3">
<label for="peso" class="form-label">Peso</label>
<input type="number" step="0.01" id="peso" name="peso" class="form-control"
value="<?= old('peso', $catalogoLibrosEntity->peso) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="autor_entidad" class="form-label">Autor entidad</label>
<input type="text" id="autor_entidad" name="autor_entidad" class="form-control"
value="<?= old('autor_entidad', $catalogoLibrosEntity->autor_entidad) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="traductor" class="form-label">Traductor</label>
<input type="text" id="traductor" name="traductor" class="form-control"
value="<?= old('traductor', $catalogoLibrosEntity->traductor) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="ilustrador" class="form-label">Ilustrador</label>
<input type="text" id="ilustrador" name="ilustrador" class="form-control"
value="<?= old('ilustrador', $catalogoLibrosEntity->ilustrador) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="idioma" class="form-label">Idioma</label>
<input type="text" id="idioma" name="idioma" class="form-control"
value="<?= old('idioma', $catalogoLibrosEntity->idioma ?? 'spa') ?>">
</div>
<div class="col-md-2 mb-3">
<label for="num_edic" class="form-label">Nº Edición</label>
<input type="number" id="num_edic" name="num_edic" class="form-control"
value="<?= old('num_edic', $catalogoLibrosEntity->num_edic ?? 1) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="fecha_disponibilidad" class="form-label">Fecha Disponibilidad</label>
<input type="date" id="fecha_disponibilidad" name="fecha_disponibilidad" class="form-control"
value="<?= old('fecha_disponibilidad', $catalogoLibrosEntity->fecha_disponibilidad?->format('Y-m-d')) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="fecha_public" class="form-label">Fecha Publicación</label>
<input type="date" id="fecha_public" name="fecha_public" class="form-control"
value="<?= old('fecha_public', $catalogoLibrosEntity->fecha_public?->format('Y-m-d')) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="num_fotos" class="form-label">Nº Fotos</label>
<input type="number" id="num_fotos" name="num_fotos" class="form-control"
value="<?= old('num_fotos', $catalogoLibrosEntity->num_fotos ?? 0) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="num_ilustr" class="form-label">Nº Ilustraciones</label>
<input type="number" id="num_ilustr" name="num_ilustr" class="form-control"
value="<?= old('num_ilustr', $catalogoLibrosEntity->num_ilustr ?? 0) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="num_ilustr_color" class="form-label">Nº Ilustraciones Color</label>
<input type="number" id="num_ilustr_color" name="num_ilustr_color" class="form-control"
value="<?= old('num_ilustr_color', $catalogoLibrosEntity->num_ilustr_color ?? 0) ?>">
</div>
<div class="col-md-2 mb-3">
<label for="num_ilustr_bn" class="form-label">Nº Ilustraciones B/N</label>
<input type="number" id="num_ilustr_bn" name="num_ilustr_bn" class="form-control"
value="<?= old('num_ilustr_bn', $catalogoLibrosEntity->num_ilustr_bn ?? 0) ?>">
</div>
<div class="col-md-6 mb-3">
<label for="resumen" class="form-label">Resumen</label>
<textarea id="resumen" name="resumen" rows="4" class="form-control"><?= old('resumen', $catalogoLibrosEntity->resumen) ?></textarea>
</div>
<div class="col-md-6 mb-3">
<label for="resumen_breve" class="form-label">Resumen breve</label>
<textarea id="resumen_breve" name="resumen_breve" rows="4" class="form-control"><?= old('resumen_breve', $catalogoLibrosEntity->resumen_breve) ?></textarea>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,20 @@
<?php if (!empty($catalogoLibrosEntity->user_created_id)): ?>
<div
class="container-fluid d-flex flex-md-row flex-column justify-content-between align-items-md-center gap-1 container-p-x py-3">
<div class="col-md-6">
<p>
<strong><?= lang("Catalogo.created_by_at") ?></strong>
<span id="created_by"><?= $catalogoLibrosEntity->createdUser ?></span>,
<span id="created_at"><?= $catalogoLibrosEntity->created_at ?></span>
</p>
</div>
<div class="col-md-6">
<p>
<strong><?= lang("Catalogo.updated_by_at") ?></strong>
<span id="updated_by"><?= $catalogoLibrosEntity->updatedUser ?></span>,
<span id="updated_at_footer"><?= $catalogoLibrosEntity->updated_at ?></span>
</p>
</div>
</div>
<?php endif; ?>

View File

@ -0,0 +1,48 @@
<?= $this->include("themes/_commonPartialsBs/select2bs5") ?>
<?= $this->include("themes/_commonPartialsBs/sweetalert") ?>
<?= $this->extend('themes/vuexy/main/defaultlayout') ?>
<?= $this->section("content") ?>
<div class="row">
<div class="col-12">
<div class="card card-info">
<div class="card-header">
<h3 class="card-title"><?= $boxTitle ?? $pageTitle ?></h3>
</div><!--//.card-header -->
<form id="catalogoLibroForm" class="card-body" method="post" action="<?= $formAction ?>">
<?= csrf_field() ?>
<!-- card-body -->
<div class="card-body">
<?= view("themes/_commonPartialsBs/_alertBoxes") ?>
<?= !empty($validation->getErrors()) ? $validation->listErrors("bootstrap_style") : "" ?>
<?= view("themes/vuexy/form/catalogo/_datosGeneralesFormItems") ?>
<?= view("themes/vuexy/form/catalogo/_otrosDatosFormItems") ?>
<?= view("themes/vuexy/form/catalogo/_configuracionLibroFormItems") ?>
<?= view("themes/vuexy/form/catalogo/_trackingFormItems") ?>
</div>
<!-- /.card-body -->
<!-- card-footer -->
<div class="pt-4">
<?php if (auth()->user()->can('catalogo.edit')): ?>
<input type="submit" class="btn btn-primary float-start me-sm-3 me-1" name="save"
value="<?= lang("Basic.global.Save") ?>" />
<?php endif; ?>
<?= anchor(route_to("catalogoLibrosList"), lang("Basic.global.Cancel"), ["class" => "btn btn-secondary float-start"]) ?>
</div>
<!-- /.card-footer -->
</form>
</div><!-- //.card -->
</div><!--//.col -->
</div><!--//.row -->
<?= $this->endSection() ?>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/css/safekat.css') ?>">
<?= $this->endSection() ?>
<?= $this->section("additionalExternalJs") ?>
<script type="module" src="<?= site_url('assets/js/safekat/pages/catalogo/catalogo.js?' . 'token' . '=' . (csrf_token() ?? "token")) ?>"></script>
<?= $this->endSection() ?>

View File

@ -0,0 +1,80 @@
<?= $this->include('themes/_commonPartialsBs/datatables') ?>
<?= $this->include('themes/_commonPartialsBs/sweetalert') ?>
<?= $this->extend('themes/vuexy/main/defaultlayout') ?>
<?= $this->section('content'); ?>
<div class="row">
<div class="col-md-12">
<div class="card card-info">
<div class="card-header">
<h3 class="card-title"><?= lang('Catalogo.listingPage') ?></h3>
<?= anchor(route_to('catalogoLibrosAdd'), lang('Basic.global.addNew') . ' ' . lang('Catalogo.libro'), ['class' => 'btn btn-primary float-end']); ?>
</div><!--//.card-header -->
<div class="card-body">
<?= view('themes/_commonPartialsBs/_alertBoxes'); ?>
<table id="tableOfCatalogoLibros" class="table table-striped table-hover" style="width: 100%;">
<thead>
<tr>
<th><?= lang('Catalogo.portada') ?></th>
<th><?= lang('Catalogo.id') ?></th>
<th><?= lang('Catalogo.titulo') ?></th>
<th><?= lang('Catalogo.cliente') ?></th>
<th><?= lang('Catalogo.edicion') ?></th>
<th><?= lang('Catalogo.autor') ?></th>
<th><?= lang('Catalogo.isbn') ?></th>
<th><?= lang('Catalogo.ean') ?></th>
<th><?= lang('Catalogo.paginas') ?></th>
<th class="text-nowrap" style="min-width: 85px;"><?= lang('Basic.global.Action') ?></th>
</tr>
<tr>
<th></th>
<th></th>
<th><input type="text" class="form-control filtro_catalogo" name="titulo"></th>
<th><input type="text" class="form-control filtro_catalogo" name="cliente"></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div><!--//.card-body -->
<div class="card-footer">
</div><!--//.card-footer -->
</div><!--//.card -->
</div><!--//.col -->
</div><!--//.row -->
<?= $this->endSection() ?>
<?= $this->section('css') ?>
<link rel="stylesheet"
href="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.bootstrap5.min.css") ?>">
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.css') ?>" />
<?= $this->endSection() ?>
<?= $this->section('additionalExternalJs') ?>
<script
src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/dataTables.buttons.min.js") ?>"></script>
<script
src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.bootstrap5.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.html5.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/buttons/buttons.print.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/jszip/jszip.min.js") ?>"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/pdfmake/pdfmake.min.js") ?>"
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="<?= site_url("/themes/vuexy/vendor/libs/datatables-sk/plugins/pdfmake/vfs_fonts.js") ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.js') ?>"></script>
<script type="module" src="<?= site_url("assets/js/safekat/pages/catalogo/list.js") ?>"></script>
<?= $this->endSection() ?>

View File

@ -186,10 +186,10 @@ var editor_lineas = new $.fn.dataTable.Editor( {
{
name: "cantidad",
attr: {
type: "text",
name : "cantidad",
class :"autonumeric"
}
type: "text",
name : "cantidad",
class :"autonumeric"
}
}, {
name: "descripcion",
type: "textarea",
@ -199,11 +199,21 @@ var editor_lineas = new $.fn.dataTable.Editor( {
}
}, {
name: "iva",
type: "select",
options: [
{ label: "4%", value: 4 },
{ label: "21%", value: 21 }
],
attr: {
type: "text",
name : "iva",
class :"autonumeric"
}
class: "autonumeric"
}
}, {
name: "base",
attr: {
type: "text",
name : "total",
class :"autonumeric"
}
}, {
name: "pedido_linea_impresion_id",
type: "hidden"
@ -213,9 +223,6 @@ var editor_lineas = new $.fn.dataTable.Editor( {
}, {
name: "id",
type: "hidden"
}, {
name: "base",
type: "hidden"
},
]
} );
@ -321,7 +328,9 @@ var tableLineas = $('#tableOfLineasFactura').DataTable({
if(row.pedido_linea_impresion_id != null && <?= (auth()->user()->inGroup('cliente-admin') || auth()->user()->inGroup('cliente-editor')) ? 0 : 1 ?>){
// se convierten a float data.total_aceptado y subtotal
var total_aceptado = parseFloat(row.total_aceptado);
var total_aceptado_revisado = parseFloat(row.total_aceptado_revisado);
var total_aceptado = total_aceptado_revisado?total_aceptado_revisado: parseFloat(row.total_aceptado);
var subtotal = parseFloat(row.base);
var error_text = '';
@ -478,7 +487,7 @@ var tableLineas = $('#tableOfLineasFactura').DataTable({
return sum;
}, 0);
const totalTotal = table.column(10).data().reduce((a, b) => parseFloat(a) + parseFloat(b), 0);
let totalTotal = table.column(10).data().reduce((a, b) => parseFloat(a) + parseFloat(b), 0);
autoNumericSubtotal.set(totalSubtotal);
autoNumericIVA_4.set(totalIVA_4);
@ -486,6 +495,7 @@ var tableLineas = $('#tableOfLineasFactura').DataTable({
$('#total-iva-sum-4').closest('tr').addClass('d-none');
}
else{
totalTotal += totalIVA_4;
$('#total-iva-sum-4').closest('tr').removeClass('d-none');
}
autoNumericIVA_21.set(totalIVA_21);
@ -493,6 +503,7 @@ var tableLineas = $('#tableOfLineasFactura').DataTable({
$('#total-iva-sum-21').closest('tr').addClass('d-none');
}
else{
totalTotal += totalIVA_21;
$('#total-iva-sum-21').closest('tr').removeClass('d-none');
}
autoNumericTotal.set(totalTotal);

View File

@ -73,6 +73,45 @@ const actionBtns_pagos = function(data) {
<?php endif; ?>
}
$(document).on('click', '.btn-delete-pago', function(e) {
const dataId = $(this).attr('data-id');
const row = $(this).closest('tr');
if ($.isNumeric(dataId)) {
asyncConfirmDialogWithParams(
"Borrar Linea de pagos",
"¿Está seguro de borrar la línea? Esta acción no se puede deshacer.",
deleteConfirmedLineaPago, function(){}, [dataId, row])
}
});
function deleteConfirmedLineaPago(params){
var factura_linea_id = params[0];
var row = params[1];
const row_data = tablePagos.row($(row)).data();
const tabla = tablePagos;
if(row_data.id != null){
var url = '<?= route_to('deleteLineaPago') ?>';
$.ajax({
url: url,
method: 'POST',
data: {
factura_id: <?= $facturaEntity->id ?>,
pago_id: row_data.id,
<?= csrf_token() ?? "token" ?> : <?= csrf_token() ?>v
}
}).done((data, textStatus, jqXHR) => {
tabla.clearPipeline().draw();
$('#tableOfLineasFactura').DataTable().clearPipeline().draw();
}).fail((jqXHR, textStatus, errorThrown) => {
popErrorAlert(jqXHR.responseJSON.messages.error)
})
}
}
const editor_pagos = new $.fn.dataTable.Editor( {
ajax: {
url: "<?= route_to('editorOfPagosFacturas') ?>",

View File

@ -9,7 +9,7 @@
<div class="col-12">
<div class="card">
<div class="card-header">
<h4><?= $boxTitle ?></h4>
<h4><?= $boxTitle ?> <?= ($envioEntity->finalizado == 0)?'':'<span class="badge text-bg-success fw-lg">FINALIZADO</span>' ?></h4>
</div>
<div class="card-body">
@ -112,41 +112,43 @@
</div>
</div>
<div class="accordion accordion-bordered">
<div class="card accordion-item active mb-5">
<h4 class="accordion-header px-4 py-3">
<?= lang("Logistica.addLineasEnvio") ?>
</h4>
<?php if ($envioEntity->finalizado == 0): ?>
<div class="accordion accordion-bordered">
<div class="card accordion-item active mb-5">
<h4 class="accordion-header px-4 py-3">
<?= lang("Logistica.addLineasEnvio") ?>
</h4>
<div id="accordionaddLineasEnvioTip" class="accordion-collapse collapse show">
<div id="accordionaddLineasEnvioTip" class="accordion-collapse collapse show">
<div class="d-flex flex-row mb-3">
<div class="col-sm-12 px-3">
<p><?= lang('Logistica.addLineasText') ?></p>
<div class="d-flex flex-row mb-3">
<div class="col-sm-12 px-3">
<p><?= lang('Logistica.addLineasText') ?></p>
</div>
</div>
<div class="d-flex flex-row mb-3">
<div class="col-sm-6 px-3">
<label for="buscadorPedidos" class="form-label">
<?= lang("Logistica.buscadorPedidosTitle2") ?>
</label>
<select id="buscadorPedidos" name="buscador_pedidos" tabindex="1" maxlength="50"
class="form-control select2bs2" style="width: 100%;">
</select>
</div>
<div class="col-sm-2 px-3">
<button id="btnAddLinea" name="btnBuscar" tabindex="1"
class="btn btn-primary mt-4 w-100">
<?= lang("Logistica.add") ?>
<ti class="ti ti-circle-plus"></ti>
</button>
</div>
</div>
</div>
<div class="d-flex flex-row mb-3">
<div class="col-sm-6 px-3">
<label for="buscadorPedidos" class="form-label">
<?= lang("Logistica.buscadorPedidosTitle2") ?>
</label>
<select id="buscadorPedidos" name="buscador_pedidos" tabindex="1" maxlength="50"
class="form-control select2bs2" style="width: 100%;">
</select>
</div>
<div class="col-sm-2 px-3">
<button id="btnAddLinea" name="btnBuscar" tabindex="1"
class="btn btn-primary mt-4 w-100">
<?= lang("Logistica.add") ?>
<ti class="ti ti-circle-plus"></ti>
</button>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<div class="accordion accordion-bordered">
@ -168,13 +170,15 @@
<i class="ti ti-select"></i>
</button>
</div>
<div class="col-sm-2 px-3">
<button id="btnEliminarLineas" name="btnEliminarLineas" tabindex="1"
class="btn btn-danger w-100">
<?= lang("Logistica.eliminar") ?>
<i class="ti ti-trash"></i>
</button>
</div>
<?php if ($envioEntity->finalizado == 0): ?>
<div class="col-sm-2 px-3">
<button id="btnEliminarLineas" name="btnEliminarLineas" tabindex="1"
class="btn btn-danger w-100">
<?= lang("Logistica.eliminar") ?>
<i class="ti ti-trash"></i>
</button>
</div>
<?php endif; ?>
<div class="col-sm-2 px-3">
<button id="btnGenerarAlbaran" name="btnGenerarAlbaran" tabindex="1"
class="btn btn-success w-100">
@ -244,35 +248,90 @@
<input type="number" id="cajas" name="cajas" tabindex="1" maxlength="50"
class="form-control" value="<?= old('cajas', $envioEntity->cajas) ?>">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="accordion accordion-bordered mt-3" id="accordioAlbaranes">
<div class="accordion accordion-bordered mt-3 mb-5" id="accordioAlbaranes">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingAlbaranes">
<button type="button" class="accordion-button" data-bs-toggle="collapse"
data-bs-target="#accordionAlbaranesTip" aria-expanded="false"
aria-controls="accordionAlbaranesTip">
<h3><?= lang("Pedidos.albaranes") ?></h3>
</button>
</h2>
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingAlbaranes">
<button type="button" class="accordion-button" data-bs-toggle="collapse"
data-bs-target="#accordionAlbaranesTip" aria-expanded="false"
aria-controls="accordionAlbaranesTip">
<h3><?= lang("Pedidos.albaranes") ?></h3>
</button>
</h2>
<div id="accordionAlbaranesTip" class="accordion-collapse collapse show"
data-bs-parent="#accordioAlbaranes">
<div id="contenedorAlbaranes" class="accordion-body">
<div id="accordionAlbaranesTip" class="accordion-collapse collapse show"
data-bs-parent="#accordioAlbaranes">
<div id="contenedorAlbaranes" class="accordion-body">
</div>
</div>
</div>
</div>
<div class="accordion accordion-bordered">
<div class="card accordion-item active mb-5">
<h4 class="accordion-header px-4 py-3">
<?= lang("Logistica.acciones") ?>
</h4>
<div class="d-flex flex-row mb-3">
<div class="col-sm-3 px-3">
<label for="codigoSeguimiento" class="form-label">
<?= lang("Logistica.codigoSeguimiento") ?>
</label>
<input type="text" id="codigoSeguimiento" name="codigo_seguimiento" tabindex="1"
maxlength="100" class="form-control"
<?= ($envioEntity->finalizado == 0) ? "" : "readonly" ?>
value="<?= old('codigo_seguimiento', $envioEntity->codigo_seguimiento) ?>">
</div>
<div class="col-sm-3 px-3">
<label for="empresaMensajeria" class="form-label">
<?= lang("Logistica.empresaMensajería") ?>
</label>
<?php if ($envioEntity->finalizado == 0): ?>
<select id="empresaMensajeria" name="empresa_mensajeria" tabindex="1" maxlength="50"
class="form-control select2bs2" style="width: 100%;">
<?php if ($envioEntity->proveedor_id): ?>
<option value="<?= $envioEntity->proveedor_id ?>" "selected">
<?= $envioEntity->proveedor_nombre ?>
</option>
<?php endif; ?>
</select>
<?php else: ?>
<input type="text" id="empresaMensajeriaInput" name="empresa_mensajeria_input"
tabindex="1" maxlength="100" class="form-control" readonly
value="<?= old('empresa_mensajeria', $envioEntity->proveedor_nombre) ?>">
<?php endif; ?>
</div>
<?php if ($envioEntity->finalizado == 0): ?>
<div class="col-sm-3 px-3">
<button id="finalizarEnvio" name="finalizar_envio" tabindex="1"
class="btn btn-primary mt-4 w-100 btn-finalizar">
<?= lang("Logistica.finalizarEnvio") ?>
<ti class="ti ti-check"></ti>
</button>
</div>
<div class="col-sm-3 px-3">
<button id="finalizarEnvioYOTs" name="finalizar_envio_ots" tabindex="1"
class="btn btn-primary mt-4 w-100 btn-finalizar">
<?= lang("Logistica.finalizarEnvioYOTs") ?>
<ti class="ti ti-checks"></ti>
</button>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,5 +1,6 @@
<?= $this->include("themes/_commonPartialsBs/sweetalert") ?>
<?= $this->include('themes/_commonPartialsBs/datatables') ?>
<?= $this->include("themes/_commonPartialsBs/select2bs5") ?>
<?= $this->extend('themes/vuexy/main/defaultlayout') ?>
<?= $this->section('content'); ?>
@ -22,15 +23,35 @@
<div class="accordion-body px-4 py-3">
<div class="row">
<div class="mb-1 col-sm-4">
<div class="mb-1 col-sm-6">
<label for="buscadorPedidos" class="form-label">
<?= lang("Logistica.buscadorPedidosTitle") ?>
<?= lang("Logistica.buscadorPedidosTitle2") ?>
</label>
<input id="buscadorPedidos" name="buscador_pedidos" tabindex="1" maxlength="50"
class="form-control" value="">
<select id="buscadorPedidos" name="buscador_pedidos" tabindex="1" maxlength="50"
class="form-control select2bs2">
</select>
</div>
</div>
<div class="row select-direcciones d-none">
<div class="col-sm-6 px-3">
<label for="selectDirecciones" class="form-label">
<?= lang("Logistica.selectDirecciones") ?>
</label>
<select id="selectDirecciones" name="select_direcciones" tabindex="1" maxlength="50"
class="form-control select2bs2" style="width: 100%;">
</select>
</div>
</div>
<div class="row add-envio d-none">
<div class="col-sm-2 px-3">
<button id="btnAddEnvio" name="btn_add_envio" tabindex="1"
class="btn btn-primary mt-4 w-100">
<?= lang("Logistica.add") ?>
<ti class="ti ti-circle-plus"></ti>
</button>
</div>
</div>
</div>
</div>
@ -87,10 +108,10 @@
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.css') ?>" />
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.css') ?>" />
<?= $this->endSection() ?>
<?= $this->section('additionalExternalJs') ?>
<script src="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.js') ?>"></script>
<script type="module" src="<?= site_url("assets/js/safekat/pages/logistica/envio.js") ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.js') ?>"></script>
<script type="module" src="<?= site_url("assets/js/safekat/pages/logistica/envio.js") ?>"></script>
<?= $this->endSection() ?>

View File

@ -12,48 +12,24 @@
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-4">
<button type="button" style="height: 80px;" class="btn btn-primary w-100 "
onclick="window.location.href='<?= route_to('selectEnvios', 'simple') ?>'"
>
<?= lang('Logistica.envioSimple') ?>
</button>
<div class="grid">
<div class="item" onclick="location.href='<?= route_to('gestionEnvios') ?>'">
<img src="<?= site_url("assets/img/logistica/envios.jpg") ?>" alt="Envíos">
<div><span><?= lang("Logistica.gestionEnvios"); ?></span></div>
</div>
<div class="col-4">
<button type="button" style="height: 80px;" class="btn btn-primary w-100 " >
<?= lang('Logistica.envioMultiple') ?>
</button>
<div class="item">
<img src="<?= site_url("assets/img/logistica/envios_ferros.png") ?>" alt="Envío de Ferros/Prototipos">
<div><span><?= lang("Logistica.envioFerros"); ?></span></div>
</div>
<div class="col-4">
<button type="button" style="height: 80px;" class="btn btn-primary w-100 " >
<?= lang('Logistica.envioConjunto') ?>
</button>
<div class="item">
<img src="<?= site_url("assets/img/logistica/impresionEtiquetas.jpg") ?>" alt="Etiquetas de títulos">
<div><span><?= lang("Logistica.etiquetasTitulos"); ?></span></div>
</div>
</div>
<div class="row mb-3">
<div class="col-4">
<button type="button" style="height: 80px;" class="btn btn-primary w-100 " >
<?= lang('Logistica.etiquetasTitulos') ?>
</button>
</div>
<div class="col-4">
<button type="button" style="height: 80px;" class="btn btn-primary w-100 " >
<?= lang('Logistica.etiquetasEnvio') ?>
</button>
</div>
<div class="col-4">
<button type="button" style="height: 80px;" class="btn btn-primary w-100 " >
<?= lang('Logistica.envioFerros') ?>
</button>
</div>
</div>
<div class="row">
<div class="col-4">
<button type="button" style="height: 80px;" class="btn btn-primary w-100 " >
<?= lang('Logistica.cerrarOTauto') ?>
</button>
<div class="item">
<img src="<?= site_url("assets/img/logistica/albaranes.png") ?>" alt="Albaranes">
<div><span><?= lang("Logistica.albaranes"); ?></span></div>
</div>
</div>
</div>
</div>
@ -63,6 +39,7 @@
<?= $this->endSection(); ?>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/css/logisticaPanel.css') ?>">
<?= $this->endSection() ?>

View File

@ -1,35 +1,33 @@
<?php
/**
* MENU CATALOGO
*/
if (auth()->user()->inGroup('beta')) {
?>
if (auth()->user()->can('catalogo.menu')) {
?>
<!-- Catalogue -->
<li class="menu-item">
<a href="javascript:void(0);" class="menu-link menu-toggle beta">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon tf-icons ti ti-book"></i>
<div><?= lang("App.menu_catalogo") ?></div>
<?= lang("App.menu_catalogo") ?>
</a>
<ul class="menu-sub">
<li class="menu-item">
<a href="<?= site_url("catalogo/catalogo") ?>" class="menu-link beta">
<div><?= lang("App.menu_catalogo_libros") ?></div>
</a>
</li>
<li class="menu-item">
<a href="<?= site_url("catalogo/catalogo/nuevo") ?>" class="menu-link beta">
<div><?= lang("App.menu_catalogo_nuevo") ?></div>
</a>
</li>
<li class="menu-item">
<a href="<?= site_url("catalogo/catalogo/categorias") ?>" class="menu-link beta">
<div><?= lang("App.menu_catalogo_categorias") ?></div>
</a>
</li>
<?php if (auth()->user()->can('catalogo.menu')) { ?>
<li class="menu-item">
<a href="<?= route_to("catalogoLibrosList") ?>" class="menu-link">
<?= lang("App.menu_catalogo_libros") ?>
</a>
</li>
<?php } ?>
<?php if (auth()->user()->can('catalogo.create')) { ?>
<li class="menu-item">
<a href="<?= route_to("catalogoLibrosAdd") ?>" class="menu-link">
<?= lang("App.menu_catalogo_nuevo") ?>
</a>
</li>
<?php } ?>
<li class="menu-item">
<a href="<?= site_url("catalogo/catalogo/importar") ?>" class="menu-link beta">
<div> <?= lang("App.menu_catalogo_importar") ?></div>
<?= lang("App.menu_catalogo_importar") ?>
</a>
</li>
</ul>

View File

@ -7,29 +7,10 @@ if (auth()->user()->inGroup('beta')) {
?>
<!-- Logistic -->
<li class="menu-item">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<a href="<?= route_to("LogisticaPanel") ?>" class="menu-link">
<i class="menu-icon tf-icons ti ti-truck"></i>
<div> <?= lang("App.menu_logistica") ?></div>
</a>
<ul class="menu-sub">
<li class="menu-item">
<a href="<?= route_to("LogisticaPanel") ?>" class="menu-link">
<div> <?= lang("App.menu_logistica_panel") ?></div>
</a>
<a href="<?= site_url("envioslogistica/logistica") ?>" class="menu-link">
<div> <?= lang("App.menu_logistica_envios") ?></div>
</a>
</li>
<li class="menu-item">
<a href="<?= site_url("envioslogistica/logistica/etiquetas") ?>" class="menu-link">
<div> <?= lang("App.menu_logistica_etiquetas") ?></div>
</a>
</li>
<li class="menu-item">
<a href="<?= site_url("envioslogistica/logistica/tracking") ?>" class="menu-link">
<div> <?= lang("App.menu_logistica_tracking") ?></div>
</a>
</li>
</ul>
</li>
<?php } ?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -0,0 +1,418 @@
import ClassSelect from '../../components/select2.js';
import Ajax from '../../components/ajax.js';
class Catalogo {
constructor() {
/* Definiciones */
this.tirada_no_pod = 100;
this.tirada_pod = 1;
/* Mapeado de elementos */
this.tipo_impresion = $("#tipo_impresion");
this.paginas_cubierta = $("#cubierta_paginas");
this.sobrecubiertaItems = $('.sobrecubierta_items').add('.sobrecubierta_pod_items');
this.paginasSobrecubierta = $('#sobrecubierta_paginas');
this.negro = $('#negro_paginas');
this.color = $('#color_paginas');
this.total = $('#paginas');
/* Select2 para clientes */
this.cliente = new ClassSelect($("#cliente_id"), '/catalogo/libros/clientlist', "Seleccione un cliente");
/* Select2 para tipos de encuadernacion */
this.encuadernacion = new ClassSelect($("#encuadernacion_id"), '/importador/getencuadernacion', "Seleccione una encuadernación");
/* Select2 para impresion en Negro */
this.selectPapelNegro = new ClassSelect($("#negro_papel_id"), '/presupuestoadmin/papelgenerico', "Seleccione un papel", false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
tirada: () => this.tirada_no_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: 0,
lomo: 0,
tipo: () => this.tipo_impresion.val().includes('hq') ? 'negrohq' : 'negro',
});
this.selectPapelNegroPod = new ClassSelect($("#negro_pod_papel_id"), '/presupuestoadmin/papelgenerico', "Seleccione un papel", false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
tirada: () => this.tirada_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: 0,
lomo: 0,
tipo: () => this.tipo_impresion.val().includes('hq') ? 'negrohq' : 'negro',
});
this.selectGramajeNegro = new ClassSelect($('#negro_gramaje'), '/presupuestoadmin/papelgramaje', 'Seleccione un gramaje', false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
papel_generico: () => this.selectPapelNegro.getVal(),
tirada: () => this.tirada_no_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: 0,
lomo: 0,
tipo: () => this.tipo_impresion.val().includes('hq') ? 'negrohq' : 'negro',
});
this.selectGramajeNegroPod = new ClassSelect($('#negro_pod_gramaje'), '/presupuestoadmin/papelgramaje', 'Seleccione un gramaje', false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
papel_generico: () => this.selectPapelNegroPod.getVal(),
tirada: () => this.tirada_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: 0,
lomo: 0,
tipo: () => this.tipo_impresion.val().includes('hq') ? 'negrohq' : 'negro',
});
/* Select2 para impresion en Color */
this.selectPapelColor = new ClassSelect($("#color_papel_id"), '/presupuestoadmin/papelgenerico', "Seleccione un papel", false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
tirada: () => this.tirada_no_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: 0,
lomo: 0,
tipo: () => this.tipo_impresion.val().includes('hq') ? 'colorhq' : 'color',
});
this.selectPapelColorPod = new ClassSelect($("#color_pod_papel_id"), '/presupuestoadmin/papelgenerico', "Seleccione un papel", false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
tirada: () => this.tirada_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: 0,
lomo: 0,
tipo: () => this.tipo_impresion.val().includes('hq') ? 'colorhq' : 'color',
});
this.selectGramajeColor = new ClassSelect($('#color_gramaje'), '/presupuestoadmin/papelgramaje', 'Seleccione un gramaje', false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
papel_generico: () => this.selectPapelColor.getVal(),
tirada: () => this.tirada_no_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: 0,
lomo: 0,
tipo: () => this.tipo_impresion.val().includes('hq') ? 'colorhq' : 'color',
});
this.selectGramajeColorPod = new ClassSelect($('#color_pod_gramaje'), '/presupuestoadmin/papelgramaje', 'Seleccione un gramaje', false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
papel_generico: () => this.selectPapelColorPod.getVal(),
tirada: () => this.tirada_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: 0,
lomo: 0,
tipo: () => this.tipo_impresion.val().includes('hq') ? 'colorhq' : 'color',
});
/* Select2 para impresion de cubiertas */
this.selectPapelCubierta = new ClassSelect($("#cubierta_papel_id"), '/presupuestoadmin/papelgenerico', "Seleccione un papel", false,
{
tipo_impresion: this.encuadernacion.getVal(),
tirada: () => this.tirada_no_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: () => $('#cubierta_ancho_solapas').val(),
lomo: () => 0,
tipo: 'colorhq',
uso: 'cubierta',
});
this.selectPapelCubiertaPod = new ClassSelect($("#cubierta_pod_papel_id"), '/presupuestoadmin/papelgenerico', "Seleccione un papel", false,
{
tipo_impresion: this.encuadernacion.getVal(),
tirada: () => this.tirada_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: () => $('#cubierta_ancho_solapas').val(),
lomo: () => 0,
tipo: 'colorhq',
uso: 'cubierta',
});
this.selectGramajeCubierta = new ClassSelect($('#cubierta_gramaje'), '/presupuestoadmin/papelgramaje', 'Seleccione un gramaje', false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
papel_generico: () => this.selectPapelCubierta.getVal(),
tirada: () => this.tirada_no_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: () => $('#cubierta_ancho_solapas').val(),
lomo: 0,
tipo: 'colorhq',
});
this.selectGramajeCubiertaPod = new ClassSelect($('#cubierta_pod_gramaje'), '/presupuestoadmin/papelgramaje', 'Seleccione un gramaje', false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
papel_generico: () => this.selectPapelCubiertaPod.getVal(),
tirada: () => this.tirada_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: () => $('#cubierta_ancho_solapas').val(),
lomo: 0,
tipo: 'colorhq',
});
this.acabadoCubierta = new ClassSelect($("#cubierta_acabado_id"),
'/serviciosacabados/getacabados',
'Seleccione acabado',
false,
{
"cubierta": 1
}
);
/* Select2 para impresion de sobrecubierta */
this.selectPapelSobrecubierta = new ClassSelect($("#sobrecubierta_papel_id"), '/presupuestoadmin/papelgenerico', "Seleccione un papel", false,
{
tipo_impresion: this.encuadernacion.getVal(),
tirada: () => this.tirada_no_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: () => $('#sobrecubierta_ancho_solapas').val(),
lomo: () => 0,
tipo: 'colorhq',
uso: 'sobrecubierta',
});
this.selectPapelSobrecubiertaPod = new ClassSelect($("#sobrecubierta_pod_papel_id"), '/presupuestoadmin/papelgenerico', "Seleccione un papel", false,
{
tipo_impresion: this.encuadernacion.getVal(),
tirada: () => this.tirada_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: () => $('#sobrecubierta_ancho_solapas').val(),
lomo: () => 0,
tipo: 'colorhq',
uso: 'sobrecubierta',
});
this.selectGramajeSobrecubierta = new ClassSelect($('#sobrecubierta_gramaje'), '/presupuestoadmin/papelgramaje', 'Seleccione un gramaje', false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
papel_generico: () => this.selectPapelSobrecubierta.getVal(),
tirada: () => this.tirada_no_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: () => $('#sobrecubierta_ancho_solapas').val(),
lomo: 0,
tipo: 'colorhq',
});
this.selectGramajeSobrecubiertaPod = new ClassSelect($('#sobrecubierta_pod_gramaje'), '/presupuestoadmin/papelgramaje', 'Seleccione un gramaje', false,
{
tipo_impresion: () => this.encuadernacion.getVal(),
papel_generico: () => this.selectPapelSobrecubiertaPod.getVal(),
tirada: () => this.tirada_pod,
ancho: () => this.getDimensionLibro().ancho,
alto: () => this.getDimensionLibro().alto,
sopalas: () => $('#sobrecubierta_ancho_solapas').val(),
lomo: 0,
tipo: 'colorhq',
});
this.acabadosSobrecubierta = new ClassSelect($("#sobrecubierta_acabado_id"),
'/serviciosacabados/getacabados',
'Seleccione acabado',
false,
{
"sobrecubierta": 1
}
);
}
init() {
const self = this;
// Eliminar elementos que no se usan
$('[id*="_pod_paginas"]').remove();
$('[id*="_pod_ancho_solapas"]').remove();
$('[id*="_pod_acabado_id"]').remove();
// Fuerza el foco en el campo de búsqueda de select2
$(document).on('select2:open', () => {
document.querySelector('.select2-search__field').focus();
});
this.cliente.init();
this.tipo_impresion.select2();
this.paginas_cubierta.select2();
this.paginasSobrecubierta.select2();
this.encuadernacion.init();
this.selectPapelNegro.init();
this.selectPapelNegroPod.init();
this.selectGramajeNegro.init();
this.selectGramajeNegroPod.init();
this.selectPapelColor.init();
this.selectPapelColorPod.init();
this.selectGramajeColor.init();
this.selectGramajeColorPod.init();
this.selectPapelCubierta.init();
this.selectPapelCubiertaPod.init();
this.selectGramajeCubierta.init();
this.selectGramajeCubiertaPod.init();
this.acabadoCubierta.init();
this.selectPapelSobrecubierta.init();
this.selectPapelSobrecubiertaPod.init();
this.selectGramajeSobrecubierta.init();
this.selectGramajeSobrecubiertaPod.init();
this.acabadosSobrecubierta.init();
// Al cargar la página
this.toggleSobrecubiertaFields();
// Inicializacino de eventos
this.selectPapelNegro.item.on('select2:select', function () {
self.selectGramajeNegro.empty();
});
this.total.on('input change', () => {
this.validarMultiploDe4([this.total, this.color, this.negro]);
});
this.color.on('input change', this.actualizarDesdeColor.bind(this));
this.negro.on('input change', this.actualizarDesdeNegro.bind(this));
this.tipo_impresion.on("change", this.updateOpcionesImpresion.bind(this));
// Al cambiar el selector de paginas de sobrecubierta
this.paginasSobrecubierta.on('change', this.toggleSobrecubiertaFields.bind(this));
// Al cambiar el tipo de encuadernacion
this.encuadernacion.onChange(this.enableSobrecubiertaLines.bind(this));
this.updateOpcionesImpresion();
$(document).on('change', '.warning-change', function () {
$(this).addClass('bg-warning');
let select2Container = $(this).next('.select2').find('.select2-selection');
select2Container.addClass('bg-warning');
});
}
actualizarDesdeColor() {
const total = parseInt(this.total.val(), 10) || 0;
const color = parseInt(this.color.val(), 10) || 0;
const negro = Math.max(total - color, 0);
this.negro.val(negro);
this.validarMultiploDe4([this.total, this.color, this.negro]);
}
actualizarDesdeNegro() {
const total = parseInt(this.total.val(), 10) || 0;
const negro = parseInt(this.negro.val(), 10) || 0;
const color = Math.max(total - negro, 0);
this.color.val(color);
this.validarMultiploDe4([this.total, this.color, this.negro]);
}
validarMultiploDe4(campos) {
campos.forEach($el => {
const val = parseInt($el.val(), 10) || 0;
if (val % 4 !== 0) {
$el.css('color', 'red');
} else {
$el.css('color', '');
}
});
}
updateOpcionesImpresion() {
$('.negro_items').off('change');
$('.color_items').off('change');
const selValue = this.tipo_impresion.val();
// Buscar elementos por clase dinámica (negro-*-line y color-*-line)
const elements_negro = $('*').filter(function () {
return [...this.classList].some(cls => /^negro.*line$/.test(cls));
});
const elements_color = $('*').filter(function () {
return [...this.classList].some(cls => /^color.*line$/.test(cls));
});
if (selValue.includes('color')) {
elements_color.removeClass('d-none');
} else {
elements_color.addClass('d-none');
}
elements_negro.removeClass('d-none');
}
getDimensionLibro() {
let ancho = $('#ancho').val();
let alto = $('#alto').val();;
return { ancho, alto };
}
toggleSobrecubiertaFields() {
if (this.paginasSobrecubierta.val() === '1') {
this.sobrecubiertaItems.prop('disabled', false).trigger('change.select2');
} else {
this.sobrecubiertaItems.prop('disabled', true).trigger('change.select2');
}
}
enableSobrecubiertaLines() {
// Buscar elementos por clase dinámica (sobrecubierta-*-line)
const elements_sobrecubierta = $('*').filter(function () {
return [...this.classList].some(cls => /^sobrecubierta.*line$/.test(cls));
});
switch (parseInt(this.encuadernacion.getVal(), 10)) {
case 5:
case 6:
case 7:
case 8:
case 21:
console.log("Desactivar sobrecubierta:" + this.encuadernacion.getVal());
elements_sobrecubierta.addClass('d-none');
break;
default:
console.log("Activar sobrecubierta:" + this.encuadernacion.getVal());
elements_sobrecubierta.removeClass('d-none');
break;
}
}
}
document.addEventListener('DOMContentLoaded', function () {
let catalogo = new Catalogo();
catalogo.init();
});
export default Catalogo;

View File

@ -0,0 +1,114 @@
import Ajax from '../../components/ajax.js';
document.addEventListener('DOMContentLoaded', function () {
const lastColNr = $('#tableOfCatalogoLibros').find("tr:first th").length - 1;
const theTable = $('#tableOfCatalogoLibros').DataTable({
processing: true,
serverSide: true,
autoWidth: true,
orderCellsTop: true,
responsive: true,
scrollX: true,
lengthMenu: [5, 10, 25, 50, 75, 100, 250, 500, 1000, 2500],
pageLength: 10,
lengthChange: true,
dom: 'lfBrtip',
buttons: [
'copy', 'csv', 'excel', 'print', {
extend: 'pdfHtml5',
orientation: 'landscape',
pageSize: 'A4'
}
],
order: [[1, 'asc']],
language: {
url: "/themes/vuexy/vendor/libs/datatables-sk/plugins/i18n/es-ES.json"
},
ajax: {
url: '/catalogo/libros/datatable',
method: 'GET'
},
columnDefs: [
{
orderable: false,
searchable: false,
targets: [lastColNr]
}
],
columns: [
{ data: 'portada' },
{ data: 'id' },
{ data: 'titulo' },
{ data: 'cliente' },
{
data: 'edicion',
className: "text-center"
},
{ data: 'autor' },
{ data: 'isbn' },
{ data: 'ean' },
{
data: 'paginas',
className: "text-center"
},
{ data: 'actionBtns' }
]
});
$(document).on('click', '.btn-edit', function (e) {
window.location.href = '/catalogo/libros/edit/' + $(this).attr('data-id');
});
$(document).on('click', '.btn-delete', function (e) {
e.preventDefault();
const row = $(this).closest('tr')[0]._DT_RowIndex;
const dataId = $(this).attr('data-id');
Swal.fire({
title: '¿Estás seguro?',
text: 'Esta acción no se puede deshacer.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí',
cancelButtonText: 'No',
reverseButtons: false,
buttonsStyling: true,
customClass: {
confirmButton: 'btn btn-danger', // rojo para "Sí"
cancelButton: 'btn btn-secondary' // gris para "No"
}
}).then((result) => {
if (result.isConfirmed) {
new Ajax(
'/catalogo/libros/delete/' + dataId,
{},
{},
(data, textStatus, jqXHR) => {
theTable.clearPipeline();
theTable.row($(row)).invalidate().draw();
popSuccessAlert(data.msg ?? jqXHR.statusText);
},
(error) => {
console.error(error);
Swal.fire('Error', 'No se pudo eliminar el libro.', 'error');
}
).get();
}
});
});
$(document).on("keyup", ".filtro_catalogo", (event) => {
let columnName = $(event.currentTarget).attr("name");
let columnIndex = $('#tableOfCatalogoLibros').DataTable().columns().eq(0).filter(function (index) {
return $('#tableOfCatalogoLibros').DataTable().column(index).dataSrc() === columnName;
})[0];
$('#tableOfCatalogoLibros').DataTable().column(columnIndex).search($(event.currentTarget).val()).draw()
})
});

View File

@ -1,35 +1,55 @@
import Ajax from '../../components/ajax.js';
import ClassSelect from '../../components/select2.js';
$(()=>{
$(() => {
$('#buscadorPedidos').on('keydown', function(e) {
if (e.key === 'Enter' || e.keyCode === 13) {
e.preventDefault(); // Evita el submit si está dentro de un form
let search = $(this).val().trim();
new Ajax(
'/logistica/buscar/'+search,
{},
{},
function(response) {
if(!response.status){
popErrorAlert(response.message);
}
if(response.data){
window.open(`${window.location.origin}/logistica/envio/${response.data.id_envio}`);
}
},
function(xhr, status, error) {
if(status == 'error' && typeof(error)== 'string')
popErrorAlert(error);
else
popErrorAlert(error.responseJSON.message);
}
).get();
}
const selectPedidos = new ClassSelect($('#buscadorPedidos'), '/logistica/selectPedidosForEnvio', "");
selectPedidos.init();
const selectDirecciones = new ClassSelect($('#selectDirecciones'), '/logistica/selectDireccionForEnvio', "", true, {
pedido_id: () => selectPedidos.getVal()
});
selectDirecciones.init();
selectPedidos.item.on('select2:open', () => {
$('.select-direcciones').addClass('d-none');
$('.add-envio').addClass('d-none');
})
selectPedidos.item.on('change', () => {
selectDirecciones.empty();
$('.select-direcciones').removeClass('d-none');
})
selectDirecciones.item.on('select2:open', () => {
$('.add-envio').addClass('d-none');
})
selectDirecciones.item.on('change', () => {
$('.add-envio').removeClass('d-none');
})
$('#btnAddEnvio').on('click', () => {
const pedido_id = selectPedidos.getVal();
const direccionSeleccionada = selectDirecciones.getText();
$.post('/logistica/generateEnvio', {
pedido_id: pedido_id,
direccion: direccionSeleccionada
}, function (response) {
if (response.status) {
window.open(`${window.location.origin}/logistica/envio/${response.data.id_envio}`);
selectDirecciones.empty();
selectPedidos.empty();
$('.select-direcciones').addClass('d-none');
$('.add-envio').addClass('d-none');
} else {
popErrorAlert(response.message);
}
}).fail(function (xhr, status, error) {
popErrorAlert(error);
});
})
const tableEnvios = $('#tableOfEnvios').DataTable({
processing: true,
@ -55,7 +75,7 @@ $(()=>{
{ "data": "cp" },
{ "data": "email" },
{ "data": "telefono" },
{
{
"data": "finalizado",
"className": "text-center",
},
@ -78,5 +98,7 @@ $(()=>{
$(document).on('click', '.btn-edit', function (e) {
window.location.href = '/logistica/envio/' + $(this).attr('data-id');
});
});

View File

@ -28,9 +28,18 @@ class EnvioEdit {
this.btnGenerarAlbaran = $("#btnGenerarAlbaran");
this.btnbtnSelectAll = $("#btnSelectAll");
this.cajas = $("#cajas");
this.codigoSeguimiento = $("#codigoSeguimiento");
if (!$("#empresaMensajeriaInput").length) {
this.proveedor = new ClassSelect($("#empresaMensajeria"), '/compras/proveedores/getProveedores', "", true, { 'tipo_id': 2 });
}
}
init() {
if (!$("#empresaMensajeriaInput").length) {
this.proveedor.init();
}
this.table = $('#tableLineasEnvio').DataTable({
processing: true,
serverSide: true,
@ -53,14 +62,14 @@ class EnvioEdit {
footerCallback: function (row, data, start, end, display) {
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));
@ -117,7 +126,7 @@ class EnvioEdit {
buttonsStyling: false
});
$(e.currentTarget).val(0);
}
}
}).fail(() => {
Swal.fire({
title: 'Error',
@ -205,12 +214,240 @@ class EnvioEdit {
const checkboxes = this.table.$('input[type="checkbox"]');
const allChecked = checkboxes.length === checkboxes.filter(':checked').length;
checkboxes.prop('checked', !allChecked);
});
this.codigoSeguimiento.on('change', (e) => {
const value = $(e.currentTarget).val();
$.post('/logistica/updateCodigoSeguimiento', {
id: $('#id').val(),
codigo_seguimiento: value
}, function (response) {
if (!response.status) {
Swal.fire({
title: 'Error',
text: response.message,
icon: 'error',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
$(e.currentTarget).val(value.substring(0, 100));
}
}).fail(() => {
Swal.fire({
title: 'Error',
text: 'No se pudo actualizar el código de seguimiento.',
icon: 'error',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
$(e.currentTarget).val(value.substring(0, 100));
}
);
});
if (!$("#empresaMensajeriaInput").length) {
this.proveedor.onChange((e) => {
const value = this.proveedor.getVal();
$.post('/logistica/updateProveedorEnvio', {
id: $('#id').val(),
proveedor_id: value
}, function (response) {
if (!response.status) {
Swal.fire({
title: 'Error',
text: response.message,
icon: 'error',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
this.proveedor.setVal(0);
}
}).fail(() => {
Swal.fire({
title: 'Error',
text: 'No se pudo actualizar el proveedor.',
icon: 'error',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
this.proveedor.setVal(0);
}
);
});
}
);
$('#finalizarEnvio').on('click', (e) => {
if (!this._checkDatosFinalizar())
return;
Swal.fire({
title: 'Finalizar envío',
text: '¿Está seguro de que desea finalizar el envío?',
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('/logistica/finalizarEnvio', {
id: $('#id').val()
}, function (response) {
if (response.status) {
Swal.fire({
text: response.message,
icon: 'success',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
}).then(() => {
window.location.reload();
});
} else {
Swal.fire({
title: 'Error',
text: response.message,
icon: 'error',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
}
}).fail(() => {
Swal.fire({
title: 'Error',
text: 'No se pudo finalizar el envío.',
icon: 'error',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
});
}
});
});
$('#finalizarEnvioYOTs').on('click', (e) => {
if (!this._checkDatosFinalizar())
return;
Swal.fire({
title: 'Finalizar envío y las OTs',
text: '¿Está seguro de que desea finalizar el envío y las OTs de las líneas de envío?',
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('/logistica/finalizarEnvio', {
id: $('#id').val(),
finalizar_ots: true
}, function (response) {
if (response.status) {
Swal.fire({
text: response.message,
icon: 'success',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
}).then(() => {
window.location.reload();
});
} else {
Swal.fire({
title: 'Error',
text: response.message,
icon: 'error',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
}
}).fail(() => {
Swal.fire({
title: 'Error',
text: 'No se pudo finalizar el envío.',
icon: 'error',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
});
}
});
});
this._getAlbaranes();
}
_checkDatosFinalizar() {
if (this.codigoSeguimiento.val().length <= 0 || this.proveedor.getVal() <= 0) {
Swal.fire({
title: 'Atención!',
text: 'Debe seleccionar un proveedor y un código de seguimiento antes de finalizar el envío.',
icon: 'info',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary me-1',
},
buttonsStyling: false
});
return false;
}
return true;
}
_getAlbaranes() {
$.get('/albaranes/albaranesEnvio', {
envio_id: $('#id').val(),

View File

@ -11,13 +11,18 @@ class Resumen {
const self = this;
this.toastPresupuestoTotal = null
$(".update-totales").on("change", function () {
self.updateTotales(null, { updateLP: true, updateServicios: true, updateEnvio: true });
self.updateTotales({ updateLP: true, updateServicios: true, updateEnvio: true });
});
$(document).on('update-totales', async function () {
await self.updateTotales();
$(document).trigger('update-totales-completed');
});
$('#total_descuentoPercent').on('change', function () {
this.updateTotales({ updateLP: false, updateServicios: false, updateEnvio: false }, false);
}.bind(this));
$("#totalDespuesDecuento").on('change', this.updateToastSummary.bind(this))
}

View File

@ -0,0 +1,26 @@
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.item {
text-align: center;
}
.item img {
width: 100%;
max-width: 300px;
height: auto;
}
.item p {
margin-top: 10px;
font-size: 1rem;
}
@media (max-width: 700px) {
.grid {
grid-template-columns: 1fr;
}
}