diff --git a/pom.xml b/pom.xml index cf69953..d1d4ba6 100644 --- a/pom.xml +++ b/pom.xml @@ -191,6 +191,16 @@ ${project.basedir}/src/main/resources/lib/org.json.jar + + org.apache.xmlgraphics + batik-transcoder + 1.17 + + + org.apache.xmlgraphics + batik-codec + 1.17 + diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java index d04a2d3..7876f73 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.http.CacheControl; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import jakarta.validation.Validator; @@ -151,7 +152,8 @@ public class PresupuestoController { return ResponseEntity.badRequest().body(errores); } Map resultado = new HashMap<>(); - Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); + Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), + locale); resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("lomo", datosInterior.get("lomo")); resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale)); @@ -273,7 +275,8 @@ public class PresupuestoController { } } - Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); + Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), + locale); resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("lomo", datosInterior.get("lomo")); @@ -309,10 +312,11 @@ public class PresupuestoController { presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción } } - Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); + Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), + locale); resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("lomo", datosInterior.get("lomo")); - + return ResponseEntity.ok(resultado); } @@ -335,10 +339,11 @@ public class PresupuestoController { } Map resultado = new HashMap<>(); - Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); + Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), + locale); resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("lomo", datosInterior.get("lomo")); - + return ResponseEntity.ok(resultado); } @@ -650,8 +655,7 @@ public class PresupuestoController { "presupuesto.reimprimir.success.title", "presupuesto.reimprimir.success.text", "presupuesto.reimprimir.error.title", - "presupuesto.reimprimir.error.internal" - ); + "presupuesto.reimprimir.error.internal"); Map translations = translationService.getTranslations(locale, keys); model.addAttribute("languageBundle", translations); @@ -676,7 +680,7 @@ public class PresupuestoController { "presupuesto.add.error.save.title", "presupuesto.iva-reducido", "presupuesto.iva-reducido-descripcion", - "presupuesto.duplicar.title", + "presupuesto.duplicar.title", "presupuesto.duplicar.text", "presupuesto.duplicar.confirm", "presupuesto.duplicar.cancelar", @@ -908,11 +912,11 @@ public class PresupuestoController { return ResponseEntity.badRequest().body(errores); } - try{ + try { Map lomos = presupuestoService.obtenerLomos(presupuesto); presupuesto.setLomo(Double.valueOf(lomos.get("lomoInterior").toString())); presupuesto.setLomoCubierta(Double.valueOf(lomos.get("lomoCubierta").toString())); - }catch(Exception ex){ + } catch (Exception ex) { log.error("Error al obtener lomos desde SK API", ex); } @@ -941,6 +945,30 @@ public class PresupuestoController { } } + @GetMapping("/api/plantilla-cubierta.png") + public ResponseEntity getPlantillaCubierta( + @RequestParam("tipo") String tipoLibro, + @RequestParam("tapa") String tapa, + @RequestParam("ancho") Integer ancho, + @RequestParam("alto") Integer alto, + @RequestParam("lomo") Integer lomo, + @RequestParam("solapas") Integer solapas, + Locale locale) { + + Presupuesto.TipoEncuadernacion tipoEncuadernacion = Presupuesto.TipoEncuadernacion.valueOf(tipoLibro); + Presupuesto.TipoCubierta tipoCubierta = Presupuesto.TipoCubierta.valueOf(tapa); + + byte[] png = presupuestoService.obtenerPlantillaCubierta(tipoEncuadernacion, tipoCubierta, ancho, alto, lomo, + solapas, locale); + if (png == null) + return ResponseEntity.notFound().build(); + + return ResponseEntity.ok() + .cacheControl(CacheControl.noCache()) + .contentType(MediaType.IMAGE_PNG) + .body(png); + } + @PostMapping("/{id}/comentario") @ResponseBody public String actualizarComentario(@PathVariable Long id, @@ -952,18 +980,17 @@ public class PresupuestoController { @PostMapping("/api/duplicar/{id}") @ResponseBody public Map duplicarPresupuesto( - @PathVariable Long id, - @RequestParam(name = "titulo", defaultValue = "") String titulo) { - + @PathVariable Long id, + @RequestParam(name = "titulo", defaultValue = "") String titulo) { + Long entity = presupuestoService.duplicarPresupuesto(id, titulo); return Map.of("id", entity); } - @PostMapping("/api/reimprimir/{id}") @ResponseBody public Map reimprimirPresupuesto(@PathVariable Long id) { - + Long entity = presupuestoService.reimprimirPresupuesto(id); return Map.of("id", entity); } diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java b/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java index b851cf8..fe2fbb3 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java @@ -6,6 +6,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.Locale; @@ -42,6 +44,15 @@ import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatricesReposito import com.imprimelibros.erp.presupuesto.marcapaginas.MarcapaginasRepository; import com.imprimelibros.erp.users.UserDao; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.springframework.core.io.ClassPathResource; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.text.NumberFormat; + import jakarta.servlet.http.HttpServletRequest; import com.imprimelibros.erp.externalApi.skApiClient; @@ -1300,7 +1311,7 @@ public class PresupuestoService { public Map obtenerLomos(Presupuesto presupuesto) { try { Map response = apiClient.getLomos(this.toSkApiRequest(presupuesto)); - + return response; } catch (Exception e) { System.out.println("Error obteniendo lomos: " + e.getMessage()); @@ -1308,6 +1319,65 @@ public class PresupuestoService { } } + public byte[] obtenerPlantillaCubierta( + Presupuesto.TipoEncuadernacion tipoLibro, + Presupuesto.TipoCubierta tapa, + Integer ancho, + Integer alto, + Integer lomo, + Integer solapas, + Locale locale) { + + try { + String plantillaName = "plantilla"; + if (tipoLibro == Presupuesto.TipoEncuadernacion.grapado) { + plantillaName += "-grapado"; + } + if (solapas > 0) { + plantillaName += "-solapas"; + } + plantillaName += ".svg"; + + Integer sangrado = 5; // mm, + if (tapa != Presupuesto.TipoCubierta.tapaBlanda && tipoLibro != Presupuesto.TipoEncuadernacion.grapado) { + sangrado = 20; + } + + Integer ancho_t = lomo + sangrado * 2 + ancho * 2 + solapas * 2; + Integer alto_t = alto + sangrado * 2; + + // 3) Leer SVG template como texto + String basePath = "static/assets/images/imprimelibros/presupuestador/templates-cubierta/"; + String svg = readClasspathText(basePath + plantillaName); + + // 4) Sustituciones {{...}} + Map vars = new HashMap<>(); + NumberFormat nf = NumberFormat.getIntegerInstance(locale); + + vars.put("ancho", nf.format(ancho)); // mm o lo que representes + vars.put("alto", nf.format(alto)); + vars.put("ancho_t", nf.format(ancho_t)); + vars.put("alto_t", nf.format(alto_t)); + vars.put("lomo_t", nf.format(lomo != null ? lomo : 0)); + vars.put("solapa_t", nf.format(solapas != null ? solapas : 0)); + vars.put("sangrado", nf.format(sangrado)); + vars.put("portada", messageSource.getMessage("presupuesto.plantilla-cubierta.portada", null, locale)); + vars.put("contraportada", + messageSource.getMessage("presupuesto.plantilla-cubierta.contraportada", null, locale)); + vars.put("lomo", messageSource.getMessage("presupuesto.plantilla-cubierta.lomo", null, locale)); + vars.put("solapa", messageSource.getMessage("presupuesto.plantilla-cubierta.solapa", null, locale)); + svg = replaceMustache(svg, vars); + + // 5) Render SVG -> PNG (Batik) + svg = sanitizeForBatik(svg); + return svgToPng(svg, /* dpi */ 300f); + + } catch (Exception e) { + System.err.println("Error obteniendo plantilla de cubierta: " + e.getMessage()); + return null; + } + } + /** * PRIVADO (futuro botón "Guardar"): persiste el presupuesto como borrador. */ @@ -1551,4 +1621,77 @@ public class PresupuestoService { return ip; } + private static String readClasspathText(String path) throws IOException { + ClassPathResource res = new ClassPathResource(path); + try (InputStream in = res.getInputStream()) { + return new String(in.readAllBytes(), StandardCharsets.UTF_8); + } + } + + private static String replaceMustache(String svg, Map vars) { + String out = svg; + for (var entry : vars.entrySet()) { + out = out.replace("{{" + entry.getKey() + "}}", entry.getValue()); + } + return out; + } + + private static byte[] svgToPng(String svgXml, float dpi) throws Exception { + PNGTranscoder t = new PNGTranscoder(); + + // Esto SÍ es correcto (convierte unidades a mm según DPI) + t.addTranscodingHint(PNGTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, 25.4f / dpi); + + // ❌ NO uses KEY_AOI con null (provoca tu error) + // t.addTranscodingHint(PNGTranscoder.KEY_AOI, null); + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + TranscoderInput input = new TranscoderInput(new StringReader(svgXml)); + TranscoderOutput output = new TranscoderOutput(out); + t.transcode(input, output); + return out.toByteArray(); + } + } + + private static String sanitizeForBatik(String svg) { + String out = svg; + + // 1) Batik: context-stroke/context-fill + out = out.replace("context-stroke", "#000"); + out = out.replace("context-fill", "none"); + + // 2) Batik: auto-start-reverse + out = out.replace("auto-start-reverse", "auto"); + + // 3) Reemplazar markers Triangle*/marker6* -> Arrow2S* + out = replaceMarkerAttr(out, "marker-start", "Arrow2Sstart"); + out = replaceMarkerAttr(out, "marker-end", "Arrow2Send"); + + // 4) Lo MISMO pero cuando viene dentro de style="...marker-start:url(#X);..." + out = replaceMarkerInStyle(out, "marker-start", "Arrow2Sstart"); + out = replaceMarkerInStyle(out, "marker-end", "Arrow2Send"); + + return out; + } + + private static String replaceMarkerAttr(String svg, String attr, String newId) { + // Soporta: marker-start="url(#Triangle-5)" o marker-start='url( #Triangle-5 )' + Pattern p = Pattern.compile( + "(" + attr + + "\\s*=\\s*)([\"'])\\s*url\\(\\s*#(Triangle[^\\s\\)\"']*|marker6[^\\s\\)\"']*)\\s*\\)\\s*\\2", + Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(svg); + return m.replaceAll("$1$2url(#" + newId + ")$2"); + } + + private static String replaceMarkerInStyle(String svg, String prop, String newId) { + // Soporta dentro de style: marker-start:url(#Triangle-5) (con espacios + // opcionales) + Pattern p = Pattern.compile( + "(" + prop + "\\s*:\\s*)url\\(\\s*#(Triangle[^\\s\\)\"';]*|marker6[^\\s\\)\"';]*)\\s*\\)", + Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(svg); + return m.replaceAll("$1url(#" + newId + ")"); + } + } diff --git a/src/main/resources/i18n/presupuesto_es.properties b/src/main/resources/i18n/presupuesto_es.properties index c98e538..7c5a1de 100644 --- a/src/main/resources/i18n/presupuesto_es.properties +++ b/src/main/resources/i18n/presupuesto_es.properties @@ -117,6 +117,10 @@ presupuesto.continuar-cubierta=Diseño cubierta # Pestaña cubierta presupuesto.plantilla-cubierta=Plantilla de cubierta presupuesto.plantilla-cubierta-text=Recuerde que la cubierta es el conjunto formado por la portada, contraportada, lomo y solapas, en caso de que las lleve.


Si tiene dudas de las medidas puede solicitarnos una plantilla cuando haga el pedido. +presupuesto.plantilla-cubierta.portada=Portada +presupuesto.plantilla-cubierta.contraportada=Contraportada +presupuesto.plantilla-cubierta.lomo=Lomo +presupuesto.plantilla-cubierta.solapa=Solapa presupuesto.tipo-cubierta=Tipo de cubierta presupuesto.tipo-cubierta-descripcion=Seleccione el tipo de cubierta y sus opciones presupuesto.tapa-blanda=Tapa blanda diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado-solapas.svg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado-solapas.svg new file mode 100644 index 0000000..1ca6929 --- /dev/null +++ b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado-solapas.svg @@ -0,0 +1,674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{contraportada}} + + + + {{portada}} + + + + {{solapa}} + + + + + {{solapa}} + + + {{alto}} mm + {{ancho}} mm + {{ancho}} mm + + {{ancho_t}} mm + + {{solapa_t}} mm + + {{solapa_t}} mm + {{alto_t}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + + + + + diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado.svg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado.svg new file mode 100644 index 0000000..872c0c9 --- /dev/null +++ b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado.svg @@ -0,0 +1,502 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{contraportada}} + + + + {{portada}} + + + {{alto}} mm + {{ancho}} mm + {{ancho}} mm + + {{ancho_t}} mm + {{alto_t}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + + + + + diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-solapas.svg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-solapas.svg new file mode 100644 index 0000000..13136e3 --- /dev/null +++ b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-solapas.svg @@ -0,0 +1,748 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{contraportada}} + + + + {{portada}} + + + + {{solapa}} + + + + + {{solapa}} + + + + {{lomo}} + + + {{alto}} mm + {{ancho}} mm + {{ancho}} mm + + {{ancho_t}} mm + {{lomo_t}} mm + + {{solapa_t}} mm + + {{solapa_t}} mm + {{alto_t}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + + diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla.svg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla.svg new file mode 100644 index 0000000..b605f7b --- /dev/null +++ b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla.svg @@ -0,0 +1,576 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{contraportada}} + + + + {{portada}} + + + + {{lomo}} + + + {{alto}} mm + {{ancho}} mm + {{ancho}} mm + + {{ancho_t}} mm + {{lomo_t}} mm + {{alto_t}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + + diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js index 14dc7c3..eb5b5b2 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js @@ -732,7 +732,7 @@ export default class PresupuestoWizard { this.formato.val(option.val()).trigger('change'); } } - + this.formatoPersonalizado.trigger('change'); $('.paginas').trigger('change'); this.ivaReducido.prop('checked', this.formData.datosGenerales.ivaReducido); @@ -968,7 +968,7 @@ export default class PresupuestoWizard { } else { const maxSolapas = data.solapas ?? 120; const lomo = data.lomo ?? 0; - + $('.solapas-presupuesto').attr('max', maxSolapas); $('.max-solapa-text').text(function (_, textoActual) { return textoActual.replace(/\d+/, maxSolapas); @@ -993,16 +993,16 @@ export default class PresupuestoWizard { this.acabadoSobrecubierta.val(this.formData.cubierta.sobrecubierta.acabado); this.fajaSobrecubierta.val(this.formData.cubierta.faja.acabado); - if(lomo < 10){ + if (lomo < 10) { this.formData.cubierta.cabezada = "NOCAB"; this.cabezada.val("NOCAB"); this.cabezada.prop("disabled", true); - if(this.formData.cubierta.tipoCubierta === 'tapaDuraLomoRedondo'){ - this.formData.cubierta.tipoCubierta = 'tapaDura'; + if (this.formData.cubierta.tipoCubierta === 'tapaDuraLomoRedondo') { + this.formData.cubierta.tipoCubierta = 'tapaDura'; } $("#tapaDuraLomoRedondo").addClass("d-none"); } - else{ + else { this.cabezada.prop("disabled", false); $("#tapaDuraLomoRedondo").removeClass("d-none"); } @@ -1144,13 +1144,21 @@ export default class PresupuestoWizard { #initCubierta() { this.btn_plantilla_cubierta.on('click', () => { + let url = `/presupuesto/api/plantilla-cubierta.png + ?tipo=${this.formData.datosGenerales.tipoEncuadernacion} + &tapa=${this.formData.cubierta.tipoCubierta} + &ancho=${this.formData.datosGenerales.ancho} + &alto=${this.formData.datosGenerales.alto} + &lomo=${Math.round(this.formData.lomo) || 0} + &solapas=${this.formData.cubierta.solapasCubierta == 1 ? this.formData.cubierta.tamanioSolapasCubierta : 0}`; + url = url.trim().replace(/\s+/g, ''); Swal.fire({ position: 'top-end', icon: 'info', title: window.languageBundle.get('presupuesto.plantilla-cubierta'), html: `
- +
${window.languageBundle.get('presupuesto.plantilla-cubierta-text')} @@ -1225,7 +1233,7 @@ export default class PresupuestoWizard { const dataToStore = this.#getCubiertaData(); this.#updateCubiertaData(dataToStore); this.#cacheFormData(); - + Summary.updateTapaCubierta(); }); @@ -1443,7 +1451,7 @@ export default class PresupuestoWizard { const tipoCubierta = $('.tapa-cubierta input:checked').val() || 'tapaBlanda'; let solapas = 0; - if(tipoCubierta === 'tapaBlanda'){ + if (tipoCubierta === 'tapaBlanda') { solapas = $('.solapas-cubierta input:checked').val() == 'conSolapas' ? 1 : 0 || 0; } const tamanioSolapasCubierta = $('#tamanio-solapas-cubierta').val() || '80'; @@ -1692,7 +1700,7 @@ export default class PresupuestoWizard { this.divTiradas.append(item.renderCol(this.divTiradas)); } - if(data.lomo_cubierta) { + if (data.lomo_cubierta) { this.formData.lomoCubierta = data.lomo_cubierta; this.#cacheFormData(); } diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/plantilla-cubierta.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/plantilla-cubierta.js new file mode 100644 index 0000000..3e43652 --- /dev/null +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/plantilla-cubierta.js @@ -0,0 +1,33 @@ +$(() => { + $('#btn-plantilla-cubierta').on('click', () => { + let url = `/presupuesto/api/plantilla-cubierta.png + ?tipo=${$('#tipoEncuadernacion').val()} + &tapa=${$('#tipoCubierta').val()} + &ancho=${$('#ancho').val()} + &alto=${$('#alto').val()} + &lomo=${Math.round($('#lomo').val()) || 0} + &solapas=${$('#solapasCubierta').val() == 1 ? $('#tamanioSolapasCubierta').val() : 0}`; + url = url.trim().replace(/\s+/g, ''); + Swal.fire({ + position: 'top-end', + icon: 'info', + title: window.languageBundle.get('presupuesto.plantilla-cubierta'), + html: ` +
+ +
+
+ ${window.languageBundle.get('presupuesto.plantilla-cubierta-text')} +
+ `, + confirmButtonClass: 'btn btn-primary w-xs mt-2', + showConfirmButton: false, + showCloseButton: true, + buttonsStyling: false, + customClass: { + confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar + cancelButton: 'btn btn-light' // clases para cancelar + }, + }); + }); +}); diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen_final.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen_final.html index d64acac..4bcd3c5 100644 --- a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen_final.html +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen_final.html @@ -16,8 +16,19 @@
-
REIMPRESION
+
REIMPRESION
+ +
+
+ +
+
diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html index 0c361fb..638db40 100644 --- a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html @@ -44,18 +44,40 @@ + + + + + + + +
-

Resumen del +

+ Resumen del presupuesto

-
REIMPRESION
+
REIMPRESION
+
+ +
+
+ +
@@ -207,7 +229,7 @@
- +
@@ -234,6 +256,8 @@ + +