tengo el texto del resumen final

This commit is contained in:
Jaime Jiménez
2025-09-23 13:25:06 +02:00
parent 479cecf52b
commit 85681b4d6e
13 changed files with 360 additions and 74 deletions

View File

@ -0,0 +1,13 @@
package com.imprimelibros.erp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebConfig {
@Bean
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
}

View File

@ -4,6 +4,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoImpresion;
import java.util.*;
@Component
@ -19,4 +23,16 @@ public class TranslationService {
}
return translations;
}
public String label(TipoEncuadernacion t, Locale locale) {
return messageSource.getMessage(t.getMessageKey(), null, locale);
}
public String label(TipoImpresion t, Locale locale) {
return messageSource.getMessage(t.getMessageKey(), null, locale);
}
public String label(TipoCubierta t, Locale locale) {
return messageSource.getMessage(t.getMessageKey(), null, locale);
}
}

View File

@ -19,15 +19,54 @@ import jakarta.persistence.*;
public class Presupuesto implements Cloneable{
public enum TipoEncuadernacion {
fresado, cosido, grapado, espiral, wireo
fresado("presupuesto.fresado"),
cosido("presupuesto.cosido"),
grapado("presupuesto.grapado"),
espiral("presupuesto.espiral"),
wireo("presupuesto.wireo");
private final String messageKey;
TipoEncuadernacion(String messageKey) {
this.messageKey = messageKey;
}
public String getMessageKey() {
return messageKey;
}
}
public enum TipoImpresion {
negro, negrohq, color, colorhq
negro("presupuesto.blanco-negro"),
negrohq("presupuesto.blanco-negro-premium"),
color("presupuesto.color"),
colorhq("presupuesto.color-premium");
private final String messageKey;
TipoImpresion(String messageKey) {
this.messageKey = messageKey;
}
public String getMessageKey() {
return messageKey;
}
}
public enum TipoCubierta {
tapaBlanda, tapaDura, tapaDuraLomoRedondo
tapaBlanda("presupuesto.tapa-blanda"),
tapaDura("presupuesto.tapa-dura"),
tapaDuraLomoRedondo("presupuesto.tapa-dura-lomo-redondo");
private final String messageKey;
TipoCubierta(String messageKey) {
this.messageKey = messageKey;
}
public String getMessageKey() {
return messageKey;
}
}
@Override
@ -44,6 +83,7 @@ public class Presupuesto implements Cloneable{
private Long id;
@NotNull(message = "{presupuesto.errores.tipo-encuadernacion}", groups = PresupuestoValidationGroups.DatosGenerales.class)
@Enumerated(EnumType.STRING)
@Column(name = "tipo_encuadernacion")
private TipoEncuadernacion tipoEncuadernacion = TipoEncuadernacion.fresado;
@ -104,6 +144,7 @@ public class Presupuesto implements Cloneable{
private Integer paginasColorTotal;
@NotNull(message = "{presupuesto.errores.tipo-impresion}", groups = PresupuestoValidationGroups.Interior.class)
@Enumerated(EnumType.STRING)
@Column(name = "tipo_impresion")
private TipoImpresion tipoImpresion = TipoImpresion.negro;
@ -116,6 +157,7 @@ public class Presupuesto implements Cloneable{
private Integer gramajeInterior;
@NotNull(message = "{presupuesto.errores.tipo-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class)
@Enumerated(EnumType.STRING)
@Column(name = "tipo_cubierta")
private TipoCubierta tipoCubierta = TipoCubierta.tapaBlanda;

View File

@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.core.JsonProcessingException;
@ -376,14 +377,31 @@ public class PresupuestoController {
}
// Se hace un post para no tener problemas con la longitud de la URL
@PostMapping("/public/getresumen")
public ResponseEntity<?> getResumen(@ModelAttribute("presupuesto") Presupuesto presupuesto,
@RequestParam String summary, Locale locale) throws JsonProcessingException {
Map<String, Object> summaryMap = new ObjectMapper().readValue(summary,
new TypeReference<Map<String, Object>>() {
});
return ResponseEntity.ok(presupuestoService.getResumen(presupuesto, summaryMap, locale));
@PostMapping("/public/resumen")
public ResponseEntity<?> getResumen(@RequestBody PresupuestoRequest req, Locale locale) {
Presupuesto p = req.getPresupuesto();
String[] servicios = req.getServicios() != null ? req.getServicios() : new String[0];
return ResponseEntity.ok(presupuestoService.getResumen(p, servicios, locale));
}
public static class PresupuestoRequest {
private Presupuesto presupuesto;
private String[] servicios;
public Presupuesto getPresupuesto() {
return presupuesto;
}
public void setPresupuesto(Presupuesto p) {
this.presupuesto = p;
}
public String[] getServicios() {
return servicios;
}
public void setServicios(String[] servicios) {
this.servicios = servicios;
}
}
}

View File

@ -25,6 +25,7 @@ import com.imprimelibros.erp.configurationERP.VariableService;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
import com.imprimelibros.erp.presupuesto.classes.PresupuestadorItems;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter;
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPrecios;
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPreciosRepository;
import com.imprimelibros.erp.presupuesto.marcapaginas.Marcapaginas;
@ -56,9 +57,11 @@ public class PresupuestoService {
protected MarcapaginasRepository marcapaginasRepository;
private final PresupuestadorItems presupuestadorItems;
private final PresupuestoFormatter presupuestoFormatter;
public PresupuestoService(PresupuestadorItems presupuestadorItems) {
public PresupuestoService(PresupuestadorItems presupuestadorItems, PresupuestoFormatter presupuestoFormatter) {
this.presupuestadorItems = presupuestadorItems;
this.presupuestoFormatter = presupuestoFormatter;
}
public boolean validateDatosGenerales(int[] tiradas) {
@ -301,15 +304,6 @@ public class PresupuestoService {
"cabezada", presupuesto.getCabezada(),
"lomoRedondo", presupuesto.getTipoCubierta() == TipoCubierta.tapaDuraLomoRedondo ? 1 : 0);
/*
* Map<String, Object> servicios = Map.of(
* "retractilado", 0,
* "retractilado5", 0,
* "ferro", 0,
* "ferroDigital", 0,
* "marcapaginas", 0,
* "prototipo", 0);
*/
Map<String, Object> body = new HashMap<>();
body.put("tipo_impresion_id", this.getTipoImpresionId(presupuesto));
body.put("tirada", Arrays.stream(presupuesto.getTiradas())
@ -781,22 +775,13 @@ public class PresupuestoService {
return out;
}
public Map<String, Object> getResumen(Presupuesto presupuesto, Map<String, Object> summary, Locale locale) {
public Map<String, Object> getResumen(Presupuesto presupuesto, String[] servicios, Locale locale) {
Map<String, Object> resumen = new HashMap<>();
resumen.put("titulo", presupuesto.getTitulo());
resumen.put("texto", presupuestoFormatter.resumen(presupuesto, servicios, locale));
/*
* Integer tirada = presupuesto.getSelectedTirada() != null ?
* presupuesto.getSelectedTirada() : 0;
* if (Arrays.asList(servicios).contains("deposito-legal")) {
* tirada += 4;
* }
*/
String textoResumen = messageSource.getMessage("presupuesto.resumen-texto", null, locale);
textoResumen = textoResumen.replace("{tipoEncuadernacion}", summary.get("encuadernacion").toString());
resumen.put("resumen", textoResumen);
return resumen;
}
}

View File

@ -10,6 +10,8 @@ import java.util.Map;
@Component
public class PresupuestadorItems {
@Autowired
private MessageSource messageSource;

View File

@ -0,0 +1,33 @@
package com.imprimelibros.erp.presupuesto.classes;
import java.util.Locale;
import java.util.Map;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
@Component
public class PresupuestoAcabados {
private final MessageSource messageSource;
public PresupuestoAcabados(MessageSource messageSource) {
this.messageSource = messageSource;
}
private static final Map<Integer, String> ACABADO_KEYS = Map.of(
0, "presupuesto.acabado-ninguno",
1, "presupuesto.acabado-plastificado-brillo-1c",
5, "presupuesto.acabado-plastificado-mate-1c",
8, "presupuesto.acabado-plastificado-mate-1c-antirrayado",
2, "presupuesto.acabado-plastificado-mate-uvi",
3, "presupuesto.acabado-plastificado-mate-uvi3d",
4, "presupuesto.acabado-plastificado-mate-uvi-braile",
9, "presupuesto.acabado-plastificado-sandy-1c"
);
public String labelAcabado(int id, Locale locale) {
String key = ACABADO_KEYS.get(id);
return key != null ? messageSource.getMessage(key, null, locale) : String.valueOf(id);
}
}

View File

@ -0,0 +1,135 @@
package com.imprimelibros.erp.presupuesto.classes;
import org.springframework.stereotype.Component;
import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.presupuesto.Presupuesto;
import org.springframework.context.MessageSource;
import java.util.Arrays;
import java.util.Locale;
@Component
public class PresupuestoFormatter {
private final TranslationService translationService;
private final MessageSource ms;
private final PresupuestoPapeles papeles;
private final PresupuestoAcabados acabados;
public PresupuestoFormatter(
TranslationService translationService,
MessageSource ms,
PresupuestoPapeles papeles,
PresupuestoAcabados acabados) {
this.translationService = translationService;
this.ms = ms;
this.papeles = papeles;
this.acabados = acabados;
}
public String resumen(Presupuesto p, String[] servicios, Locale locale) {
String encuadernacion = translationService.label(p.getTipoEncuadernacion(), locale);
String tipoImpresion = translationService.label(p.getTipoImpresion(), locale);
String tapaCubierta = translationService.label(p.getTipoCubierta(), locale);
Object[] args = {
Arrays.asList(servicios).contains("deposito-legal") ? p.getSelectedTirada() + 4 : p.getSelectedTirada(),
encuadernacion,
tipoImpresion,
(p.getPaginasColorTotal() != null ? p.getPaginasColorTotal() : p.getPaginasColor()) + p.getPaginasNegro(),
p.getAncho(), p.getAlto(),
papeles.labelPapel(p.getPapelInteriorId(), locale), p.getGramajeInterior(),
tapaCubierta,
papeles.labelPapel(p.getPapelCubiertaId(), locale), p.getGramajeCubierta(),
};
String textoResumen = ms.getMessage("presupuesto.resumen-texto", args, locale);
textoResumen += "<ul>";
// tapa blanda
if (p.getTipoCubierta() == Presupuesto.TipoCubierta.tapaBlanda) {
String impresionCubierta = ms.getMessage(
"presupuesto.resumen-texto-impresion-caras-cubierta",
new Object[] {
p.getCubiertaCaras() == 2
? ms.getMessage("presupuesto.una-cara", null, locale)
: ms.getMessage("presupuesto.dos-caras", null, locale)
},
locale
);
textoResumen += impresionCubierta;
if (p.getSolapasCubierta()) {
textoResumen += ms.getMessage(
"presupuesto.resumen-texto-solapas-cubierta",
new Object[] { p.getTamanioSolapasCubierta() },
locale
);
}
}
// tapa dura
else if (p.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura
|| p.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
String textImpresionGuardas = "";
if (p.getGuardasImpresas() == 0) {
textImpresionGuardas = ms.getMessage("presupuesto.guardas-no-impresas", null, locale);
} else if (p.getGuardasImpresas() == 4) {
textImpresionGuardas = ms.getMessage("presupuesto.guardas-impresas-una-cara", null, locale);
} else if (p.getGuardasImpresas() == 8) {
textImpresionGuardas = ms.getMessage("presupuesto.guardas-impresas-dos-caras", null, locale);
}
textoResumen += ms.getMessage(
"presupuesto.resumen-texto-guardas-cabezada",
new Object[] {
textImpresionGuardas,
papeles.labelPapel(p.getPapelGuardasId(), locale),
p.getGramajeGuardas(),
papeles.labelCabezada(p.getCabezada(), locale),
},
locale
);
}
if (p.getAcabado() != null) {
textoResumen += ms.getMessage(
"presupuesto.resumen-texto-acabado-cubierta",
new Object[] { acabados.labelAcabado(p.getAcabado(), locale) },
locale
);
}
textoResumen += ms.getMessage("presupuesto.resumen-texto-end", null, locale);
if (Boolean.TRUE.equals(p.getSobrecubierta())) {
textoResumen += ms.getMessage(
"presupuesto.resumen-texto-sobrecubierta",
new Object[] {
papeles.labelPapel(p.getPapelSobrecubiertaId(), locale),
p.getGramajeSobrecubierta(),
acabados.labelAcabado(p.getAcabadoSobrecubierta(), locale),
p.getTamanioSolapasSobrecubierta()
},
locale
);
}
if (Boolean.TRUE.equals(p.getFaja())) {
textoResumen += ms.getMessage(
"presupuesto.resumen-texto-faja",
new Object[] {
papeles.labelPapel(p.getPapelFajaId(), locale),
p.getGramajeFaja(),
p.getAltoFaja(),
acabados.labelAcabado(p.getAcabadoFaja(), locale),
p.getTamanioSolapasFaja()
},
locale
);
}
textoResumen += ms.getMessage("presupuesto.resumen-texto-end", null, locale);
return textoResumen;
}
}

View File

@ -0,0 +1,43 @@
package com.imprimelibros.erp.presupuesto.classes;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import java.util.Locale;
import java.util.Map;
@Component
public class PresupuestoPapeles {
private final MessageSource messageSource;
public PresupuestoPapeles(MessageSource messageSource) {
this.messageSource = messageSource;
}
private static final Map<Integer, String> PAPEL_KEYS = Map.of(
2, "presupuesto.estucado-mate",
3, "presupuesto.offset-blanco",
4, "presupuesto.offset-ahuesado",
5, "presupuesto.cartulina-grafica-cubierta",
6, "presupuesto.offset-ahuesado-volumen",
7, "presupuesto.offset-blanco-volumen"
);
private static final Map<String, String> CABEZADA_COLOR_KEYS = Map.of(
"WHI", "presupuesto.cabezada-blanca",
"GRE", "presupuesto.cabezada-verde",
"BLUE", "presupuesto.cabezada-azul",
"REDYEL", "presupuesto.cabezada-roja-amarilla"
);
public String labelPapel(int id, Locale locale) {
String key = PAPEL_KEYS.get(id);
return key != null ? messageSource.getMessage(key, null, locale) : String.valueOf(id);
}
public String labelCabezada(String code, Locale locale) {
String key = CABEZADA_COLOR_KEYS.get(code);
return key != null ? messageSource.getMessage(key, null, locale) : code;
}
}

View File

@ -79,6 +79,9 @@ presupuesto.impresion-cubierta=Impresión de cubierta
presupuesto.impresion-cubierta-help=La cubierta se puede imprimir por anverso y reverso, como en el caso de las revistas, pero para un libro normal con portada y contraportada, la impresión de cubierta es a una cara.
presupuesto.una-cara=Una cara
presupuesto.dos-caras=Dos caras
presupuesto.guardas-impresas-una-cara=impresas a una cara
presupuesto.guardas-impresas-dos-caras=impresas a dos caras
presupuesto.guardas-no-impresas=no impresas
presupuesto.tamanio-solapa=Tamaño solapas
presupuesto.papel-guardas=Papel de guardas
presupuesto.guardas-impresas=Guardas impresas
@ -157,9 +160,17 @@ presupuesto.calcular-presupuesto=Calcular presupuesto
presupuesto.consultar-soporte=Consultar con soporte
# Pestaña resumen del presupuesto
presupuesto.resumen-texto={tirada} unidades encuadernadas en {tipoEncuadernacion} con {totalPaginas} páginas en formato {formato}. \
\n Papel interior {papelInterior} {gramajeInterior}. \
\n Cubierta en {tapaCubierta} {solapasCubierta} en {papelCubierta} {gramajeCubierta}.
presupuesto.resumen-texto=Impresion de {0} unidades encuadernadas en {1} en {2} con {3} páginas en formato {4} x {5} mm. \
<ul> \
<li>Papel interior {6} {7} gr.</li> \
<li>Cubierta {8} en {9} {10} gr.</li>
presupuesto.resumen-texto-impresion-caras-cubierta=<li>Impresa a {0}.</li>
presupuesto.resumen-texto-solapas-cubierta=<li>Solapas de {0} mm.</li>
presupuesto.resumen-texto-guardas-cabezada= <li>Guardas {0} en {1} {2}. Color de la cabezada: {3}.</li>
presupuesto.resumen-texto-acabado-cubierta= <li>Acabado {0}. </li>
presupuesto.resumen-texto-end=</ul>
presupuesto.resumen-texto-sobrecubierta=<li>Sobrecubierta impresa en {0} {1} gr. <ul><li>Acabado {2}</li><li>Solapas: {3} mm.</li></ul></li>
presupuesto.resumen-texto-faja=<li>Faja impresa en {0} {1} gr. con un alto de {2} mm. <ul><li>Acabado {3}</li><li>Solapas:{4} mm.</li></ul></li>
presupuesto.volver-extras=Volver a extras
# Resumen del presupuesto

View File

@ -37,8 +37,8 @@ class PresupuestoCliente {
solapasCubierta: 0,
tamanioSolapasCubierta: '80',
cubiertaCaras: 2,
guardasPapelId: 3,
guardasGramaje: 170,
papelGuardasId: 3,
gramajeGuardas: 170,
guardasImpresas: 0,
cabezada: 'WHI',
papelCubiertaId: 3,
@ -1124,8 +1124,8 @@ class PresupuestoCliente {
const solapas = $('.solapas-cubierta.selected').id == 'sin-solapas' ? 0 : 1 || 0;
const tamanioSolapasCubierta = $('#tamanio-solapas-cubierta').val() || '80';
const cubiertaCaras = parseInt(this.carasImpresionCubierta.val()) || 2;
const guardasPapelId = parseInt($('#papel-guardas option:selected').data('papel-id')) || 3;
const guardasGramaje = parseInt($('#papel-guardas option:selected').data('gramaje')) || 170;
const papelGuardasId = parseInt($('#papel-guardas option:selected').data('papel-id')) || 3;
const gramajeGuardas = parseInt($('#papel-guardas option:selected').data('gramaje')) || 170;
const guardasImpresas = parseInt(this.guardasImpresas) || 0;
const cabezada = this.cabezada.val() || 'WHI';
const papelCubiertaId = $('#div-papel-cubierta .image-container.selected').data('sk-id') || this.formData.cubierta.papelCubiertaId || 3;
@ -1145,11 +1145,11 @@ class PresupuestoCliente {
return {
tipoCubierta: tipoCubierta,
solapas: solapas,
solapasCubierta: solapas,
tamanioSolapasCubierta: tamanioSolapasCubierta,
cubiertaCaras: cubiertaCaras,
guardasPapelId: guardasPapelId,
guardasGramaje: guardasGramaje,
papelGuardasId: papelGuardasId,
gramajeGuardas: gramajeGuardas,
guardasImpresas: guardasImpresas,
cabezada: cabezada,
papelCubiertaId: papelCubiertaId,
@ -1176,11 +1176,11 @@ class PresupuestoCliente {
#updateCubiertaData(data) {
this.formData.cubierta.tipoCubierta = data.tipoCubierta;
this.formData.cubierta.solapas = data.solapas;
this.formData.cubierta.solapasCubierta = data.solapasCubierta;
this.formData.cubierta.tamanioSolapasCubierta = data.tamanioSolapasCubierta;
this.formData.cubierta.cubiertaCaras = data.cubiertaCaras;
this.formData.cubierta.guardasPapelId = data.guardasPapelId;
this.formData.cubierta.guardasGramaje = data.guardasGramaje;
this.formData.cubierta.papelGuardasId = data.papelGuardasId;
this.formData.cubierta.gramajeGuardas = data.gramajeGuardas;
this.formData.cubierta.guardasImpresas = data.guardasImpresas;
this.formData.cubierta.cabezada = data.cabezada;
this.formData.cubierta.papelCubiertaId = data.papelCubiertaId;
@ -1237,15 +1237,15 @@ class PresupuestoCliente {
$('.tapa-dura-options').removeClass('d-none');
$('.tapa-blanda-options').addClass('d-none');
$('#papel-guardas option[data-papel-id="' +
this.formData.cubierta.guardasPapelId + '"][data-gramaje="' +
this.formData.cubierta.guardasGramaje + '"]').prop('selected', true).trigger('change');
this.formData.cubierta.papelGuardasId + '"][data-gramaje="' +
this.formData.cubierta.gramajeGuardas + '"]').prop('selected', true).trigger('change');
this.guardasImpresas.val(this.formData.cubierta.guardasImpresas);
this.cabezada.val(this.formData.cubierta.cabezada);
}
$(`#${this.formData.cubierta.tipoCubierta}`).trigger('click');
if (this.formData.cubierta.solapas === 0) {
if (this.formData.cubierta.solapasCubierta === 0) {
$('.solapas-cubierta#sin-solapas').addClass('selected');
this.divSolapasCubierta.addClass('d-none');
}
@ -1389,20 +1389,20 @@ class PresupuestoCliente {
this.#changeTab('pills-seleccion-tirada');
this.summaryTableExtras.addClass('d-none');
} else {
const summaryData = Summary.getSummaryData();
const presupuestoData = this.#getPresupuestoData();
const payload = {
...dotify(this.#getPresupuestoData(), 'presupuesto'),
summary: JSON.stringify(Summary.getSummaryData()),
const body = {
presupuesto: this.#getPresupuestoData(), // objeto JS con campos que coincidan con la entidad
servicios: this.formData.servicios.servicios // array de strings
};
$.ajax({
url: '/presupuesto/public/getresumen',
url: '/presupuesto/public/resumen',
type: 'POST',
data: payload,
contentType: 'application/json',
data: JSON.stringify(body)
}).then((data) => {
console.log("Extras validados correctamente", data);
$('#resumen-titulo').text(data.titulo);
$('#resumen-texto').html(data.texto);
}).catch((error) => {
console.error("Error obtener resumen: ", error);
});

View File

@ -163,20 +163,3 @@ export function updateExtras() {
}
}
export function getSummaryData() {
const data = {};
$(".data-summary").each(function () {
const $el = $(this);
// comprobar si la tabla y el tr están visibles
if ($el.closest("table").is(":visible") && $el.closest("tr").is(":visible")) {
const key = $el.data("id-summary"); // atributo data-id-summary
const value = $el.text().trim(); // texto visible
data[key] = value;
}
});
return data;
}

View File

@ -14,6 +14,11 @@
</div>
<!-- End Ribbon Shape -->
<div class="col-9 mx-auto mt-4">
<h5 id="resumen-titulo" class="text-center"></h5>
<p id="resumen-texto"></p>
</div>
<div class="d-flex justify-content-between align-items-center mt-4 w-100">
<button id="btn-prev-resumen" type="button"
class="btn btn-light d-flex align-items-center btn-change-tab-resumen">