añadido generar factura desde pedido

This commit is contained in:
2025-03-23 13:14:46 +01:00
parent fc62ef582a
commit a18207202c
21 changed files with 301 additions and 87 deletions

View File

@ -747,6 +747,7 @@ $routes->group('pedidos', ['namespace' => 'App\Controllers\Pedidos'], function (
$routes->post('getlineas', 'Pedido::getLineas', ['as' => 'tablaLineasPedido']);
$routes->post('cambiarestado', 'Pedido::cambiarEstado', ['as' => 'cambiarEstadoPedido']);
$routes->post('update/(:any)', 'Pedido::update/$1', ['as' => 'actualizarPedido']);
$routes->post('insertfactura', 'Pedido::addFactura');
$routes->get('xml/(:num)', 'Pedido::get_xml_pedido/$1', ['as' => 'getXMLPedido']);
$routes->post('produccion/(:num)', 'Pedido::to_produccion/$1', ['as' => 'toProduccion']);
});

View File

@ -7,6 +7,7 @@ use App\Entities\Facturas\FacturaEntity;
use App\Models\Clientes\ClienteModel;
use App\Models\Collection;
use Hermawan\DataTables\DataTable;
use Exception;
class Facturas extends \App\Controllers\BaseResourceController
{
@ -463,6 +464,37 @@ class Facturas extends \App\Controllers\BaseResourceController
}
}
public function delete($id = null)
{
$user_id = auth()->user()->id;
$datetime = (new \CodeIgniter\I18n\Time("now"));
$rawResult = $this->model->where('id', $id)
->set([
'deleted_at' => $datetime->format('Y-m-d H:i:s'),
'user_updated_id' => $user_id,
])
->update();
if (!$rawResult) {
return $this->failNotFound(lang('Basic.global.deleteError', [$objName]));
}
$modelLineas = model('\App\Models\Facturas\FacturaLineaModel');
$rawResult = $modelLineas->where('factura_id', $id)
->set([
'deleted_at' => $datetime->format('Y-m-d H:i:s'),
'user_updated_id' => $user_id,
])
->update();
$this->model->db->query('DELETE FROM facturas_pedidos_lineas WHERE factura_id=' . $id);
// $message = lang('Basic.global.deleteSuccess', [$objName]); IMN commented
$message = lang('Basic.global.deleteSuccess', [lang('Basic.global.record')]);
$response = $this->respondDeleted(['id' => $id, 'msg' => $message]);
return $response;
}
public function menuPedidosPendientes($cliente_id)
{
@ -608,81 +640,102 @@ class Facturas extends \App\Controllers\BaseResourceController
}
}
public function addLineaPedidoImpresion($factura_id)
public function addLineaPedidoImpresion($factura_id, $data = -1)
{
if ($this->request->isAJAX()) {
$model_pedido_linea = model('\App\Models\Pedidos\PedidoLineaModel');
$model_presupuesto = model('\App\Models\Presupuestos\PresupuestoModel');
$model_factura_linea = model('\App\Models\Facturas\FacturaLineaModel');
try {
if ($this->request) {
if ($this->request->isAJAX())
$pedido_linea_id = $this->request->getPost('lineaPedido') ?? 0;
$linea = $model_pedido_linea->find($pedido_linea_id);
} else {
if ($data == -1) {
return "Error: sin datos";
}
$pedido_linea_id = $data;
}
$factura = $this->model->find($factura_id);
$model_pedido_linea = model('\App\Models\Pedidos\PedidoLineaModel');
$model_presupuesto = model('\App\Models\Presupuestos\PresupuestoModel');
$model_factura_linea = model('\App\Models\Facturas\FacturaLineaModel');
$model_factura = model('\App\Models\Facturas\FacturaModel');
if ($factura) {
try {
if ($linea) {
$presupuesto = $model_presupuesto->find($linea->presupuesto_id);
$linea = $model_pedido_linea->find($pedido_linea_id);
if ($presupuesto) {
// Se añade la linea de factura
$descripcion = $model_presupuesto->generarLineaPedido($presupuesto->id, true, $linea->pedido_id);
$cantidad = intval($presupuesto->tirada) - intval($this->model->getCantidadLineaPedidoFacturada($linea->id));
$base = $cantidad * floatval($presupuesto->total_precio_unidad);
$base = round($base, 2);
$total_iva = $base * ($presupuesto->iva_reducido == 1 ? 0.04 : 0.21);
// se redondea a dos decimales
$total_iva = round($total_iva, 2);
$total = $base + $total_iva;
$factura = $model_factura->find($factura_id);
$data = (object) [
'factura_id' => $factura_id,
'pedido_linea_impresion_id' => $linea->pedido_id,
'descripcion' => $descripcion[0]->concepto,
'cantidad' => $cantidad,
'precio_unidad' => $presupuesto->total_precio_unidad,
'iva' => $presupuesto->iva_reducido == 1 ? 4 : 21,
'base' => $base,
'total_iva' => $total_iva,
'total' => $total,
'user_updated_id' => auth()->user()->id,
if ($factura) {
if ($linea) {
$presupuesto = $model_presupuesto->find($linea->presupuesto_id);
if ($presupuesto) {
// Se añade la linea de factura
$descripcion = $model_presupuesto->generarLineaPedido($presupuesto->id, true, $linea->pedido_id);
$cantidad = intval($presupuesto->tirada) - intval($model_factura->getCantidadLineaPedidoFacturada($linea->id));
$base = $cantidad * floatval($presupuesto->total_precio_unidad);
$base = round($base, 2);
$total_iva = $base * ($presupuesto->iva_reducido == 1 ? 0.04 : 0.21);
// se redondea a dos decimales
$total_iva = round($total_iva, 2);
$total = $base + $total_iva;
$data = (object) [
'factura_id' => $factura_id,
'pedido_linea_impresion_id' => $linea->pedido_id,
'descripcion' => $descripcion[0]->concepto,
'cantidad' => $cantidad,
'precio_unidad' => $presupuesto->total_precio_unidad,
'iva' => $presupuesto->iva_reducido == 1 ? 4 : 21,
'base' => $base,
'total_iva' => $total_iva,
'total' => $total,
'user_updated_id' => auth()->user()->id,
];
$model_factura_linea->insert($data);
$id = $model_factura_linea->getInsertID();
if ($id) {
$model_factura_linea->addFacturaPedidoLinea($factura_id, $linea->id, $cantidad);
$newTokenHash = csrf_hash();
$csrfTokenName = csrf_token();
$data = [
'error' => 0,
'id' => $id,
$csrfTokenName => $newTokenHash
];
$model_factura_linea->insert($data);
$id = $model_factura_linea->getInsertID();
if ($id) {
$model_factura_linea->addFacturaPedidoLinea($factura_id, $linea->id, $cantidad);
$newTokenHash = csrf_hash();
$csrfTokenName = csrf_token();
$data = [
'error' => 0,
'id' => $id,
$csrfTokenName => $newTokenHash
];
return $this->respond($data);
if ($this->request) {
if ($this->request->isAJAX())
return $this->respond($data);
} else {
// remove csrf token
unset($data[$csrfTokenName]);
return $data;
}
}
}
}
} catch (Exception $e) {
}
} catch (Exception $e) {
$newTokenHash = csrf_hash();
$csrfTokenName = csrf_token();
$data = [
'error' => 1,
'error_text' => $e->getMessage(),
$csrfTokenName => $newTokenHash
];
return $this->respond($data);
} else {
return $this->failUnauthorized('Invalid request', 403);
if ($this->request) {
if ($this->request->isAJAX())
return $this->respond($data);
} else {
unset($data[$csrfTokenName]);
return $data;
}
}
}

View File

@ -2,6 +2,7 @@
namespace App\Controllers\Pedidos;
use App\Controllers\BaseController;
use App\Controllers\Facturacion\Facturas;
use App\Entities\Pedidos\PedidoEntity;
use App\Models\Collection;
use App\Models\Pedidos\PedidoModel;
@ -366,6 +367,74 @@ class Pedido extends \App\Controllers\BaseResourceController
}
}
public function addFactura(){
if($this->request->isAJAX()){
$modelFactura = model('App\Models\Facturas\FacturaModel');
$modelFacturaLinea = model('App\Models\Facturas\FacturaLineaModel');
$pedido_id = $this->request->getPost('pedido_id');
$serie_id = $this->request->getPost('serie_id');
$datosFactura = $this->model->obtenerDatosForFactura($pedido_id);
if(count($datosFactura) == 0){
return $this->respond(['status' => 'error', 'message' => 'Error obteniendo datos de factura']);
}
$datosFactura = $datosFactura[0];
$modelFactura->insert([
'cliente_id' => $datosFactura->cliente_id,
'serie_id' => $serie_id,
'estado' => 'borrador',
'estado_pago' => 'pendiente',
'fecha_factura_at' => date('Y-m-d'),
'cliente_nombre' => $datosFactura->cliente_nombre,
'cliente_cif' => $datosFactura->cliente_cif,
'cliente_pais' => $datosFactura->cliente_pais,
'cliente_address' => $datosFactura->cliente_direccion,
'cliente_cp' => $datosFactura->cliente_cp,
'cliente_cuidad' => $datosFactura->cliente_ciudad,
'cliente_provincia' => $datosFactura->cliente_provincia,
'user_created_id' => auth()->user()->id,
'user_updated_id' => auth()->user()->id
]);
$factura_id = $modelFactura->getInsertID();
if($factura_id){
$model_pedido_linea = model('\App\Models\Pedidos\PedidoLineaModel');
$lineas = $model_pedido_linea->where('pedido_id', $pedido_id)->first();
$facturas = new Facturas();
$result = $facturas->addLineaPedidoImpresion($factura_id, $lineas->id);
if($result['error'] == 0){
// Se actualiza el precio total de la factura obtenido de la linea añadida
$linea_added = $modelFacturaLinea->where('factura_id', $factura_id)->first();
$modelFactura->set([
'base' => $linea_added->base,
'total' => $linea_added->total,
'pendiente' => $linea_added->total,
])->where('id', $factura_id)->update();
return $this->respond(['status' => 'success', 'id' => $factura_id, 'message' => lang('Basic.global.success')]);
}else{
return $this->respond(['status' => 'error', 'message' => 'Error insertando lineas de factura']);
}
}
return $this->respond(['status' => 'error', 'message' => 'Error insertando factura']);
}else{
return $this->failUnauthorized('Invalid request', 403);
}
}
private function obtenerDatosFormulario(&$pedidoEntity){
$datos = $this->model->obtenerDatosForm($pedidoEntity->id);

View File

@ -71,6 +71,7 @@ return [
"fechaVencimiento" => "Fecha Vencimiento",
"fechaCobro" => "Fecha Cobro",
"cantidad" => "Cantidad",
"ejemplares" => "Ejemplares",
"addPago" => "Añadir Pago",
"facturaPagada" => "Factura rectificativa ya abonada",

View File

@ -77,6 +77,7 @@ return [
'facturas' => 'Facturas',
'addFactura' => 'Añadir Factura',
'showTotal' => 'Mostrar totales',

View File

@ -49,7 +49,8 @@ class FacturaLineaModel extends \App\Models\BaseModel {
->join("albaranes t5", "t5.pedido_id = t2.pedido_id", "left")
->join("albaranes_lineas t6", "t6.albaran_id = t5.id", "left")
->where("t1.factura_id", $factura_id)
->where("t1.deleted_at", null);
->where("t1.deleted_at", null)
->groupBy('t1.id');
return $builder;
}

View File

@ -29,9 +29,11 @@ class FacturaModel extends \App\Models\BaseModel
const SORTABLE_PEDIDOS = [
1 => "t1.numero",
2 => "t2.nombre",
3 => "t1.estado",
4 => "t1.fecha_factura_at",
5 => "t1.total",
3 => "t4.cantidad",
4 => "t2.nombre",
5 => "t1.estado",
6 => "t1.fecha_factura_at",
7 => "t1.total",
];
// Lista de columnas basada en los campos de la tabla, para asignación masiva
@ -185,6 +187,7 @@ class FacturaModel extends \App\Models\BaseModel
->table($this->table . " t1")
->select(
"t1.id AS id, t1.numero AS numero, t2.nombre AS serie, t1.estado AS estado,
t4.cantidad AS ejemplares,
DATE_FORMAT(t1.fecha_factura_at, '%d/%m/%Y') AS fecha_factura_at, t1.total AS total"
);

View File

@ -76,6 +76,36 @@ class PedidoModel extends \App\Models\BaseModel
return $builder->get()->getResultObject();
}
public function obtenerDatosForFactura($pedido_id = -1){
$builder = $this->db
->table($this->table . " t1")
->select("
t3.cliente_id, t4.nombre as cliente_nombre, t4.cif as cliente_cif, t4.direccion as cliente_direccion,
t5.nombre as cliente_pais, t4.cp as cliente_cp, t4.ciudad as cliente_ciudad, t6.nombre as cliente_provincia
"
)
->join("pedidos_linea t2", "t2.pedido_id = t1.id", "left")
->join("presupuestos t3", "t2.presupuesto_id = t3.id", "left")
->join("clientes t4", "t4.id = t3.cliente_id", "left")
->join("lg_paises t5", "t5.id = t4.pais_id", "left")
->join("lg_provincias t6", "t6.id = t4.provincia_id", "left");
$builder->where("t1.id", $pedido_id);
return $builder->get()->getResultObject();
}
public function addFacturaPedidoLinea($pedido_id, $factura_id, $cantidad)
{
return $this->db
->table("facturas_pedidos_lineas")
->insert([
"factura_id" => $factura_id,
"pedido_id" => $pedido_id,
"cantidad" => $cantidad
]);
}
public function obtenerLineasPedido($pedido_id)
{
$builder = $this->db
@ -91,6 +121,7 @@ class PedidoModel extends \App\Models\BaseModel
foreach ($builder->get()->getResultObject() as $row) {
array_push($lineasPresupuesto, $model_presupuesto->generarLineaPedido($row->presupuesto_id)[0]);
}
$builder->groupBy("t1.id");
return $lineasPresupuesto;
}

View File

@ -120,8 +120,7 @@ $('#addNewPedidoImpresion').on('click', function(){
// se ajustan el ancho de las columnas
$('#pedidoImpresion').val(null).trigger('change');
// Se actualiza la tabla de lineas de factura
tableLineas.clearPipeline();
$('#tableOfLineasFactura').DataTable().columns.adjust().draw();
$('#tableOfLineasFactura').DataTable().clearPipeline().draw();
}
});
});

View File

@ -17,12 +17,12 @@
<th>id</th>
<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>
<th ></th>
<th style="width: 10%;"><?= lang('Facturas.unidades') ?></th>
<th ><?= lang('Facturas.concepto') ?></th>
<th style="width: 10%;"><?= lang('Facturas.precioUnidad') ?></th>
<th style="width: 10%;"><?= lang('Facturas.iva') ?></th>
<th style="width: 10%;"><?= lang('Facturas.subtotal') ?></th>
<th></th>
<th></th>
</tr>
@ -395,6 +395,10 @@ var tableLineas = $('#tableOfLineasFactura').DataTable({
}
],
drawCallback: function() {
$(this.api().table().container()).find('table').css('width', '100%');
this.api().columns.adjust();
},
footerCallback: function (row, data, start, end, display) {
updateFooterLineas(this.api());
}

View File

@ -10,12 +10,19 @@
<div id="accordionFacturasTip" class="accordion-collapse collapse show" data-bs-parent="#accordioFacturas">
<div class="accordion-body">
<?php if ($pedidoEntity->estado == 'finalizado') : ?>
<div class="d-flex justify-content-end">
<button type="button" class="btn btn-primary btn-md" id="add-factura" ><span><?= lang("Pedidos.addFactura") ?><i class="ti ti-receipt-2 ti-xs"></i></span></button>
</div>
<?php endif; ?>
<table id="tableOfFacturas" class="table table-striped table-hover" style="width: 100%;grid-template-columns: 1fr 1fr 6fr 1fr 1fr 1fr;">
<thead>
<tr>
<th style="max-width:60px;"></th>
<th>ID</th>
<th><?= lang('Facturas.numeroFactura') ?></th>
<th><?= lang('Facturas.ejemplares') ?></th>
<th><?= lang('Facturas.serieFacturacion') ?></th>
<th><?= lang('Facturas.estado') ?></th>
<th><?= lang('Facturas.fechaFactura') ?></th>
@ -43,7 +50,7 @@ const actionBtns_facturas = function(data) {
</td>`;
};
tablaFacturasPedido = $('#tableOfFacturas').DataTable({
window.tablaFacturasPedido = $('#tableOfFacturas').DataTable({
processing: true,
serverSide: true,
autoWidth: true,
@ -75,6 +82,7 @@ tablaFacturasPedido = $('#tableOfFacturas').DataTable({
{ 'data': actionBtns_facturas },
{ 'data': 'id' },
{ 'data': 'numero' },
{ 'data': 'ejemplares'},
{ 'data': 'serie' },
{ 'data': 'estado',
render: function(data, type, row, meta) {

View File

@ -96,6 +96,10 @@ var tableOfLineasPedido = new DataTable('#tableOfLineasPedido',{
orderable: false,
},
],
drawCallback: function(){
$(this.api().table().container()).find('table').css('width', '100%');
this.api().columns.adjust();
},
footerCallback: function (row, data, start, end, display) {
let api = this.api();
@ -124,7 +128,7 @@ var tableOfLineasPedido = new DataTable('#tableOfLineasPedido',{
$(document).on('click', '.btn-view', function(e) {
<?php if (auth()->user()->inGroup('admin') || auth()->user()->inGroup('beta')): ?>
<?php if (!auth()->user()->inGroup('cliente-admin') && !auth()->user()->inGroup('cliente-editor')): ?>
var url = '<?= route_to('editarPresupuestoAdmin', ':id') ?>';
<?php else: ?>
var url = '<?= route_to('editarPresupuestoCliente2', ':id') ?>';

View File

@ -70,6 +70,7 @@
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/vendor/libs/sweetalert2/sweetalert2.css') ?>" />
<link rel="stylesheet" href="<?= site_url('themes/vuexy/css/pedidos.css') ?>">
<?= $this->endSection() ?>
@ -89,4 +90,6 @@
<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 type="module" src="<?= site_url("assets/js/safekat/pages/pedidos/pedidos.js") ?>"></script>
<?= $this->endSection() ?>

View File

@ -61,9 +61,12 @@ class Cliente {
document.addEventListener('DOMContentLoaded', function () {
const locale = document.querySelector('meta[name="locale"]').getAttribute('content');
const dropdown = document.querySelector(".dropdown-language");
const activeItem = dropdown.querySelector(".dropdown-menu .dropdown-item");
let locale = 'es';
if (activeItem) {
locale = activeItem.getAttribute("data-language");
}
new Ajax('/translate/getTranslation', { locale: locale, translationFile: ['ClienteContactos', 'ClientePrecios'] }, {},
function(translations) {

View File

@ -147,7 +147,12 @@ class ClienteList {
document.addEventListener('DOMContentLoaded', function () {
const locale = document.querySelector('meta[name="locale"]').getAttribute('content');
const dropdown = document.querySelector(".dropdown-language");
const activeItem = dropdown.querySelector(".dropdown-menu .dropdown-item");
let locale = 'es';
if (activeItem) {
locale = activeItem.getAttribute("data-language");
}
new Ajax('/translate/getTranslation', { locale: locale, translationFile: ['Clientes', 'FormasPago', 'Users'] }, {},
function (translations) {

View File

@ -182,7 +182,12 @@ class MaquinasList {
document.addEventListener('DOMContentLoaded', function () {
const locale = document.querySelector('meta[name="locale"]').getAttribute('content');
const dropdown = document.querySelector(".dropdown-language");
const activeItem = dropdown.querySelector(".dropdown-menu .dropdown-item");
let locale = 'es';
if (activeItem) {
locale = activeItem.getAttribute("data-language");
}
new Ajax('/translate/getTranslation', { locale: locale, translationFile: ['Maquinas'] }, {},
function (translations) {

View File

@ -775,10 +775,13 @@ class PlantillasTarifasClienteForm {
document.addEventListener('DOMContentLoaded', function () {
const locale = document.querySelector('meta[name="locale"]').getAttribute('content');
const dropdown = document.querySelector(".dropdown-language");
const activeItem = dropdown.querySelector(".dropdown-menu .dropdown-item");
let locale = 'es';
if (activeItem) {
locale = activeItem.getAttribute("data-language");
}
new Ajax('/translate/getTranslation', { locale: locale, translationFile: ['ClientePrecios'] }, {},
function (translations) {
window.language = JSON.parse(translations);

View File

@ -152,7 +152,7 @@ class PresupuestoAdminAdd {
datos.papel_formato_alto = this.altoPersonalizado.val()
}
datos.selectedTirada = this.tirada.val();
return datos;
}
@ -161,9 +161,9 @@ class PresupuestoAdminAdd {
const tirada = parseInt($('#tirada').val());
const POD = parseInt($('#POD').val());
let merma = 0;
merma = tirada * 0.1 <= POD ? tirada * 0.1 : POD;
$('#mermacubierta').val(parseInt(merma))
$('#merma').val(parseInt(merma))
}
@ -172,8 +172,13 @@ class PresupuestoAdminAdd {
document.addEventListener('DOMContentLoaded', function () {
const locale = document.querySelector('meta[name="locale"]').getAttribute('content');
const dropdown = document.querySelector(".dropdown-language");
const activeItem = dropdown.querySelector(".dropdown-menu .dropdown-item");
let locale = 'es';
if (activeItem) {
locale = activeItem.getAttribute("data-language");
}
new Ajax('/translate/getTranslation', { locale: locale, translationFile: ['Presupuestos', 'PresupuestosDirecciones'] }, {},
function (translations) {
window.language = JSON.parse(translations);

View File

@ -640,7 +640,12 @@ class PresupuestoAdminEdit {
document.addEventListener('DOMContentLoaded', function () {
const locale = document.querySelector('meta[name="locale"]').getAttribute('content');
const dropdown = document.querySelector(".dropdown-language");
const activeItem = dropdown.querySelector(".dropdown-menu .dropdown-item");
let locale = 'es';
if (activeItem) {
locale = activeItem.getAttribute("data-language");
}
$(document).on("keydown", "textarea", function (event) {
if (event.key === "Enter" && !event.shiftKey) {

View File

@ -992,7 +992,12 @@ document.addEventListener('DOMContentLoaded', function () {
}
}
const locale = document.querySelector('meta[name="locale"]').getAttribute('content');
const dropdown = document.querySelector(".dropdown-language");
const activeItem = dropdown.querySelector(".dropdown-menu .dropdown-item");
let locale = 'es';
if (activeItem) {
locale = activeItem.getAttribute("data-language");
}
new Ajax('/translate/getTranslation', { locale: locale, translationFile: 'Presupuestos' }, {},
initialize,

View File

@ -293,7 +293,12 @@ class Ticket {
document.addEventListener('DOMContentLoaded', function () {
const locale = document.querySelector('meta[name="locale"]').getAttribute('content');
const dropdown = document.querySelector(".dropdown-language");
const activeItem = dropdown.querySelector(".dropdown-menu .dropdown-item");
let locale = 'es';
if (activeItem) {
locale = activeItem.getAttribute("data-language");
}
new Ajax('/translate/getTranslation', { locale: locale, translationFile: ['Tickets', 'datePicker'] }, {},
function (translations) {