mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-24 09:40:21 +00:00
Merge branch 'feat/resumen_presupuesto' into 'main'
trabajando en el envio de los datos al backend para generar el resumen. no... See merge request jjimenez/erp-imprimelibros!7
This commit is contained in:
2
pom.xml
2
pom.xml
@ -28,7 +28,7 @@
|
|||||||
<url />
|
<url />
|
||||||
</scm>
|
</scm>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>24</java.version>
|
<java.version>21</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
13
src/main/java/com/imprimelibros/erp/config/WebConfig.java
Normal file
13
src/main/java/com/imprimelibros/erp/config/WebConfig.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.stereotype.Component;
|
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.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ -19,4 +23,16 @@ public class TranslationService {
|
|||||||
}
|
}
|
||||||
return translations;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,15 +19,54 @@ import jakarta.persistence.*;
|
|||||||
public class Presupuesto implements Cloneable{
|
public class Presupuesto implements Cloneable{
|
||||||
|
|
||||||
public enum TipoEncuadernacion {
|
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 {
|
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 {
|
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
|
@Override
|
||||||
@ -44,6 +83,7 @@ public class Presupuesto implements Cloneable{
|
|||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@NotNull(message = "{presupuesto.errores.tipo-encuadernacion}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
@NotNull(message = "{presupuesto.errores.tipo-encuadernacion}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(name = "tipo_encuadernacion")
|
@Column(name = "tipo_encuadernacion")
|
||||||
private TipoEncuadernacion tipoEncuadernacion = TipoEncuadernacion.fresado;
|
private TipoEncuadernacion tipoEncuadernacion = TipoEncuadernacion.fresado;
|
||||||
|
|
||||||
@ -104,6 +144,7 @@ public class Presupuesto implements Cloneable{
|
|||||||
private Integer paginasColorTotal;
|
private Integer paginasColorTotal;
|
||||||
|
|
||||||
@NotNull(message = "{presupuesto.errores.tipo-impresion}", groups = PresupuestoValidationGroups.Interior.class)
|
@NotNull(message = "{presupuesto.errores.tipo-impresion}", groups = PresupuestoValidationGroups.Interior.class)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(name = "tipo_impresion")
|
@Column(name = "tipo_impresion")
|
||||||
private TipoImpresion tipoImpresion = TipoImpresion.negro;
|
private TipoImpresion tipoImpresion = TipoImpresion.negro;
|
||||||
|
|
||||||
@ -116,6 +157,7 @@ public class Presupuesto implements Cloneable{
|
|||||||
private Integer gramajeInterior;
|
private Integer gramajeInterior;
|
||||||
|
|
||||||
@NotNull(message = "{presupuesto.errores.tipo-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class)
|
@NotNull(message = "{presupuesto.errores.tipo-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(name = "tipo_cubierta")
|
@Column(name = "tipo_cubierta")
|
||||||
private TipoCubierta tipoCubierta = TipoCubierta.tapaBlanda;
|
private TipoCubierta tipoCubierta = TipoCubierta.tapaBlanda;
|
||||||
|
|
||||||
@ -535,4 +577,16 @@ public class Presupuesto implements Cloneable{
|
|||||||
public void setPresupuestoMaquetacionData(String presupuestoMaquetacionData) {
|
public void setPresupuestoMaquetacionData(String presupuestoMaquetacionData) {
|
||||||
this.presupuestoMaquetacionData = presupuestoMaquetacionData;
|
this.presupuestoMaquetacionData = presupuestoMaquetacionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String resumenPresupuesto() {
|
||||||
|
return String.format("%s - %s - %dx%d mm - %d Páginas (N:%d C:%d) - Tira:%d",
|
||||||
|
this.titulo,
|
||||||
|
this.tipoEncuadernacion,
|
||||||
|
this.ancho,
|
||||||
|
this.alto,
|
||||||
|
this.paginasNegro + this.paginasColorTotal,
|
||||||
|
this.paginasNegro,
|
||||||
|
this.paginasColorTotal,
|
||||||
|
this.selectedTirada != null ? this.selectedTirada : 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.imprimelibros.erp.presupuesto;
|
|||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -18,11 +19,10 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.imprimelibros.erp.externalApi.skApiClient;
|
import com.imprimelibros.erp.externalApi.skApiClient;
|
||||||
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
|
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
|
||||||
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
|
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
|
||||||
@ -44,6 +44,12 @@ public class PresupuestoController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
protected MessageSource messageSource;
|
protected MessageSource messageSource;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public PresupuestoController(ObjectMapper objectMapper){
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/public/validar/datos-generales")
|
@PostMapping("/public/validar/datos-generales")
|
||||||
public ResponseEntity<?> validarDatosGenerales(
|
public ResponseEntity<?> validarDatosGenerales(
|
||||||
@Validated(PresupuestoValidationGroups.DatosGenerales.class) Presupuesto presupuesto,
|
@Validated(PresupuestoValidationGroups.DatosGenerales.class) Presupuesto presupuesto,
|
||||||
@ -115,21 +121,14 @@ public class PresupuestoController {
|
|||||||
|
|
||||||
if (calcular) {
|
if (calcular) {
|
||||||
|
|
||||||
HashMap<String, Object> price = new HashMap<>();
|
HashMap<String, Object> price = presupuestoService.calcularPresupuesto(presupuesto, locale);
|
||||||
String priceStr = apiClient.getPrice(presupuestoService.toSkApiRequest(presupuesto));
|
|
||||||
|
|
||||||
try {
|
|
||||||
price = new ObjectMapper().readValue(priceStr, new TypeReference<>() {
|
|
||||||
});
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
price = new HashMap<>();
|
|
||||||
price.put("error", messageSource.getMessage("presupuesto.error-obtener-precio", null, locale));
|
|
||||||
}
|
|
||||||
if (!price.containsKey("data")) {
|
if (!price.containsKey("data")) {
|
||||||
return ResponseEntity.badRequest()
|
return ResponseEntity.badRequest()
|
||||||
.body(messageSource.getMessage("presupuesto.error-obtener-precio", null, locale));
|
.body(messageSource.getMessage("presupuesto.error-obtener-precio", null, locale));
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(price.get("data"));
|
return ResponseEntity.ok(price.get("data"));
|
||||||
|
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
}
|
}
|
||||||
@ -153,7 +152,7 @@ public class PresupuestoController {
|
|||||||
|
|
||||||
Map<String, Object> resultado = new HashMap<>();
|
Map<String, Object> resultado = new HashMap<>();
|
||||||
// servicios extra
|
// servicios extra
|
||||||
resultado.putAll(presupuestoService.obtenerServiciosExtras(presupuesto, locale, apiClient));
|
resultado.putAll(presupuestoService.obtenerServiciosExtras(presupuesto, locale));
|
||||||
Map<String, String> language = new HashMap<>();
|
Map<String, String> language = new HashMap<>();
|
||||||
language.put("calcular", messageSource.getMessage("presupuesto.calcular", null, locale));
|
language.put("calcular", messageSource.getMessage("presupuesto.calcular", null, locale));
|
||||||
resultado.put("language", language);
|
resultado.put("language", language);
|
||||||
@ -375,4 +374,15 @@ public class PresupuestoController {
|
|||||||
return ResponseEntity.ok(resultado);
|
return ResponseEntity.ok(resultado);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Se hace un post para no tener problemas con la longitud de la URL
|
||||||
|
@PostMapping("/public/resumen")
|
||||||
|
public ResponseEntity<?> getResumen(@RequestBody Map<String, Object> body, Locale locale) {
|
||||||
|
Presupuesto p = objectMapper.convertValue(body.get("presupuesto"), Presupuesto.class);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Map<String, Object>> serviciosList = (List<Map<String, Object>>) body.getOrDefault("servicios", List.of());
|
||||||
|
|
||||||
|
return ResponseEntity.ok(presupuestoService.getResumen(p, serviciosList, locale));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,14 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
@ -25,6 +27,7 @@ import com.imprimelibros.erp.configurationERP.VariableService;
|
|||||||
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
|
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
|
||||||
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
|
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
|
||||||
import com.imprimelibros.erp.presupuesto.classes.PresupuestadorItems;
|
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.MaquetacionPrecios;
|
||||||
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPreciosRepository;
|
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPreciosRepository;
|
||||||
import com.imprimelibros.erp.presupuesto.marcapaginas.Marcapaginas;
|
import com.imprimelibros.erp.presupuesto.marcapaginas.Marcapaginas;
|
||||||
@ -43,9 +46,6 @@ public class PresupuestoService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
protected MessageSource messageSource;
|
protected MessageSource messageSource;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected skApiClient skApiClient;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected MaquetacionPreciosRepository maquetacionPreciosRepository;
|
protected MaquetacionPreciosRepository maquetacionPreciosRepository;
|
||||||
|
|
||||||
@ -56,9 +56,13 @@ public class PresupuestoService {
|
|||||||
protected MarcapaginasRepository marcapaginasRepository;
|
protected MarcapaginasRepository marcapaginasRepository;
|
||||||
|
|
||||||
private final PresupuestadorItems presupuestadorItems;
|
private final PresupuestadorItems presupuestadorItems;
|
||||||
|
private final PresupuestoFormatter presupuestoFormatter;
|
||||||
|
private final skApiClient apiClient;
|
||||||
|
|
||||||
public PresupuestoService(PresupuestadorItems presupuestadorItems) {
|
public PresupuestoService(PresupuestadorItems presupuestadorItems, PresupuestoFormatter presupuestoFormatter, skApiClient apiClient) {
|
||||||
this.presupuestadorItems = presupuestadorItems;
|
this.presupuestadorItems = presupuestadorItems;
|
||||||
|
this.presupuestoFormatter = presupuestoFormatter;
|
||||||
|
this.apiClient = apiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateDatosGenerales(int[] tiradas) {
|
public boolean validateDatosGenerales(int[] tiradas) {
|
||||||
@ -301,15 +305,6 @@ public class PresupuestoService {
|
|||||||
"cabezada", presupuesto.getCabezada(),
|
"cabezada", presupuesto.getCabezada(),
|
||||||
"lomoRedondo", presupuesto.getTipoCubierta() == TipoCubierta.tapaDuraLomoRedondo ? 1 : 0);
|
"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<>();
|
Map<String, Object> body = new HashMap<>();
|
||||||
body.put("tipo_impresion_id", this.getTipoImpresionId(presupuesto));
|
body.put("tipo_impresion_id", this.getTipoImpresionId(presupuesto));
|
||||||
body.put("tirada", Arrays.stream(presupuesto.getTiradas())
|
body.put("tirada", Arrays.stream(presupuesto.getTiradas())
|
||||||
@ -459,18 +454,18 @@ public class PresupuestoService {
|
|||||||
Map<String, Object> requestBody = new HashMap<>();
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
requestBody.put("tirada",
|
requestBody.put("tirada",
|
||||||
presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : tirada_min);
|
presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : tirada_min);
|
||||||
Double precio_retractilado = skApiClient.getRetractilado(requestBody);
|
Double precio_retractilado = apiClient.getRetractilado(requestBody);
|
||||||
return precio_retractilado != null
|
return precio_retractilado != null
|
||||||
? NumberFormat.getNumberInstance(locale)
|
? NumberFormat.getNumberInstance(locale)
|
||||||
.format(Math.round(precio_retractilado * 100.0) / 100.0)
|
.format(Math.round(precio_retractilado * 100.0) / 100.0)
|
||||||
: "0,00";
|
: "0,00";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> obtenerServiciosExtras(Presupuesto presupuesto, Locale locale, skApiClient apiClient) {
|
public Map<String, Object> obtenerServiciosExtras(Presupuesto presupuesto, Locale locale) {
|
||||||
|
|
||||||
List<Object> opciones = new ArrayList<>();
|
List<Object> opciones = new ArrayList<>();
|
||||||
|
|
||||||
Double price_prototipo = this.obtenerPrototipo(presupuesto, apiClient);
|
Double price_prototipo = this.obtenerPrototipo(presupuesto);
|
||||||
|
|
||||||
opciones.add(new HashMap<String, String>() {
|
opciones.add(new HashMap<String, String>() {
|
||||||
{
|
{
|
||||||
@ -567,7 +562,7 @@ public class PresupuestoService {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Double obtenerPrototipo(Presupuesto presupuesto, skApiClient apiClient) {
|
private Double obtenerPrototipo(Presupuesto presupuesto) {
|
||||||
|
|
||||||
// Obtenemos el precio de 1 unidad para el ejemplar de prueba
|
// Obtenemos el precio de 1 unidad para el ejemplar de prueba
|
||||||
HashMap<String, Object> price = new HashMap<>();
|
HashMap<String, Object> price = new HashMap<>();
|
||||||
@ -760,12 +755,15 @@ public class PresupuestoService {
|
|||||||
resultado.put("precio_unitario", precio_unidad);
|
resultado.put("precio_unitario", precio_unidad);
|
||||||
resultado.put("precio_total", pvp);
|
resultado.put("precio_total", pvp);
|
||||||
HashMap<String, String> language = new HashMap<>();
|
HashMap<String, String> language = new HashMap<>();
|
||||||
language.put("precio_unidad", messageSource.getMessage("presupuesto.marcapaginas.precio-unidad", null, locale));
|
language.put("precio_unidad",
|
||||||
language.put("precio_total", messageSource.getMessage("presupuesto.marcapaginas.precio-total", null, locale));
|
messageSource.getMessage("presupuesto.marcapaginas.precio-unidad", null, locale));
|
||||||
|
language.put("precio_total",
|
||||||
|
messageSource.getMessage("presupuesto.marcapaginas.precio-total", null, locale));
|
||||||
language.put("add_to_presupuesto",
|
language.put("add_to_presupuesto",
|
||||||
messageSource.getMessage("presupuesto.add-to-presupuesto", null, locale));
|
messageSource.getMessage("presupuesto.add-to-presupuesto", null, locale));
|
||||||
language.put("cancel", messageSource.getMessage("app.cancelar", null, locale));
|
language.put("cancel", messageSource.getMessage("app.cancelar", null, locale));
|
||||||
language.put("presupuesto_marcapaginas", messageSource.getMessage("presupuesto.marcapaginas", null, locale));
|
language.put("presupuesto_marcapaginas",
|
||||||
|
messageSource.getMessage("presupuesto.marcapaginas", null, locale));
|
||||||
resultado.put("language", language);
|
resultado.put("language", language);
|
||||||
return resultado;
|
return resultado;
|
||||||
|
|
||||||
@ -777,4 +775,85 @@ public class PresupuestoService {
|
|||||||
out.put("precio_total", 0.0);
|
out.put("precio_total", 0.0);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getResumen(Presupuesto presupuesto, List<Map<String, Object>> servicios, Locale locale) {
|
||||||
|
|
||||||
|
Map<String, Object> resumen = new HashMap<>();
|
||||||
|
resumen.put("titulo", presupuesto.getTitulo());
|
||||||
|
|
||||||
|
Presupuesto pressupuestoTemp = presupuesto.clone();
|
||||||
|
|
||||||
|
resumen.put("imagen", "/assets/images/imprimelibros/presupuestador/" + presupuesto.getTipoEncuadernacion() + ".png");
|
||||||
|
resumen.put("imagen_alt", messageSource.getMessage("presupuesto." + presupuesto.getTipoEncuadernacion(), null, locale));
|
||||||
|
|
||||||
|
boolean hayDepositoLegal = servicios != null && servicios.stream()
|
||||||
|
.map(m -> java.util.Objects.toString(m.get("id"), "")) // null-safe -> String
|
||||||
|
.map(String::trim)
|
||||||
|
.anyMatch("deposito-legal"::equals);
|
||||||
|
|
||||||
|
if(hayDepositoLegal){
|
||||||
|
pressupuestoTemp.setSelectedTirada(presupuesto.getSelectedTirada()+4);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, Object> precios = this.calcularPresupuesto(pressupuestoTemp, locale);
|
||||||
|
if(precios.containsKey("error")){
|
||||||
|
resumen.put("error", precios.get("error"));
|
||||||
|
return resumen;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, Object> linea = new HashMap<>();
|
||||||
|
Double precio_unitario = 0.0;
|
||||||
|
Double precio_total = 0.0;
|
||||||
|
Integer counter = 0;
|
||||||
|
linea.put("descripcion", presupuestoFormatter.resumen(presupuesto, servicios, locale));
|
||||||
|
linea.put("cantidad", presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : 0);
|
||||||
|
precio_unitario = ((List<Double>) ((Map<String, Object>) precios.get("data")).get("precios"))
|
||||||
|
.get(0);
|
||||||
|
precio_total = precio_unitario * presupuesto.getSelectedTirada();
|
||||||
|
linea.put("precio_unitario", precio_unitario);
|
||||||
|
linea.put("precio_total", BigDecimal.valueOf(precio_total).setScale(2, RoundingMode.HALF_UP));
|
||||||
|
resumen.put("linea" + counter, linea);
|
||||||
|
counter++;
|
||||||
|
|
||||||
|
if(hayDepositoLegal) {
|
||||||
|
|
||||||
|
linea = new HashMap<>();
|
||||||
|
linea.put("descripcion", messageSource.getMessage("presupuesto.resumen-deposito-legal", null, locale));
|
||||||
|
linea.put("cantidad", 4);
|
||||||
|
linea.put("precio_unitario", precio_unitario);
|
||||||
|
linea.put("precio_total", BigDecimal.valueOf(precio_unitario * 4).setScale(2, RoundingMode.HALF_UP));
|
||||||
|
resumen.put("linea" + counter, linea);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Object>> serviciosExtras = new ArrayList<>();
|
||||||
|
|
||||||
|
if(servicios != null){
|
||||||
|
for (Map<String, Object> servicio : servicios) {
|
||||||
|
HashMap<String, Object> servicioData = new HashMap<>();
|
||||||
|
servicioData.put("descripcion", servicio.get("label"));
|
||||||
|
servicioData.put("precio", servicio.get("price"));
|
||||||
|
serviciosExtras.add(servicioData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resumen.put("servicios", serviciosExtras);
|
||||||
|
|
||||||
|
return resumen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, Object> calcularPresupuesto(Presupuesto presupuesto, Locale locale) {
|
||||||
|
|
||||||
|
HashMap<String, Object> price = new HashMap<>();
|
||||||
|
String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuesto));
|
||||||
|
|
||||||
|
try {
|
||||||
|
price = new ObjectMapper().readValue(priceStr, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
price = new HashMap<>();
|
||||||
|
price.put("error", messageSource.getMessage("presupuesto.error-obtener-precio", null, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
return price;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import java.util.Map;
|
|||||||
@Component
|
@Component
|
||||||
public class PresupuestadorItems {
|
public class PresupuestadorItems {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MessageSource messageSource;
|
private MessageSource messageSource;
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
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;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@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, List<Map<String, Object>> 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 = {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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.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.una-cara=Una cara
|
||||||
presupuesto.dos-caras=Dos caras
|
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.tamanio-solapa=Tamaño solapas
|
||||||
presupuesto.papel-guardas=Papel de guardas
|
presupuesto.papel-guardas=Papel de guardas
|
||||||
presupuesto.guardas-impresas=Guardas impresas
|
presupuesto.guardas-impresas=Guardas impresas
|
||||||
@ -157,7 +160,28 @@ presupuesto.calcular-presupuesto=Calcular presupuesto
|
|||||||
presupuesto.consultar-soporte=Consultar con soporte
|
presupuesto.consultar-soporte=Consultar con soporte
|
||||||
|
|
||||||
# Pestaña resumen del presupuesto
|
# Pestaña resumen del presupuesto
|
||||||
|
presupuesto.resumen.tabla.descripcion=Descripción
|
||||||
|
presupuesto.resumen.tabla.cantidad=Cantidad
|
||||||
|
presupuesto.resumen.tabla.precio-unidad=Precio/unidad
|
||||||
|
presupuesto.resumen.tabla.precio-total=Precio total
|
||||||
|
presupuesto.resumen.tabla.base=Base
|
||||||
|
presupuesto.resumen.tabla.iva=I.V.A. (4%)
|
||||||
|
presupuesto.resumen.tabla.total=Total presupuesto
|
||||||
|
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.resumen-deposito-legal=Ejemplares para el Depósito Legal
|
||||||
presupuesto.volver-extras=Volver a extras
|
presupuesto.volver-extras=Volver a extras
|
||||||
|
presupuesto.resumen.inicie-sesion=Inicie sesión para continuar
|
||||||
|
presupuesto.resumen.agregar-cesta=Agregar a la cesta
|
||||||
|
|
||||||
# Resumen del presupuesto
|
# Resumen del presupuesto
|
||||||
presupuesto.resumen-presupuesto=Resumen presupuesto
|
presupuesto.resumen-presupuesto=Resumen presupuesto
|
||||||
|
|||||||
@ -213,7 +213,8 @@
|
|||||||
|
|
||||||
/* ===== Tiradas (pricing cards) ===== */
|
/* ===== Tiradas (pricing cards) ===== */
|
||||||
.tirada-card {
|
.tirada-card {
|
||||||
--il-accent: #92b2a7; /* verde grisáceo */
|
--il-accent: #92b2a7;
|
||||||
|
/* verde grisáceo */
|
||||||
--radius: 18px;
|
--radius: 18px;
|
||||||
--sel-scale-y: 1.12;
|
--sel-scale-y: 1.12;
|
||||||
/* cuánto más alta la seleccionada (crece arriba y abajo) */
|
/* cuánto más alta la seleccionada (crece arriba y abajo) */
|
||||||
@ -352,19 +353,23 @@
|
|||||||
|
|
||||||
|
|
||||||
.nav-link.active .bg-soft-primary {
|
.nav-link.active .bg-soft-primary {
|
||||||
background-color: #ffffff33 !important; /* #4c5c63 al 20% */
|
background-color: #ffffff33 !important;
|
||||||
|
/* #4c5c63 al 20% */
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link.active .text-primary {
|
.nav-link.active .text-primary {
|
||||||
color: #ffffff !important; /* #4c5c63 al 20% */
|
color: #ffffff !important;
|
||||||
|
/* #4c5c63 al 20% */
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link:not(.active) .bg-soft-primary {
|
.nav-link:not(.active) .bg-soft-primary {
|
||||||
background-color: #4c5c6366 !important; /* #4c5c63 al 20% */
|
background-color: #4c5c6366 !important;
|
||||||
|
/* #4c5c63 al 20% */
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link:not(.active) .text-primary {
|
.nav-link:not(.active) .text-primary {
|
||||||
color: #000000 !important; /* #4c5c63 al 20% */
|
color: #000000 !important;
|
||||||
|
/* #4c5c63 al 20% */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* base */
|
/* base */
|
||||||
@ -376,20 +381,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* hover no seleccionado */
|
/* hover no seleccionado */
|
||||||
.btn-check-service + .btn-service-option:hover {
|
.btn-check-service+.btn-service-option:hover {
|
||||||
background-color: rgba(146, 178, 167, 0.3);
|
background-color: rgba(146, 178, 167, 0.3);
|
||||||
color: #92b2a7;
|
color: #92b2a7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* seleccionado */
|
/* seleccionado */
|
||||||
.btn-check-service:checked + .btn-service-option {
|
.btn-check-service:checked+.btn-service-option {
|
||||||
background-color: #92b2a7;
|
background-color: #92b2a7;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: #92b2a7;
|
border-color: #92b2a7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hover estando seleccionado (que no se aclare) */
|
/* hover estando seleccionado (que no se aclare) */
|
||||||
.btn-check-service:checked + .btn-service-option:hover {
|
.btn-check-service:checked+.btn-service-option:hover {
|
||||||
background-color: #92b2a7;
|
background-color: #92b2a7;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
@ -408,3 +413,69 @@
|
|||||||
.form-switch-custom.form-switch-presupuesto .form-check-input:checked::before {
|
.form-switch-custom.form-switch-presupuesto .form-check-input:checked::before {
|
||||||
color: #92b2a7;
|
color: #92b2a7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==== Paso al resumen ==== */
|
||||||
|
/* ---- Ajustes rápidos ---- */
|
||||||
|
/* Valores por defecto (col-9 / col-3 ≈ 75% / 25%) */
|
||||||
|
#presupuesto-row{
|
||||||
|
--main-col: 75%;
|
||||||
|
--summary-col: 25%;
|
||||||
|
--il-dur-main: 1.4s;
|
||||||
|
--il-dur-summary: .6s;
|
||||||
|
--il-delay-main: .15s; /* empieza un poco después */
|
||||||
|
--il-delay-summary: 0s;
|
||||||
|
--il-ease-main: cubic-bezier(.2,.8,.2,1);
|
||||||
|
--il-ease-summary: cubic-bezier(.4,0,.2,1);
|
||||||
|
--il-shift: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forzamos que el ancho venga de las variables (y sea animable) */
|
||||||
|
@media (prefers-reduced-motion: no-preference){
|
||||||
|
#presupuesto-row .col-main{
|
||||||
|
flex: 0 0 var(--main-col) !important;
|
||||||
|
max-width: var(--main-col) !important;
|
||||||
|
transition:
|
||||||
|
flex-basis var(--il-dur-main) var(--il-ease-main) var(--il-delay-main),
|
||||||
|
max-width var(--il-dur-main) var(--il-ease-main) var(--il-delay-main);
|
||||||
|
}
|
||||||
|
#presupuesto-row .summary-col{
|
||||||
|
flex: 0 0 var(--summary-col) !important;
|
||||||
|
max-width: var(--summary-col) !important;
|
||||||
|
transition:
|
||||||
|
flex-basis var(--il-dur-summary) var(--il-ease-summary) var(--il-delay-summary),
|
||||||
|
max-width var(--il-dur-summary) var(--il-ease-summary) var(--il-delay-summary),
|
||||||
|
opacity var(--il-dur-summary) var(--il-ease-summary) var(--il-delay-summary),
|
||||||
|
transform var(--il-dur-summary) var(--il-ease-summary) var(--il-delay-summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estado expandido: cambiamos SOLO las variables (esto sí se anima) */
|
||||||
|
#presupuesto-row.expanded{
|
||||||
|
--main-col: 100%;
|
||||||
|
--summary-col: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presupuesto-row.expanded .summary-col{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(var(--il-shift));
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px){
|
||||||
|
/* Evita que las columnas se vayan a la siguiente línea durante la animación */
|
||||||
|
#presupuesto-row{
|
||||||
|
display: flex; /* por si acaso algún wrapper cambia el display */
|
||||||
|
flex-wrap: nowrap; /* <-- clave */
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
/* Permite que las columnas puedan encoger sin forzar salto de línea */
|
||||||
|
#presupuesto-row .col-main,
|
||||||
|
#presupuesto-row .summary-col{
|
||||||
|
min-width: 0; /* <-- clave para que el contenido no fuerce ancho */
|
||||||
|
}
|
||||||
|
/* Opcional: evita “asomar” algo durante el slide */
|
||||||
|
#presupuesto-row .summary-col{
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import imagen_presupuesto from "./imagen-presupuesto.js";
|
|||||||
import ServiceOptionCard from "./service-option-card.js";
|
import ServiceOptionCard from "./service-option-card.js";
|
||||||
import TiradaCard from "./tirada-price-card.js";
|
import TiradaCard from "./tirada-price-card.js";
|
||||||
import * as Summary from "./summary.js";
|
import * as Summary from "./summary.js";
|
||||||
|
import { formateaMoneda } from "../utils.js";
|
||||||
|
|
||||||
class PresupuestoCliente {
|
class PresupuestoCliente {
|
||||||
|
|
||||||
@ -36,8 +37,8 @@ class PresupuestoCliente {
|
|||||||
solapasCubierta: 0,
|
solapasCubierta: 0,
|
||||||
tamanioSolapasCubierta: '80',
|
tamanioSolapasCubierta: '80',
|
||||||
cubiertaCaras: 2,
|
cubiertaCaras: 2,
|
||||||
guardasPapelId: 3,
|
papelGuardasId: 3,
|
||||||
guardasGramaje: 170,
|
gramajeGuardas: 170,
|
||||||
guardasImpresas: 0,
|
guardasImpresas: 0,
|
||||||
cabezada: 'WHI',
|
cabezada: 'WHI',
|
||||||
papelCubiertaId: 3,
|
papelCubiertaId: 3,
|
||||||
@ -156,6 +157,9 @@ class PresupuestoCliente {
|
|||||||
// pestaña extras
|
// pestaña extras
|
||||||
this.divExtras = $('#div-extras');
|
this.divExtras = $('#div-extras');
|
||||||
|
|
||||||
|
// pestaña resumen
|
||||||
|
this.tablaResumen = $('#resumen-tabla-final');
|
||||||
|
|
||||||
// resumen
|
// resumen
|
||||||
this.summaryTableInterior = $('#summary-interior');
|
this.summaryTableInterior = $('#summary-interior');
|
||||||
this.summaryTableCubierta = $('#summary-cubierta');
|
this.summaryTableCubierta = $('#summary-cubierta');
|
||||||
@ -1123,8 +1127,8 @@ class PresupuestoCliente {
|
|||||||
const solapas = $('.solapas-cubierta.selected').id == 'sin-solapas' ? 0 : 1 || 0;
|
const solapas = $('.solapas-cubierta.selected').id == 'sin-solapas' ? 0 : 1 || 0;
|
||||||
const tamanioSolapasCubierta = $('#tamanio-solapas-cubierta').val() || '80';
|
const tamanioSolapasCubierta = $('#tamanio-solapas-cubierta').val() || '80';
|
||||||
const cubiertaCaras = parseInt(this.carasImpresionCubierta.val()) || 2;
|
const cubiertaCaras = parseInt(this.carasImpresionCubierta.val()) || 2;
|
||||||
const guardasPapelId = parseInt($('#papel-guardas option:selected').data('papel-id')) || 3;
|
const papelGuardasId = parseInt($('#papel-guardas option:selected').data('papel-id')) || 3;
|
||||||
const guardasGramaje = parseInt($('#papel-guardas option:selected').data('gramaje')) || 170;
|
const gramajeGuardas = parseInt($('#papel-guardas option:selected').data('gramaje')) || 170;
|
||||||
const guardasImpresas = parseInt(this.guardasImpresas) || 0;
|
const guardasImpresas = parseInt(this.guardasImpresas) || 0;
|
||||||
const cabezada = this.cabezada.val() || 'WHI';
|
const cabezada = this.cabezada.val() || 'WHI';
|
||||||
const papelCubiertaId = $('#div-papel-cubierta .image-container.selected').data('sk-id') || this.formData.cubierta.papelCubiertaId || 3;
|
const papelCubiertaId = $('#div-papel-cubierta .image-container.selected').data('sk-id') || this.formData.cubierta.papelCubiertaId || 3;
|
||||||
@ -1144,11 +1148,11 @@ class PresupuestoCliente {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
tipoCubierta: tipoCubierta,
|
tipoCubierta: tipoCubierta,
|
||||||
solapas: solapas,
|
solapasCubierta: solapas,
|
||||||
tamanioSolapasCubierta: tamanioSolapasCubierta,
|
tamanioSolapasCubierta: tamanioSolapasCubierta,
|
||||||
cubiertaCaras: cubiertaCaras,
|
cubiertaCaras: cubiertaCaras,
|
||||||
guardasPapelId: guardasPapelId,
|
papelGuardasId: papelGuardasId,
|
||||||
guardasGramaje: guardasGramaje,
|
gramajeGuardas: gramajeGuardas,
|
||||||
guardasImpresas: guardasImpresas,
|
guardasImpresas: guardasImpresas,
|
||||||
cabezada: cabezada,
|
cabezada: cabezada,
|
||||||
papelCubiertaId: papelCubiertaId,
|
papelCubiertaId: papelCubiertaId,
|
||||||
@ -1175,11 +1179,11 @@ class PresupuestoCliente {
|
|||||||
#updateCubiertaData(data) {
|
#updateCubiertaData(data) {
|
||||||
|
|
||||||
this.formData.cubierta.tipoCubierta = data.tipoCubierta;
|
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.tamanioSolapasCubierta = data.tamanioSolapasCubierta;
|
||||||
this.formData.cubierta.cubiertaCaras = data.cubiertaCaras;
|
this.formData.cubierta.cubiertaCaras = data.cubiertaCaras;
|
||||||
this.formData.cubierta.guardasPapelId = data.guardasPapelId;
|
this.formData.cubierta.papelGuardasId = data.papelGuardasId;
|
||||||
this.formData.cubierta.guardasGramaje = data.guardasGramaje;
|
this.formData.cubierta.gramajeGuardas = data.gramajeGuardas;
|
||||||
this.formData.cubierta.guardasImpresas = data.guardasImpresas;
|
this.formData.cubierta.guardasImpresas = data.guardasImpresas;
|
||||||
this.formData.cubierta.cabezada = data.cabezada;
|
this.formData.cubierta.cabezada = data.cabezada;
|
||||||
this.formData.cubierta.papelCubiertaId = data.papelCubiertaId;
|
this.formData.cubierta.papelCubiertaId = data.papelCubiertaId;
|
||||||
@ -1236,15 +1240,15 @@ class PresupuestoCliente {
|
|||||||
$('.tapa-dura-options').removeClass('d-none');
|
$('.tapa-dura-options').removeClass('d-none');
|
||||||
$('.tapa-blanda-options').addClass('d-none');
|
$('.tapa-blanda-options').addClass('d-none');
|
||||||
$('#papel-guardas option[data-papel-id="' +
|
$('#papel-guardas option[data-papel-id="' +
|
||||||
this.formData.cubierta.guardasPapelId + '"][data-gramaje="' +
|
this.formData.cubierta.papelGuardasId + '"][data-gramaje="' +
|
||||||
this.formData.cubierta.guardasGramaje + '"]').prop('selected', true).trigger('change');
|
this.formData.cubierta.gramajeGuardas + '"]').prop('selected', true).trigger('change');
|
||||||
this.guardasImpresas.val(this.formData.cubierta.guardasImpresas);
|
this.guardasImpresas.val(this.formData.cubierta.guardasImpresas);
|
||||||
this.cabezada.val(this.formData.cubierta.cabezada);
|
this.cabezada.val(this.formData.cubierta.cabezada);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(`#${this.formData.cubierta.tipoCubierta}`).trigger('click');
|
$(`#${this.formData.cubierta.tipoCubierta}`).trigger('click');
|
||||||
|
|
||||||
if (this.formData.cubierta.solapas === 0) {
|
if (this.formData.cubierta.solapasCubierta === 0) {
|
||||||
$('.solapas-cubierta#sin-solapas').addClass('selected');
|
$('.solapas-cubierta#sin-solapas').addClass('selected');
|
||||||
this.divSolapasCubierta.addClass('d-none');
|
this.divSolapasCubierta.addClass('d-none');
|
||||||
}
|
}
|
||||||
@ -1380,6 +1384,8 @@ class PresupuestoCliente {
|
|||||||
******************************/
|
******************************/
|
||||||
#initExtras() {
|
#initExtras() {
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
|
||||||
$(document).on('click', '.btn-change-tab-extras', (e) => {
|
$(document).on('click', '.btn-change-tab-extras', (e) => {
|
||||||
|
|
||||||
const id = e.currentTarget.id;
|
const id = e.currentTarget.id;
|
||||||
@ -1388,6 +1394,34 @@ class PresupuestoCliente {
|
|||||||
this.#changeTab('pills-seleccion-tirada');
|
this.#changeTab('pills-seleccion-tirada');
|
||||||
this.summaryTableExtras.addClass('d-none');
|
this.summaryTableExtras.addClass('d-none');
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
const servicios = [];
|
||||||
|
$('.service-checkbox:checked').each(function () {
|
||||||
|
const $servicio = $(this);
|
||||||
|
servicios.push({
|
||||||
|
id: $servicio.attr('id') ?? $(`label[for="${$servicio.attr('id')}"] .service-title`).text().trim(),
|
||||||
|
label: $(`label[for="${$servicio.attr('id')}"] .service-title`).text().trim(),
|
||||||
|
price: $servicio.data('price') ?? $(`label[for="${$servicio.attr('id')}"] .service-price`).text().trim().replace(" " + self.divExtras.data('currency'), ''),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
presupuesto: this.#getPresupuestoData(),
|
||||||
|
servicios: servicios
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/presupuesto/public/resumen',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(body)
|
||||||
|
}).then((data) => {
|
||||||
|
$('#resumen-titulo').text(data.titulo);
|
||||||
|
this.#updateResumenTable(data);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error("Error obtener resumen: ", error);
|
||||||
|
});
|
||||||
|
|
||||||
this.#changeTab('pills-resumen');
|
this.#changeTab('pills-resumen');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1445,19 +1479,84 @@ class PresupuestoCliente {
|
|||||||
|
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* EXTRAS
|
* RESUMEN
|
||||||
******************************/
|
******************************/
|
||||||
#initResumen() {
|
#initResumen() {
|
||||||
|
const $row = $('#presupuesto-row');
|
||||||
|
|
||||||
|
// 1) Transición al cambiar de pestaña (click o programático)
|
||||||
|
$(document).on('shown.bs.tab', '.custom-nav .nav-link', (e) => {
|
||||||
|
const targetSelector = $(e.target).data('bs-target'); // ej: "#pills-resumen"
|
||||||
|
if (targetSelector === '#pills-resumen') {
|
||||||
|
$row.addClass('expanded');
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
} else {
|
||||||
|
$row.removeClass('expanded');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2) Botón "atrás" en Resumen
|
||||||
$(document).on('click', '.btn-change-tab-resumen', (e) => {
|
$(document).on('click', '.btn-change-tab-resumen', (e) => {
|
||||||
const id = e.currentTarget.id;
|
if (e.currentTarget.id === 'btn-prev-resumen') {
|
||||||
if (id === 'btn-prev-resumen') {
|
|
||||||
this.#changeTab('pills-extras');
|
this.#changeTab('pills-extras');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 3) Estado inicial si ya cargas en Resumen
|
||||||
|
$(function () {
|
||||||
|
const activeTarget = $('.custom-nav .nav-link.active').data('bs-target');
|
||||||
|
$('#presupuesto-row').toggleClass('expanded', activeTarget === '#pills-resumen');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#updateResumenTable(data) {
|
||||||
|
this.tablaResumen.find('tbody').empty();
|
||||||
|
|
||||||
|
const lineas = Object.keys(data).filter(k => k.startsWith("linea")).sort((a, b) => {
|
||||||
|
const numA = parseInt(a.replace("linea", ""), 10);
|
||||||
|
const numB = parseInt(b.replace("linea", ""), 10);
|
||||||
|
return numA - numB;
|
||||||
|
});
|
||||||
|
const servicios = data.servicios || [];
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
|
||||||
|
const locale = document.documentElement.lang || 'es-ES';
|
||||||
|
|
||||||
|
for (const l of lineas) {
|
||||||
|
const row = `
|
||||||
|
<tr>
|
||||||
|
<td>${l=="linea0" ? `<img style="max-width: 60px; height: auto;" src="${data.imagen}" alt="${data.imagen_alt}" class="img-fluid" />` : ''}</td>
|
||||||
|
<td>${data[l].descripcion}</td>
|
||||||
|
<td class="text-center">${data[l].cantidad}</td>
|
||||||
|
<td class="text-center">${formateaMoneda(data[l].precio_unitario, 4, locale)}</td>
|
||||||
|
<td class="text-end">${formateaMoneda(data[l].precio_total, 2, locale)}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
total += data[l].precio_total;
|
||||||
|
this.tablaResumen.find('tbody').append(row);
|
||||||
|
}
|
||||||
|
for (const s of servicios) {
|
||||||
|
const row = `
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>${s.descripcion}</td>
|
||||||
|
<td class="text-center">1</td>
|
||||||
|
<td class="text-center">${formateaMoneda(s.precio, 2, locale)}</td>
|
||||||
|
<td class="text-end">${formateaMoneda(s.precio, 2, locale)}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
total += s.precio;
|
||||||
|
this.tablaResumen.find('tbody').append(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#resumen-base').text(formateaMoneda(total, 2, locale));
|
||||||
|
$('#resumen-iva').text(formateaMoneda(total * 0.04, 2, locale));
|
||||||
|
$('#resumen-total').text(formateaMoneda(total * 1.04, 2, locale));
|
||||||
|
}
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* END EXTRAS
|
* END RESUMEN
|
||||||
******************************/
|
******************************/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -75,6 +75,9 @@ $(document).on("submit", "#maquetacionForm", function (e) {
|
|||||||
stored.servicios.datosMaquetacion.resultado.num_paginas_estimadas = json.numPaginasEstimadas;
|
stored.servicios.datosMaquetacion.resultado.num_paginas_estimadas = json.numPaginasEstimadas;
|
||||||
stored.servicios.datosMaquetacion.resultado.precio_pagina_estimado = json.precioPaginaEstimado;
|
stored.servicios.datosMaquetacion.resultado.precio_pagina_estimado = json.precioPaginaEstimado;
|
||||||
stored.servicios.datosMaquetacion.resultado.precio = json.precio;
|
stored.servicios.datosMaquetacion.resultado.precio = json.precio;
|
||||||
|
if(stored.servicios.servicios.includes("maquetacion") === false) {
|
||||||
|
stored.servicios.servicios.push("maquetacion");
|
||||||
|
}
|
||||||
sessionStorage.setItem("formData", JSON.stringify(stored));
|
sessionStorage.setItem("formData", JSON.stringify(stored));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -73,6 +73,9 @@ $(document).on("submit", "#marcapaginasForm", function (e) {
|
|||||||
const stored = JSON.parse(sessionStorage.getItem("formData"));
|
const stored = JSON.parse(sessionStorage.getItem("formData"));
|
||||||
stored.servicios.datosMarcapaginas.resultado.precio_unitario = json.precio_unitario;
|
stored.servicios.datosMarcapaginas.resultado.precio_unitario = json.precio_unitario;
|
||||||
stored.servicios.datosMarcapaginas.resultado.precio = json.precio_total;
|
stored.servicios.datosMarcapaginas.resultado.precio = json.precio_total;
|
||||||
|
if(stored.servicios.servicios.includes("marcapaginas") === false) {
|
||||||
|
stored.servicios.servicios.push("marcapaginas");
|
||||||
|
}
|
||||||
sessionStorage.setItem("formData", JSON.stringify(stored));
|
sessionStorage.setItem("formData", JSON.stringify(stored));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -152,7 +152,7 @@ export function updateExtras() {
|
|||||||
const price = $(`label[for="${$servicio.attr('id')}"] .service-price`).text().trim() || $servicio.attr('price');
|
const price = $(`label[for="${$servicio.attr('id')}"] .service-price`).text().trim() || $servicio.attr('price');
|
||||||
const $row = $('<tr>').append(
|
const $row = $('<tr>').append(
|
||||||
$('<td>').append($('<span>').text(resumen)),
|
$('<td>').append($('<span>').text(resumen)),
|
||||||
$('<td class="text-end">').text(price)
|
$('<td class="text-end data-summary" data-id-summary="servicio-' + $servicio.attr('id') + '">').text(price)
|
||||||
);
|
);
|
||||||
$tbody.append($row);
|
$tbody.append($row);
|
||||||
});
|
});
|
||||||
@ -162,3 +162,4 @@ export function updateExtras() {
|
|||||||
$table.addClass('d-none');
|
$table.addClass('d-none');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
function formateaMoneda(valor, digits = 2, locale = 'es-ES', currency = 'EUR') {
|
export function formateaMoneda(valor, digits = 2, locale = 'es-ES', currency = 'EUR') {
|
||||||
try {
|
try {
|
||||||
return new Intl.NumberFormat(locale, { style: 'currency', currency, minimumFractionDigits: digits, useGrouping: true }).format(valor);
|
return new Intl.NumberFormat(locale, { style: 'currency', currency, minimumFractionDigits: digits, useGrouping: true }).format(valor);
|
||||||
} catch {
|
} catch {
|
||||||
return valor;
|
return valor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export { formateaMoneda };
|
|
||||||
|
|
||||||
|
|
||||||
function formateaNumero({
|
export function formateaNumero({
|
||||||
valor,
|
valor,
|
||||||
digits = 2,
|
digits = 2,
|
||||||
style = 'decimal',
|
style = 'decimal',
|
||||||
@ -31,10 +30,38 @@ function formateaNumero({
|
|||||||
|
|
||||||
return new Intl.NumberFormat(locale, opts).format(n);
|
return new Intl.NumberFormat(locale, opts).format(n);
|
||||||
}
|
}
|
||||||
export { formateaNumero };
|
|
||||||
|
|
||||||
|
|
||||||
function isNumber(value) {
|
export function isNumber(value) {
|
||||||
return !isNaN(Number(value)) && value.trim() !== '';
|
|
||||||
|
if(typeof value === 'string') {
|
||||||
|
if(value.trim() === '') return false;
|
||||||
|
}
|
||||||
|
return !isNaN(Number(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aplana un objeto a "prefijo.clave" (sin arrays)
|
||||||
|
export function dotify(obj, prefix = '') {
|
||||||
|
const out = {};
|
||||||
|
const walk = (o, path) => {
|
||||||
|
Object.entries(o).forEach(([k, v]) => {
|
||||||
|
const key = path ? `${path}.${k}` : k;
|
||||||
|
if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
|
||||||
|
walk(v, key);
|
||||||
|
} else {
|
||||||
|
out[key] = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
walk(obj, prefix);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convierte {a:1, b:2} en {"summary[a]":1, "summary[b]":2}
|
||||||
|
export function bracketPrefix(obj, prefix) {
|
||||||
|
const out = {};
|
||||||
|
Object.entries(obj).forEach(([k, v]) => {
|
||||||
|
out[`${prefix}[${k}]`] = v;
|
||||||
|
});
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
export { isNumber };
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
<div class="animate-fadeInUpBounce">
|
|
||||||
|
|
||||||
<!-- Ribbon Shape -->
|
|
||||||
<div class="card ribbon-box border shadow-none mb-lg-0 material-shadow">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="ribbon ribbon-primary ribbon-shape" th:text="#{presupuesto.resumen}">Resumen
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ribbon-content mt-4">
|
|
||||||
<div id="div-extras" class="hstack gap-2 justify-content-center flex-wrap">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- End Ribbon Shape -->
|
|
||||||
|
|
||||||
<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">
|
|
||||||
<i class=" ri-arrow-left-circle-line label-icon align-middle fs-16 me-2"></i>
|
|
||||||
<span th:text="#{presupuesto.volver-extras}">Volver a extras</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
<div class="animate-fadeInUpBounce">
|
||||||
|
|
||||||
|
<!-- Ribbon Shape -->
|
||||||
|
<div class="card ribbon-box border shadow-none mb-lg-0 material-shadow">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="ribbon ribbon-primary ribbon-shape" th:text="#{presupuesto.resumen}">Resumen
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ribbon-content mt-4">
|
||||||
|
<div id="div-extras" class="hstack gap-2 justify-content-center flex-wrap">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Ribbon Shape -->
|
||||||
|
|
||||||
|
<div class="col-9 mx-auto mt-4">
|
||||||
|
<h5 id="resumen-titulo" class="text-center"></h5>
|
||||||
|
<table id="resumen-tabla-final" class="table table-borderless table-striped mt-3"
|
||||||
|
th:data-currency="#{app.currency}">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th th:text="#{presupuesto.resumen.tabla.descripcion}">Descripción</th>
|
||||||
|
<th th:text="#{presupuesto.resumen.tabla.cantidad}">Cantidad</th>
|
||||||
|
<th th:text="#{presupuesto.resumen.tabla.precio-unidad}">Precio unitario</th>
|
||||||
|
<th th:text="#{presupuesto.resumen.tabla.precio-total}">Precio total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="table-active">
|
||||||
|
<th colspan="4" class="text-end" th:text="#{presupuesto.resumen.tabla.base}">Total</th>
|
||||||
|
<th class="text-end" id="resumen-base">0,00 €</th>
|
||||||
|
</tr>
|
||||||
|
<tr class="table-active">
|
||||||
|
<th colspan="4" class="text-end" th:text="#{presupuesto.resumen.tabla.iva}">IVA (4%)</th>
|
||||||
|
<th class="text-end" id="resumen-iva">0,00 €</th>
|
||||||
|
</tr>
|
||||||
|
<tr class="table-active">
|
||||||
|
<th colspan="4" class="text-end" th:text="#{presupuesto.resumen.tabla.total}">Total con IVA</th>
|
||||||
|
<th class="text-end" id="resumen-total">0,00 €</th>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</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">
|
||||||
|
<i class=" ri-arrow-left-circle-line label-icon align-middle fs-16 me-2"></i>
|
||||||
|
<span th:text="#{presupuesto.volver-extras}">Volver a extras</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div th:unless="${#authorization.expression('isAuthenticated()')}">
|
||||||
|
<button id="btn-add-cart" type="button"
|
||||||
|
class="btn btn-secondary d-flex align-items-center btn-change-tab-resumen">
|
||||||
|
<i class="mdi mdi-login label-icon align-middle fs-16 me-2"></i>
|
||||||
|
<span th:text="#{presupuesto.resumen.inicie-sesion}">Inicie sesión para continuar</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||||
|
<button id="btn-add-cart" type="button"
|
||||||
|
class="btn btn-secondary d-flex align-items-center">
|
||||||
|
<span th:text="#{presupuesto.resumen.agregar-cesta}">Agregar a la cesta</span>
|
||||||
|
<i class="ri-shopping-cart-2-line fs-16 ms-2"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -1,4 +1,4 @@
|
|||||||
<div class="col-xl-3">
|
<div class="col-xl-3 summary-col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@ -20,19 +20,19 @@
|
|||||||
<td class="align-items-center">
|
<td class="align-items-center">
|
||||||
<span th:text="#{presupuesto.resumen-encuadernacion}"></span>
|
<span th:text="#{presupuesto.resumen-encuadernacion}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-encuadernacion" class="text-end">Fresado</td>
|
<td id="summary-encuadernacion" class="text-end data-summary" data-id-summary="encuadernacion">Fresado</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.formato}"></span>
|
<span th:text="#{presupuesto.formato}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-formato" class="text-end"></td>
|
<td id="summary-formato" class="text-end data-summary" data-id-summary="formato"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.paginas}"></span>
|
<span th:text="#{presupuesto.paginas}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-paginas" class="text-end"></td>
|
<td id="summary-paginas" class="text-end data-summary" data-id-summary="paginas"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="ps-3">
|
<td class="ps-3">
|
||||||
@ -60,19 +60,19 @@
|
|||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.tipo-interior}"></span>
|
<span th:text="#{presupuesto.tipo-interior}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-tipo-interior" class="text-end"></td>
|
<td id="summary-tipo-interior" class="text-end data-summary" data-id-summary="tipo-interior"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.papel-interior}"></span>
|
<span th:text="#{presupuesto.papel-interior}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-papel-interior" class="text-end"></td>
|
<td id="summary-papel-interior" class="text-end data-summary" data-id-summary="papel-interior"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.gramaje-interior}"></span>
|
<span th:text="#{presupuesto.gramaje-interior}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-gramaje-interior" class="text-end"></td>
|
<td id="summary-gramaje-interior" class="text-end data-summary" data-id-summary="gramaje-interior"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -87,61 +87,61 @@
|
|||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.tipo-cubierta}"></span>
|
<span th:text="#{presupuesto.tipo-cubierta}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-tapa-cubierta" class="text-end"></td>
|
<td id="summary-tapa-cubierta" class="text-end data-summary" data-id-summary="tapa-cubierta"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="tapa-blanda-row d-none">
|
<tr class="tapa-blanda-row d-none">
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.solapas}"></span>
|
<span th:text="#{presupuesto.solapas}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-cubierta-solapas" class="text-end"></td>
|
<td id="summary-cubierta-solapas" class="text-end data-summary" data-id-summary="cubierta-solapas"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="tapa-blanda-row d-none">
|
<tr class="tapa-blanda-row d-none">
|
||||||
<td class="ps-3">
|
<td class="ps-3">
|
||||||
<span class="ps-3" th:text="#{presupuesto.tamanio-solapa}"></span>
|
<span class="ps-3" th:text="#{presupuesto.tamanio-solapa}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-tamanio-solapa" class="text-end"></td>
|
<td id="summary-tamanio-solapa" class="text-end data-summary" data-id-summary="tamanio-solapa"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="tapa-blanda-row d-none">
|
<tr class="tapa-blanda-row d-none">
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.impresion-cubierta}"></span>
|
<span th:text="#{presupuesto.impresion-cubierta}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-impresion-cubierta" class="text-end"></td>
|
<td id="summary-impresion-cubierta" class="text-end data-summary" data-id-summary="impresion-cubierta"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="tapa-dura-row d-none">
|
<tr class="tapa-dura-row d-none">
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.papel-guardas}"></span>
|
<span th:text="#{presupuesto.papel-guardas}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-papel-guardas" class="text-end"></td>
|
<td id="summary-papel-guardas" class="text-end data-summary" data-id-summary="papel-guardas"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="tapa-dura-row d-none">
|
<tr class="tapa-dura-row d-none">
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.guardas-impresas}"></span>
|
<span th:text="#{presupuesto.guardas-impresas}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-guardas-impresas" class="text-end"></td>
|
<td id="summary-guardas-impresas" class="text-end data-summary" data-id-summary="guardas-impresas"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="tapa-dura-row d-none">
|
<tr class="tapa-dura-row d-none">
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.cabezada}"></span>
|
<span th:text="#{presupuesto.cabezada}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-cabezada" class="text-end"></td>
|
<td id="summary-cabezada" class="text-end data-summary" data-id-summary="cabezada"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.papel-cubierta}"></span>
|
<span th:text="#{presupuesto.papel-cubierta}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-papel-cubierta" class="text-end"></td>
|
<td id="summary-papel-cubierta" class="text-end data-summary" data-id-summary="papel-cubierta"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.gramaje-cubierta}"></span>
|
<span th:text="#{presupuesto.gramaje-cubierta}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-gramaje-cubierta" class="text-end"></td>
|
<td id="summary-gramaje-cubierta" class="text-end data-summary" data-id-summary="gramaje-cubierta"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.acabado}"></span>
|
<span th:text="#{presupuesto.acabado}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-acabado-cubierta" class="text-end"></td>
|
<td id="summary-acabado-cubierta" class="text-end data-summary" data-id-summary="acabado-cubierta"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -156,19 +156,19 @@
|
|||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.papel-gramaje}"></span>
|
<span th:text="#{presupuesto.papel-gramaje}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-sobrecubierta-papel-gramaje" class="text-end"></td>
|
<td id="summary-sobrecubierta-papel-gramaje" class="text-end data-summary" data-id-summary="sobrecubierta-papel-gramaje"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.tamanio-solapa}"></span>
|
<span th:text="#{presupuesto.tamanio-solapa}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-sobrecubierta-tamanio-solapa" class="text-end"></td>
|
<td id="summary-sobrecubierta-tamanio-solapa" class="text-end data-summary" data-id-summary="sobrecubierta-tamanio-solapa"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.acabado}"></span>
|
<span th:text="#{presupuesto.acabado}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-sobrecubierta-acabado" class="text-end"></td>
|
<td id="summary-sobrecubierta-acabado" class="text-end data-summary" data-id-summary="sobrecubierta-acabado"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -183,25 +183,25 @@
|
|||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.papel-gramaje}"></span>
|
<span th:text="#{presupuesto.papel-gramaje}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-faja-papel-gramaje" class="text-end"></td>
|
<td id="summary-faja-papel-gramaje" class="text-end data-summary" data-id-summary="faja-papel-gramaje"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.faja-alto}"></span>
|
<span th:text="#{presupuesto.faja-alto}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-faja-alto-faja" class="text-end"></td>
|
<td id="summary-faja-alto-faja" class="text-end data-summary" data-id-summary="faja-alto-faja"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.tamanio-solapa}"></span>
|
<span th:text="#{presupuesto.tamanio-solapa}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-faja-tamanio-solapa" class="text-end"></td>
|
<td id="summary-faja-tamanio-solapa" class="text-end data-summary" data-id-summary="faja-tamanio-solapa"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span th:text="#{presupuesto.acabado}"></span>
|
<span th:text="#{presupuesto.acabado}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td id="summary-faja-acabado" class="text-end"></td>
|
<td id="summary-faja-acabado" class="text-end data-summary" data-id-summary="faja-acabado"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
<div
|
<div
|
||||||
th:replace="imprimelibros/partials/modal-form :: modal('marcapaginasModal', 'presupuesto.marcapaginas', 'modal-md', 'marcapaginasModalBody')">
|
th:replace="imprimelibros/partials/modal-form :: modal('marcapaginasModal', 'presupuesto.marcapaginas', 'modal-md', 'marcapaginasModalBody')">
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row" id="presupuesto-row">
|
||||||
<div class="col-xl-9">
|
<div class="col-xl-9 col-main">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body checkout-tab">
|
<div class="card-body checkout-tab">
|
||||||
|
|
||||||
@ -118,7 +118,7 @@
|
|||||||
<div class="tab-pane fade" id="pills-resumen" role="tabpanel"
|
<div class="tab-pane fade" id="pills-resumen" role="tabpanel"
|
||||||
aria-labelledby="pills-resumen-tab">
|
aria-labelledby="pills-resumen-tab">
|
||||||
|
|
||||||
<div th:include="~{imprimelibros/presupuestos/presupuestador-items/_resumen.html}"></div>
|
<div th:include="~{imprimelibros/presupuestos/presupuestador-items/_resumen_final.html}"></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- end tab pane -->
|
<!-- end tab pane -->
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user