mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-24 09:40:21 +00:00
Compare commits
2 Commits
6bea279066
...
4a535ab644
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a535ab644 | |||
| 400251ac3d |
5576
logs/erp.log
5576
logs/erp.log
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
package com.imprimelibros.erp.configurationERP;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/configuracion/variables-sistema")
|
||||
@PreAuthorize("hasRole('SUPERADMIN')")
|
||||
public class VariablesController {
|
||||
|
||||
@GetMapping()
|
||||
public String list(Model model, Locale locale) {
|
||||
return new String();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.imprimelibros.erp.facturacion.service;
|
||||
|
||||
import com.imprimelibros.erp.common.Utils;
|
||||
import com.imprimelibros.erp.configurationERP.VariableService;
|
||||
import com.imprimelibros.erp.facturacion.*;
|
||||
import com.imprimelibros.erp.facturacion.dto.FacturaGuardarDto;
|
||||
import com.imprimelibros.erp.facturacion.dto.FacturaLineaUpsertDto;
|
||||
@ -46,6 +47,7 @@ public class FacturacionService {
|
||||
private final Utils utils;
|
||||
private final MessageSource messageSource;
|
||||
private final PedidoService pedidoService;
|
||||
private final VariableService variableService;
|
||||
|
||||
public FacturacionService(
|
||||
FacturaRepository facturaRepo,
|
||||
@ -56,7 +58,8 @@ public class FacturacionService {
|
||||
UserService userService,
|
||||
Utils utils,
|
||||
MessageSource messageSource,
|
||||
PedidoService pedidoService) {
|
||||
PedidoService pedidoService,
|
||||
VariableService variableService) {
|
||||
this.facturaRepo = facturaRepo;
|
||||
this.lineaFacturaRepository = lineaFacturaRepository;
|
||||
this.serieRepo = serieRepo;
|
||||
@ -66,16 +69,17 @@ public class FacturacionService {
|
||||
this.utils = utils;
|
||||
this.messageSource = messageSource;
|
||||
this.pedidoService = pedidoService;
|
||||
this.variableService = variableService;
|
||||
}
|
||||
|
||||
public SerieFactura getDefaultSerieFactura() {
|
||||
List<SerieFactura> series = serieRepo.findAll();
|
||||
if (series.isEmpty()) {
|
||||
|
||||
Long defaultSerieId = variableService.getValorEntero("serie_facturacion_default").longValue();
|
||||
SerieFactura serie = serieRepo.findById(defaultSerieId).orElse(null);
|
||||
if (serie == null) {
|
||||
throw new IllegalStateException("No hay ninguna serie de facturación configurada.");
|
||||
}
|
||||
// Aquí simplemente devolvemos la primera. Puedes implementar lógica más
|
||||
// compleja si es necesario.
|
||||
return series.get(0);
|
||||
return serie;
|
||||
}
|
||||
|
||||
public Factura getFactura(Long facturaId) {
|
||||
@ -128,6 +132,21 @@ public class FacturacionService {
|
||||
lineaFactura.setFactura(factura);
|
||||
lineasFactura.add(lineaFactura);
|
||||
}
|
||||
if(pedido.getEnvio() > 0){
|
||||
FacturaLinea lineaEnvio = new FacturaLinea();
|
||||
lineaEnvio.setDescripcion(messageSource.getMessage("facturas.lineas.gastos-envio", null, "Gastos de envío", locale));
|
||||
lineaEnvio.setCantidad(1);
|
||||
BigDecimal baseEnvio = BigDecimal.valueOf(pedido.getEnvio()).setScale(2, RoundingMode.HALF_UP);
|
||||
lineaEnvio.setBaseLinea(baseEnvio);
|
||||
BigDecimal iva21Envio = baseEnvio.multiply(BigDecimal.valueOf(0.21)).setScale(2, RoundingMode.HALF_UP);
|
||||
lineaEnvio.setIva21Linea(iva21Envio);
|
||||
lineaEnvio.setIva4Linea(BigDecimal.ZERO);
|
||||
lineaEnvio.setTotalLinea(baseEnvio.add(iva21Envio));
|
||||
lineaEnvio.setCreatedBy(pedido.getCreatedBy());
|
||||
lineaEnvio.setCreatedAt(Instant.now());
|
||||
lineaEnvio.setFactura(factura);
|
||||
lineasFactura.add(lineaEnvio);
|
||||
}
|
||||
factura.setLineas(lineasFactura);
|
||||
|
||||
factura = facturaRepo.save(factura);
|
||||
@ -264,7 +283,7 @@ public class FacturacionService {
|
||||
|
||||
private String buildNumeroFactura(String prefijo, long numero) {
|
||||
String pref = (prefijo == null) ? "" : prefijo.trim();
|
||||
String num = String.format("%07d", numero);
|
||||
String num = String.format("%05d", numero);
|
||||
return pref.isBlank() ? num : (pref + " " + num + "/" + LocalDate.now().getYear());
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,8 @@ public class PedidoLinea {
|
||||
ferro_cliente("pedido.estado.ferro_cliente", 8),
|
||||
produccion("pedido.estado.produccion", 9),
|
||||
terminado("pedido.estado.terminado", 10),
|
||||
cancelado("pedido.estado.cancelado", 11);
|
||||
enviado("pedido.estado.enviado", 11),
|
||||
cancelado("pedido.estado.cancelado", 12);
|
||||
|
||||
private final String messageKey;
|
||||
private final int priority;
|
||||
|
||||
@ -306,7 +306,7 @@ public class PedidoService {
|
||||
}
|
||||
|
||||
if (pedidoLinea.getEstado().getPriority() >= PedidoLinea.Estado.haciendo_ferro.getPriority() &&
|
||||
pedidoLinea.getEstado().getPriority() < PedidoLinea.Estado.terminado.getPriority()) {
|
||||
pedidoLinea.getEstado().getPriority() < PedidoLinea.Estado.enviado.getPriority()) {
|
||||
PedidoLinea.Estado estadoOld = pedidoLinea.getEstado();
|
||||
Map<String, Object> result = skApiClient.checkPedidoEstado(
|
||||
Long.valueOf(pedidoLinea.getPresupuesto().getProveedorRef2().toString()), locale);
|
||||
@ -406,7 +406,7 @@ public class PedidoService {
|
||||
}
|
||||
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
|
||||
for (PedidoLinea linea : lineas) {
|
||||
if (linea.getEstado() != PedidoLinea.Estado.terminado) {
|
||||
if (linea.getEstado() != PedidoLinea.Estado.terminado && linea.getEstado() != PedidoLinea.Estado.enviado) {
|
||||
linea.setEstado(PedidoLinea.Estado.cancelado);
|
||||
pedidoLineaRepository.save(linea);
|
||||
}
|
||||
|
||||
@ -263,7 +263,7 @@ public class PedidosController {
|
||||
linea.put("buttons", buttons);
|
||||
}
|
||||
|
||||
if(pedidoLinea.getEstado() != PedidoLinea.Estado.cancelado && pedidoLinea.getEstado() != PedidoLinea.Estado.terminado) {
|
||||
if(pedidoLinea.getEstado() != PedidoLinea.Estado.cancelado && pedidoLinea.getEstado() != PedidoLinea.Estado.terminado && pedidoLinea.getEstado() != PedidoLinea.Estado.enviado) {
|
||||
showCancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 0024-series-facturacion-seeder
|
||||
author: jjo
|
||||
context: demo
|
||||
changes:
|
||||
|
||||
# --- SERIES ---
|
||||
- sql:
|
||||
splitStatements: true
|
||||
stripComments: true
|
||||
sql: |
|
||||
INSERT INTO series_facturas
|
||||
(nombre_serie, prefijo, tipo, numero_actual, created_at, updated_at, created_by, updated_by)
|
||||
SELECT
|
||||
'IMPRESIÓN DIGITAL', 'IMPR', 'facturacion', 1, NOW(), NOW(), 1, 1
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM series_facturas WHERE prefijo = 'IMPR'
|
||||
);
|
||||
|
||||
INSERT INTO series_facturas
|
||||
(nombre_serie, prefijo, tipo, numero_actual, created_at, updated_at, created_by, updated_by)
|
||||
SELECT
|
||||
'RECT. IMPRESIÓN DIGITAL', 'REC IL', 'facturacion', 1, NOW(), NOW(), 1, 1
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM series_facturas WHERE prefijo = 'REC IL'
|
||||
);
|
||||
|
||||
# --- VARIABLES (con el id real de la serie) ---
|
||||
# serie_facturacion_default -> id de la serie con prefijo IMPR
|
||||
- sql:
|
||||
splitStatements: true
|
||||
stripComments: true
|
||||
sql: |
|
||||
INSERT INTO variables (clave, valor)
|
||||
SELECT
|
||||
'serie_facturacion_default',
|
||||
CAST(sf.id AS CHAR)
|
||||
FROM series_facturas sf
|
||||
WHERE sf.prefijo = 'IMPR'
|
||||
LIMIT 1
|
||||
ON DUPLICATE KEY UPDATE valor = VALUES(valor);
|
||||
|
||||
# sere_facturacion_rect_default -> id de la serie con prefijo REC IL
|
||||
- sql:
|
||||
splitStatements: true
|
||||
stripComments: true
|
||||
sql: |
|
||||
INSERT INTO variables (clave, valor)
|
||||
SELECT
|
||||
'sere_facturacion_rect_default',
|
||||
CAST(sf.id AS CHAR)
|
||||
FROM series_facturas sf
|
||||
WHERE sf.prefijo = 'REC IL'
|
||||
LIMIT 1
|
||||
ON DUPLICATE KEY UPDATE valor = VALUES(valor);
|
||||
|
||||
rollback:
|
||||
- sql:
|
||||
splitStatements: true
|
||||
stripComments: true
|
||||
sql: |
|
||||
DELETE FROM variables
|
||||
WHERE clave IN ('serie_facturacion_default', 'sere_facturacion_rect_default');
|
||||
|
||||
DELETE FROM series_facturas
|
||||
WHERE prefijo IN ('IMPR', 'REC IL');
|
||||
@ -44,4 +44,6 @@ databaseChangeLog:
|
||||
- include:
|
||||
file: db/changelog/changesets/0022-add-estados-pago-to-pedidos-lineas-3.yml
|
||||
- include:
|
||||
file: db/changelog/changesets/0023-facturacion.yml
|
||||
file: db/changelog/changesets/0023-facturacion.yml
|
||||
- include:
|
||||
file: db/changelog/changesets/0024-series-facturacion-seeder.yml
|
||||
@ -45,6 +45,8 @@ facturas.lineas.delete.title=¿Eliminar línea de factura?
|
||||
facturas.lineas.delete.text=Esta acción no se puede deshacer.
|
||||
facturas.lineas.error.base=La base imponible no es válida.
|
||||
|
||||
facturas.lineas.gastos-envio=Gastos de envío
|
||||
|
||||
facturas.direccion.titulo=Dirección de Facturación
|
||||
facturas.direccion.razon-social=Razón Social
|
||||
facturas.direccion.identificacion-fiscal=Identificación Fiscal
|
||||
|
||||
@ -28,6 +28,7 @@ pedido.estado.esperando_aceptacion_ferro=Esperando aceptación de ferro
|
||||
pedido.estado.ferro_cliente=Esperando aprobación de ferro
|
||||
pedido.estado.produccion=Producción
|
||||
pedido.estado.terminado=Terminado
|
||||
pedido.estado.enviado=Enviado
|
||||
pedido.estado.cancelado=Cancelado
|
||||
|
||||
pedido.module-title=Pedidos
|
||||
|
||||
@ -29,18 +29,16 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
|
||||
/* Estos márgenes sustituyen a tu padding grande en .page-content */
|
||||
margin: 15mm 14mm 50mm 14mm; /* bottom grande para el footer */
|
||||
margin: 15mm 14mm 47mm 14mm;
|
||||
|
||||
@bottom-center {
|
||||
content: element(pdfFooter);
|
||||
content: element(footer);
|
||||
/* llamamos al elemento “footer” */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
@ -52,11 +50,12 @@ body {
|
||||
|
||||
.page-content {
|
||||
padding: 0;
|
||||
/* ↑ deja 10mm extra para no pisar el footer */
|
||||
box-sizing: border-box;
|
||||
/* para que el padding no desborde */
|
||||
}
|
||||
|
||||
|
||||
|
||||
body.has-watermark {
|
||||
background-image: none !important;
|
||||
}
|
||||
@ -156,19 +155,22 @@ body.has-watermark {
|
||||
/* Nueva banda verde PRESUPUESTO */
|
||||
.doc-banner {
|
||||
width: 100%;
|
||||
background-color: #92b2a7 !important; /* ← tu verde corporativo */
|
||||
background-color: #92b2a7 !important;
|
||||
/* ← tu verde corporativo */
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 2mm 0;
|
||||
margin-bottom: 4mm;
|
||||
display: block; /* evita conflictos */
|
||||
display: block;
|
||||
/* evita conflictos */
|
||||
}
|
||||
|
||||
.banner-text {
|
||||
font-family: "Open Sans", Arial, sans-serif !important;
|
||||
font-weight: 400;
|
||||
font-size: 20pt;
|
||||
letter-spacing: 8px; /* ← configurable */
|
||||
letter-spacing: 8px;
|
||||
/* ← configurable */
|
||||
}
|
||||
|
||||
|
||||
@ -212,8 +214,10 @@ body.has-watermark {
|
||||
/* Specs 2 columnas */
|
||||
.specs-wrapper {
|
||||
width: 180mm;
|
||||
margin-left: 15mm; /* ← margen izquierdo real del A4 */
|
||||
margin-right: auto; /* opcional */
|
||||
margin-left: 15mm;
|
||||
/* ← margen izquierdo real del A4 */
|
||||
margin-right: auto;
|
||||
/* opcional */
|
||||
color: #5c5c5c;
|
||||
font-size: 9pt;
|
||||
}
|
||||
@ -230,32 +234,44 @@ body.has-watermark {
|
||||
table-layout: fixed;
|
||||
margin-bottom: 6mm;
|
||||
}
|
||||
|
||||
.specs .col {
|
||||
display: table-cell;
|
||||
width: 50%;
|
||||
padding-right: 6mm;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.specs .col:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
/* Listas sin margen superior por defecto */
|
||||
ul, ol {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0rem; /* si quieres algo abajo */
|
||||
padding-left: 1.25rem; /* sangría */
|
||||
ul,
|
||||
ol {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0rem;
|
||||
/* si quieres algo abajo */
|
||||
padding-left: 1.25rem;
|
||||
/* sangría */
|
||||
}
|
||||
|
||||
/* Párrafos con menos margen inferior */
|
||||
p {
|
||||
margin: 0 0 .5rem;
|
||||
p {
|
||||
margin: 0 0 .5rem;
|
||||
}
|
||||
|
||||
/* Si una lista va justo después de un texto o título, que no tenga hueco arriba */
|
||||
p + ul, p + ol,
|
||||
h1 + ul, h2 + ul, h3 + ul, h4 + ul, h5 + ul, h6 + ul,
|
||||
div + ul, div + ol {
|
||||
p+ul,
|
||||
p+ol,
|
||||
h1+ul,
|
||||
h2+ul,
|
||||
h3+ul,
|
||||
h4+ul,
|
||||
h5+ul,
|
||||
h6+ul,
|
||||
div+ul,
|
||||
div+ol {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@ -336,6 +352,20 @@ div + ul, div + ol {
|
||||
|
||||
/* Footer */
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
left: 14mm;
|
||||
right: 14mm;
|
||||
bottom: 18mm;
|
||||
border-top: 1px solid var(--line);
|
||||
padding-top: 4mm;
|
||||
font-size: 7.5pt;
|
||||
color: var(--muted);
|
||||
z-index: 10;
|
||||
/* sobre la marca */
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
.footer .address {
|
||||
display: table-cell;
|
||||
@ -357,70 +387,90 @@ div + ul, div + ol {
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
|
||||
/* Caja a página completa SIN vw/vh y SIN z-index negativo */
|
||||
.watermark {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0; /* ocupa toda la HOJA */
|
||||
pointer-events: none;
|
||||
z-index: 0; /* debajo del contenido */
|
||||
.page-count {
|
||||
margin-top: 2mm;
|
||||
text-align: right;
|
||||
font-size: 9pt;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.watermark img {
|
||||
position: absolute;
|
||||
top: 245mm; /* baja/sube (70–85%) */
|
||||
left: 155mm; /* desplaza a la derecha si quieres */
|
||||
transform: translate(-50%, -50%) rotate(-15deg);
|
||||
width: 60%; /* tamaño grande, ya no hay recorte por márgenes */
|
||||
max-width: none;
|
||||
.page::after {
|
||||
content: counter(page);
|
||||
}
|
||||
|
||||
.pages::after {
|
||||
content: counter(pages);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.items-table {
|
||||
width: 100%;
|
||||
border-color: #92b2a7 ;
|
||||
border-color: #92b2a7;
|
||||
border-collapse: collapse;
|
||||
|
||||
}
|
||||
|
||||
.items-table thead th {
|
||||
background-color: #f3f6f9;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.items-table tbody td {
|
||||
font-size: small;
|
||||
color: #000
|
||||
}
|
||||
|
||||
.items-table td.desc{
|
||||
font-family: "Open Sans" !important;
|
||||
color: #5c5c5c !important;
|
||||
font-size: 9pt !important;
|
||||
line-height: 1.25 !important;
|
||||
}
|
||||
|
||||
/* TODO lo que esté dentro (p, span, ul, li, b, i, etc. del HTML manual) */
|
||||
.items-table td.desc *{
|
||||
font-family: "Open Sans" !important;
|
||||
color: #5c5c5c !important;
|
||||
font-size: 9pt !important;
|
||||
line-height: 1.25 !important;
|
||||
}
|
||||
|
||||
|
||||
.items-table thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
|
||||
.items-table tfoot {
|
||||
display: table-footer-group;
|
||||
}
|
||||
|
||||
.items-table tr,
|
||||
.items-table td,
|
||||
.items-table th {
|
||||
break-inside: avoid;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
|
||||
.page-number {
|
||||
position: absolute;
|
||||
bottom: -3mm;
|
||||
right: 0;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
|
||||
/* Saca el footer fuente fuera del papel (pero sigue existiendo para capturarlo) */
|
||||
.pdf-footer-source {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -200mm; /* cualquier valor grande negativo */
|
||||
width: 100%;
|
||||
.page-number .pn:before {
|
||||
content: counter(page) " / " counter(pages);
|
||||
}
|
||||
|
||||
/* El footer que se captura */
|
||||
#pdf-footer {
|
||||
position: running(pdfFooter);
|
||||
}
|
||||
|
||||
/* Estilo del footer ya dentro del margin-box */
|
||||
.footer {
|
||||
border-top: 1px solid var(--line);
|
||||
padding-top: 4mm;
|
||||
padding-bottom: 6mm; /* aire para que no quede pegado abajo */
|
||||
.pdf-footer-running {
|
||||
position: running(footer);
|
||||
/* lo registra como elemento repetible */
|
||||
font-size: 7.5pt;
|
||||
color: var(--muted);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Numeración */
|
||||
#pdf-footer .page-number {
|
||||
margin-top: 2mm;
|
||||
text-align: right;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
#pdf-footer .page-number .pn::before {
|
||||
content: " " counter(page) "/" counter(pages);
|
||||
width: 100%;
|
||||
border-top: 1px solid var(--line);
|
||||
padding-top: 4mm;
|
||||
/* el resto de tus estilos internos (address, privacy, etc.) */
|
||||
}
|
||||
@ -42,7 +42,7 @@ $(() => {
|
||||
if (estadoSpan.length) {
|
||||
estadoSpan.text(response.state);
|
||||
}
|
||||
if (response.stateKey === 'terminado' || response.stateKey === 'cancelado') {
|
||||
if (response.stateKey === 'enviado' || response.stateKey === 'cancelado') {
|
||||
$(`.update-estado-button[data-linea-id='${lineaId}']`)
|
||||
.closest('.update-estado-button')
|
||||
.addClass('d-none');
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="es">
|
||||
<html xmlns:th="http://www.thymeleaf.org " lang="es">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@ -10,14 +10,13 @@
|
||||
|
||||
<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="pdf-footer-running">
|
||||
<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 -
|
||||
@ -39,10 +38,10 @@
|
||||
</div>
|
||||
|
||||
<div class="page-number">
|
||||
<span th:text="#{pdf.page} ?: 'Página'">Página</span>
|
||||
<span class="pn"></span>
|
||||
<span th:text="#{pdf.page} ?: 'Página'">Página</span><span> </span><span class="pn"></span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -121,15 +120,15 @@
|
||||
<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>
|
||||
<th class="num" th:text="#{pdf.factura.lineas.base}">Base</th>
|
||||
<th class="num" th:text="#{pdf.factura.lineas.iva_4}">I.V.A. 4%</th>
|
||||
<th class="num" th:text="#{pdf.factura.lineas.iva_21}">I.V.A. 21%</th>
|
||||
<th class="num" 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="desc" 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>
|
||||
|
||||
Reference in New Issue
Block a user