terminando pdf de facturas

This commit is contained in:
2026-01-02 21:47:06 +01:00
parent bf823281a5
commit 6bea279066
30 changed files with 7112 additions and 6245 deletions

View File

@ -9,6 +9,8 @@
<link th:href="@{/assets/css/presupuestador.css}" rel="stylesheet"
th:unless="${#authorization.expression('isAuthenticated()')}" />
<link th:href="@{/assets/libs/datatables/dataTables.bootstrap5.min.css}" rel="stylesheet" />
<link sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')"
th:href="@{/assets/libs/quill/quill.snow.css}" rel="stylesheet" type="text/css" />
</th:block>
</head>
@ -68,6 +70,9 @@
<script th:src="@{/assets/libs/datatables/buttons.print.min.js}"></script>
<script th:src="@{/assets/libs/datatables/buttons.colVis.min.js}"></script>
<script sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')"
th:src="@{/assets/libs/quill/quill.min.js}"></script>
<script type="module" th:src="@{/assets/js/pages/imprimelibros/facturas/view.js}"></script>
</th:block>

View File

@ -15,17 +15,17 @@
<div class="row g-3">
<!-- Número -->
<!-- Número (solo lectura siempre, normalmente) -->
<div class="col-md-3">
<label class="form-label" th:text="#{facturas.form.numero-factura}">Número</label>
<input type="text" class="form-control" th:value="${factura.numeroFactura}"
th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
<label class="form-label" th:text="#{facturas.form.numero-factura}">Número de factura</label>
<input id="facturaNumero" type="text" class="form-control" th:value="${factura.numeroFactura}" readonly>
</div>
<!-- Serie -->
<div class="col-md-3">
<label class="form-label" th:text="#{facturas.form.serie}">Serie facturación</label>
<select class="form-control js-select2-factura" data-url="/configuracion/series-facturacion/api/get-series" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
<label class="form-label" th:text="#{facturas.form.serie}">Serie</label>
<select id="facturaSerieId" class="form-control js-select2-factura"
data-url="/configuracion/series-facturacion/api/get-series" th:attr="disabled=${isReadonly}">
<option th:value="${factura.serie != null ? factura.serie.id : ''}"
th:text="${factura.serie != null ? factura.serie.nombreSerie : ''}" selected>
</option>
@ -35,19 +35,20 @@
<!-- Cliente -->
<div class="col-md-6">
<label class="form-label" th:text="#{facturas.form.cliente}">Cliente</label>
<select class="form-control js-select2-factura" data-url="/users/api/get-users" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
<select id="facturaClienteId" class="form-control js-select2-factura" data-url="/users/api/get-users"
th:attr="disabled=${isReadonly}">
<option th:value="${factura.cliente != null ? factura.cliente.id : ''}"
th:text="${factura.cliente != null ? factura.cliente.fullName : ''}" selected>
</option>
</select>
</div>
<!-- Fecha emisión -->
<div class="col-md-3">
<label class="form-label" th:text="#{facturas.form.fecha-emision}">Fecha</label>
<input type="text" class="form-control" th:value="${factura.fechaEmision != null
? #temporals.format(factura.fechaEmision, 'dd/MM/yyyy')
: ''}" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
<input id="facturaFechaEmision" type="text" class="form-control"
th:value="${factura.fechaEmision != null ? #temporals.format(factura.fechaEmision, 'dd/MM/yyyy') : ''}"
th:attr="readonly=${isReadonly}, data-estado=${factura.estado.name()}">
</div>
<!-- Notas -->
@ -67,7 +68,7 @@
<div class="col-md-6">
<label class="form-label" th:text="#{facturas.direccion.razon-social}">Razón Social</label>
<input type="text" class="form-control" th:value="${direccionFacturacion != null
<input type="text" id="dirRazonSocial" class="form-control" th:value="${direccionFacturacion != null
? direccionFacturacion.razonSocial
: ''}" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
</div>
@ -75,7 +76,7 @@
<div class="col-md-6">
<label class="form-label" th:text="#{facturas.direccion.identificacion-fiscal}">Identificacion
Fiscal</label>
<input type="text" class="form-control" th:value="${direccionFacturacion != null
<input type="text" id="dirIdentificacionFiscal" class="form-control" th:value="${direccionFacturacion != null
? direccionFacturacion.identificacionFiscal
: ''}" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
</div>
@ -83,39 +84,39 @@
<div class="col-md-9">
<label class="form-label" th:text="#{facturas.direccion.direccion}">Dirección</label>
<input type="text" class="form-control" th:value="${direccionFacturacion != null
<input type="text" id="dirDireccion" class="form-control" th:value="${direccionFacturacion != null
? direccionFacturacion.direccion
: ''}" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
</div>
<div class="col-md-3">
<label class="form-label" th:text="#{facturas.direccion.codigo-postal}">Código Postal</label>
<input type="text" class="form-control" th:value="${direccionFacturacion != null
<input type="text" id="dirCp" class="form-control" th:value="${direccionFacturacion != null
? direccionFacturacion.cp
: ''}" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
</div>
<div class="col-md-3">
<label class="form-label" th:text="#{facturas.direccion.ciudad}">Ciudad</label>
<input type="text" class="form-control" th:value="${direccionFacturacion != null
<input type="text" id="dirCiudad" class="form-control" th:value="${direccionFacturacion != null
? direccionFacturacion.ciudad
: ''}" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
</div>
<div class="col-md-3">
<label class="form-label" th:text="#{facturas.direccion.provincia}">Provincia</label>
<input type="text" class="form-control" th:value="${direccionFacturacion != null
<input type="text" id="dirProvincia" class="form-control" th:value="${direccionFacturacion != null
? direccionFacturacion.provincia
: ''}" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
</div>
<div class="col-md-3">
<label class="form-label" th:text="#{facturas.direccion.pais}">País</label>
<select class="form-control js-select2-factura" data-url="/api/paises" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
<select id="dirPais" class="form-control js-select2-factura" data-url="/api/paises"
th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
<option th:value="${direccionFacturacion != null
? direccionFacturacion.pais.keyword
: ''}"
th:text="${direccionFacturacion != null
? direccionFacturacion.pais.code3
: ''}" th:text="${direccionFacturacion != null
? #messages.msg('paises.' + direccionFacturacion.pais.keyword)
: ''}" selected>
</option>
@ -124,7 +125,7 @@
<div class="col-md-3">
<label class="form-label" th:text="#{facturas.direccion.telefono}">Teléfono</label>
<input type="text" class="form-control" th:value="${direccionFacturacion != null
<input type="text" id="dirTelefono" class="form-control" th:value="${direccionFacturacion != null
? direccionFacturacion.telefono
: ''}" th:attrappend="readonly=${isReadonly} ? 'readonly' : null">
</div>
@ -133,18 +134,18 @@
<div class="row g-3 mt-4 justify-content-end">
<div class="col-md-12 text-end">
<th:block th:if="${factura.estado.name() == 'borrador'}">
<button type="button" class="btn btn-secondary me-2" id="btn-validar-factura"
th:text="#{facturas.form.btn.validar}">Validar factura</button>
<button type="button" class="btn btn-secondary me-2" id="btn-guardar-factura"
th:text="#{facturas.form.btn.guardar}">Guardar</button>
</th:block>
<th:block th:if="${factura.estado.name() == 'validada'}">
<button type="button" class="btn btn-secondary me-2" id="btn-borrador-factura"
th:text="#{facturas.form.btn.borrador}">Pasar a borrador</button>
</th:block>
<button type="button" class="btn btn-secondary me-2" id="btn-imprimir-factura"
th:text="#{facturas.form.btn.imprimir}">Imprimir factura</button>
<th:block th:if="${factura.estado.name() == 'borrador'}">
<button type="button" class="btn btn-secondary me-2" id="btn-validar-factura"
th:text="#{facturas.form.btn.validar}">Validar factura</button>
<button type="button" class="btn btn-secondary me-2" id="btn-guardar-factura"
th:text="#{facturas.form.btn.guardar}">Guardar</button>
</th:block>
<th:block th:if="${factura.estado.name() == 'validada'}">
<button type="button" class="btn btn-secondary me-2" id="btn-borrador-factura"
th:text="#{facturas.form.btn.borrador}">Pasar a borrador</button>
</th:block>
<button type="button" class="btn btn-secondary me-2" id="btn-imprimir-factura"
th:text="#{facturas.form.btn.imprimir}">Imprimir factura</button>
</div>
</div>
</th:block>

View File

@ -47,7 +47,7 @@
<div id="pagos" class="accordion-collapse collapse show" aria-labelledby="pagosHeader"
data-bs-parent="#pagosFactura">
<div class="accordion-body">
<!-- pagos -->
<div th:replace="~{imprimelibros/facturas/partials/factura-pagos :: factura-pagos (factura=${factura})}"></div>
</div>
</div>
</div>

View File

@ -7,10 +7,11 @@
</button>
</div>
</th:block>
<table class="table table-bordered table-striped table-nowrap w-100">
<table class="table table-bordered table-striped table-wrap w-100">
<thead>
<tr>
<th th:if="${factura.estado != null && factura.estado.name() == 'borrador'}" th:text="#{facturas.lineas.acciones}">Acciones</th>
<th th:if="${factura.estado != null && factura.estado.name() == 'borrador'}"
th:text="#{facturas.lineas.acciones}">Acciones</th>
<th class="w-75" th:text="#{facturas.lineas.descripcion}">Descripción</th>
<th th:text="#{facturas.lineas.base}">Base</th>
<th th:text="#{facturas.lineas.iva_4}">I.V.A. 4%</th>
@ -21,16 +22,26 @@
<tbody>
<tr th:each="lineaFactura : ${factura.lineas}">
<td th:if="${factura.estado != null && factura.estado.name() == 'borrador'}">
<button type="button" class="btn btn-secondary btn-sm me-2"
th:attr="data-linea-id=${lineaFactura.id}" th:text="#{facturas.lineas.acciones.editar}">
<i class="fas fa-edit"></i>
<button type="button" class="btn btn-secondary btn-sm me-2 btn-edit-linea-factura" th:attr="
data-linea-id=${lineaFactura.id},
data-base=${lineaFactura.baseLinea},
data-iva4=${lineaFactura.iva4Linea},
data-iva21=${lineaFactura.iva21Linea}
">
<i class="fas fa-edit me-1"></i>
<span th:text="#{facturas.lineas.acciones.editar}">Editar</span>
</button>
<button type="button" class="btn btn-danger btn-sm"
th:attr="data-linea-id=${lineaFactura.id}" th:text="#{facturas.lineas.acciones.eliminar}">
<button type="button" class="btn btn-danger btn-sm btn-delete-linea-factura" th:attr="data-linea-id=${lineaFactura.id}"
th:text="#{facturas.lineas.acciones.eliminar}">
<i class="fas fa-trash-alt"></i>
</button>
<!-- IMPORTANTE: guardamos el HTML aquí (no en data-*) -->
<textarea class="d-none" th:attr="id=${'linea-desc-' + lineaFactura.id}"
th:text="${lineaFactura.descripcion}"></textarea>
</td>
<td th:utext="${lineaFactura.descripcion}">Descripción de la línea</td>
<td class="text-end" th:text="${#numbers.formatCurrency(lineaFactura.baseLinea)}">0.00</td>
<td class="text-end" th:text="${#numbers.formatCurrency(lineaFactura.iva4Linea)}">0.00</td>
@ -40,20 +51,33 @@
</tbody>
<tfoot>
<tr>
<td class="text-end fw-bold" th:attr="colspan=${factura.estado != null && factura.estado.name() == 'borrador' ? 5 : 4}" th:text="#{facturas.lineas.base}">Base</td>
<td class="text-end fw-bold"
th:attr="colspan=${factura.estado != null && factura.estado.name() == 'borrador' ? 5 : 4}"
th:text="#{facturas.lineas.base}">Base</td>
<td class="text-end" colspan="1" th:text="${#numbers.formatCurrency(factura.baseImponible)}">0.00</td>
</tr>
<tr>
<td class="text-end fw-bold" th:attr="colspan=${factura.estado != null && factura.estado.name() == 'borrador' ? 5 : 4}" th:text="#{facturas.lineas.iva_4}">I.V.A. 4%</td>
<td class="text-end fw-bold"
th:attr="colspan=${factura.estado != null && factura.estado.name() == 'borrador' ? 5 : 4}"
th:text="#{facturas.lineas.iva_4}">I.V.A. 4%</td>
<td class="text-end" colspan="1" th:text="${#numbers.formatCurrency(factura.iva4)}">0.00</td>
</tr>
<tr>
<td class="text-end fw-bold" th:attr="colspan=${factura.estado != null && factura.estado.name() == 'borrador' ? 5 : 4}" th:text="#{facturas.lineas.iva_21}">I.V.A. 21%</td>
<td class="text-end fw-bold"
th:attr="colspan=${factura.estado != null && factura.estado.name() == 'borrador' ? 5 : 4}"
th:text="#{facturas.lineas.iva_21}">I.V.A. 21%</td>
<td class="text-end" colspan="1" th:text="${#numbers.formatCurrency(factura.iva21)}">0.00</td>
</tr>
<tr>
<td class="text-end fw-bold text-uppercase" th:attr="colspan=${factura.estado != null && factura.estado.name() == 'borrador' ? 5 : 4}" th:text="#{facturas.lineas.total}">Total</td>
<td class="text-end fw-bold" colspan="1" th:text="${#numbers.formatCurrency(factura.totalFactura)}">0.00</td>
<td class="text-end fw-bold text-uppercase"
th:attr="colspan=${factura.estado != null && factura.estado.name() == 'borrador' ? 5 : 4}"
th:text="#{facturas.lineas.total}">Total</td>
<td class="text-end fw-bold" colspan="1" th:text="${#numbers.formatCurrency(factura.totalFactura)}">0.00
</td>
</tr>
</table>
<!-- Modal líneas factura (crear/editar) -->
<th:block th:replace="~{imprimelibros/facturas/partials/linea-modal :: linea-modal}"></th:block>
</div>

View File

@ -0,0 +1,69 @@
<!-- imprimelibros/facturas/partials/factura-pagos.html -->
<div th:fragment="factura-pagos (factura)">
<div class="mb-3">
<button type="button" class="btn btn-secondary" id="btn-add-pago-factura">
<i class="fas fa-plus-circle me-2"></i>
<span th:text="#{facturas.pagos.acciones.agregar}">Agregar pago</span>
</button>
</div>
<table class="table table-bordered table-striped table-wrap w-100">
<thead>
<tr>
<th th:text="#{facturas.pagos.acciones}">Acciones</th>
<th th:text="#{facturas.pagos.metodo}">Método</th>
<th th:text="#{facturas.pagos.fecha}">Fecha</th>
<th class="text-center w-50" th:text="#{facturas.pagos.notas}">Notas</th>
<th class="text-end" th:text="#{facturas.pagos.cantidad}">Cantidad</th>
</tr>
</thead>
<tbody>
<tr th:each="pago : ${factura.pagos}" th:if="${pago.deletedAt == null}">
<td>
<button type="button" class="btn btn-secondary btn-sm me-2 btn-edit-pago-factura" th:attr="
data-pago-id=${pago.id},
data-metodo=${pago.metodoPago},
data-cantidad=${pago.cantidadPagada},
data-fecha=${#temporals.format(pago.fechaPago,'yyyy-MM-dd')}">
<i class="fas fa-edit me-1"></i>
<span th:text="#{facturas.pagos.acciones.editar}">Editar</span>
</button>
<button type="button" class="btn btn-danger btn-sm btn-delete-pago-factura"
th:attr="data-pago-id=${pago.id}">
<i class="fas fa-trash-alt"></i>
<span th:text="#{facturas.pagos.acciones.eliminar}">Eliminar</span>
</button>
<!-- notas en HTML (igual que líneas: guardadas en textarea oculto) -->
<textarea class="d-none" th:attr="id=${'pago-notas-' + pago.id}" th:text="${pago.notas}"></textarea>
</td>
<td th:text="${#messages.msg('facturas.pagos.tipo.' + pago.metodoPago.name().toLowerCase())}">
TPV/Tarjeta
</td>
<!-- Formato visual dd/MM/yyyy -->
<td th:text="${#temporals.format(pago.fechaPago,'dd/MM/yyyy')}">01/01/2026 10:00</td>
<td class="text-muted">
<span th:if="${pago.notas == null || #strings.isEmpty(pago.notas)}"></span>
<span th:if="${pago.notas != null && !#strings.isEmpty(pago.notas)}"
th:utext="${pago.notas}"></span>
</td>
<td class="text-end" th:text="${#numbers.formatCurrency(pago.cantidadPagada)}">0,00 €</td>
</tr>
</tbody>
<tfoot>
<tr>
<th colspan="4" class="text-end" th:text="#{facturas.pagos.total_pagado}">Total pagado</th>
<th class="text-end" th:text="${#numbers.formatCurrency(factura.totalPagado)}">0,00 €</th>
</tr>
</tfoot>
</table>
<!-- Modal pagos (crear/editar) -->
<th:block th:replace="~{imprimelibros/facturas/partials/pago-modal :: pago-modal}"></th:block>
</div>

View File

@ -0,0 +1,79 @@
<!-- imprimelibros/facturas/partials/linea-modal.html -->
<div th:fragment="linea-modal">
<div class="modal fade" id="lineaFacturaModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="lineaFacturaModalTitle" th:text="#{facturas.lineas.titulo}">Línea de factura</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
</div>
<div class="modal-body">
<!-- hidden: id de la línea (vacío = nueva) -->
<input type="hidden" id="lineaFacturaId" value=""/>
<!-- Descripción con Quill -->
<div class="mb-3">
<label class="form-label" th:text="#{facturas.lineas.descripcion}">Descripción</label>
<!-- Quill Snow Editor -->
<div id="lineaFacturaDescripcionEditor"
class="snow-editor" style="min-height: 200px;"
data-contenido=""></div>
</div>
<div class="row g-3">
<div class="col-12 col-md-4">
<label for="lineaFacturaBase" class="form-label"
th:text="#{facturas.lineas.base}">Base</label>
<input type="text"
class="form-control text-end"
id="lineaFacturaBase"
inputmode="decimal"
autocomplete="off"
placeholder="0,00">
</div>
<div class="col-12 col-md-4">
<label for="lineaFacturaIva4" class="form-label"
th:text="#{facturas.lineas.iva_4}">I.V.A. 4%</label>
<input type="text"
class="form-control text-end"
id="lineaFacturaIva4"
inputmode="decimal"
autocomplete="off"
placeholder="0,00">
<div class="form-text" th:text="#{facturas.lineas.iva_4.help}">Introduce el importe del I.V.A. (no el %).</div>
</div>
<div class="col-12 col-md-4">
<label for="lineaFacturaIva21" class="form-label"
th:text="#{facturas.lineas.iva_21}">I.V.A. 21%</label>
<input type="text"
class="form-control text-end"
id="lineaFacturaIva21"
inputmode="decimal"
autocomplete="off"
placeholder="0,00">
<div class="form-text" th:text="#{facturas.lineas.iva_21.help}">Introduce el importe del I.V.A. (no el %).</div>
</div>
</div>
<!-- zona errores -->
<div class="alert alert-danger d-none mt-3" id="lineaFacturaModalError"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal" th:text="#{app.cancelar}">Cancelar</button>
<button type="button" class="btn btn-secondary" id="btnGuardarLineaFactura" th:text="#{app.guardar}">
Guardar
</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,66 @@
<!-- imprimelibros/facturas/partials/pago-modal.html -->
<div th:fragment="pago-modal">
<div class="modal fade" id="pagoFacturaModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="pagoFacturaModalTitle" th:text="#{facturas.pagos.titulo}">
Pago de factura
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
</div>
<div class="modal-body">
<input type="hidden" id="pagoFacturaId" value="" />
<div class="row g-3">
<div class="col-12 col-md-4">
<label for="pagoFacturaMetodo" class="form-label"
th:text="#{facturas.pagos.tipo}">Tipo de pago</label>
<select class="form-select" id="pagoFacturaMetodo">
<option value="tpv_tarjeta" th:text="#{facturas.pagos.tipo.tpv_tarjeta}">TPV/Tarjeta
</option>
<option value="tpv_bizum" th:text="#{facturas.pagos.tipo.tpv_bizum}">TPV/Bizum</option>
<option value="transferencia" th:text="#{facturas.pagos.tipo.transferencia}">
Transferencia</option>
<option value="otros" th:text="#{facturas.pagos.tipo.otros}">Otros</option>
</select>
</div>
<div class="col-12 col-md-4">
<label for="pagoFacturaCantidad" class="form-label"
th:text="#{facturas.pagos.cantidad}">Cantidad</label>
<input type="text" class="form-control text-end" id="pagoFacturaCantidad"
inputmode="decimal" autocomplete="off" placeholder="0,00">
</div>
<div class="col-12 col-md-4">
<label for="pagoFacturaFecha" class="form-label" th:text="#{facturas.pagos.fecha}">Fecha de
pago</label>
<input type="text" class="form-control" id="pagoFacturaFecha" autocomplete="off"
placeholder="dd/mm/aaaa hh:mm">
</div>
</div>
<div class="mt-3">
<label class="form-label" th:text="#{facturas.pagos.notas}">Notas</label>
<div id="pagoFacturaNotasEditor" class="snow-editor" style="min-height: 180px;"
data-contenido=""></div>
</div>
<div class="alert alert-danger d-none mt-3" id="pagoFacturaModalError"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal"
th:text="#{app.cancelar}">Cancelar</button>
<button type="button" class="btn btn-secondary" id="btnGuardarPagoFactura" th:text="#{app.guardar}">
Guardar
</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -14,6 +14,7 @@
<th:block th:replace="~{imprimelibros/partials/head-css :: head-css}" />
<link href="/assets/libs/sweetalert2/sweetalert2.min.css" rel="stylesheet" type="text/css" />
<link href="/assets/libs/select2/select2.min.css" rel="stylesheet" />
<link rel="stylesheet" th:href="@{/assets/libs/flatpickr/flatpickr.min.css}">
<th:block layout:fragment="pagecss" />
</head>
@ -38,6 +39,13 @@
<script src="/assets/libs/jquery/jquery-3.6.0.min.js"></script>
<script src="/assets/libs/sweetalert2/sweetalert2.min.js"></script>
<script src="/assets/libs/select2/select2.min.js"></script>
<script defer th:src="@{/assets/libs/flatpickr/flatpickr.min.js}"></script>
<th:block th:with="fpLang=${#locale.language}">
<script defer th:src="@{'/assets/libs/flatpickr/l10n/' + ${fpLang} + '.js'}"
onerror="console.error('No se pudo cargar flatpickr locale:', this.src)">
</script>
</th:block>
<th:block layout:fragment="pagejs" />
<script th:src="@{/assets/js/app.js}"></script>
<script th:src="@{/assets/js/pages/imprimelibros/languageBundle.js}"></script>

View File

@ -0,0 +1,165 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="es">
<head>
<meta charset="UTF-8" />
<title th:text="'Factura ' + ${factura.numeroFactura}">Factura</title>
<link rel="stylesheet" href="assets/css/bootstrap-for-pdf.css" />
<link rel="stylesheet" href="assets/css/facturapdf.css" />
</head>
<body class="has-watermark">
<div class="watermark">
<img src="assets/images/logo-watermark.png" alt="Marca de agua" />
</div>
<!-- PIE -->
<div class="pdf-footer-source">
<div class="footer" id="pdf-footer">
<div class="privacy">
<div class="pv-title" th:text="#{pdf.politica-privacidad}">Política de privacidad</div>
<div class="pv-text" th:text="#{pdf.politica-privacidad.responsable}">Responsable: Impresión Imprime Libros -
CIF:
B04998886 - Teléfono de contacto: 910052574</div>
<div class="pv-text" th:text="#{pdf.politica-privacidad.correo-direccion}">Correo electrónico:
info@imprimelibros.com - Dirección postal: Calle José Picón, Nº 28 Local A, 28028, Madrid</div>
<div class="pv-text" th:text="#{pdf.politica-privacidad.aviso}">
Le comunicamos que los datos que usted nos facilite quedarán incorporados
en nuestro registro interno de actividades de tratamiento con el fin de
llevar a cabo una adecuada gestión fiscal y contable.
Los datos proporcionados se conservarán mientras se mantenga la relación
comercial o durante los años necesarios para cumplir con las obligaciones legales.
Así mismo, los datos no serán cedidos a terceros salvo en aquellos casos en que exista
una obligación legal. Tiene derecho a acceder a sus datos personales, rectificar
los datos inexactos, solicitar su supresión, limitar alguno de los tratamientos
u oponerse a algún uso vía e-mail, personalmente o mediante correo postal.
</div>
</div>
<div class="page-number">
<span th:text="#{pdf.page} ?: 'Página'">Página</span>
<span class="pn"></span>
</div>
</div>
</div>
<div class="page-content">
<!-- HEADER: logo izq + caja empresa dcha -->
<!-- HEADER: logo izq + caja empresa dcha (tabla, sin flex) -->
<table class="il-header">
<tr>
<td class="il-left">
<img src="assets/images/logo-light.png" alt="ImprimeLibros" class="il-logo" />
</td>
<td class="il-right">
<div class="il-company-box">
<span class="corner tl"></span>
<span class="corner tr"></span>
<span class="corner bl"></span>
<span class="corner br"></span>
<div class="company-line company-name" th:text="#{pdf.company.name} ?: 'ImprimeLibros'">
ImprimeLibros ERP</div>
<div class="company-line" th:text="#{pdf.company.address} ?: ''">C/ José Picón, 28 local A</div>
<div class="company-line">
<span th:text="#{pdf.company.postalcode} ?: '28028'">28028</span>
<span th:text="#{pdf.company.city} ?: 'Madrid'">Madrid</span>
</div>
<div class="company-line" th:text="#{pdf.company.phone} ?: '+34 910052574'">+34 910052574</div>
</div>
</td>
</tr>
</table>
<!-- BANDA SUPERIOR -->
<div class="doc-banner">
<div th:text="#{pdf.factura} ?: 'FACTURA'" class="banner-text">FACTURA</div>
</div>
<!-- FICHA Nº / CLIENTE / FECHA -->
<table class="sheet-info">
<tr>
<td class="text-start w-50"><span th:text="#{'pdf.factura.number'}" class="lbl">FACTURA Nº:</span> <span
class="val" th:text="${factura.numeroFactura}">153153</span></td>
<td class="text-end"><span class="lbl" th:text="#{pdf.presupuesto.date}">FECHA:</span> <span class="val"
th:text="${#temporals.format(factura.fechaEmision, 'dd/MM/yyyy')}">10/10/2025</span></td>
</tr>
</table>
<table class="sheet-info">
<tr>
<td class="text-start"><span th:text="#{'pdf.factura.razon-social'}" class="lbl">Razón Social:</span> <span
class="val" th:text="${direccionFacturacion.razonSocial}">153153</span></td>
<td class="text-end"><span th:text="#{'pdf.factura.direccion'}" class="lbl">Dirección:</span>
</td>
</tr>
<tr>
<td class="text-start"><span th:text="#{'pdf.factura.identificacion-fiscal'}" class="lbl">Identificación
Fiscal:</span> <span class="val" th:text="${direccionFacturacion.identificacionFiscal}">153153</span></td>
<td class="text-end">
<span class="val"
th:text="${direccionFacturacion.direccion + ', ' + direccionFacturacion.cp + ', ' + direccionFacturacion.ciudad}">153153</span>
</td>
</tr>
<tr>
<td class="text-start">
</td>
<td class="text-end">
<span class="val"
th:text="${direccionFacturacion.provincia + ', ' + #messages.msg('paises.' + direccionFacturacion.pais.keyword)}">153153</span>
</td>
</tr>
</table>
<!-- DATOS TÉCNICOS -->
<table class="items-table table table-bordered table-striped table-wrap w-100">
<thead>
<tr>
<th class="w-75" th:text="#{pdf.factura.lineas.descripcion}">Descripción</th>
<th th:text="#{pdf.factura.lineas.base}">Base</th>
<th th:text="#{pdf.factura.lineas.iva_4}">I.V.A. 4%</th>
<th th:text="#{pdf.factura.lineas.iva_21}">I.V.A. 21%</th>
<th th:text="#{pdf.factura.lineas.total}">Total</th>
</tr>
</thead>
<tbody>
<tr th:each="lineaFactura : ${factura.lineas}">
<td th:utext="${lineaFactura.descripcion}">Descripción de la línea</td>
<td class="text-end" th:text="${#numbers.formatCurrency(lineaFactura.baseLinea)}">0.00</td>
<td class="text-end" th:text="${#numbers.formatCurrency(lineaFactura.iva4Linea)}">0.00</td>
<td class="text-end" th:text="${#numbers.formatCurrency(lineaFactura.iva21Linea)}">0.00</td>
<td class="text-end" th:text="${#numbers.formatCurrency(lineaFactura.totalLinea)}">0.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<td class="text-end fw-bold" colspan="4" th:text="#{pdf.factura.lineas.base}">Base</td>
<td class="text-end" colspan="1" th:text="${#numbers.formatCurrency(factura.baseImponible)}">0.00</td>
</tr>
<tr>
<td class="text-end fw-bold" colspan="4" th:text="#{pdf.factura.lineas.iva_4}">I.V.A. 4%</td>
<td class="text-end" colspan="1" th:text="${#numbers.formatCurrency(factura.iva4)}">0.00</td>
</tr>
<tr>
<td class="text-end fw-bold" colspan="4" th:text="#{pdf.factura.lineas.iva_21}">I.V.A. 21%</td>
<td class="text-end" colspan="1" th:text="${#numbers.formatCurrency(factura.iva21)}">0.00</td>
</tr>
<tr>
<td class="text-end fw-bold text-uppercase" colspan="4" th:text="#{pdf.factura.lineas.total}">Total</td>
<td class="text-end fw-bold" colspan="1" th:text="${#numbers.formatCurrency(factura.totalFactura)}">0.00
</td>
</tr>
</tfoot>
</table>
</div>
</body>
</html>