editar factura

This commit is contained in:
2024-07-07 19:25:39 +02:00
parent c7d8d0c5cc
commit 5605a88846
21 changed files with 1132 additions and 31 deletions

View File

@ -76,7 +76,7 @@ $routes->group('configuracion', ['namespace' => 'App\Controllers\Configuracion']
$routes->match(['get', 'post'], 'edit/(:num)', 'SeriesFacturas::edit/$1', ['as' => 'seriesFacturasEdit']); $routes->match(['get', 'post'], 'edit/(:num)', 'SeriesFacturas::edit/$1', ['as' => 'seriesFacturasEdit']);
$routes->get('delete/(:num)', 'SeriesFacturas::delete/$1', ['as' => 'seriesFacturasDelete']); $routes->get('delete/(:num)', 'SeriesFacturas::delete/$1', ['as' => 'seriesFacturasDelete']);
$routes->post('datatable', 'SeriesFacturas::datatable', ['as' => 'seriesFacturasDT']); $routes->post('datatable', 'SeriesFacturas::datatable', ['as' => 'seriesFacturasDT']);
$routes->post('menuitemsFacturas', 'SeriesFacturas::menuItemsFacturas', ['as' => 'menuItemsOfSeriesFacturas']);
}); });
}); });
@ -652,8 +652,14 @@ $routes->group('facturas', ['namespace' => 'App\Controllers\Facturacion'], funct
$routes->get('list', 'Facturas::list', ['as' => 'facturasList']); $routes->get('list', 'Facturas::list', ['as' => 'facturasList']);
$routes->post('datatable', 'Facturas::datatable', ['as' => 'dataTableOfFacturas']); $routes->post('datatable', 'Facturas::datatable', ['as' => 'dataTableOfFacturas']);
$routes->get('add', 'Facturas::add', ['as' => 'newFactura']);
$routes->post('add', 'Facturas::add', ['as' => 'createFactura']);
$routes->get('edit/(:any)', 'Facturas::edit/$1', ['as' => 'editarFactura']);
$routes->post('update/(:any)', 'Facturas::update/$1', ['as' => 'actualizarFactura']);
$routes->post('datatable/(:any)', 'FacturasLineas::datatable/$1', ['as' => 'dataTableOfLineasFacturas']);
}); });
$routes->group( $routes->group(
'printpresupuestos', 'printpresupuestos',
['namespace' => 'App\Controllers\Pdf'], ['namespace' => 'App\Controllers\Pdf'],

View File

@ -312,10 +312,6 @@ class Cliente extends \App\Controllers\BaseResourceController
$onlyActiveOnes = false; $onlyActiveOnes = false;
try{ try{
$menu = $this->model->getSelect2MenuItems($columns2select, $columns2select[1], $onlyActiveOnes, $searchStr); $menu = $this->model->getSelect2MenuItems($columns2select, $columns2select[1], $onlyActiveOnes, $searchStr);
$nonItem = new \stdClass;
$nonItem->id = '';
$nonItem->text = '- ' . lang('Basic.global.None') . ' -';
array_unshift($menu, $nonItem);
} }
catch(Exception $e){ catch(Exception $e){
$menu = []; $menu = [];

View File

@ -238,10 +238,24 @@ class SeriesFacturas extends BaseResourceController
$onlyActiveOnes = false; $onlyActiveOnes = false;
$menu = $this->model->getSelect2MenuItems($columns2select, $columns2select[1], $onlyActiveOnes, $searchStr); $menu = $this->model->getSelect2MenuItems($columns2select, $columns2select[1], $onlyActiveOnes, $searchStr);
$nonItem = new \stdClass; $nonItem = new \stdClass;
$nonItem->id = '';
$nonItem->text = '- ' . lang('Basic.global.None') . ' -'; $newTokenHash = csrf_hash();
array_unshift($menu, $nonItem); $csrfTokenName = csrf_token();
$data = [
'menu' => $menu,
$csrfTokenName => $newTokenHash
];
return $this->respond($data);
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function menuItemsFacturas()
{
if ($this->request->isAJAX()) {
$menu = $this->model->getMenuItemsFacturas();
$newTokenHash = csrf_hash(); $newTokenHash = csrf_hash();
$csrfTokenName = csrf_token(); $csrfTokenName = csrf_token();
$data = [ $data = [

View File

@ -3,6 +3,8 @@
namespace App\Controllers\Facturacion; namespace App\Controllers\Facturacion;
use App\Models\Facturas\FacturaModel; use App\Models\Facturas\FacturaModel;
use App\Entities\Facturas\FacturaEntity;
use App\Models\Clientes\ClienteModel;
use App\Models\Collection; use App\Models\Collection;
@ -22,14 +24,14 @@ class Facturas extends \App\Controllers\BaseResourceController
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger) public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{ {
$this->viewData['pageTitle'] = lang('Pedidos.moduleTitle'); $this->viewData['pageTitle'] = lang('Facturas.facturas');
// Se indica que este controlador trabaja con soft_delete // Se indica que este controlador trabaja con soft_delete
$this->viewData = ['usingServerSideDataTable' => true]; $this->viewData = ['usingServerSideDataTable' => true];
// Breadcrumbs // Breadcrumbs
$this->viewData['breadcrumb'] = [ $this->viewData['breadcrumb'] = [
['title' => lang("App.menu_pedidos"), 'route' => "javascript:void(0);", 'active' => false], ['title' => lang("App.menu_facturas"), 'route' => "javascript:void(0);", 'active' => false],
]; ];
parent::initController($request, $response, $logger); parent::initController($request, $response, $logger);
@ -51,10 +53,9 @@ class Facturas extends \App\Controllers\BaseResourceController
$viewData = [ $viewData = [
'currentModule' => static::$controllerSlug, 'currentModule' => static::$controllerSlug,
'pageSubTitle' => lang('Basic.global.ManageAllRecords', [lang('Pedidos.pedido')]), 'pageSubTitle' => lang('Basic.global.ManageAllRecords', [lang('Facturas.facturas')]),
'usingServerSideDataTable' => true, 'usingServerSideDataTable' => true,
'pageTitle' => lang('Facturas.facturas'), 'pageTitle' => lang('Facturas.facturas'),
'estadoPedidos' => 'todos',
['title' => lang("App.menu_facturas"), 'route' => site_url('facturas/list'), 'active' => true] ['title' => lang("App.menu_facturas"), 'route' => site_url('facturas/list'), 'active' => true]
]; ];
@ -66,6 +67,117 @@ class Facturas extends \App\Controllers\BaseResourceController
return view(static::$viewPath . 'viewFacturasList', $viewData); return view(static::$viewPath . 'viewFacturasList', $viewData);
} }
public function add()
{
if ($this->request->getPost()) :
$nullIfEmpty = true; // !(phpversion() >= '8.1');
$postData = $this->request->getPost();
$noException = true;
$allData = true;
if( !isset($postData['cliente_id']) || !isset($postData['serie_id']) ) {
$this->viewData['errorMessage'] = lang('Facturas.errors.requiredFields');
$this->session->setFlashdata('formErrors', $this->model->errors());
$allData = false;
$noException = false;
}
try {
$clienteModel = model('App\Models\Clientes\ClienteModel');
$datosCliente = $clienteModel->getClienteDataFacturas($postData['cliente_id']);
if(count($datosCliente)>0){
// add array data datosCliente to postData
$postData = array_merge($postData, $datosCliente[0]);
}
} catch (\Exception $e) {
$noException = false;
$this->dealWithException($e);
}
$sanitizedData = $this->sanitized($postData, $nullIfEmpty);
$sanitizedData['user_updated_id'] = auth()->user()->id;
$sanitizedData['user_created_id'] = auth()->user()->id;
if ($allData && $successfulResult = $this->canValidate()) : // if ($successfulResult = $this->validate($this->formValidationRules) ) :
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;
endif;
if ($noException && $successfulResult) :
$id = $this->model->db->insertID();
$message = lang('Basic.global.saveSuccess', [lang('Basic.global.record')]) . '.';
return redirect()->to(route_to('editarFactura', $id))->with('sweet-success', $message);
endif; // $noException && $successfulResult
endif; // ($requestMethod === 'post')
$this->viewData['factura'] = isset($sanitizedData) ? new FacturaEntity($sanitizedData) : new FacturaEntity();
$this->viewData['formAction'] = route_to('createFactura');
$this->viewData['boxTitle'] = lang('Basic.global.addNew') . ' ' . lang('Facturas.facturas') . ' ' . lang('Basic.global.addNewSuffix');
helper('form');
$this->viewData['usingSelect2'] = true;
$validation = \Config\Services::validation();
$this->viewData['validation'] = $validation;
$viewFilePath = static::$viewPath . 'viewAddFactura';
return view($viewFilePath, $this->viewData);
} // end function add()
public function edit($id=null){
if ($id == null) :
return $this->redirect2listView();
endif;
$id = filter_var($id, FILTER_SANITIZE_URL);
$factura = $this->model->find($id);
if ($factura == false) :
$message = lang('Basic.global.notFoundWithIdErr', [mb_strtolower(lang('Facturas.factura')), $id]);
return $this->redirect2listView('sweet-error', $message);
endif;
$this->obtenerDatosFormulario($factura);
$this->viewData['facturaEntity'] = $factura;
$this->viewData['boxTitle'] = lang('Basic.global.edit2') . ' ' . lang('Facturas.factura') . ' ' . lang('Basic.global.edit3');
return $this->displayForm(__METHOD__, $id);
}
public function datatable(){ public function datatable(){
if ($this->request->isAJAX()) { if ($this->request->isAJAX()) {
@ -87,13 +199,118 @@ class Facturas extends \App\Controllers\BaseResourceController
return $this->respond(Collection::datatable( return $this->respond(Collection::datatable(
$resourceData, $resourceData,
$model_linea->getResource("")->countAllResults(), $this->model->getResource("")->countAllResults(),
$model_linea->getResource($search)->countAllResults() $this->model->getResource($search)->countAllResults()
)); ));
} else { } else {
return $this->failUnauthorized('Invalid request', 403); return $this->failUnauthorized('Invalid request', 403);
} }
} }
public function update($id = null){
if ($this->request->isAJAX()) {
$newTokenHash = csrf_hash();
$csrfTokenName = csrf_token();
if ($id == null) :
$data = [
'error' => 2,
$csrfTokenName => $newTokenHash
];
return $this->respond($data);
endif;
$id = filter_var($id, FILTER_SANITIZE_URL);
$facturaEntity = $this->model->find($id);
if ($facturaEntity == false) :
$message = lang('Basic.global.notFoundWithIdErr', [mb_strtolower(lang('Factura.factura')), $id]);
$data = [
'error' => $message,
$csrfTokenName => $newTokenHash
];
return $this->respond($data);
endif;
if ($this->request->getPost()) :
$nullIfEmpty = true; // !(phpversion() >= '8.1');
$postData = $this->request->getPost();
$sanitizedData = $this->sanitized($postData, $nullIfEmpty);
// JJO
$sanitizedData['user_updated_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('Facturas.factura'))]);
$this->session->setFlashdata('formErrors', $this->model->errors());
endif;
$facturaEntity->fill($sanitizedData);
endif;
if ($noException && $successfulResult) :
$id = $facturaEntity->id ?? $id;
$message = lang('Basic.global.updateSuccess', [lang('Basic.global.record')]) . '.';
$data = [
'error' => 0,
$csrfTokenName => $newTokenHash
];
return $this->respond($data);
endif; // $noException && $successfulResult
endif; // ($requestMethod === 'post')
$data = [
'error' => 1,
$csrfTokenName => $newTokenHash
];
return $this->respond($data);
}
else {
return $this->failUnauthorized('Invalid request', 403);
}
}
/*************************************
* FUNCIONES AUXILIARES
************************************/
private function obtenerDatosFormulario(&$factura){
if($factura->estado == 'borrador'){
$serieModel = model('App\Models\Configuracion\SeriesFacturasModel');
$serie = $serieModel->find($factura->serie_id);
if($serie){
$factura->numero = str_replace("{numero}", $serie->next, $serie->formato);
}
}
$clienteModel = model('App\Models\Clientes\ClienteModel');
$cliente = $clienteModel->find($factura->cliente_id);
$factura->cliente_alias = $cliente->alias;
$serieModel = model('App\Models\Configuracion\SeriesFacturasModel');
$serie = $serieModel->find($factura->serie_id);
$factura->serie_nombre = $serie->nombre;
$factura->fecha_factura_at_text = $factura->fecha_factura_at ? date('d/m/Y', strtotime($factura->fecha_factura_at)) : '';
}
} }

View File

@ -0,0 +1,45 @@
<?php
namespace App\Controllers\Facturacion;
use App\Models\Facturas\FacturaLineaModel;
use App\Models\Collection;
class FacturasLineas extends \App\Controllers\BaseResourceController
{
protected $modelName = FacturaLineaModel::class;
protected $format = 'json';
protected static $controllerSlug = 'factura-lineas';
public function datatable($factura_id = null){
if ($this->request->isAJAX() && $factura_id != null) {
$reqData = $this->request->getPost();
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);
return $response;
}
$start = $reqData['start'] ?? 0;
$length = $reqData['length'] ?? 5;
$search = $reqData['search']['value'];
$requestedOrder = $reqData['order']['0']['column'] ?? 0;
//$order = FacturaModel::SORTABLE[$requestedOrder >= 0 ? $requestedOrder : 0];
$dir = $reqData['order']['0']['dir'] ?? 'asc';
$resourceData = $this->model->getResource($factura_id)->orderBy(1, $dir)->limit($length, $start)->get()->getResultObject();
return $this->respond(Collection::datatable(
$resourceData,
$this->model->getResource($factura_id)->countAllResults(),
$this->model->getResource($factura_id)->countAllResults()
));
} else {
return $this->failUnauthorized('Invalid request', 403);
}
}
}

View File

@ -258,6 +258,17 @@ class Pedido extends \App\Controllers\BaseResourceController
} }
} }
public function obtenerPedidosForFacturas(){
if ($this->request->isAJAX()) {
$reqData = $this->request->getPost();
$start = $reqData['start'] ?? 0;
}
else {
return $this->failUnauthorized('Invalid request', 403);
}
}
public function getlineas(){ public function getlineas(){
if ($this->request->isAJAX()) { if ($this->request->isAJAX()) {

View File

@ -43,8 +43,6 @@ class FacturaEntity extends \CodeIgniter\Entity\Entity
'factura_retificativa_id' => 'int', 'factura_retificativa_id' => 'int',
'cliente_id' => 'int', 'cliente_id' => 'int',
'serie_id' => 'int', 'serie_id' => 'int',
'estado' => 'int',
'estado_pago' => 'int',
'base' => 'float', 'base' => 'float',
'total' => 'float', 'total' => 'float',
'pendiente' => 'float', 'pendiente' => 'float',

View File

@ -1,6 +1,7 @@
<?php <?php
return [ return [
'id' => 'ID',
'factura' => 'Invoice', 'factura' => 'Invoice',
'facturaList' => 'Invoice List', 'facturaList' => 'Invoice List',
'facturas' => 'Invoices', 'facturas' => 'Invoices',
@ -54,4 +55,14 @@ return [
'giroDocimiliado' => 'Direct Debit', 'giroDocimiliado' => 'Direct Debit',
'pagare' => 'Promissory Note', 'pagare' => 'Promissory Note',
'transferencia' => 'Transfer', 'transferencia' => 'Transfer',
'datosFactura' => 'Invoice Data',
'addPedidosImpresion' => 'Add Print Orders',
'addPedidosMaquetacion' => 'Add Layout Orders',
'peiddoImpresion' => 'Print Order',
'peiddoMaquetacion' => 'Layout Order',
'nuevaLinea' => 'New Line',
'errors' => [
'requiredFields' => 'Fields marked with * are required',
]
]; ];

View File

@ -89,6 +89,7 @@ return [
'ok' => 'Ok', 'ok' => 'Ok',
'wait' => 'Espere', 'wait' => 'Espere',
'yes' => 'Si', 'yes' => 'Si',
'no' => 'No',
], ],

View File

@ -1,6 +1,7 @@
<?php <?php
return [ return [
'id' => 'ID',
'factura' => 'Factura', 'factura' => 'Factura',
'facturaList' => 'Listado de Facturas', 'facturaList' => 'Listado de Facturas',
'facturas' => 'Facturas', 'facturas' => 'Facturas',
@ -54,4 +55,14 @@ return [
'giroDomiciliado' => 'Giro domiciliado', 'giroDomiciliado' => 'Giro domiciliado',
'pagare' => 'Pagaré', 'pagare' => 'Pagaré',
'transferencia' => 'Transferencia', 'transferencia' => 'Transferencia',
'datosFactura' => 'Datos Factura',
'addPedidosImpresion' => 'Añadir Pedidos Impresión',
'addPedidosMaquetacion' => 'Añadir Pedidos Maquetación',
'pedidoImpresion' => 'Pedido Impresión',
'pedidoMaquetacion' => 'Pedido Maquetación',
'nuevaLinea' => 'Nueva Línea',
'errors' => [
'requiredFields' => 'Los campos marcados con * son obligatorios',
]
]; ];

View File

@ -315,4 +315,21 @@ class ClienteModel extends \App\Models\BaseModel
public function creditoDisponible($cliente_id){ public function creditoDisponible($cliente_id){
return true; return true;
} }
public function getClienteDataFacturas($cliente_id){
$builder = $this->db
->table($this->table . " t1")
->select(
"
t1.nombre AS cliente_nombre, t1.direccion AS cliente_address, t1.cif AS cliente_cif,
t2.nombre AS cliente_pais, t1.cp AS cliente_cp, t1.ciudad AS cliente_ciudad,
t3.nombre AS cliente_provincia, t1.credito_asegurado AS creditoAsegurado"
)
->where("t1.is_deleted", 0)
->where("t1.id", $cliente_id);
$builder->join("lg_paises t2", "t1.pais_id = t2.id", "left");
$builder->join("lg_provincias t3", "t1.provincia_id = t3.id", "left");
return $builder->get()->getResultArray();
}
} }

View File

@ -91,4 +91,21 @@ class SeriesFacturasModel extends \App\Models\BaseModel
->orLike("t1.grupo", $search) ->orLike("t1.grupo", $search)
->groupEnd(); ->groupEnd();
} }
public function getMenuItemsFacturas(){
$resultSorting = $this->getPrimaryKeyName();
$id = 'id AS id';
$text = 'nombre AS text';
$queryBuilder = $this->db->table($this->table);
$queryBuilder->select([$id, $text]);
$queryBuilder->where('tipo', 'facturacion');
$queryBuilder->where('grupo', '1');
$queryBuilder->orderBy($resultSorting);
$result = $queryBuilder->get()->getResult();
return $result;
}
} }

View File

@ -30,4 +30,19 @@ class FacturaLineaModel extends \App\Models\BaseModel {
protected $useSoftDeletes = true; protected $useSoftDeletes = true;
public static $labelField = "id"; public static $labelField = "id";
public function getResource($factura_id)
{
$builder = $this->db
->table($this->table . " t1")
->select(
"t1.id AS id, t1.factura_id AS factura_id,
t1.pedido_impresion_id AS pedido_impresion_id, t1.pedido_maquetacion_id AS pedido_maquetacion_id,
t1.descripcion AS concepto, t1.cantidad as cantidad, t1.precio_unidad AS precio_unidad, t1.iva AS iva,
t1.base AS base, t1.total_iva AS total_iva, t1.total AS total, t1.data AS data,"
)
->where("t1.factura_id", $factura_id);
return $builder;
}
} }

View File

@ -28,7 +28,7 @@ class FacturaModel extends \App\Models\BaseModel {
'pedido_id', 'pedido_id',
'factura_retificada_id', 'factura_retificada_id',
'factura_retificativa_id', 'factura_retificativa_id',
'customer_id', 'cliente_id',
'serie_id', 'serie_id',
'numero', 'numero',
'estado', 'estado',
@ -40,13 +40,13 @@ class FacturaModel extends \App\Models\BaseModel {
'pendiente', 'pendiente',
'total_pagos', 'total_pagos',
'creditoAsegurado', 'creditoAsegurado',
'customer_nombre', 'cliente_nombre',
'customer_address', 'cliente_address',
'customer_cif', 'cliente_cif',
'customer_pais', 'cliente_pais',
'customer_cp', 'cliente_cp',
'customer_ciudad', 'cliente_ciudad',
'customer_provincia', 'cliente_provincia',
'created_at', 'created_at',
'updated_at', 'updated_at',
'deleted_at', 'deleted_at',
@ -69,10 +69,10 @@ class FacturaModel extends \App\Models\BaseModel {
$builder = $this->db $builder = $this->db
->table($this->table . " t1") ->table($this->table . " t1")
->select( ->select(
"t1.id AS id, t1.numero AS numero, t1.fecha_factura_at AS fecha_factura_at, "t1.id AS id, t1.numero AS numero, DATE_FORMAT(t1.fecha_factura_at, '%d/%m/%Y') AS fecha_factura_at,
t2.nombre AS cliente, t1.base AS base, t1.total AS total, t1.pendiente AS pendiente, t2.nombre AS cliente, t1.base AS base, t1.total AS total, t1.pendiente AS pendiente,
t1.creditoAsegurado AS creditoAsegurado, t1.estado AS estado, t1.estado_pago AS estado_pago, t1.creditoAsegurado AS creditoAsegurado, t1.estado AS estado, t1.estado_pago AS estado_pago,
t4.nombre AS forma_pago, t3.fecha_vencimiento_at AS venciemento" t4.nombre AS forma_pago, DATE_FORMAT(t3.fecha_vencimiento_at, '%d/%m/%Y') AS vencimiento"
); );
$builder->join("clientes t2", "t2.id = t1.cliente_id", "left"); $builder->join("clientes t2", "t2.id = t1.cliente_id", "left");

View File

@ -0,0 +1,87 @@
<div class="row px-0">
<div style="padding-left: 0px;" class="col-md-12 col-lg-6 accordion accordion accordion-bordered mt-3 accordion-without-arrow" id="addPedidosImpresion">
<div class="card accordion-item active mt-3 mx-2">
<h3 class="accordion-header" id="headingAddPedidosImpresion">
<button type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordionIcon-1" aria-controls="accordionIcon-1">
<h4><?= lang('Facturas.addPedidosImpresion') ?> </h4>
</button>
</h3>
<div id="accordionAddPedidosImpresionTip" class="accordion show" data-bs-parent="#accordioAddPedidosImpresion">
<div class="accordion-body">
<div class="row">
<div class="col-md-12 col-lg-10">
<select name="pedidoImpresion" id="pedidoImpresion" class="form-select">
</select>
</div>
<div class="col-md-12 col-lg-2">
<button
type="button"
class="btn btn-label-primary float-start me-sm-3 me-1"
name="addNewPedidoImpresion"
id="addNewPedidoImpresion" >
<?= lang("Basic.global.addNew") ?>
</button>
</div>
</div> <!--//row -->
</div> <!--//accordion-body -->
</div> <!--//accordionFechasTip-->
</div> <!--//card-->
</div>
<div style="padding-right: 0px;" class="col-md-12 col-lg-6 accordion accordion accordion-bordered mt-3 accordion-without-arrow" id="addPedidosMaquetacion">
<div class="card accordion-item active mt-3 mx-2">
<h3 class="accordion-header" id="headingAddPedidosMaquetacion">
<button type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordionIcon-1" aria-controls="accordionIcon-1">
<h4><?= lang('Facturas.addPedidosMaquetacion') ?> </h4>
</button>
</h3>
<div id="accordionAddPedidosMaquetacionTip" class="accordion show" data-bs-parent="#accordioAddPedidosMaquetacion">
<div class="accordion-body">
<div class="row">
<div class="col-md-12 col-lg-10">
<select name="pedidoMaquetacion" id="pedidoMaquetacion" class="form-select">
</select>
</div>
<div class="col-md-12 col-lg-2">
<button
type="button"
class="btn btn-label-primary float-start me-sm-3 me-1"
name="addNewPedidoMaquetacion"
id="addNewPedidoMaquetacion" >
<?= lang("Basic.global.addNew") ?>
</button>
</div>
</div> <!--//row -->
</div> <!--//accordion-body -->
</div> <!--//accordionFechasTip-->
</div> <!--//card-->
</div>
</div> <!--//row -->
<?=$this->section('additionalInlineJs') ?>
$('#pedidoImpresion').select2({
placeholder: "<?= lang('Facturas.pedidoImpresion') ?>",
allowClear: true,
width: '100%'
});
$('#pedidoMaquetacion').select2({
placeholder: "<?= lang('Facturas.pedidoMaquetacion') ?>",
allowClear: true,
width: '100%'
});
<?=$this->endSection() ?>

View File

@ -0,0 +1,307 @@
<div class="accordion accordion-bordered mt-3" id="cabeceraFactura">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingFacturas">
<button type="button" class="accordion-button" data-bs-toggle="collapse" data-bs-target="#accordionFacturaTip" aria-expanded="false" aria-controls="accordionFacturaTip">
<h3><?= lang("Facturas.datosFactura") ?></h3>
</button>
</h2>
<div id="accordionFacturaTip" class="accordion-collapse collapse show" data-bs-parent="#accordioFactura">
<div class="accordion-body">
<div class="row">
<h4><?= lang('Facturas.factura') . ': '?> <span style="color:red;"> <?= lang('Facturas.' . $facturaEntity->estado)?> </span></h4>
</div>
<div class="row mb-2">
<div class="col-md-12 col-lg-2 px-4">
<?php if ($facturaEntity->estado === 'borrador') : ?>
<div class="mb-1">
<label for="id" class="form-label">
<?= lang('Facturas.id') ?>
</label>
<input disabled id="id" name="id" tabindex="1" maxLength="11" class="form-control" value="<?= old('id', $facturaEntity->id) ?>" >
</div>
<?php else : ?>
<div class="mb-1">
<label for="id" class="form-label">
<?= lang('Facturas.numeroFactura') ?>
</label>
<input disabled id="numero" name="numero" tabindex="1" maxLength="11" class="form-control" value="<?= old('numero', $facturaEntity->numero) ?>" >
</div>
<?php endif; ?>
</div>
<div class="col-md-12 col-lg-2 px-4">
<div class="mb-1">
<label for="serie_id" class="form-label">
<?= lang('Facturas.serieFacturacion') ?>
</label>
<select id="serie_id" tabindex="2" name="serie_id" class="form-control select2bs2" style="width: 100%;">
<option value="<?= old('serie_id', $facturaEntity->serie_id) ?>" selected><?= old('serie_nombre', $facturaEntity->serie_nombre) ?></option>
</select>
</div>
</div><!--//.mb-3 -->
<div class="col-md-12 col-lg-2 px-4">
<div class="mb-1">
<label for="creditoAsegurado" class="form-label">
<?= lang('Facturas.creditoAsegurado') ?>
</label>
<select id="creditoAsegurado" tabindex="3" name="creditoAsegurado" class="form-control select2bs2" style="width: 100%;">
<option value="0" <?= ($facturaEntity->creditoAsegurado == 0)?'selected':'' ?>> <?= lang('Basic.global.no') ?> </option>
<option value="1" <?= ($facturaEntity->creditoAsegurado == 1)?'selected':'' ?>> <?= lang('Basic.global.yes') ?> </option>
</select>
</div>
</div><!--//.mb-3 -->
<div class="col-md-12 col-lg-2 px-4">
<div class="mb-1">
<label for="fecha_factura_at" class="form-label">
<?= lang('Facturas.fechaFactura') ?>
</label>
<input type="text" value="" tabindex="4" id="fecha_factura_at" name="fecha_factura_at" tabindex="1" maxLength="11" class="form-control" value="<?= old('fecha_factura_at', $facturaEntity->fecha_factura_at) ?>" >
</div>
</div>
<div class="col-md-12 col-lg-4 px-4">
<div class="mb-1">
<label for="cliente_id" class="form-label">
<?= lang('Facturas.cliente') ?>
<div class="btn-group btn-group-sm">
<a href="<?= route_to('editarCliente', $facturaEntity->cliente_id); ?>" target="_blank" ><i class="ti ti-file-search ti-sm btn-edit mx-2" data-id="${data.id}"></i></a>
</div>
</label>
<select id="cliente_id" tabindex="5" name="tabindex="5"" class="form-control select2bs2" style="width: 100%;">
<option value="<?= old('cliente_id', $facturaEntity->cliente_id) ?>" selected><?= old('cliente_alias', $facturaEntity->cliente_alias) ?></option>
</select>
</div>
</div><!--//.mb-3 -->
</div><!--//.row -->
<div class="row mb-2">
<div class="col-md-12 col-lg-6 px-4">
<div class="mb-1">
<label for="cliente_nombre" class="form-label">
<?= lang('Facturas.razonSocial') ?>
</label>
<input id="cliente_nombre" name="cliente_nombre" tabindex="6" class="form-control"" value="<?= old('cliente_nombre', $facturaEntity->cliente_nombre) ?>"></input>
</div>
</div>
<div class="col-md-12 col-lg-3 px-4">
<div class="mb-1">
<label for="cliente_cif" class="form-label">
<?= lang('Facturas.cif') ?>
</label>
<input id="cliente_cif" name="cliente_cif" tabindex="7" class="form-control" value="<?= old('cliente_cif', $facturaEntity->cliente_cif) ?>"></input>
</div>
</div>
<div class="col-md-12 col-lg-3 px-4">
<div class="mb-1">
<label for="cliente_pais" class="form-label">
<?= lang('Facturas.pais') ?>
</label>
<input id="cliente_pais" name="cliente_pais" tabindex="8" class="form-control"" value="<?= old('cliente_pais', $facturaEntity->cliente_pais) ?>"></input>
</div>
</div>
</div><!--//.row -->
<div class="row mb-3">
<div class="col-md-12 col-lg-4 px-4">
<div class="mb-1">
<label for="cliente_direccion" class="form-label">
<?= lang('Facturas.direccion') ?>
</label>
<input id="cliente_direccion" name="cliente_direccion" tabindex="6" class="form-control"" value="<?= old('cliente_address', $facturaEntity->cliente_address) ?>"></input>
</div>
</div>
<div class="col-md-12 col-lg-2 px-4">
<div class="mb-1">
<label for="cliente_cp" class="form-label">
<?= lang('Facturas.cp') ?>
</label>
<input id="cliente_cp" name="cliente_cp" tabindex="7" class="form-control" value="<?= old('cliente_cp', $facturaEntity->cliente_cp) ?>"></input>
</div>
</div>
<div class="col-md-12 col-lg-3 px-4">
<div class="mb-1">
<label for="cliente_ciudad" class="form-label">
<?= lang('Facturas.localidad') ?>
</label>
<input id="cliente_ciudad" name="cliente_ciudad" tabindex="8" class="form-control"" value="<?= old('cliente_ciudad', $facturaEntity->cliente_ciudad) ?>"></input>
</div>
</div>
<div class="col-md-12 col-lg-3 px-4">
<div class="mb-1">
<label for="cliente_provincia" class="form-label">
<?= lang('Facturas.provincia') ?>
</label>
<input id="cliente_provincia" name="cliente_provincia" tabindex="8" class="form-control"" value="<?= old('cliente_provincia', $facturaEntity->cliente_provincia) ?>"></input>
</div>
</div>
</div><!--//.row -->
<div class="row">
<div class="col-md-12 col-lg-12 px-4">
<div class="pt-4">
<?php if ($facturaEntity->estado === 'validada' && (auth()->user()->inGroup('beta') || auth()->user()->inGroup('admin'))) : ?>
<button
type="button"
class="btn btn-label-primary float-start me-sm-3 me-1"
name="pasarBorrador"
id="pasarBorrador" >
<span class="ti-xs ti ti-save me-1"></span>
<?= lang("Facturas.pasarBorrador") ?>
</button>
<?php endif; ?>
<button
type="button"
class="btn btn-label-primary float-start me-sm-3 me-1"
name="exportar_lineas"
id="exportar_lineas" >
<span class="ti-xs ti ti-file-spreadsheet me-1"></span>
<?= lang("Facturas.exportarLineas") ?>
</button>
<button
type="button"
class="btn btn-label-primary float-start me-sm-3 me-1"
name="duplicar"
id="duplicar" >
<span class="ti-xs ti ti-copy me-1"></span>
<?= lang("Facturas.duplicar") ?>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?=$this->section('additionalInlineJs') ?>
$("#fecha_factura_at").flatpickr({
defaultDate: <?= $facturaEntity->fecha_factura_at_text ? "'".$facturaEntity->fecha_factura_at_text."'" : 'null' ?>,
dateFormat: "d/m/Y",
locale: {
firstDayOfWeek: 1,
weekdays: {
shorthand: ['Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa'],
longhand: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
},
months: {
shorthand: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Оct', 'Nov', 'Dic'],
longhand: ['Enero', 'Febreo', 'Мarzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
},
},
onChange: function(selectedDates, dateStr, instance) {
<?php if ($facturaEntity->estado == 'borrador'): ?>
updateDate('fecha_entrega_at', dateStr);
<?php endif; ?>
}
});
function updateDate(elementId, dateStr) {
var id = <?=$facturaEntity->id ?>;
data = {
<?= csrf_token() ?? "token" ?>: <?= csrf_token() ?>v,
};
var parts = dateStr.split('/');
var newFormat = parts[2] + '-' + parts[1] + '-' + parts[0]; // Asume dateStr en formato d/m/Y.
data[elementId] = newFormat;
var url = '<?= route_to('actualizarFactura', ':id') ?>';
url = url.replace(':id', id );
$.ajax({
url: url,
type: 'POST',
data: data,
success: function(response){
if('error' in response){
}
}
});
}
$('#serie_id').select2({
allowClear: false,
minimumResultsForSearch: -1,
ajax: {
url: '<?= route_to("menuItemsOfSeriesFacturas") ?>',
type: 'post',
dataType: 'json',
data: function(params) {
return {
id: 'id',
text: 'nombre',
searchTerm: params.term,
<?= csrf_token() ?? "token" ?> : <?= csrf_token() ?>v
};
},
delay: 60,
processResults: function(response) {
yeniden(response.<?= csrf_token() ?>);
return {
results: response.menu
};
},
cache: true
}
});
$('#cliente_id').select2({
allowClear: false,
ajax: {
url: '<?= route_to("menuItemsOfClientes") ?>',
type: 'post',
dataType: 'json',
data: function(params) {
return {
id: 'id',
text: 'alias',
searchTerm: params.term,
<?= csrf_token() ?? "token" ?> : <?= csrf_token() ?>v
};
},
delay: 60,
processResults: function(response) {
yeniden(response.<?= csrf_token() ?>);
return {
results: response.menu
};
},
cache: true
}
});
<?=$this->endSection() ?>

View File

@ -0,0 +1,164 @@
<div class="accordion accordion-bordered mt-3" id="lineasFactura">
<div class="card accordion-item active">
<h2 class="accordion-header" id="headingLineasFacturas">
<button type="button" class="accordion-button" data-bs-toggle="collapse" data-bs-target="#accordionLineasFacturaTip" aria-expanded="false" aria-controls="accordionLineasFacturaTip">
<h3><?= lang("Facturas.lineas") ?></h3>
</button>
</h2>
<div id="accordionLineasFacturaTip" class="accordion-collapse collapse show" data-bs-parent="#accordioLineasFactura">
<div class="accordion-body">
<table id="tableOfLineasFactura" class="table table-striped table-hover" style="width: 100%;grid-template-columns: 1fr 1fr 6fr 1fr 1fr 1fr;">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th><?= lang('Facturas.unidades') ?></th>
<th><?= lang('Facturas.concepto') ?></th>
<th><?= lang('Facturas.precioUnidad') ?></th>
<th><?= lang('Facturas.iva') ?></th>
<th><?= lang('Facturas.subtotal') ?></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<?php if($facturaEntity->estado =='borrador') : ?>
<div class="row">
<div class="col-md-12 col-lg-2">
<button
type="button"
class="btn btn-label-primary float-start me-sm-3 me-1"
name="addLineaFactura"
id="addLineaFactura" >
<?= lang("Facturas.nuevaLinea") ?>
</button>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?=$this->section('additionalInlineJs') ?>
const actionBtns = function(data) {
return `
<td class="text-right py-0 align-middle">
<div class="btn-group btn-group-sm">
<a href="javascript:void(0);"><i class="ti ti-trash ti-sm btn-edit mx-2" data-id="${data.id}"></i></a>
<a href="javascript:void(0);"><i class="ti ti-pencil ti-sm btn-edit mx-2" data-id="${data.id}"></i></a>
</div>
</td>`;
};
var editor = new $.fn.dataTable.Editor( {
ajax: {
url: "<?= route_to('editorOfLineasFacturas') ?>",
headers: {
<?= csrf_token() ?? "token" ?> : <?= csrf_token() ?>v,
},
},
table : "#tableOfLineasFactura",
idSrc: 'id',
fields: [
{
name: "unidades",
}, {
name: "concepto",
type: "textarea"
}, {
name: "precio_unidad",
}, {
name: "iva",
}, {
"name": "factura_id",
"type": "hidden"
}, {
"name": "pedido_impresion_id",
"type": "hidden"
},{
"name": "pedido_maquetacion_id",
"type": "hidden"
},
]
} );
editor.on( 'preSubmit', function ( e, d, type ) {
if ( type === 'create'){
d.data[0]['factura_id'] = <?= $facturaEntity->id ?>;
}
else if(type === 'edit' ) {
for (v in d.data){
d.data[v]['factura_id'] = <?= $facturaEntity->id ?>;
}
}
});
editor.on( 'postSubmit', function ( e, json, data, action ) {
yeniden(json.<?= csrf_token() ?>);
});
editor.on( 'submitSuccess', function ( e, json, data, action ) {
tableLineas.clearPipeline();
tableLineas.draw();
});
var tableLineas = $('#tableOfLineasFactura').DataTable({
processing: true,
serverSide: true,
autoWidth: true,
responsive: true,
scrollX: true,
columns: [
{data: null, render: actionBtns},
{data: "pedido_impresion_id"},
{data: "pedido_maquetacion_id"},
{data: "unidades"},
{data: "concepto"},
{data: "precio_unidad"},
{data: "iva"},
{data: "subtotal"}
],
order: [[1, "asc"]],
dom: 't',
language: {
url: "/themes/vuexy/vendor/libs/datatables-sk/plugins/i18n/es-ES.json"
},
ajax : $.fn.dataTable.pipeline( {
url: '<?= route_to('dataTableOfLineasFacturas', $facturaEntity->id) ?>',
method: 'POST',
headers: {'X-Requested-With': 'XMLHttpRequest'},
async: true,
}),
columnDefs: [
{
orderable: false,
searchable: false,
targets: [0]
},
{
visible: false,
targets: [1, 2]
}
],
});
<?=$this->endSection() ?>

View File

@ -0,0 +1,118 @@
<?= $this->include('themes/_commonPartialsBs/datatables') ?>
<?= $this->include("themes/_commonPartialsBs/select2bs5") ?>
<?= $this->include("themes/_commonPartialsBs/sweetalert") ?>
<?= $this->include('themes/_commonPartialsBs/_confirm2delete') ?>
<?=$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="addFacturaForm" class="card-body" method="post" action="<?= $formAction ?>">
<?= csrf_field() ?>
<?= view("themes/_commonPartialsBs/_alertBoxes") ?>
<?= !empty($validation->getErrors()) ? $validation->listErrors("bootstrap_style") : "" ?>
<div class="row">
<div class="col-md-12 col-lg-6 px-4">
<div class="mb-3">
<label for="cliente_id" class="form-label">
<?= lang('Presupuestos.clienteId') ?>*
</label>
<select id="cliente_id" name="cliente_id" class="form-control select2bs2" style="width: 100%;"></select>
</div><!--//.mb-3 -->
</div><!--//.col -->
</div><!--//.row -->
<div class="row">
<div class="col-md-12 col-lg-6 px-4">
<div class="mb-3">
<label for="serie_id" class="form-label">
<?= lang('Facturas.serieFacturacion') ?>*
</label>
<select id="serie_id" name="serie_id" class="form-control select2bs2" style="width: 100%;"></select>
</div><!--//.mb-3 -->
</div><!--//.col -->
</div><!--//.row -->
<div class="pt-4">
<input type="submit"
class="btn btn-primary float-start me-sm-3 me-1"
name="save"
value="<?= lang("Basic.global.Save") ?>"
/>
<?= anchor(route_to("tarifaAcabadoList"), lang("Basic.global.Cancel"), ["class" => "btn btn-secondary float-start"]) ?>
</div><!-- /.card-footer -->
</form>
</div><!-- //.card -->
</div><!--//.col -->
</div><!--//.row -->
<?= $this->endSection() ?>
<?= $this->section("additionalInlineJs") ?>
$('#cliente_id').select2({
allowClear: false,
ajax: {
url: '<?= route_to("menuItemsOfClientes") ?>',
type: 'post',
dataType: 'json',
data: function(params) {
return {
id: 'id',
text: 'nombre',
searchTerm: params.term,
<?= csrf_token() ?? "token" ?> : <?= csrf_token() ?>v
};
},
delay: 60,
processResults: function(response) {
yeniden(response.<?= csrf_token() ?>);
return {
results: response.menu
};
},
cache: true
}
});
$('#serie_id').select2({
allowClear: false,
minimumResultsForSearch: -1,
ajax: {
url: '<?= route_to("menuItemsOfSeriesFacturas") ?>',
type: 'post',
dataType: 'json',
data: function(params) {
return {
id: 'id',
text: 'nombre',
searchTerm: params.term,
<?= csrf_token() ?? "token" ?> : <?= csrf_token() ?>v
};
},
delay: 60,
processResults: function(response) {
yeniden(response.<?= csrf_token() ?>);
return {
results: response.menu
};
},
cache: true
}
});
<?= $this->endSection() ?>

View File

@ -0,0 +1,67 @@
<?= $this->include('themes/_commonPartialsBs/datatables') ?>
<?= $this->include("themes/_commonPartialsBs/select2bs5") ?>
<?= $this->include("themes/_commonPartialsBs/sweetalert") ?>
<?= $this->include('themes/_commonPartialsBs/_confirm2delete') ?>
<?=$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="addFacturaForm" class="card-body" method="post" action="<?= $formAction ?>">
<?= csrf_field() ?>
<?= view("themes/_commonPartialsBs/_alertBoxes") ?>
<?= !empty($validation->getErrors()) ? $validation->listErrors("bootstrap_style") : "" ?>
<?= view("themes/vuexy/form/facturas/_facturaCabeceraItems") ?>
<?php if($facturaEntity->estado =='borrador') : ?>
<?= view("themes/vuexy/form/facturas/_addPedidosItems") ?>
<?php endif; ?>
<?= view("themes/vuexy/form/facturas/_facturaLineasItems") ?>
<div class="pt-4">
<input type="submit"
class="btn btn-primary float-start me-sm-3 me-1"
name="save"
value="<?= lang("Basic.global.Save") ?>"
/>
<?= anchor(route_to("tarifaAcabadoList"), lang("Basic.global.Cancel"), ["class" => "btn btn-secondary float-start"]) ?>
</div><!-- /.card-footer -->
</form>
</div><!-- //.card -->
</div><!--//.col -->
</div><!--//.row -->
<?= $this->endSection() ?>
<?= $this->section("additionalInlineJs") ?>
<?= $this->endSection() ?>
<?=$this->section('css') ?>
<link rel="stylesheet" href="<?= site_url("/themes/vuexy/vendor/libs/flatpickr/flatpickr.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/css/sk-datatables.css') ?>">
<link rel="stylesheet" href="<?= site_url('themes/vuexy/css/datatables-editor/editor.dataTables.min.css') ?>">
<?=$this->endSection() ?>
<?= $this->section('additionalExternalJs') ?>
<script src="<?= site_url("/themes/vuexy/vendor/libs/flatpickr/flatpickr.js") ?>"></script>
<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/select/dataTables.select.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/js/datatables-editor/dataTables.editor.min.js') ?>"></script>
<?=$this->endSection() ?>

View File

@ -141,8 +141,7 @@
} }
} }
}, },
{ 'data': 'total_presupuesto' }, { 'data': 'forma_pago',
{ 'data': forma_pago,
render: function(data, type, row, meta) { render: function(data, type, row, meta) {
switch(data){ switch(data){
case "cheque": case "cheque":
@ -176,14 +175,14 @@
} }
} }
}, },
{ 'data': vencimiento }, { 'data': 'vencimiento' },
{ 'data': actionBtns } { 'data': actionBtns }
] ]
}); });
$(document).on('click', '.btn-edit', function(e) { $(document).on('click', '.btn-edit', function(e) {
var url = '<?= route_to('editarPedido', ':id') ?>'; var url = '<?= route_to('editarFactura', ':id') ?>';
url = url.replace(':id', `${$(this).attr('data-id')}` ); url = url.replace(':id', `${$(this).attr('data-id')}` );
window.location.href = url; window.location.href = url;
}); });

View File

@ -12,7 +12,7 @@ if (auth()->user()->inGroup('beta')) {
</a> </a>
<ul class="menu-sub"> <ul class="menu-sub">
<li class="menu-item"> <li class="menu-item">
<a href="<?= route_to('facturasList') ?>" class="menu-link"> <a href="<?= route_to('newFactura') ?>" class="menu-link">
<?= lang("App.menu_facturas_nueva") ?> <?= lang("App.menu_facturas_nueva") ?>
</a> </a>
</li> </li>