Compare commits
17 Commits
3f151be9a7
...
64dc8f4003
| Author | SHA1 | Date | |
|---|---|---|---|
| 64dc8f4003 | |||
| 7e8d585347 | |||
| f6a2b196e1 | |||
| 3d9444c3d6 | |||
| ea1c485c80 | |||
| 6b883ffab2 | |||
| baf4cb6ae5 | |||
| 185088f487 | |||
| a1b8fb01fc | |||
| a593a1af78 | |||
| 3f89f323cf | |||
| e7589de194 | |||
| 29dbd21693 | |||
| eb0b5610d8 | |||
| 8b34d6dca9 | |||
| 14f6633b83 | |||
| 3a4e80b1d3 |
6
Dockerfile
Normal file
@ -0,0 +1,6 @@
|
||||
FROM eclipse-temurin:24-jre-alpine
|
||||
WORKDIR /app
|
||||
COPY erp*.jar app.jar
|
||||
EXPOSE 8080
|
||||
ENV JAVA_OPTS=""
|
||||
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar /app/app.jar"]
|
||||
43
docker-compose.prod.yml
Normal file
@ -0,0 +1,43 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
imprimelibros-db:
|
||||
image: mysql:8.0
|
||||
container_name: imprimelibros-db
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: NrXz6DK6UoN
|
||||
MYSQL_DATABASE: imprimelibros
|
||||
MYSQL_USER: imprimelibros_user
|
||||
MYSQL_PASSWORD: om91irrDctd
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
networks:
|
||||
- imprimelibros-network
|
||||
restart: always
|
||||
ports:
|
||||
- "3309:3306" # host:container
|
||||
|
||||
imprimelibros-app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: imprimelibros-app:latest
|
||||
container_name: imprimelibros-app
|
||||
depends_on:
|
||||
- imprimelibros-db
|
||||
environment:
|
||||
SPRING_DATASOURCE_URL: jdbc:mysql://imprimelibros-db:3306/imprimelibros?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Europe/Madrid
|
||||
SPRING_DATASOURCE_USERNAME: imprimelibros_user
|
||||
SPRING_DATASOURCE_PASSWORD: om91irrDctd
|
||||
ports:
|
||||
- "8080:8080"
|
||||
restart: always
|
||||
networks:
|
||||
- imprimelibros-network
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
|
||||
networks:
|
||||
imprimelibros-network:
|
||||
driver: bridge
|
||||
24
docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
imprimelibros-db:
|
||||
image: mysql:8.0
|
||||
container_name: imprimelibros-db
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: NrXz6DK6UoN
|
||||
MYSQL_DATABASE: imprimelibros
|
||||
MYSQL_USER: imprimelibros_user
|
||||
MYSQL_PASSWORD: om91irrDctd
|
||||
ports:
|
||||
- "3309:3306"
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
networks:
|
||||
- imprimelibros-network
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
|
||||
networks:
|
||||
imprimelibros-network:
|
||||
driver: bridge
|
||||
50
pom.xml
@ -1,39 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.imprimelibros</groupId>
|
||||
<artifactId>erp</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>erp</name>
|
||||
<description>ERP for Imprime Libros</description>
|
||||
<url/>
|
||||
<url />
|
||||
<licenses>
|
||||
<license/>
|
||||
<license />
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
<developer />
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
<connection />
|
||||
<developerConnection />
|
||||
<tag />
|
||||
<url />
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>24</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
@ -42,36 +39,59 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nz.net.ultraq.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-layout-dialect</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf.extras</groupId>
|
||||
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -83,4 +103,4 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
@ -0,0 +1,65 @@
|
||||
package com.imprimelibros.erp.config;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Configuration
|
||||
public class InternationalizationConfig implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
public LocaleResolver localeResolver() {
|
||||
SessionLocaleResolver slr = new SessionLocaleResolver();
|
||||
slr.setDefaultLocale(Locale.of("es"));
|
||||
return slr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
|
||||
interceptor.setParamName("lang");
|
||||
registry.addInterceptor(interceptor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageSource messageSource() throws IOException {
|
||||
ReloadableResourceBundleMessageSource source = new ReloadableResourceBundleMessageSource();
|
||||
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
Resource[] resources = resolver.getResources("classpath*:i18n/*.properties");
|
||||
|
||||
// Extraer los nombres base sin extensión ni sufijos (_en, _es, etc.)
|
||||
Set<String> basenames = Arrays.stream(resources)
|
||||
.map(res -> {
|
||||
try {
|
||||
String uri = Objects.requireNonNull(res.getURI()).toString();
|
||||
// Ej: file:/.../i18n/login_en.properties
|
||||
String path = uri.substring(uri.indexOf("/i18n/") + 1); // i18n/login_en.properties
|
||||
String base = path.replaceAll("_[a-z]{2}\\.properties$", "") // login.properties
|
||||
.replaceAll("\\.properties$", "");
|
||||
return "classpath:" + base;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
source.setBasenames(basenames.toArray(new String[0]));
|
||||
source.setDefaultEncoding("UTF-8");
|
||||
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package com.imprimelibros.erp.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers(
|
||||
"/",
|
||||
"/assets/**",
|
||||
"/css/**",
|
||||
"/js/**",
|
||||
"/images/**",
|
||||
"/public/**",
|
||||
"/error",
|
||||
"/presupuesto/public/**")
|
||||
.permitAll()
|
||||
.anyRequest().authenticated())
|
||||
.csrf(csrf -> csrf
|
||||
.ignoringRequestMatchers("/presupuesto/public/**"))
|
||||
.formLogin(login -> login
|
||||
// .loginPage("/login") añadir cuando se tenga login personalizado
|
||||
.permitAll())
|
||||
.logout(logout -> logout.permitAll());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.imprimelibros.erp.configurationERP;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "variables")
|
||||
public class Variable {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String clave;
|
||||
|
||||
private String valor;
|
||||
|
||||
// Getters y setters
|
||||
|
||||
public Long getId() { return id; }
|
||||
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getClave() { return clave; }
|
||||
|
||||
public void setClave(String clave) { this.clave = clave; }
|
||||
|
||||
public String getValor() { return valor; }
|
||||
|
||||
public void setValor(String valor) { this.valor = valor; }
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.imprimelibros.erp.configurationERP;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface VariableRepository extends JpaRepository<Variable, Long> {
|
||||
Optional<Variable> findByClave(String clave);
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.imprimelibros.erp.configurationERP;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class VariableService {
|
||||
|
||||
@Autowired
|
||||
private VariableRepository variableRepository;
|
||||
|
||||
public Integer getValorEntero(String clave) {
|
||||
return variableRepository.findByClave(clave)
|
||||
.<Integer>map(v -> Integer.parseInt(v.getValor()))
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException("No se encontró la variable con clave '" + clave + "'"));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package com.imprimelibros.erp.externalApi;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class AuthService {
|
||||
|
||||
@Value("${safekat.api.url}")
|
||||
private String skApiUrl;
|
||||
@Value("${safekat.api.email}")
|
||||
private String skApiEmail;
|
||||
@Value("${safekat.api.password}")
|
||||
private String skApiPassword;
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
private String currentToken;
|
||||
|
||||
public synchronized String getToken() {
|
||||
if (currentToken == null) {
|
||||
currentToken = fetchNewToken();
|
||||
}
|
||||
return currentToken;
|
||||
}
|
||||
|
||||
public synchronized void invalidateToken() {
|
||||
currentToken = null;
|
||||
}
|
||||
|
||||
private String fetchNewToken() {
|
||||
Map<String, String> credentials = Map.of(
|
||||
"email", this.skApiEmail,
|
||||
"password", this.skApiPassword);
|
||||
ResponseEntity<Map> response = restTemplate.postForEntity(this.skApiUrl + "auth/jwt", credentials, Map.class);
|
||||
return (String) response.getBody().get("access_token");
|
||||
}
|
||||
}
|
||||
165
src/main/java/com/imprimelibros/erp/externalApi/skApiClient.java
Normal file
@ -0,0 +1,165 @@
|
||||
package com.imprimelibros.erp.externalApi;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Service
|
||||
public class skApiClient {
|
||||
|
||||
@Value("${safekat.api.url}")
|
||||
private String skApiUrl;
|
||||
|
||||
private final AuthService authService;
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public skApiClient(AuthService authService) {
|
||||
this.authService = authService;
|
||||
this.restTemplate = new RestTemplate();
|
||||
}
|
||||
|
||||
public String getPrice(Map<String, Object> requestBody) {
|
||||
return performWithRetry(() -> {
|
||||
String url = this.skApiUrl + "api/calcular";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(authService.getToken());
|
||||
|
||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
entity,
|
||||
String.class);
|
||||
|
||||
try {
|
||||
Map<String, Object> responseBody = new ObjectMapper().readValue(response.getBody(), Map.class);
|
||||
|
||||
if (responseBody.get("error") == null) {
|
||||
return new ObjectMapper().writeValueAsString(
|
||||
Map.of("data", responseBody.get("data")));
|
||||
} else {
|
||||
return "{\"error\": 1}";
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
return "{\"error\": 1}";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Integer getMaxSolapas(Map<String, Object> requestBody) {
|
||||
try {
|
||||
String jsonResponse = performWithRetry(() -> {
|
||||
String url = this.skApiUrl + "api/calcular-solapas";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(authService.getToken()); // token actualizado
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("clienteId", requestBody.get("clienteId"));
|
||||
data.put("tamanio", requestBody.get("tamanio"));
|
||||
data.put("tirada", requestBody.get("tirada"));
|
||||
data.put("paginas", requestBody.get("paginas"));
|
||||
data.put("paginasColor", requestBody.get("paginasColor"));
|
||||
data.put("papelInteriorDiferente", 0);
|
||||
data.put("paginasCuadernillo", requestBody.get("paginasCuadernillo"));
|
||||
data.put("tipo", requestBody.get("tipo"));
|
||||
data.put("isColor", requestBody.get("isColor"));
|
||||
data.put("isHq", requestBody.get("isHq"));
|
||||
data.put("interior", requestBody.get("interior"));
|
||||
|
||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(data, headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
entity,
|
||||
String.class);
|
||||
|
||||
return response.getBody();
|
||||
});
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode root = mapper.readTree(jsonResponse);
|
||||
|
||||
if (root.get("data") == null || !root.get("data").isInt()) {
|
||||
throw new RuntimeException("Respuesta inesperada de calcular-solapas: " + jsonResponse);
|
||||
}
|
||||
|
||||
return root.get("data").asInt();
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
// Fallback al 80% del ancho
|
||||
Map<String, Object> tamanio = (Map<String, Object>) requestBody.get("tamanio");
|
||||
if (tamanio == null || tamanio.get("ancho") == null)
|
||||
throw new RuntimeException("Tamaño no válido en la solicitud: " + requestBody);
|
||||
else {
|
||||
int ancho = (int) tamanio.get("ancho");
|
||||
return (int) Math.floor(ancho * 0.8); // 80% del ancho
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Double getRetractilado(Map<String, Object> requestBody) {
|
||||
|
||||
String value = performWithRetry(() -> {
|
||||
String url = this.skApiUrl + "api/calcular-retractilado";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(authService.getToken());
|
||||
|
||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
entity,
|
||||
String.class);
|
||||
|
||||
try {
|
||||
Map<String, Object> responseBody = new ObjectMapper().readValue(response.getBody(), Map.class);
|
||||
return responseBody.get("data").toString();
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
return "0.0"; // Fallback en caso de error
|
||||
}
|
||||
});
|
||||
try {
|
||||
return Double.parseDouble(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new RuntimeException("Error al parsear el valor de retractilado: " + value, e);
|
||||
}
|
||||
}
|
||||
|
||||
/******************
|
||||
* PRIVATE METHODS
|
||||
******************/
|
||||
private String performWithRetry(Supplier<String> request) {
|
||||
try {
|
||||
return request.get();
|
||||
} catch (HttpClientErrorException.Unauthorized e) {
|
||||
// Token expirado, renovar y reintentar
|
||||
authService.invalidateToken();
|
||||
try {
|
||||
return request.get(); // segundo intento
|
||||
} catch (HttpClientErrorException ex) {
|
||||
throw new RuntimeException("La autenticación ha fallado tras renovar el token.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/main/java/com/imprimelibros/erp/home/HomeController.java
Normal file
@ -0,0 +1,48 @@
|
||||
package com.imprimelibros.erp.home;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import com.imprimelibros.erp.configurationERP.VariableService;
|
||||
import com.imprimelibros.erp.i18n.TranslationService;
|
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@Controller
|
||||
public class HomeController {
|
||||
|
||||
@Autowired
|
||||
private TranslationService translationService;
|
||||
@Autowired
|
||||
private VariableService variableService;
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(Model model, Authentication authentication, Locale locale) {
|
||||
|
||||
boolean isAuthenticated = authentication != null && authentication.isAuthenticated()
|
||||
&& !(authentication instanceof AnonymousAuthenticationToken);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
|
||||
List<String> keys = List.of(
|
||||
"presupuesto.plantilla-cubierta",
|
||||
"presupuesto.plantilla-cubierta-text",
|
||||
"presupuesto.impresion-cubierta",
|
||||
"presupuesto.impresion-cubierta-help");
|
||||
|
||||
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||
model.addAttribute("languageBundle", translations);
|
||||
model.addAttribute("pod", variableService.getValorEntero("POD"));
|
||||
model.addAttribute("ancho_alto_min", variableService.getValorEntero("ancho_alto_min"));
|
||||
model.addAttribute("ancho_alto_max", variableService.getValorEntero("ancho_alto_max"));
|
||||
}
|
||||
return "imprimelibros/home";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.imprimelibros.erp.i18n;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Component
|
||||
public class TranslationService {
|
||||
|
||||
@Autowired
|
||||
private MessageSource messageSource;
|
||||
|
||||
public Map<String, String> getTranslations(Locale locale, List<String> keys) {
|
||||
Map<String, String> translations = new HashMap<>();
|
||||
for (String key : keys) {
|
||||
translations.put(key, messageSource.getMessage(key, null, locale));
|
||||
}
|
||||
return translations;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.imprimelibros.erp.login;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
public class LoginController {
|
||||
|
||||
@GetMapping("/login")
|
||||
public String index(Model model, Locale locale) {
|
||||
return "imprimelibros/login/login";
|
||||
}
|
||||
}
|
||||
516
src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java
Normal file
@ -0,0 +1,516 @@
|
||||
package com.imprimelibros.erp.presupuesto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import com.imprimelibros.erp.presupuesto.validation.ConsistentTiradas;
|
||||
import com.imprimelibros.erp.presupuesto.validation.Par;
|
||||
import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups;
|
||||
import com.imprimelibros.erp.presupuesto.validation.Tamanio;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@ConsistentTiradas(groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Tamanio(groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Entity
|
||||
@Table(name = "presupuesto")
|
||||
public class Presupuesto implements Cloneable{
|
||||
|
||||
public enum TipoEncuadernacion {
|
||||
fresado, cosido, grapado, espiral, wireo
|
||||
}
|
||||
|
||||
public enum TipoImpresion {
|
||||
negro, negrohq, color, colorhq
|
||||
}
|
||||
|
||||
public enum TipoCubierta {
|
||||
tapaBlanda, tapaDura, tapaDuraLomoRedondo
|
||||
}
|
||||
|
||||
@Override
|
||||
public Presupuesto clone() {
|
||||
try {
|
||||
return (Presupuesto) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.tipo-encuadernacion}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Column(name = "tipo_encuadernacion")
|
||||
private TipoEncuadernacion tipoEncuadernacion = TipoEncuadernacion.fresado;
|
||||
|
||||
@NotBlank(message = "{presupuesto.errores.titulo}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Column(name = "titulo")
|
||||
private String titulo;
|
||||
|
||||
@Column(name = "autor")
|
||||
private String autor;
|
||||
|
||||
@Column(name = "isbn")
|
||||
private String isbn;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.tirada1}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Column(name = "tirada1")
|
||||
private Integer tirada1;
|
||||
|
||||
@Column(name = "tirada2")
|
||||
private Integer tirada2;
|
||||
|
||||
@Column(name = "tirada3")
|
||||
private Integer tirada3;
|
||||
|
||||
@Column(name = "tirada4")
|
||||
private Integer tirada4;
|
||||
|
||||
@Column(name = "selected_tirada")
|
||||
private Integer selectedTirada;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.ancho}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Column(name = "ancho")
|
||||
private Integer ancho;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.alto}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Column(name = "alto")
|
||||
private Integer alto;
|
||||
|
||||
@Column(name = "formatoPersonalizado")
|
||||
private Boolean formatoPersonalizado;
|
||||
|
||||
@Par(message = "{presupuesto.errores.paginasNegro.par}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@NotNull(message = "{presupuesto.errores.paginasNegro.required}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Column(name = "paginas_negro")
|
||||
private Integer paginasNegro;
|
||||
|
||||
@Par(message = "{presupuesto.errores.paginasColor.par}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@NotNull(message = "{presupuesto.errores.paginasColor.required}", groups = PresupuestoValidationGroups.DatosGenerales.class)
|
||||
@Column(name = "paginas_color")
|
||||
private Integer paginasColor;
|
||||
|
||||
@Column(name = "posicion_paginas_color")
|
||||
private String posicionPaginasColor;
|
||||
|
||||
@Column(name = "paginas_color_total")
|
||||
private Integer paginasColorTotal;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.tipo-impresion}", groups = PresupuestoValidationGroups.Interior.class)
|
||||
@Column(name = "tipo_impresion")
|
||||
private TipoImpresion tipoImpresion = TipoImpresion.negro;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.papel-interior}", groups = PresupuestoValidationGroups.Interior.class)
|
||||
@Column(name = "papel_interior_id")
|
||||
private Integer papelInteriorId;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.gramaje-interior}", groups = PresupuestoValidationGroups.Interior.class)
|
||||
@Column(name = "gramaje_interior")
|
||||
private Integer gramajeInterior;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.tipo-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class)
|
||||
@Column(name = "tipo_cubierta")
|
||||
private TipoCubierta tipoCubierta = TipoCubierta.tapaBlanda;
|
||||
|
||||
@Column(name = "solapas_cubierta")
|
||||
private Boolean solapasCubierta = false;
|
||||
|
||||
@Column(name = "tamanio_solapas_cubierta")
|
||||
private Integer tamanioSolapasCubierta;
|
||||
|
||||
@Column(name = "cubierta_caras")
|
||||
private Integer cubiertaCaras;
|
||||
|
||||
@Column(name = "papel_guardas_id")
|
||||
private Integer papelGuardasId;
|
||||
|
||||
@Column(name = "gramaje_guardas")
|
||||
private Integer gramajeGuardas;
|
||||
|
||||
@Column(name = "guardas_impresas")
|
||||
private Integer guardasImpresas;
|
||||
|
||||
@Column(name = "cabezada")
|
||||
private String cabezada;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.papel-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class)
|
||||
@Column(name = "papel_cubierta_id")
|
||||
private Integer papelCubiertaId = 2;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.gramaje-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class)
|
||||
@Column(name = "gramaje_cubierta")
|
||||
private Integer gramajeCubierta = 240;
|
||||
|
||||
@NotNull(message = "{presupuesto.errores.acabado-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class)
|
||||
@Column(name = "acabado")
|
||||
private Integer acabado = 1;
|
||||
|
||||
@Column(name = "sobrecubierta")
|
||||
private Boolean sobrecubierta = false;
|
||||
@Column(name = "papel_sobrecubierta_id")
|
||||
private Integer papelSobrecubiertaId = 2;
|
||||
@Column(name = "gramaje_sobrecubierta")
|
||||
private Integer gramajeSobrecubierta = 170;
|
||||
@Column(name = "tamanio_solapas_sobrecubierta")
|
||||
private Integer tamanioSolapasSobrecubierta = 80;
|
||||
@Column(name = "acabado_sobrecubierta")
|
||||
private Integer acabadoSobrecubierta = 0; // 0: sin acabado,
|
||||
|
||||
@Column(name = "faja")
|
||||
private Boolean faja = false;
|
||||
@Column(name = "papel_faja_id")
|
||||
private Integer papelFajaId = 2;
|
||||
@Column(name = "gramaje_faja")
|
||||
private Integer gramajeFaja = 170;
|
||||
@Column(name = "tamanio_solapas_faja")
|
||||
private Integer tamanioSolapasFaja = 80;
|
||||
@Column(name = "acabado_faja")
|
||||
private Integer acabadoFaja = 0; // 0: sin acabado
|
||||
@Column(name = "alto_faja")
|
||||
private Integer altoFaja = 0;
|
||||
|
||||
// Getters y Setters
|
||||
public String getAutor() {
|
||||
return autor;
|
||||
}
|
||||
|
||||
public void setAutor(String autor) {
|
||||
this.autor = autor;
|
||||
}
|
||||
|
||||
public String getIsbn() {
|
||||
return isbn;
|
||||
}
|
||||
|
||||
public void setIsbn(String isbn) {
|
||||
this.isbn = isbn;
|
||||
}
|
||||
|
||||
public Integer getTirada1() {
|
||||
return tirada1;
|
||||
}
|
||||
|
||||
public void setTirada1(Integer tirada1) {
|
||||
this.tirada1 = tirada1;
|
||||
}
|
||||
|
||||
public Integer getTirada2() {
|
||||
return tirada2;
|
||||
}
|
||||
|
||||
public void setTirada2(Integer tirada2) {
|
||||
this.tirada2 = tirada2;
|
||||
}
|
||||
|
||||
public Integer getTirada3() {
|
||||
return tirada3;
|
||||
}
|
||||
|
||||
public void setTirada3(Integer tirada3) {
|
||||
this.tirada3 = tirada3;
|
||||
}
|
||||
|
||||
public Integer getTirada4() {
|
||||
return tirada4;
|
||||
}
|
||||
|
||||
public Integer[] getTiradas() {
|
||||
return new Integer[] { tirada1, tirada2, tirada3, tirada4 };
|
||||
}
|
||||
|
||||
public void setTirada4(Integer tirada4) {
|
||||
this.tirada4 = tirada4;
|
||||
}
|
||||
|
||||
public Integer getAncho() {
|
||||
return ancho;
|
||||
}
|
||||
|
||||
public void setAncho(Integer ancho) {
|
||||
this.ancho = ancho;
|
||||
}
|
||||
|
||||
public Integer getAlto() {
|
||||
return alto;
|
||||
}
|
||||
|
||||
public void setAlto(Integer alto) {
|
||||
this.alto = alto;
|
||||
}
|
||||
|
||||
public Boolean getFormatoPersonalizado() {
|
||||
return formatoPersonalizado;
|
||||
}
|
||||
|
||||
public void setFormatoPersonalizado(Boolean formatoPersonalizado) {
|
||||
this.formatoPersonalizado = formatoPersonalizado;
|
||||
}
|
||||
|
||||
public String getTitulo() {
|
||||
return titulo;
|
||||
}
|
||||
|
||||
public void setTitulo(String titulo) {
|
||||
this.titulo = titulo;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getPaginasNegro() {
|
||||
return paginasNegro;
|
||||
}
|
||||
|
||||
public void setPaginasNegro(Integer paginasNegro) {
|
||||
this.paginasNegro = paginasNegro;
|
||||
}
|
||||
|
||||
public Integer getPaginasColor() {
|
||||
return paginasColor;
|
||||
}
|
||||
|
||||
public void setPaginasColor(Integer paginasColor) {
|
||||
this.paginasColor = paginasColor;
|
||||
}
|
||||
|
||||
public TipoEncuadernacion getTipoEncuadernacion() {
|
||||
return tipoEncuadernacion;
|
||||
}
|
||||
|
||||
public void setTipoEncuadernacion(TipoEncuadernacion tipoEncuadernacion) {
|
||||
this.tipoEncuadernacion = tipoEncuadernacion;
|
||||
}
|
||||
|
||||
public String getPosicionPaginasColor() {
|
||||
return posicionPaginasColor;
|
||||
}
|
||||
|
||||
public void setPosicionPaginasColor(String posicionPaginasColor) {
|
||||
this.posicionPaginasColor = posicionPaginasColor;
|
||||
}
|
||||
|
||||
public Integer getPaginasColorTotal() {
|
||||
return paginasColorTotal;
|
||||
}
|
||||
|
||||
public void setPaginasColorTotal(Integer paginasColorTotal) {
|
||||
this.paginasColorTotal = paginasColorTotal;
|
||||
}
|
||||
|
||||
public TipoImpresion getTipoImpresion() {
|
||||
return tipoImpresion;
|
||||
}
|
||||
|
||||
public void setTipoImpresion(TipoImpresion tipoImpresion) {
|
||||
this.tipoImpresion = tipoImpresion;
|
||||
}
|
||||
|
||||
public Integer getPapelInteriorId() {
|
||||
return papelInteriorId;
|
||||
}
|
||||
|
||||
public void setPapelInteriorId(Integer papelInteriorId) {
|
||||
this.papelInteriorId = papelInteriorId;
|
||||
}
|
||||
|
||||
public Integer getGramajeInterior() {
|
||||
return gramajeInterior;
|
||||
}
|
||||
|
||||
public void setGramajeInterior(Integer gramajeInterior) {
|
||||
this.gramajeInterior = gramajeInterior;
|
||||
}
|
||||
|
||||
public TipoCubierta getTipoCubierta() {
|
||||
return tipoCubierta;
|
||||
}
|
||||
|
||||
public void setTipoCubierta(TipoCubierta tipoCubierta) {
|
||||
this.tipoCubierta = tipoCubierta;
|
||||
}
|
||||
|
||||
public Boolean getSolapasCubierta() {
|
||||
return solapasCubierta;
|
||||
}
|
||||
|
||||
public void setSolapasCubierta(Boolean solapasCubierta) {
|
||||
this.solapasCubierta = solapasCubierta;
|
||||
}
|
||||
|
||||
public Integer getTamanioSolapasCubierta() {
|
||||
return tamanioSolapasCubierta;
|
||||
}
|
||||
|
||||
public void setTamanioSolapasCubierta(Integer tamanioSolapasCubierta) {
|
||||
this.tamanioSolapasCubierta = tamanioSolapasCubierta;
|
||||
}
|
||||
|
||||
public Integer getCubiertaCaras() {
|
||||
return cubiertaCaras;
|
||||
}
|
||||
|
||||
public void setCubiertaCaras(Integer cubiertaCaras) {
|
||||
this.cubiertaCaras = cubiertaCaras;
|
||||
}
|
||||
|
||||
public Integer getPapelGuardasId() {
|
||||
return papelGuardasId;
|
||||
}
|
||||
|
||||
public void setPapelGuardasId(Integer papelGuardasId) {
|
||||
this.papelGuardasId = papelGuardasId;
|
||||
}
|
||||
|
||||
public Integer getGramajeGuardas() {
|
||||
return gramajeGuardas;
|
||||
}
|
||||
|
||||
public void setGramajeGuardas(Integer gramajeGuardas) {
|
||||
this.gramajeGuardas = gramajeGuardas;
|
||||
}
|
||||
|
||||
public Integer getGuardasImpresas() {
|
||||
return guardasImpresas;
|
||||
}
|
||||
|
||||
public void setGuardasImpresas(Integer guardasImpresas) {
|
||||
this.guardasImpresas = guardasImpresas;
|
||||
}
|
||||
|
||||
public String getCabezada() {
|
||||
return cabezada;
|
||||
}
|
||||
|
||||
public void setCabezada(String cabezada) {
|
||||
this.cabezada = cabezada;
|
||||
}
|
||||
|
||||
public Integer getPapelCubiertaId() {
|
||||
return papelCubiertaId;
|
||||
}
|
||||
|
||||
public void setPapelCubiertaId(Integer papelCubiertaId) {
|
||||
this.papelCubiertaId = papelCubiertaId;
|
||||
}
|
||||
|
||||
public Integer getGramajeCubierta() {
|
||||
return gramajeCubierta;
|
||||
}
|
||||
|
||||
public void setGramajeCubierta(Integer gramajeCubierta) {
|
||||
this.gramajeCubierta = gramajeCubierta;
|
||||
}
|
||||
|
||||
public Integer getAcabado() {
|
||||
return acabado;
|
||||
}
|
||||
|
||||
public void setAcabado(Integer acabado) {
|
||||
this.acabado = acabado;
|
||||
}
|
||||
|
||||
public Boolean getSobrecubierta() {
|
||||
return sobrecubierta;
|
||||
}
|
||||
|
||||
public void setSobrecubierta(Boolean sobrecubierta) {
|
||||
this.sobrecubierta = sobrecubierta;
|
||||
}
|
||||
|
||||
public Integer getPapelSobrecubiertaId() {
|
||||
return papelSobrecubiertaId;
|
||||
}
|
||||
|
||||
public void setPapelSobrecubiertaId(Integer papelSobrecubiertaId) {
|
||||
this.papelSobrecubiertaId = papelSobrecubiertaId;
|
||||
}
|
||||
|
||||
public Integer getGramajeSobrecubierta() {
|
||||
return gramajeSobrecubierta;
|
||||
}
|
||||
|
||||
public void setGramajeSobrecubierta(Integer gramajeSobrecubierta) {
|
||||
this.gramajeSobrecubierta = gramajeSobrecubierta;
|
||||
}
|
||||
|
||||
public Integer getTamanioSolapasSobrecubierta() {
|
||||
return tamanioSolapasSobrecubierta;
|
||||
}
|
||||
|
||||
public void setTamanioSolapasSobrecubierta(Integer tamanioSolapasSobrecubierta) {
|
||||
this.tamanioSolapasSobrecubierta = tamanioSolapasSobrecubierta;
|
||||
}
|
||||
|
||||
public Integer getAcabadoSobrecubierta() {
|
||||
return acabadoSobrecubierta;
|
||||
}
|
||||
|
||||
public void setAcabadoSobrecubierta(Integer acabadoSobrecubierta) {
|
||||
this.acabadoSobrecubierta = acabadoSobrecubierta;
|
||||
}
|
||||
|
||||
public Boolean getFaja() {
|
||||
return faja;
|
||||
}
|
||||
|
||||
public void setFaja(Boolean faja) {
|
||||
this.faja = faja;
|
||||
}
|
||||
|
||||
public Integer getPapelFajaId() {
|
||||
return papelFajaId;
|
||||
}
|
||||
|
||||
public void setPapelFajaId(Integer papelFajaId) {
|
||||
this.papelFajaId = papelFajaId;
|
||||
}
|
||||
|
||||
public Integer getGramajeFaja() {
|
||||
return gramajeFaja;
|
||||
}
|
||||
|
||||
public void setGramajeFaja(Integer gramajeFaja) {
|
||||
this.gramajeFaja = gramajeFaja;
|
||||
}
|
||||
|
||||
public Integer getTamanioSolapasFaja() {
|
||||
return tamanioSolapasFaja;
|
||||
}
|
||||
|
||||
public void setTamanioSolapasFaja(Integer tamanioSolapasFaja) {
|
||||
this.tamanioSolapasFaja = tamanioSolapasFaja;
|
||||
}
|
||||
|
||||
public Integer getAcabadoFaja() {
|
||||
return acabadoFaja;
|
||||
}
|
||||
|
||||
public void setAcabadoFaja(Integer acabadoFaja) {
|
||||
this.acabadoFaja = acabadoFaja;
|
||||
}
|
||||
|
||||
public Integer getAltoFaja() {
|
||||
return altoFaja;
|
||||
}
|
||||
|
||||
public void setAltoFaja(Integer altoFaja) {
|
||||
this.altoFaja = altoFaja;
|
||||
}
|
||||
|
||||
public Integer getSelectedTirada() {
|
||||
return selectedTirada;
|
||||
}
|
||||
|
||||
public void setSelectedTirada(Integer selectedTirada) {
|
||||
this.selectedTirada = selectedTirada;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,304 @@
|
||||
package com.imprimelibros.erp.presupuesto;
|
||||
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.imprimelibros.erp.externalApi.skApiClient;
|
||||
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
|
||||
import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/presupuesto")
|
||||
public class PresupuestoController {
|
||||
|
||||
@Autowired
|
||||
protected PresupuestoService presupuestoService;
|
||||
|
||||
@Autowired
|
||||
protected skApiClient apiClient;
|
||||
|
||||
@Autowired
|
||||
protected MessageSource messageSource;
|
||||
|
||||
@PostMapping("/public/validar/datos-generales")
|
||||
public ResponseEntity<?> validarDatosGenerales(
|
||||
@Validated(PresupuestoValidationGroups.DatosGenerales.class) Presupuesto presupuesto,
|
||||
BindingResult result, Locale locale) {
|
||||
|
||||
Map<String, String> errores = new HashMap<>();
|
||||
|
||||
// errores de campos individuales
|
||||
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
|
||||
|
||||
// errores globales (@ConsistentTiradas...)
|
||||
result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));
|
||||
|
||||
if (!errores.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
|
||||
// opciones color
|
||||
Map<String, Object> resultado = presupuestoService.obtenerOpcionesColor(presupuesto, locale);
|
||||
|
||||
// opciones papel interior
|
||||
resultado.putAll(presupuestoService.obtenerOpcionesPapelInterior(presupuesto, locale));
|
||||
|
||||
// opciones gramaje interior
|
||||
resultado.putAll(presupuestoService.obtenerOpcionesGramajeInterior(presupuesto));
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/validar/interior")
|
||||
public ResponseEntity<?> validarInterior(
|
||||
@Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto,
|
||||
BindingResult result, Locale locale) {
|
||||
|
||||
Map<String, String> errores = new HashMap<>();
|
||||
|
||||
// errores de campos individuales
|
||||
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
|
||||
|
||||
// errores globales (@ConsistentTiradas...)
|
||||
result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));
|
||||
|
||||
if (!errores.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
Map<String, Object> resultado = new HashMap<>();
|
||||
resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto)));
|
||||
resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale));
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/validar/cubierta")
|
||||
public ResponseEntity<?> validarCubierta(
|
||||
@Validated(PresupuestoValidationGroups.Cubierta.class) Presupuesto presupuesto,
|
||||
@RequestParam(name = "calcular", defaultValue = "true") boolean calcular,
|
||||
BindingResult result, Locale locale) {
|
||||
|
||||
Map<String, String> errores = new HashMap<>();
|
||||
|
||||
// errores de campos individuales
|
||||
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
|
||||
|
||||
// errores globales (@ConsistentTiradas...)
|
||||
result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));
|
||||
|
||||
if (!errores.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
|
||||
if (calcular) {
|
||||
|
||||
HashMap<String, Object> price = new HashMap<>();
|
||||
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")) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(messageSource.getMessage("presupuesto.error-obtener-precio", null, locale));
|
||||
}
|
||||
return ResponseEntity.ok(price.get("data"));
|
||||
}
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@PostMapping("/public/validar/seleccion-tirada")
|
||||
public ResponseEntity<?> validarSeleccionTirada(
|
||||
Presupuesto presupuesto,
|
||||
BindingResult result, Locale locale) {
|
||||
|
||||
Map<String, String> errores = new HashMap<>();
|
||||
|
||||
// errores de campos individuales
|
||||
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
|
||||
|
||||
// errores globales (@ConsistentTiradas...)
|
||||
result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));
|
||||
|
||||
if (!errores.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
|
||||
Map<String, Object> resultado = new HashMap<>();
|
||||
// servicios extra
|
||||
resultado.putAll(presupuestoService.obtenerServiciosExtras(presupuesto, locale, apiClient));
|
||||
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/get-papel-interior")
|
||||
public ResponseEntity<?> getPapelInterior(
|
||||
@Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto,
|
||||
BindingResult result, Locale locale) {
|
||||
|
||||
Map<String, String> errores = new HashMap<>();
|
||||
|
||||
// errores de campos individuales
|
||||
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
|
||||
|
||||
if (!errores.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
|
||||
// opciones color
|
||||
Map<String, Object> resultado = presupuestoService.obtenerOpcionesPapelInterior(presupuesto, locale);
|
||||
|
||||
// opciones gramaje interior
|
||||
resultado.putAll(presupuestoService.obtenerOpcionesGramajeInterior(presupuesto));
|
||||
List<String> opciones = (List<String>) resultado.get("opciones_gramaje_interior");
|
||||
|
||||
if (opciones != null && !opciones.isEmpty()) {
|
||||
String gramajeActual = presupuesto.getGramajeInterior().toString();
|
||||
if (!opciones.contains(gramajeActual)) {
|
||||
presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción
|
||||
}
|
||||
}
|
||||
|
||||
resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto)));
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/get-gramaje-interior")
|
||||
public ResponseEntity<?> getGramajeInterior(
|
||||
@Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto,
|
||||
BindingResult result) {
|
||||
|
||||
Map<String, String> errores = new HashMap<>();
|
||||
|
||||
// errores de campos individuales
|
||||
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
|
||||
|
||||
if (!errores.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
|
||||
Map<String, Object> resultado = presupuestoService.obtenerOpcionesGramajeInterior(presupuesto);
|
||||
List<String> opciones = (List<String>) resultado.get("opciones_gramaje_interior");
|
||||
|
||||
if (opciones != null && !opciones.isEmpty()) {
|
||||
String gramajeActual = presupuesto.getGramajeInterior().toString();
|
||||
if (!opciones.contains(gramajeActual)) {
|
||||
presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción
|
||||
}
|
||||
}
|
||||
resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto)));
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/get-max-solapas")
|
||||
public ResponseEntity<?> getMaxSolapas(
|
||||
@Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto,
|
||||
BindingResult result) {
|
||||
|
||||
Map<String, String> errores = new HashMap<>();
|
||||
|
||||
// errores de campos individuales
|
||||
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
|
||||
|
||||
if (!errores.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
|
||||
Map<String, Object> resultado = new HashMap<>();
|
||||
resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto)));
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/get-papel-cubierta")
|
||||
public ResponseEntity<?> getPapelCubierta(
|
||||
Presupuesto presupuesto,
|
||||
BindingResult result, Locale locale) {
|
||||
|
||||
Map<String, Object> resultado = new HashMap<>();
|
||||
Map<String, Object> papelesCubierta = presupuestoService.obtenerOpcionesPapelCubierta(presupuesto, locale);
|
||||
List<ImagenPresupuesto> opciones = (List<ImagenPresupuesto>) presupuestoService
|
||||
.obtenerOpcionesPapelCubierta(presupuesto, locale)
|
||||
.get("opciones_papel_cubierta");
|
||||
|
||||
if (opciones != null && opciones.stream().noneMatch(
|
||||
o -> o.getExtra_data().get("sk-id").equals(String.valueOf(presupuesto.getPapelCubiertaId())))) {
|
||||
presupuesto.setPapelCubiertaId(Integer.valueOf(opciones.get(0).getExtra_data().get("sk-id")));
|
||||
}
|
||||
resultado.putAll(papelesCubierta);
|
||||
|
||||
resultado.putAll(presupuestoService.obtenerOpcionesGramajeCubierta(presupuesto));
|
||||
List<String> gramajesCubierta = (List<String>) resultado.get("opciones_gramaje_cubierta");
|
||||
if (gramajesCubierta != null && !gramajesCubierta.isEmpty()) {
|
||||
String gramajeActual = presupuesto.getGramajeCubierta().toString();
|
||||
if (!gramajesCubierta.contains(gramajeActual)) {
|
||||
presupuesto.setGramajeCubierta(Integer.parseInt(gramajesCubierta.get(0))); // Asignar primera opción
|
||||
}
|
||||
}
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/get-gramaje-cubierta")
|
||||
public ResponseEntity<?> getGramajeCubierta(
|
||||
Presupuesto presupuesto,
|
||||
BindingResult result) {
|
||||
|
||||
Map<String, Object> resultado = presupuestoService.obtenerOpcionesGramajeCubierta(presupuesto);
|
||||
List<String> gramajesCubierta = (List<String>) resultado.get("opciones_gramaje_cubierta");
|
||||
if (gramajesCubierta != null && !gramajesCubierta.isEmpty()) {
|
||||
String gramajeActual = presupuesto.getGramajeCubierta().toString();
|
||||
if (!gramajesCubierta.contains(gramajeActual)) {
|
||||
presupuesto.setGramajeCubierta(Integer.parseInt(gramajesCubierta.get(0))); // Asignar primera opción
|
||||
}
|
||||
}
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/get-acabados-cubierta")
|
||||
public ResponseEntity<?> getAcabadosCubierta(
|
||||
Presupuesto presupuesto,
|
||||
BindingResult result, Locale locale) {
|
||||
|
||||
Map<String, Object> resultado = presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale);
|
||||
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@PostMapping("/public/get-price")
|
||||
public ResponseEntity<?> getPrice(
|
||||
Presupuesto presupuesto,
|
||||
BindingResult result, Locale locale) {
|
||||
|
||||
Map<String, String> errores = new HashMap<>();
|
||||
// errores de campos individuales
|
||||
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
|
||||
// errores globales (@ConsistentTiradas...)
|
||||
result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));
|
||||
if (!errores.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
String price = apiClient.getPrice(presupuestoService.toSkApiRequest(presupuesto));
|
||||
if (price == null || price.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body("No se pudo obtener el precio. Intente nuevamente.");
|
||||
}
|
||||
return ResponseEntity.ok(price);
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,581 @@
|
||||
package com.imprimelibros.erp.presupuesto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Locale;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
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.externalApi.skApiClient;
|
||||
|
||||
@Service
|
||||
public class PresupuestoService {
|
||||
|
||||
@Autowired
|
||||
protected VariableService variableService;
|
||||
|
||||
@Autowired
|
||||
protected MessageSource messageSource;
|
||||
|
||||
@Autowired
|
||||
protected skApiClient skApiClient;
|
||||
|
||||
private final PresupuestadorItems presupuestadorItems;
|
||||
|
||||
public PresupuestoService(PresupuestadorItems presupuestadorItems) {
|
||||
this.presupuestadorItems = presupuestadorItems;
|
||||
}
|
||||
|
||||
public boolean validateDatosGenerales(int[] tiradas) {
|
||||
|
||||
for (int tirada : tiradas) {
|
||||
if (tirada <= 0) {
|
||||
return false; // Invalid tirada found
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Boolean isPOD(Presupuesto presupuesto) {
|
||||
|
||||
int pod_value = variableService.getValorEntero("POD");
|
||||
return (presupuesto.getTirada1() != null && presupuesto.getTirada1() <= pod_value) ||
|
||||
(presupuesto.getTirada2() != null && presupuesto.getTirada2() <= pod_value) ||
|
||||
(presupuesto.getTirada3() != null && presupuesto.getTirada3() <= pod_value) ||
|
||||
(presupuesto.getTirada4() != null && presupuesto.getTirada4() <= pod_value);
|
||||
}
|
||||
|
||||
public Map<String, Object> obtenerOpcionesColor(Presupuesto presupuesto, Locale locale) {
|
||||
|
||||
List<ImagenPresupuesto> opciones = new ArrayList<>();
|
||||
|
||||
if (presupuesto.getPaginasColor() > 0) {
|
||||
if (!this.isPOD(presupuesto)) {
|
||||
// POD solo color foto
|
||||
ImagenPresupuesto opcionColor = this.presupuestadorItems.getImpresionColor(locale);
|
||||
opcionColor.setSelected(Presupuesto.TipoImpresion.color.equals(presupuesto.getTipoImpresion()));
|
||||
opciones.add(opcionColor);
|
||||
}
|
||||
ImagenPresupuesto opcionColorHq = this.presupuestadorItems.getImpresionColorPremium(locale);
|
||||
if (Presupuesto.TipoImpresion.colorhq.equals(presupuesto.getTipoImpresion()))
|
||||
opcionColorHq.setSelected(true);
|
||||
opciones.add(opcionColorHq);
|
||||
} else {
|
||||
ImagenPresupuesto opcionNegro = this.presupuestadorItems.getImpresionNegro(locale);
|
||||
if (Presupuesto.TipoImpresion.negro.equals(presupuesto.getTipoImpresion()))
|
||||
opcionNegro.setSelected(true);
|
||||
opciones.add(opcionNegro);
|
||||
ImagenPresupuesto opcionNegroHq = this.presupuestadorItems.getImpresionNegroPremium(locale);
|
||||
if (Presupuesto.TipoImpresion.negrohq.equals(presupuesto.getTipoImpresion()))
|
||||
opcionNegroHq.setSelected(true);
|
||||
opciones.add(opcionNegroHq);
|
||||
}
|
||||
|
||||
boolean opcionSeleccionada = opciones.stream().anyMatch(ImagenPresupuesto::isSelected);
|
||||
if (!opcionSeleccionada) {
|
||||
opciones.get(0).setSelected(true);
|
||||
presupuesto.setTipoImpresion(Presupuesto.TipoImpresion.valueOf(opciones.get(0).getId()));
|
||||
}
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("opciones_color", opciones);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public Map<String, Object> obtenerOpcionesPapelInterior(Presupuesto presupuesto, Locale locale) {
|
||||
|
||||
List<ImagenPresupuesto> opciones = new ArrayList<>();
|
||||
|
||||
opciones.add(this.presupuestadorItems.getPapelOffsetBlanco(locale));
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
|
||||
opciones.add(this.presupuestadorItems.getPapelOffsetBlancoVolumen(locale));
|
||||
}
|
||||
opciones.add(this.presupuestadorItems.getPapelOffsetAhuesado(locale));
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
|
||||
opciones.add(this.presupuestadorItems.getPapelOffsetAhuesadoVolumen(locale));
|
||||
}
|
||||
opciones.add(this.presupuestadorItems.getPapelEstucadoMate(locale));
|
||||
|
||||
for (ImagenPresupuesto imagenPresupuesto : opciones) {
|
||||
imagenPresupuesto.setSelected(
|
||||
presupuesto.getPapelInteriorId() != null
|
||||
&& imagenPresupuesto.getExtra_data().get("sk-id").equals(
|
||||
String.valueOf(presupuesto.getPapelInteriorId())));
|
||||
}
|
||||
|
||||
boolean yaSeleccionado = opciones.stream().anyMatch(ImagenPresupuesto::isSelected);
|
||||
|
||||
if (!yaSeleccionado && !opciones.isEmpty()) {
|
||||
ImagenPresupuesto primeraOpcion = opciones.get(0);
|
||||
primeraOpcion.setSelected(true);
|
||||
presupuesto.setPapelInteriorId(Integer.parseInt(primeraOpcion.getExtra_data().get("sk-id")));
|
||||
}
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("opciones_papel_interior", opciones);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public Map<String, Object> obtenerOpcionesGramajeInterior(Presupuesto presupuesto) {
|
||||
|
||||
List<String> gramajes = new ArrayList<>();
|
||||
|
||||
final int BLANCO_OFFSET_ID = 3;
|
||||
final int BLANCO_OFFSET_VOLUMEN_ID = 7;
|
||||
final int AHUESADO_OFFSET_ID = 4;
|
||||
final int AHUESADO_OFFSET_VOLUMEN_ID = 6;
|
||||
final int ESTUCADO_MATE_ID = 2;
|
||||
|
||||
if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == BLANCO_OFFSET_ID) {
|
||||
|
||||
gramajes.add("80");
|
||||
gramajes.add("90");
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) {
|
||||
gramajes.add("100");
|
||||
gramajes.add("120");
|
||||
gramajes.add("150");
|
||||
gramajes.add("170");
|
||||
}
|
||||
} else if (presupuesto.getPapelInteriorId() != null
|
||||
&& presupuesto.getPapelInteriorId() == BLANCO_OFFSET_VOLUMEN_ID) {
|
||||
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
|
||||
gramajes.add("80");
|
||||
}
|
||||
|
||||
} else if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == AHUESADO_OFFSET_ID) {
|
||||
|
||||
gramajes.add("80");
|
||||
gramajes.add("90");
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) {
|
||||
gramajes.add("100");
|
||||
}
|
||||
|
||||
} else if (presupuesto.getPapelInteriorId() != null
|
||||
&& presupuesto.getPapelInteriorId() == AHUESADO_OFFSET_VOLUMEN_ID) {
|
||||
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
|
||||
gramajes.add("70");
|
||||
gramajes.add("80");
|
||||
}
|
||||
|
||||
} else if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == ESTUCADO_MATE_ID) {
|
||||
|
||||
if (presupuesto.getTipoImpresion() != Presupuesto.TipoImpresion.color) {
|
||||
gramajes.add("90");
|
||||
}
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) {
|
||||
gramajes.add("100");
|
||||
gramajes.add("115");
|
||||
}
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
|
||||
gramajes.add("120");
|
||||
}
|
||||
if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq ||
|
||||
presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) {
|
||||
gramajes.add("125");
|
||||
gramajes.add("135");
|
||||
gramajes.add("150");
|
||||
gramajes.add("170");
|
||||
gramajes.add("200");
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("opciones_gramaje_interior", gramajes);
|
||||
return response;
|
||||
}
|
||||
|
||||
public Map<String, Object> obtenerOpcionesPapelCubierta(Presupuesto presupuesto, Locale locale) {
|
||||
|
||||
List<ImagenPresupuesto> opciones = new ArrayList<>();
|
||||
|
||||
if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaBlanda) {
|
||||
opciones.add(this.presupuestadorItems.getCartulinaGraficaCubierta(locale));
|
||||
}
|
||||
opciones.add(this.presupuestadorItems.getEstucadoMateCubierta(locale));
|
||||
|
||||
for (ImagenPresupuesto imagenPresupuesto : opciones) {
|
||||
imagenPresupuesto.setSelected(
|
||||
presupuesto.getPapelCubiertaId() != null
|
||||
&& imagenPresupuesto.getExtra_data().get("sk-id").equals(
|
||||
String.valueOf(presupuesto.getPapelCubiertaId())));
|
||||
}
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("opciones_papel_cubierta", opciones);
|
||||
return response;
|
||||
}
|
||||
|
||||
public Map<String, Object> obtenerOpcionesGramajeCubierta(Presupuesto presupuesto) {
|
||||
|
||||
List<String> gramajes = new ArrayList<>();
|
||||
|
||||
final int CARTULINA_GRAFICA_ID = 5;
|
||||
final int ESTUCADO_MATE_ID = 2;
|
||||
|
||||
if (presupuesto.getPapelCubiertaId() != null && presupuesto.getPapelCubiertaId() == CARTULINA_GRAFICA_ID) {
|
||||
gramajes.add("240");
|
||||
gramajes.add("270");
|
||||
gramajes.add("300");
|
||||
gramajes.add("350");
|
||||
} else if (presupuesto.getPapelCubiertaId() != null && presupuesto.getPapelCubiertaId() == ESTUCADO_MATE_ID) {
|
||||
if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaBlanda) {
|
||||
|
||||
gramajes.add("250");
|
||||
gramajes.add("300");
|
||||
gramajes.add("350");
|
||||
} else {
|
||||
gramajes.add("170");
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("opciones_gramaje_cubierta", gramajes);
|
||||
return response;
|
||||
}
|
||||
|
||||
public Map<String, Object> toSkApiRequest(Presupuesto presupuesto) {
|
||||
|
||||
final int SK_CLIENTE_ID = 1284;
|
||||
final int SK_PAGINAS_CUADERNILLO = 32;
|
||||
|
||||
Map<String, Object> tamanio = Map.of(
|
||||
"ancho", presupuesto.getAncho(),
|
||||
"alto", presupuesto.getAlto());
|
||||
Map<String, Object> interior = Map.of(
|
||||
"papelInterior", presupuesto.getPapelInteriorId(),
|
||||
"gramajeInterior", presupuesto.getGramajeInterior());
|
||||
Map<String, Object> cubierta = Map.of(
|
||||
"tipoCubierta", presupuesto.getTipoCubierta().name(),
|
||||
"papelCubierta", presupuesto.getPapelCubiertaId(),
|
||||
"gramajeCubierta", presupuesto.getGramajeCubierta(),
|
||||
"carasImpresion", presupuesto.getCubiertaCaras(),
|
||||
"solapas", presupuesto.getSolapasCubierta() ? presupuesto.getTamanioSolapasCubierta() : 0,
|
||||
"acabado", presupuesto.getAcabado(),
|
||||
"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())
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList()));
|
||||
body.put("tamanio", tamanio);
|
||||
body.put("tipo", presupuesto.getTipoEncuadernacion());
|
||||
body.put("clienteId", SK_CLIENTE_ID);
|
||||
body.put("isColor", presupuesto.getTipoImpresion().name().contains("color") ? 1 : 0);
|
||||
body.put("isHq", presupuesto.getTipoImpresion().name().contains("hq") ? 1 : 0);
|
||||
body.put("paginas", presupuesto.getPaginasNegro() + presupuesto.getPaginasColor());
|
||||
body.put("paginasColor", presupuesto.getPaginasColor());
|
||||
body.put("paginasCuadernillo", SK_PAGINAS_CUADERNILLO);
|
||||
body.put("interior", interior);
|
||||
body.put("cubierta", cubierta);
|
||||
body.put("guardas", null);
|
||||
if (presupuesto.getSobrecubierta()) {
|
||||
Map<String, Object> sobrecubierta = new HashMap<>();
|
||||
sobrecubierta.put("papel", presupuesto.getPapelSobrecubiertaId());
|
||||
sobrecubierta.put("gramaje", presupuesto.getGramajeSobrecubierta());
|
||||
sobrecubierta.put("solapas", presupuesto.getTamanioSolapasSobrecubierta());
|
||||
sobrecubierta.put("acabado", presupuesto.getAcabadoSobrecubierta());
|
||||
body.put("sobrecubierta", sobrecubierta);
|
||||
}
|
||||
|
||||
if (presupuesto.getFaja()) {
|
||||
Map<String, Object> faja = new HashMap<>();
|
||||
faja.put("papel", presupuesto.getPapelFajaId());
|
||||
faja.put("gramaje", presupuesto.getGramajeFaja());
|
||||
faja.put("solapas", presupuesto.getTamanioSolapasFaja());
|
||||
faja.put("acabado", presupuesto.getAcabadoFaja());
|
||||
faja.put("alto", presupuesto.getAltoFaja());
|
||||
body.put("faja", faja);
|
||||
}
|
||||
// body.put("servicios", servicios);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
public Integer getTipoImpresionId(Presupuesto presupuesto) {
|
||||
|
||||
if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.fresado) {
|
||||
if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura ||
|
||||
presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
|
||||
return 1; // Fresado tapa dura
|
||||
} else {
|
||||
return 2; // Fresado tapa blanda
|
||||
}
|
||||
} else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.cosido) {
|
||||
if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura ||
|
||||
presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
|
||||
return 3; // Cosido tapa dura
|
||||
} else {
|
||||
return 4; // Cosido tapa blanda
|
||||
}
|
||||
} else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.espiral) {
|
||||
if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura ||
|
||||
presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
|
||||
return 5; // Espiral tapa dura
|
||||
} else {
|
||||
return 6; // Espiral tapa blanda
|
||||
}
|
||||
} else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.wireo) {
|
||||
if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura ||
|
||||
presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) {
|
||||
return 7; // Wireo tapa dura
|
||||
} else {
|
||||
return 8; // Wireo tapa blanda
|
||||
}
|
||||
} else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.grapado) {
|
||||
return 21; // Grapado
|
||||
} else {
|
||||
return 0; // Default case, no valid type
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> obtenerOpcionesAcabadosCubierta(Presupuesto presupuesto, Locale locale) {
|
||||
Map<String, Object> resultado = new HashMap<>();
|
||||
List<Object> opciones = new ArrayList<>();
|
||||
|
||||
opciones.add(new HashMap<>() {
|
||||
{
|
||||
put("name", messageSource.getMessage("presupuesto.acabado-ninguno", null, locale));
|
||||
put("sk-id", 0);
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<>() {
|
||||
{
|
||||
put("name", messageSource.getMessage("presupuesto.acabado-plastificado-brillo-1c", null, locale));
|
||||
put("sk-id", 1);
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<>() {
|
||||
{
|
||||
put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-1c", null, locale));
|
||||
put("sk-id", 5);
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<>() {
|
||||
{
|
||||
put("name",
|
||||
messageSource.getMessage("presupuesto.acabado-plastificado-mate-1c-antirrayado", null, locale));
|
||||
put("sk-id", 8);
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<>() {
|
||||
{
|
||||
put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi", null, locale));
|
||||
put("sk-id", 2);
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<>() {
|
||||
{
|
||||
put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi3d", null, locale));
|
||||
put("sk-id", 3);
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<>() {
|
||||
{
|
||||
put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi-braile", null, locale));
|
||||
put("sk-id", 4);
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<>() {
|
||||
{
|
||||
put("name", messageSource.getMessage("presupuesto.acabado-plastificado-sandy-1c", null, locale));
|
||||
put("sk-id", 9);
|
||||
}
|
||||
});
|
||||
|
||||
resultado.put("opciones_acabados_cubierta", opciones);
|
||||
return resultado;
|
||||
}
|
||||
|
||||
public Map<String, Object> aplicarMargenTiradas(Map<String, Object> data) {
|
||||
|
||||
// implementar margenes
|
||||
return (Map<String, Object>) data;
|
||||
}
|
||||
|
||||
public String obtenerPrecioRetractilado(Presupuesto presupuesto, Locale locale) {
|
||||
Integer[] tiradas = presupuesto.getTiradas();
|
||||
Integer tirada_min = Arrays.stream(tiradas)
|
||||
.filter(Objects::nonNull)
|
||||
.min(Integer::compareTo)
|
||||
.orElse(0);
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("tirada",
|
||||
presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : tirada_min);
|
||||
Double precio_retractilado = skApiClient.getRetractilado(requestBody);
|
||||
return precio_retractilado != null
|
||||
? NumberFormat.getNumberInstance(locale)
|
||||
.format(Math.round(precio_retractilado * 100.0) / 100.0)
|
||||
: "0,00";
|
||||
}
|
||||
|
||||
public Map<String, Object> obtenerServiciosExtras(Presupuesto presupuesto, Locale locale, skApiClient apiClient) {
|
||||
|
||||
List<Object> opciones = new ArrayList<>();
|
||||
|
||||
Double price_prototipo = this.obtenerPrototipo(presupuesto, apiClient);
|
||||
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "retractilado");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-retractilado", null, locale));
|
||||
put("description", "");
|
||||
put("price", obtenerPrecioRetractilado(presupuesto, locale));
|
||||
put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "service-isbn");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-isbn", null, locale));
|
||||
put("description", "");
|
||||
put("price", "30");
|
||||
put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "deposito-legal");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-deposito-legal", null, locale));
|
||||
put("description",
|
||||
messageSource.getMessage("presupuesto.extras-deposito-legal-descripcion", null, locale));
|
||||
put("price", "30");
|
||||
put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "revision-archivos");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-revision-archivos", null, locale));
|
||||
put("description", "");
|
||||
put("price", "50");
|
||||
put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "maquetacion-cubierta");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-maquetacion-cubierta", null, locale));
|
||||
put("description", "");
|
||||
put("price", "50");
|
||||
put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "ferro-digital");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-ferro-digital", null, locale));
|
||||
put("description", "");
|
||||
put("price", "0");
|
||||
put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
|
||||
put("checked", "true");
|
||||
put("allowChange", "false");
|
||||
put("ribbonText", messageSource.getMessage("presupuesto.extras-ferro-digital-ribbon", null, locale));
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "ejemplar-prueba");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-ejemplar-prueba", null, locale));
|
||||
put("description", "");
|
||||
if(price_prototipo == 0.0) {
|
||||
put("price", messageSource.getMessage("presupuesto.consultar-soporte", null, locale));
|
||||
put("priceUnit", "");
|
||||
} else {
|
||||
put("price", NumberFormat.getNumberInstance(locale)
|
||||
.format(Math.round(price_prototipo * 100.0) / 100.0));
|
||||
put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale));
|
||||
}
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "marcapaginas");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-marcapaginas", null, locale));
|
||||
put("description", "");
|
||||
put("price", messageSource.getMessage("presupuesto.extras-calcular", null, locale));
|
||||
}
|
||||
});
|
||||
opciones.add(new HashMap<String, String>() {
|
||||
{
|
||||
put("id", "maquetacion");
|
||||
put("title", messageSource.getMessage("presupuesto.extras-maquetacion", null, locale));
|
||||
put("description", "");
|
||||
put("price", messageSource.getMessage("presupuesto.extras-calcular", null, locale));
|
||||
}
|
||||
});
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("servicios_extra", opciones);
|
||||
return response;
|
||||
}
|
||||
|
||||
private Double obtenerPrototipo(Presupuesto presupuesto, skApiClient apiClient) {
|
||||
|
||||
// Obtenemos el precio de 1 unidad para el ejemplar de prueba
|
||||
HashMap<String, Object> price = new HashMap<>();
|
||||
// make a copy of "presupuesto" to avoid modifying the original object
|
||||
Presupuesto presupuestoTemp = presupuesto.clone();
|
||||
presupuestoTemp.setTirada1(1);
|
||||
presupuestoTemp.setTirada2(null);
|
||||
presupuestoTemp.setTirada3(null);
|
||||
presupuestoTemp.setTirada4(null);
|
||||
if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.color) {
|
||||
presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.colorhq);
|
||||
} else if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.negro) {
|
||||
presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.negrohq);
|
||||
}
|
||||
String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuestoTemp));
|
||||
Double price_prototipo = 0.0;
|
||||
try {
|
||||
price = new ObjectMapper().readValue(priceStr, new TypeReference<>() {
|
||||
});
|
||||
price_prototipo = ((List<Double>) ((Map<String, Object>) price.get("data")).get("precios")).get(0);
|
||||
if (price_prototipo < 25) {
|
||||
price_prototipo = 25.0;
|
||||
}
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
} catch (Exception exception) {
|
||||
}
|
||||
return price_prototipo;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package com.imprimelibros.erp.presupuesto.classes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ImagenPresupuesto {
|
||||
|
||||
private String id;
|
||||
private String imagen;
|
||||
private String alt;
|
||||
private String texto;
|
||||
private boolean selected = false;
|
||||
private Map<String, String> extra_data;
|
||||
|
||||
// Constructores
|
||||
public ImagenPresupuesto() {}
|
||||
|
||||
|
||||
public ImagenPresupuesto(String id, String imagen, String alt, String texto, boolean selected) {
|
||||
this.id = id;
|
||||
this.imagen = imagen;
|
||||
this.alt = alt;
|
||||
this.texto = texto;
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
|
||||
public ImagenPresupuesto(String id, String imagen, String alt, String texto, Map<String, String> extra_data,boolean selected) {
|
||||
this.id = id;
|
||||
this.imagen = imagen;
|
||||
this.alt = alt;
|
||||
this.texto = texto;
|
||||
this.selected = selected;
|
||||
this.extra_data = extra_data;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getImagen() {
|
||||
return imagen;
|
||||
}
|
||||
|
||||
public void setImagen(String imagen) {
|
||||
this.imagen = imagen;
|
||||
}
|
||||
|
||||
public String getAlt() {
|
||||
return alt;
|
||||
}
|
||||
|
||||
public void setAlt(String alt) {
|
||||
this.alt = alt;
|
||||
}
|
||||
|
||||
public String getTexto() {
|
||||
return texto;
|
||||
}
|
||||
|
||||
public void setTexto(String texto) {
|
||||
this.texto = texto;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
public Map<String, String> getExtra_data() {
|
||||
return extra_data;
|
||||
}
|
||||
|
||||
public void setExtra_data(Map<String, String> extra_data) {
|
||||
this.extra_data = extra_data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
package com.imprimelibros.erp.presupuesto.classes;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class PresupuestadorItems {
|
||||
|
||||
@Autowired
|
||||
private MessageSource messageSource;
|
||||
|
||||
public ImagenPresupuesto getImpresionNegro(Locale locale) {
|
||||
return new ImagenPresupuesto(
|
||||
"negro",
|
||||
"/assets/images/imprimelibros/presupuestador/blancoYnegro.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.blanco-negro", null, locale),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getImpresionNegroPremium(Locale locale) {
|
||||
return new ImagenPresupuesto(
|
||||
"negrohq",
|
||||
"/assets/images/imprimelibros/presupuestador/negroFoto.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.blanco-negro-premium", null, locale),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getImpresionColor(Locale locale) {
|
||||
|
||||
return new ImagenPresupuesto(
|
||||
"color",
|
||||
"/assets/images/imprimelibros/presupuestador/color.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.color", null, locale),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getImpresionColorPremium(Locale locale) {
|
||||
return new ImagenPresupuesto(
|
||||
"colorhq",
|
||||
"/assets/images/imprimelibros/presupuestador/colorFoto.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.color-premium", null, locale),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getPapelOffsetBlanco(Locale locale) {
|
||||
|
||||
return new ImagenPresupuesto(
|
||||
"offset-blanco",
|
||||
"/assets/images/imprimelibros/presupuestador/offset-blanco.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.offset-blanco", null, locale),
|
||||
Map.of("sk-id", "3"),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getPapelOffsetBlancoVolumen(Locale locale) {
|
||||
|
||||
return new ImagenPresupuesto(
|
||||
"offset-blanco-volumen",
|
||||
"/assets/images/imprimelibros/presupuestador/offset-blanco.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.offset-blanco-volumen", null, locale),
|
||||
Map.of("sk-id", "7"),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getPapelOffsetAhuesado(Locale locale) {
|
||||
|
||||
return new ImagenPresupuesto(
|
||||
"offset-ahuesado",
|
||||
"/assets/images/imprimelibros/presupuestador/offset-ahuesado.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.offset-ahuesado", null, locale),
|
||||
Map.of("sk-id", "4"),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getPapelOffsetAhuesadoVolumen(Locale locale) {
|
||||
|
||||
return new ImagenPresupuesto(
|
||||
"offset-ahuesado-volumen",
|
||||
"/assets/images/imprimelibros/presupuestador/offset-ahuesado-volumen.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.offset-ahuesado-volumen", null, locale),
|
||||
Map.of("sk-id", "6"),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getPapelEstucadoMate(Locale locale) {
|
||||
|
||||
return new ImagenPresupuesto(
|
||||
"estucado-mate",
|
||||
"/assets/images/imprimelibros/presupuestador/estucado-mate.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.estucado-mate", null, locale),
|
||||
Map.of("sk-id", "2"),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getCartulinaGraficaCubierta(Locale locale) {
|
||||
|
||||
return new ImagenPresupuesto(
|
||||
"cartulina-grafica-cubierta",
|
||||
"/assets/images/imprimelibros/presupuestador/cartulina-grafica.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.cartulina-grafica-cubierta", null, locale),
|
||||
Map.of("sk-id", "5"),
|
||||
false);
|
||||
}
|
||||
|
||||
public ImagenPresupuesto getEstucadoMateCubierta(Locale locale) {
|
||||
|
||||
return new ImagenPresupuesto(
|
||||
"estucado-mate-cubierta",
|
||||
"/assets/images/imprimelibros/presupuestador/estucado-mate-cubierta.png",
|
||||
"",
|
||||
messageSource.getMessage("presupuesto.estucado-mate-cubierta", null, locale),
|
||||
Map.of("sk-id", "2"),
|
||||
false);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.imprimelibros.erp.presupuesto.validation;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = ConsistentTiradasValidator.class)
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConsistentTiradas {
|
||||
String message() default "Las tiradas deben ser todas mayores o todas menores al valor POD";
|
||||
Class<?>[] groups() default {};
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.imprimelibros.erp.presupuesto.validation;
|
||||
|
||||
import com.imprimelibros.erp.configurationERP.VariableService;
|
||||
import com.imprimelibros.erp.presupuesto.Presupuesto;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
||||
public class ConsistentTiradasValidator implements ConstraintValidator<ConsistentTiradas, Presupuesto> {
|
||||
|
||||
@Autowired
|
||||
private VariableService variableService;
|
||||
@Autowired
|
||||
private MessageSource messageSource;
|
||||
|
||||
@Override
|
||||
public boolean isValid(Presupuesto presupuesto, ConstraintValidatorContext context) {
|
||||
if (presupuesto == null)
|
||||
return true;
|
||||
|
||||
Integer[] tiradas = presupuesto.getTiradas();
|
||||
Integer podValue = variableService.getValorEntero("POD");
|
||||
|
||||
boolean allAbove = true;
|
||||
boolean allBelow = true;
|
||||
|
||||
for (Integer t : tiradas) {
|
||||
if (t == null)
|
||||
continue;
|
||||
if (t <= podValue)
|
||||
allAbove = false;
|
||||
else // (t > podValue)
|
||||
allBelow = false;
|
||||
}
|
||||
|
||||
if (!(allAbove || allBelow)) {
|
||||
|
||||
String mensajeInterpolado = messageSource.getMessage(
|
||||
"presupuesto.errores.tiradas.consistentes", // clave del mensaje
|
||||
new Object[] { podValue }, // parámetros para {0}
|
||||
LocaleContextHolder.getLocale() // respeta el idioma actual
|
||||
);
|
||||
context.disableDefaultConstraintViolation();
|
||||
context.buildConstraintViolationWithTemplate(mensajeInterpolado)
|
||||
.addConstraintViolation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.imprimelibros.erp.presupuesto.validation;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = ParValidator.class)
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Par {
|
||||
String message() default "El valor debe ser un número par";
|
||||
Class<?>[] groups() default {};
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.imprimelibros.erp.presupuesto.validation;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
|
||||
public class ParValidator implements ConstraintValidator<Par, Integer> {
|
||||
|
||||
@Override
|
||||
public boolean isValid(Integer value, ConstraintValidatorContext context) {
|
||||
if (value == null) return true; // se permite null, usa @NotNull aparte si lo necesitas
|
||||
return value % 2 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package com.imprimelibros.erp.presupuesto.validation;
|
||||
|
||||
public class PresupuestoValidationGroups {
|
||||
|
||||
public interface DatosGenerales {}
|
||||
public interface Interior {}
|
||||
public interface Cubierta {}
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.imprimelibros.erp.presupuesto.validation;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = TamanioValidator.class)
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Tamanio {
|
||||
|
||||
String message() default "{presupuesto.errores.tamanio.invalido}";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package com.imprimelibros.erp.presupuesto.validation;
|
||||
|
||||
import com.imprimelibros.erp.configurationERP.VariableService;
|
||||
import com.imprimelibros.erp.presupuesto.Presupuesto;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
||||
public class TamanioValidator implements ConstraintValidator<Tamanio, Presupuesto>{
|
||||
|
||||
@Autowired
|
||||
private VariableService variableService;
|
||||
@Autowired
|
||||
private MessageSource messageSource;
|
||||
|
||||
@Override
|
||||
public boolean isValid(Presupuesto presupuesto, ConstraintValidatorContext context) {
|
||||
|
||||
if (presupuesto == null)
|
||||
return true;
|
||||
|
||||
Integer min = variableService.getValorEntero("ancho_alto_min");
|
||||
Integer max = variableService.getValorEntero("ancho_alto_max");
|
||||
|
||||
|
||||
if (presupuesto.getAncho() <= min || presupuesto.getAncho() >= max) {
|
||||
|
||||
String mensajeInterpolado = messageSource.getMessage(
|
||||
"presupuesto.errores.ancho.min_max", // clave del mensaje
|
||||
new Object[] { min, max }, // parámetros para {0}
|
||||
LocaleContextHolder.getLocale() // respeta el idioma actual
|
||||
);
|
||||
context.disableDefaultConstraintViolation();
|
||||
context.buildConstraintViolationWithTemplate(mensajeInterpolado)
|
||||
.addConstraintViolation();
|
||||
return false;
|
||||
}
|
||||
else if (presupuesto.getAlto() <= min || presupuesto.getAlto() >= max) {
|
||||
|
||||
String mensajeInterpolado = messageSource.getMessage(
|
||||
"presupuesto.errores.alto.min_max", // clave del mensaje
|
||||
new Object[] { min, max }, // parámetros para {0}
|
||||
LocaleContextHolder.getLocale() // respeta el idioma actual
|
||||
);
|
||||
context.disableDefaultConstraintViolation();
|
||||
context.buildConstraintViolationWithTemplate(mensajeInterpolado)
|
||||
.addConstraintViolation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1 +1,18 @@
|
||||
spring.application.name=erp
|
||||
logging.level.org.springframework.security=DEBUG
|
||||
|
||||
logging.level.root=WARN
|
||||
logging.level.org.springframework=ERROR
|
||||
|
||||
spring.datasource.url=jdbc:mysql://127.0.0.1:3309/imprimelibros?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Europe/Madrid&characterEncoding=utf8
|
||||
spring.datasource.username=imprimelibros_user
|
||||
spring.datasource.password=om91irrDctd
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
#safekat.api.url=http://localhost:8000/
|
||||
safekat.api.url=https://erp-dev.safekat.es/
|
||||
safekat.api.email=imnavajas@coit.es
|
||||
safekat.api.password=Safekat2024
|
||||
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=true
|
||||
1
src/main/resources/i18n/app_en.properties
Normal file
@ -0,0 +1 @@
|
||||
app.currency-symbol=€
|
||||
3
src/main/resources/i18n/app_es.properties
Normal file
@ -0,0 +1,3 @@
|
||||
app.currency-symbol=€
|
||||
app.yes=Sí
|
||||
app.no=No
|
||||
@ -1 +0,0 @@
|
||||
t-paginas=Pages
|
||||
@ -1,2 +0,0 @@
|
||||
t-home=Home
|
||||
t-home-title=Welcome to the Print Go Application
|
||||
@ -1,3 +0,0 @@
|
||||
t-menu=Menu
|
||||
t-menu-config=Configuration
|
||||
t-menu-config-impresioras=Printers
|
||||
@ -1,4 +0,0 @@
|
||||
t-printers=Printers
|
||||
t-printers-list=Printers List
|
||||
t-printers-id=ID
|
||||
t-printers-name=Name
|
||||
@ -1 +0,0 @@
|
||||
t-paginas=Páginas
|
||||
@ -1,2 +0,0 @@
|
||||
t-home=Inicio
|
||||
t-home-title=Bienvenido a la aplicación Print Go
|
||||
@ -1,3 +0,0 @@
|
||||
t-menu=Menú
|
||||
t-menu-config=Configuración
|
||||
t-menu-config-impresioras=Impresoras
|
||||
@ -1,4 +0,0 @@
|
||||
t-printers=Impresoras
|
||||
t-printers-list=Lista de Impresoras
|
||||
t-printers-id=ID
|
||||
t-printers-name=Nombre
|
||||
1
src/main/resources/i18n/login_en.properties
Normal file
@ -0,0 +1 @@
|
||||
login.login=Login
|
||||
1
src/main/resources/i18n/login_es.properties
Normal file
@ -0,0 +1 @@
|
||||
login.login=Iniciar sesión
|
||||
4
src/main/resources/i18n/presupuesto_en.properties
Normal file
@ -0,0 +1,4 @@
|
||||
presupuesto.datos-generales=General Data
|
||||
presupuesto.interior=Inside
|
||||
presupuesto.cubierta=Cover
|
||||
presupuesto.envio=Shipping
|
||||
184
src/main/resources/i18n/presupuesto_es.properties
Normal file
@ -0,0 +1,184 @@
|
||||
presupuesto.datos-generales=Datos Generales
|
||||
presupuesto.interior=Interior
|
||||
presupuesto.cubierta=Cubierta
|
||||
presupuesto.seleccion-tirada=Seleccion de tirada
|
||||
presupuesto.extras=Extras
|
||||
|
||||
# Pestaña datos generales de presupuesto
|
||||
presupuesto.informacion-libro=Información del libro
|
||||
presupuesto.datos-generales-descripcion=Datos generales del presupuesto
|
||||
presupuesto.titulo=Título*
|
||||
presupuesto.autor=Autor
|
||||
presupuesto.isbn=ISBN
|
||||
presupuesto.tirada1=Tirada 1*
|
||||
presupuesto.tirada2=Tirada 2
|
||||
presupuesto.tirada3=Tirada 3
|
||||
presupuesto.tirada4=Tirada 4
|
||||
presupuesto.tiradasPODnoPOD=No puede mezclar tiradas menores de {0} unidades con mayores de {0} unidades
|
||||
presupuesto.formato=Formato
|
||||
presupuesto.orientacion.vertical=Vertical
|
||||
presupuesto.orientacion.apaisado=Apaisado
|
||||
presupuesto.orientacion.cuadrado=Cuadrado
|
||||
presupuesto.formato-personalizado=Formato personalizado
|
||||
presupuesto.ancho=Ancho (mm)*
|
||||
presupuesto.alto=Alto (mm)*
|
||||
presupuesto.paginas-total=Total Páginas
|
||||
presupuesto.paginas-negro=Páginas Negro
|
||||
presupuesto.paginas-color=Páginas Color
|
||||
presupuesto.paginas-posicion=Posición páginas color
|
||||
presupuesto.paginas-posicion-descripcion=Intruzca la posición separada por comas. Ej: 3,5,7 ó 4-10,20,155
|
||||
presupuesto.siempre-pares=Siempre deben ser pares
|
||||
presupuesto.encuadernacion=Encuadernación
|
||||
presupuesto.fresado=Fresado
|
||||
presupuesto.fresado-descripcion=Fresado (a partir de 32 páginas)
|
||||
presupuesto.cosido=Cosido
|
||||
presupuesto.cosido-descripcion=Cosido (a partir de 32 páginas)
|
||||
presupuesto.grapado=Grapado
|
||||
presupuesto.grapado-descripcion=Grapado (entre 12 y 40 páginas)
|
||||
presupuesto.espiral=Espiral
|
||||
presupuesto.espiral-descripcion=Espiral (a partir de 20 páginas)
|
||||
presupuesto.wire-o=Wire-O
|
||||
presupuesto.wire-o-descripcion=Wire-O (a partir de 20 páginas)
|
||||
presupuesto.encuadernacion-descripcion=Seleccione la encuadernación del libro
|
||||
presupuesto.continuar-interior=Continuar a diseño interior
|
||||
|
||||
# Pestaña interior de presupuesto
|
||||
presupuesto.tipo-interior=Tipo de impresión
|
||||
presupuesto.tipo-interior-descripcion=Seleccione entre calidad estándar o premium
|
||||
presupuesto.papel-interior=Papel interior
|
||||
presupuesto.papel-interior-descripcion=Seleccione el papel para el interior
|
||||
presupuesto.gramaje-interior=Gramaje interior
|
||||
presupuesto.gramaje-interior-descripcion=Seleccione el gramaje para el interior
|
||||
presupuesto.blanco-negro=Blanco y negro
|
||||
presupuesto.blanco-negro-premium= Blanco y negro Premium
|
||||
presupuesto.color=Color
|
||||
presupuesto.color-premium=Color Premium
|
||||
presupuesto.offset-blanco=Offset Blanco
|
||||
presupuesto.offset-blanco-volumen=Offset Blanco Volumen
|
||||
presupuesto.offset-ahuesado=Offset Ahuesado
|
||||
presupuesto.offset-ahuesado-volumen=Offset Ahuesado Volumen
|
||||
presupuesto.estucado-mate=Estucado Mate
|
||||
presupuesto.volver-datos-generales=Volver a datos generales
|
||||
presupuesto.continuar-cubierta=Continuar a diseño cubierta
|
||||
|
||||
# Pestaña cubierta
|
||||
presupuesto.plantilla-cubierta=Plantilla de cubierta
|
||||
presupuesto.plantilla-cubierta-text=Recuerde que la cubierta es el conjunto formado por la portada, contraportada, lomo y solapas, en caso de que las lleve.<br/><br/><br/>Si tiene dudas de las medidas puede solicitarnos una plantilla cuando haga el pedido.
|
||||
presupuesto.tipo-cubierta=Tipo de cubierta
|
||||
presupuesto.tipo-cubierta-descripcion=Seleccione el tipo de cubierta y sus opciones
|
||||
presupuesto.tapa-blanda=Tapa blanda
|
||||
presupuesto.tapa-dura=Tapa dura
|
||||
presupuesto.tapa-dura-lomo-redondo=Tapa dura lomo redondo
|
||||
presupuesto.sin-solapas=Sin solapas
|
||||
presupuesto.con-solapas=Con solapas
|
||||
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.tamanio-solapa=Tamaño solapas
|
||||
presupuesto.papel-guardas=Papel de guardas
|
||||
presupuesto.guardas-impresas=Guardas impresas
|
||||
presupuesto.no=No
|
||||
presupuesto.cabezada=Cabezada
|
||||
presupuesto.cabezada-blanca=Blanca
|
||||
presupuesto.cabezada-verde=Verde
|
||||
presupuesto.cabezada-azul=Azul
|
||||
presupuesto.cabezada-roja-amarilla=Roja-Amarilla
|
||||
presupuesto.papel-cubierta=Papel cubierta
|
||||
presupuesto.papel-cubierta-descripcion=Seleccione el papel para la cubierta
|
||||
presupuesto.cartulina-grafica-cubierta=Cartulina gráfica estucada a una cara
|
||||
presupuesto.estucado-mate-cubierta=Estucado mate
|
||||
presupuesto.gramaje-cubierta=Gramaje cubierta
|
||||
presupuesto.gramaje-cubierta-descripcion=Seleccione el gramaje para la cubierta
|
||||
presupuesto.volver-interior=Volver a diseño interior
|
||||
presupuesto.continuar-seleccion-tirada=Continuar a selección de tirada
|
||||
presupuesto.offset=Offset
|
||||
presupuesto.estucado=Estucado
|
||||
presupuesto.verjurado=Verjurado
|
||||
presupuesto.verjurado-blanco-natural=Verjurado blanco natural
|
||||
presupuesto.verjurado-ahuesado=Verjurado ahuesado
|
||||
presupuesto.acabado=Acabado
|
||||
presupuesto.acabado-cubierta-descripcion=Seleccione el acabado para la cubierta
|
||||
presupuesto.acabado-cubierta-aviso=La falta de plastificado en la cubierta puede comprometer su calidad, ya que aumenta el riesgo de agrietamiento en los pliegues o hendidos, afectando su apariencia y resistencia.
|
||||
presupuesto.acabado-ninguno=Sin acabado
|
||||
presupuesto.acabado-plastificado-brillo-1c=Plastificado Brillo 1/C
|
||||
presupuesto.acabado-plastificado-mate-1c=Plastificado Mate 1/C
|
||||
presupuesto.acabado-plastificado-mate-1c-antirrayado=Plastificado Mate 1/C Antirrayado
|
||||
presupuesto.acabado-plastificado-mate-uvi=Plastificado Mate 1/C + Reserva UVI
|
||||
presupuesto.acabado-plastificado-mate-uvi3d=Plastificado Mate 1/C + Reserva UVI 3D
|
||||
presupuesto.acabado-plastificado-mate-uvi-braile=Plastificado Mate 1/C + Reserva UVI Braille
|
||||
presupuesto.acabado-plastificado-sandy-1c=Plastificado Sandy 1/C (tacto arena)
|
||||
presupuesto.cubierta-extras=Extras de cubierta
|
||||
presupuesto.cubierta-extras-descripcion=Seleccione las opciones adicionales para la cubierta
|
||||
presupuesto.sobrecubierta=Sobrecubierta
|
||||
presupuesto.sobrecubierta-papel=Papel sobrecubierta
|
||||
presupuesto.sobrecubierta-solapas=Tamaño solapas sobrecubierta
|
||||
presupuesto.faja=Faja
|
||||
presupuesto.faja-papel=Papel faja
|
||||
presupuesto.faja-solapas=Tamaño solapas faja
|
||||
presupuesto.faja-alto=Alto faja
|
||||
|
||||
#pestaña seleccion-tirada
|
||||
presupuesto.seleccion-tirada-descripcion=Seleccione la tirada deseada
|
||||
presupuesto.total=Total
|
||||
presupuesto.precio-unidad=Precio por unidad
|
||||
presupuesto.seleccionar-tirada=Seleccionar tirada
|
||||
presupuesto.tirada-seleccionada=Seleccionada
|
||||
presupuesto.unidades=UNIDADES
|
||||
presupuesto.volver-seleccion-tirada=Volver a selección de tirada
|
||||
presupuesto.continuar-extras-libro=Continuar a extras del libro
|
||||
presupuesto.error-obtener-precio=No se pudo obtener el precio para los datos introducidos. Por favor, contacte con el soporte técnico.
|
||||
|
||||
#pestaña extras del libro
|
||||
presupuesto.extras=Servicios Extras
|
||||
presupuesto.extras-descripcion=Seleccione los servicios adicionales que desea añadir al presupuesto
|
||||
presupuesto.extras-retractilado=Retractilado
|
||||
presupuesto.extras-isbn=ISBN
|
||||
presupuesto.extras-deposito-legal=Depósito Legal
|
||||
presupuesto.extras-deposito-legal-descripcion=Se añadirán 4 ejemplares a la tirada
|
||||
presupuesto.extras-revision-archivos=Revisión de archivos
|
||||
presupuesto.extras-maquetacion-cubierta=Maquetación de cubierta
|
||||
presupuesto.extras-ferro-digital=Ferro Digital
|
||||
presupuesto.extras-ejemplar-prueba=Ejemplar de prueba
|
||||
presupuesto.extras-marcapaginas=Marcapáginas
|
||||
presupuesto.extras-maquetacion=Maquetación
|
||||
presupuesto.extras-ferro-digital-ribbon=Incluido
|
||||
presupuesto.extras-calcular=Calcular
|
||||
presupuesto.volver-cubierta=Volver a diseño cubierta
|
||||
presupuesto.finalizar=Finalizar presupuesto
|
||||
presupuesto.calcular-presupuesto=Calcular presupuesto
|
||||
presupuesto.consultar-soporte=Consultar con soporte
|
||||
|
||||
# Resumen del presupuesto
|
||||
presupuesto.resumen-presupuesto=Resumen presupuesto
|
||||
presupuesto.resumen-libro=Libro
|
||||
presupuesto.resumen-encuadernacion=Encuadernación
|
||||
presupuesto.interior-libro=Interior del libro
|
||||
presupuesto.cubierta-libro=Cubierta del libro
|
||||
presupuesto.extras-libro=Extras del libro
|
||||
presupuesto.paginas=Páginas
|
||||
presupuesto.solapas=Solapas
|
||||
presupuesto.papel-gramaje=Papel y gramaje
|
||||
|
||||
# Errores
|
||||
presupuesto.errores-title=Corrija los siguientes errores:
|
||||
presupuesto.errores.titulo=El título es obligatorio
|
||||
presupuesto.errores.tirada1=La tirada 1 es obligatoria
|
||||
presupuesto.errores.tiradas.consistentes=Las tiradas deben ser consistentes (no mezclar tiradas menores de {0} unidades con mayores de {0} unidades)
|
||||
presupuesto.errores.paginasNegro.required=El número de páginas en negro es obligatorio
|
||||
presupuesto.errores.paginasNegro.par=El número de páginas en negro debe ser par
|
||||
presupuesto.errores.paginasColor.required=El número de páginas en color es obligatorio
|
||||
presupuesto.errores.paginasColor.par=El número de páginas en color debe ser par
|
||||
presupuesto.errores.tipo-encuadernacion=Seleccione el tipo de libro
|
||||
presupuesto.errores.ancho=El ancho no puede estar vacío
|
||||
presupuesto.errores.ancho.min_max=El ancho tiene que estar en el rango [{0}, {1}] mm;
|
||||
presupuesto.errores.alto=El alto no puede estar vacío
|
||||
presupuesto.errores.alto.min_max=El alto tiene que estar en el rango [{0}, {1}] mm
|
||||
presupuesto.errores.tipo-impresion=Seleccione el tipo de impresion
|
||||
presupuesto.errores.papel-interior=Seleccione el tipo de papel para el interior
|
||||
presupuesto.errores.gramaje-interior=Seleccione el gramaje del papel para el interior
|
||||
presupuesto.errores.tipo-cubierta=Seleccione el tipo de cubierta
|
||||
presupuesto.errores.solapas-cubierta=Seleccione si desea o no solapas en la cubierta
|
||||
presupuesto.errores.papel-cubierta=Seleccione el tipo de papel para la cubierta
|
||||
presupuesto.errores.gramaje-cubierta=Seleccione el gramaje del papel para la cubierta
|
||||
presupuesto.errores.acabado-cubierta=Seleccione el acabado de la cubierta
|
||||
@ -7,8 +7,98 @@ Website: https://themesbrand.com/
|
||||
Contact: support@themesbrand.com
|
||||
File: Main Css File
|
||||
*/
|
||||
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap");
|
||||
@import url("https://fonts.googleapis.com/css2?family=Public+Sans:wght@300;400;500;600;700&display=swap");
|
||||
/* poppins-300 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url('../fonts/poppins/poppins-v23-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* poppins-regular - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('../fonts/poppins/poppins-v23-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* poppins-500 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url('../fonts/poppins/poppins-v23-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* poppins-600 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('../fonts/poppins/poppins-v23-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* poppins-700 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('../fonts/poppins/poppins-v23-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
|
||||
/* public-sans-300 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Public Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url('../fonts/public-sans/public-sans-v20-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* public-sans-regular - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Public Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('../fonts/public-sans/public-sans-v20-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* public-sans-500 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Public Sans';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url('../fonts/public-sans/public-sans-v20-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* public-sans-600 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Public Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('../fonts/public-sans/public-sans-v20-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* public-sans-700 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Public Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('../fonts/public-sans/public-sans-v20-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: "hkgrotesk";
|
||||
src: url("../fonts/hkgrotesk-light.eot");
|
||||
|
||||
3
src/main/resources/static/assets/css/imprimelibros.css
Normal file
@ -0,0 +1,3 @@
|
||||
body {
|
||||
background-color: #fffbdd !important;
|
||||
}
|
||||
350
src/main/resources/static/assets/css/presupuestador.css
Normal file
@ -0,0 +1,350 @@
|
||||
/* === Contenedor de cada opción === */
|
||||
.image-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
/* Tamaño adaptable */
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
|
||||
/* Para evitar que la imagen sobresalga al hacer zoom */
|
||||
overflow: hidden;
|
||||
|
||||
/* Borde invisible por defecto para evitar movimiento al seleccionar */
|
||||
border: 4px solid transparent;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
|
||||
margin-inline: 5px;
|
||||
|
||||
transition: border 0.3s ease;
|
||||
}
|
||||
|
||||
/* === Borde visible cuando está seleccionada === */
|
||||
.image-container.selected {
|
||||
border-color: #687cfe;
|
||||
}
|
||||
|
||||
/* === Imagen interna === */
|
||||
.image-container img {
|
||||
max-width: 100%;
|
||||
max-height: 150px;
|
||||
display: block;
|
||||
transform-origin: center center;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
/* === Animación de zoom con rebote === */
|
||||
.image-presupuesto.zoom-anim {
|
||||
animation: zoomPop 800ms cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
||||
}
|
||||
|
||||
/* Keyframes para la animación */
|
||||
@keyframes zoomPop {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.image-container:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 10px rgba(104, 124, 254, 0.3);
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.image-container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.gramaje-radio {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 70px;
|
||||
/* Ancho mínimo */
|
||||
min-height: 70px;
|
||||
/* Alto mínimo */
|
||||
max-width: 70px;
|
||||
/* Ancho máximo */
|
||||
max-height: 70px;
|
||||
/* Alto máximo */
|
||||
}
|
||||
|
||||
@keyframes fadeInUpBounce {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: translateY(5px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fadeInUpBounce {
|
||||
animation: fadeInUpBounce 0.6s ease-out both;
|
||||
animation-delay: 0.1s;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.service-option {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
border: 1px solid #d0d7e5;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.service-option.checked {
|
||||
border-color: #4b7bec;
|
||||
background-color: #f0f5ff;
|
||||
box-shadow: 0 0 0 2px #4b7bec inset;
|
||||
}
|
||||
|
||||
.service-option .price {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
min-height: 2.2rem;
|
||||
/* ajusta según el tamaño de fuente que uses */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.service-checkbox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn-check-service+.btn {
|
||||
position: relative;
|
||||
/* <-- Esto es lo más importante */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: normal;
|
||||
line-height: 1.2;
|
||||
min-height: 180px;
|
||||
padding: 1rem 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
color: #4b7bec;
|
||||
border: 1px solid #4b7bec;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
/* ESTILO CUANDO ESTÁ MARCADO */
|
||||
.btn-check-service:checked+.btn {
|
||||
background-color: #4b7bec;
|
||||
color: #ffffff !important;
|
||||
border-color: #4b7bec;
|
||||
}
|
||||
|
||||
/* Forzamos el color blanco en los subelementos */
|
||||
.btn-check-service:checked+.btn .service-title,
|
||||
.btn-check-service:checked+.btn .service-desc,
|
||||
.btn-check-service:checked+.btn .service-price {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* Forzamos el color azul cuando no está marcado */
|
||||
.btn-check-service+.btn .service-title,
|
||||
.btn-check-service+.btn .service-desc,
|
||||
.btn-check-service+.btn .service-price {
|
||||
color: #4b7bec;
|
||||
}
|
||||
|
||||
/* ribbon-service */
|
||||
.ribbon-service {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
overflow: hidden;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ribbon-service span {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 120px;
|
||||
padding: 5px 0;
|
||||
background: #f25c5c;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
transform: rotate(45deg);
|
||||
top: 20px;
|
||||
right: -30px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
/* ===== Tiradas (pricing cards) ===== */
|
||||
.tirada-card {
|
||||
--il-accent: #4b7bec;
|
||||
--radius: 18px;
|
||||
--sel-scale-y: 1.12;
|
||||
/* cuánto más alta la seleccionada (crece arriba y abajo) */
|
||||
|
||||
/* círculo */
|
||||
--arc-w: 280px;
|
||||
--arc-h: 190px;
|
||||
--arc-y: -23px;
|
||||
/* tu valor */
|
||||
--arc-stop: 74%;
|
||||
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: var(--radius);
|
||||
background-color: #fff;
|
||||
background-image: radial-gradient(var(--arc-w) var(--arc-h) at 50% var(--arc-y),
|
||||
rgba(75, 126, 236, .24) 0 var(--arc-stop),
|
||||
transparent calc(var(--arc-stop) + 1%));
|
||||
|
||||
padding: 1.25rem 1.25rem 1rem;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
box-shadow: 0 4px 14px rgba(17, 24, 39, .06);
|
||||
overflow: hidden;
|
||||
/* integra la línea inferior */
|
||||
transform-origin: center center;
|
||||
/* ⟵ crecer hacia arriba y hacia abajo */
|
||||
will-change: transform;
|
||||
transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease, background .22s ease;
|
||||
}
|
||||
|
||||
/* sin elevación al hover */
|
||||
.tirada-card:hover {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Tipografías */
|
||||
.tirada-card .title {
|
||||
font-weight: 700;
|
||||
letter-spacing: .04em;
|
||||
font-size: .9rem;
|
||||
color: #4b7bec;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tirada-card .price-big {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
margin: .35rem 0 .15rem;
|
||||
color: #4b7bec;
|
||||
}
|
||||
|
||||
.tirada-card .per {
|
||||
font-size: .85rem;
|
||||
color: #7f8fa9;
|
||||
}
|
||||
|
||||
.tirada-card .price-row {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
.tirada-card .muted {
|
||||
color: #8e9bb3;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
/* Botón */
|
||||
.tirada-card .btn-select-tirada {
|
||||
margin-top: 1rem;
|
||||
border-radius: 999px;
|
||||
padding: .6rem 1rem;
|
||||
font-weight: 600;
|
||||
border: 2px solid var(--il-accent);
|
||||
color: var(--il-accent);
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
transition: background .2s ease, color .2s ease;
|
||||
}
|
||||
|
||||
/* ===== Seleccionada (más alta en Y, sin elevar; crece arriba y abajo) ===== */
|
||||
.tirada-card.selected {
|
||||
transform: scaleY(var(--sel-scale-y));
|
||||
/* ⟵ crecimiento simétrico vertical */
|
||||
z-index: 2;
|
||||
box-shadow: 0 18px 48px rgba(75, 126, 236, .32);
|
||||
border-color: var(--il-accent);
|
||||
|
||||
/* círculo sólido */
|
||||
background-image: radial-gradient(var(--arc-w) var(--arc-h) at 50% var(--arc-y),
|
||||
#4b7bec 0 var(--arc-stop),
|
||||
transparent calc(var(--arc-stop) + 1%));
|
||||
}
|
||||
|
||||
.tirada-card.selected:hover {
|
||||
/* evita que :hover base anule la escala */
|
||||
transform: scaleY(var(--sel-scale-y));
|
||||
}
|
||||
|
||||
.tirada-card.selected .btn-select-tirada {
|
||||
background: var(--il-accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* texto en blanco dentro del círculo */
|
||||
.tirada-card.selected .title,
|
||||
.tirada-card.selected .price-big,
|
||||
.tirada-card.selected .per {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ===== Línea inferior integrada (siempre visible en NO seleccionadas) ===== */
|
||||
.tirada-card:not(.selected)::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 1px;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
/* dentro del borde */
|
||||
height: 8px;
|
||||
background: var(--il-accent);
|
||||
border-bottom-left-radius: calc(var(--radius) - 1px);
|
||||
border-bottom-right-radius: calc(var(--radius) - 1px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tirada-card.selected::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
/* Oculta el radio */
|
||||
.tirada-card input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 228 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 531 KiB |
|
After Width: | Height: | Size: 484 KiB |
|
After Width: | Height: | Size: 481 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 28 KiB |
@ -0,0 +1,7 @@
|
||||
window.languageBundle.get = function (key, ...params) {
|
||||
let text = this[key] || key;
|
||||
params.forEach((val, i) => {
|
||||
text = text.replace(`{${i}}`, val);
|
||||
});
|
||||
return text;
|
||||
};
|
||||
@ -0,0 +1,66 @@
|
||||
class imagen_presupuesto {
|
||||
|
||||
constructor(data) {
|
||||
this.id = data.id;
|
||||
this.imagen = data.imagen;
|
||||
this.alt = data.alt || "";
|
||||
this.group = data.group || ""; // Grupo al que pertenece el radio
|
||||
this.texto = data.texto || ""; // Texto de la etiqueta
|
||||
this.selected = data.selected || false;
|
||||
this.extraClass = (data.extraClass && !(data.extraClass===undefined))? (data.extraClass + ' ') : '';
|
||||
this.extraData = data.extra_data || {}; // Datos extra opcionales
|
||||
}
|
||||
|
||||
render() {
|
||||
const contenedor = $('<div>', {
|
||||
id: this.id,
|
||||
class: `${this.extraClass + ' '}image-container imagen-selector${this.selected ? ' selected' : ''}`
|
||||
});
|
||||
// Añadir atributos extra al contenedor
|
||||
for (const [key, value] of Object.entries(this.extraData)) {
|
||||
contenedor.attr(`data-${key}`, value);
|
||||
}
|
||||
contenedor.attr("data-summary-text", this.texto); // Para el resumen
|
||||
|
||||
const input = $('<input>', {
|
||||
type: 'radio',
|
||||
name: this.group,
|
||||
value: this.id,
|
||||
hidden: true
|
||||
});
|
||||
|
||||
|
||||
const imagen = $('<img>', {
|
||||
class: 'image-presupuesto',
|
||||
id: this.id + '-img',
|
||||
src: this.imagen,
|
||||
alt: this.alt
|
||||
});
|
||||
|
||||
const etiqueta = $('<label>', {
|
||||
for: this.id + '-img',
|
||||
class: 'form-label',
|
||||
text: this.texto
|
||||
});
|
||||
|
||||
contenedor.append(imagen, etiqueta);
|
||||
return contenedor;
|
||||
}
|
||||
|
||||
setSelected(selected) {
|
||||
|
||||
this.selected = selected;
|
||||
const contenedor = $(`#${this.id}`);
|
||||
const radio = contenedor.find('input[type="radio"]');
|
||||
|
||||
if (selected) {
|
||||
contenedor.addClass('selected');
|
||||
radio.prop('checked', true).trigger('change'); // <-- esto actualiza el input y lanza evento
|
||||
} else {
|
||||
contenedor.removeClass('selected');
|
||||
radio.prop('checked', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default imagen_presupuesto;
|
||||
@ -0,0 +1,20 @@
|
||||
|
||||
$('.imagen-container-group').on('click', '.image-container', function () {
|
||||
const clicked = $(this);
|
||||
const group = clicked.closest('.imagen-container-group');
|
||||
|
||||
// Limpiar selección anterior
|
||||
group.find('.image-container').removeClass('selected')
|
||||
.find('.image-presupuesto').removeClass('zoom-anim');
|
||||
|
||||
// Marcar nuevo seleccionado
|
||||
clicked.addClass('selected');
|
||||
|
||||
// Aplicar animación de zoom
|
||||
const img = clicked.find('.image-presupuesto');
|
||||
void img[0].offsetWidth; // Forzar reflow
|
||||
img.addClass('zoom-anim');
|
||||
|
||||
|
||||
clicked.find('input[type="radio"]').prop('checked', true).trigger('change');
|
||||
});
|
||||
@ -0,0 +1,46 @@
|
||||
class ServiceOptionCard {
|
||||
|
||||
constructor({ id, title, description = '', price = '', priceUnit = '', checked = false, allowChange = true, ribbonText }) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.description = description ? description : ' ';
|
||||
this.price = price;
|
||||
this.priceUnit = priceUnit;
|
||||
this.checked = checked;
|
||||
this.allowChange = !(String(allowChange).toLowerCase() === "false");
|
||||
this.ribbonText = ribbonText;
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const ribbonHtml = this.ribbonText
|
||||
? `<div class="ribbon-service"><span>${this.ribbonText}</span></div>`
|
||||
: '';
|
||||
const $card = $(`
|
||||
<div class="col-lg-2 col-md-3 col-sm-6 mb-3">
|
||||
<input type="checkbox" class="service-checkbox data-price=${this.price} btn-check-service" id="${this.id}" name="services[]" value="${this.id}" autocomplete="off" ${this.checked ? 'checked' : ''} />
|
||||
<label class="btn btn-outline-primary w-100 text-center py-3 px-2 d-flex flex-column align-items-center justify-content-center h-100" for="${this.id}">
|
||||
${ribbonHtml}
|
||||
<h5 class="service-title mb-1">${this.title}</h5>
|
||||
<p class="service-desc mb-1 small">${this.description}</p>
|
||||
<h4 class="service-price fw-semibold mt-2 h4 mb-0">${this.price} ${this.priceUnit}</h4>
|
||||
</label>
|
||||
</div>
|
||||
`);
|
||||
|
||||
const $checkbox = $card.find('input[type="checkbox"]');
|
||||
|
||||
if (!this.allowChange) {
|
||||
// Deshabilita el cambio de estado visual
|
||||
$checkbox.on('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
return $card;
|
||||
}
|
||||
}
|
||||
|
||||
export default ServiceOptionCard;
|
||||
@ -0,0 +1,141 @@
|
||||
export function updateEncuadernacion() {
|
||||
|
||||
const $selected = $('.tipo-libro.selected');
|
||||
if ($selected.length > 0) {
|
||||
const resumen = $selected.data('summary-text') || $selected.attr('id');
|
||||
$('#summary-encuadernacion').text(resumen);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateFormato(){
|
||||
|
||||
if($('#formato-personalizado').is(':checked')) {
|
||||
$('#summary-formato').text($('#ancho').val() + 'x' + $('#alto').val() + ' mm');
|
||||
} else {
|
||||
const $selected = $('#formato option:selected');
|
||||
const resumen = $selected.data('summary-text') || $selected.val();
|
||||
$('#summary-formato').text(resumen + ' mm');
|
||||
}
|
||||
}
|
||||
|
||||
export function updatePaginas() {
|
||||
|
||||
const paginas = $('#paginas').val();
|
||||
$('#summary-paginas').text(paginas );
|
||||
|
||||
const paginasColor = $('#paginas-color').val();
|
||||
$('#summary-paginas-color').text(paginasColor );
|
||||
|
||||
const paginasNegro = $('#paginas-negro').val();
|
||||
$('#summary-paginas-negro').text(paginasNegro );
|
||||
}
|
||||
|
||||
export function updateTipoImpresion() {
|
||||
|
||||
const $selected = $('.opcion-color.selected');
|
||||
if ($selected.length > 0) {
|
||||
const resumen = $selected.data('summary-text') || $selected.attr('id');
|
||||
$('#summary-tipo-interior').text(resumen);
|
||||
}
|
||||
}
|
||||
|
||||
export function updatePapelInterior() {
|
||||
|
||||
const $selected = $('.papel-interior.selected');
|
||||
if ($selected.length > 0) {
|
||||
const resumen = $selected.data('summary-text') || $selected.val();
|
||||
$('#summary-papel-interior').text(resumen);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateGramajeInterior() {
|
||||
|
||||
const gramaje = $('input[name="gramaje-interior"]:checked');
|
||||
if(gramaje.length > 0) {
|
||||
$('#summary-gramaje-interior').text(gramaje.data('gramaje'));
|
||||
}
|
||||
}
|
||||
|
||||
export function updateTapaCubierta(){
|
||||
|
||||
const $selected = $('.tapa-cubierta.selected');
|
||||
if ($selected.length > 0) {
|
||||
const resumen = $selected.data('summary-text') || $selected.attr('id');
|
||||
$('#summary-tapa-cubierta').text(resumen);
|
||||
}
|
||||
if($selected.attr('id') === 'tapaBlanda') {
|
||||
|
||||
$('.tapa-blanda-row').removeClass('d-none');
|
||||
$('.tapa-dura-row').addClass('d-none');
|
||||
$('#summary-cubierta-solapas').text($('#sin-solapas').hasClass('selected') ? $('#sin-solapas').data('summary-text') : $('#con-solapas').data('summary-text'));
|
||||
if($('#con-solapas').hasClass('selected')) {
|
||||
$('#summary-tamanio-solapa-row').removeClass('d-none');
|
||||
$('#summary-tamanio-solapa').text($('#tamanio-solapas-cubierta').val() + ' mm');
|
||||
} else {
|
||||
$('#summary-tamanio-solapa-row').addClass('d-none');
|
||||
$('#summary-tamanio-solapa').text('');
|
||||
}
|
||||
$('#summary-impresion-cubierta-row').removeClass('d-none');
|
||||
$('#summary-impresion-cubierta').text($('#impresion-cubierta option:selected').text());
|
||||
}
|
||||
else{
|
||||
$('.tapa-blanda-row').addClass('d-none');
|
||||
$('.tapa-dura-row').removeClass('d-none');
|
||||
$('#summary-papel-guardas').text($('#papel-guardas option:selected').text());
|
||||
$('#summary-guardas-impresas').text($('#guardas-impresas option:selected').text());
|
||||
$('#summary-cabezada').text($('#cabezada option:selected').text());
|
||||
}
|
||||
}
|
||||
|
||||
export function updatePapelCubierta() {
|
||||
const $selected = $('.papel-cubierta.selected');
|
||||
if ($selected.length > 0) {
|
||||
const resumen = $selected.data('summary-text') || $selected.val();
|
||||
$('#summary-papel-cubierta').text(resumen);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateGramajeCubierta() {
|
||||
const gramaje = $('input[name="gramaje-cubierta"]:checked');
|
||||
if(gramaje.length > 0) {
|
||||
$('#summary-gramaje-cubierta').text(gramaje.data('gramaje'));
|
||||
}
|
||||
}
|
||||
|
||||
export function updateAcabadoCubierta() {
|
||||
const acabado = $('input[name="acabado-cubierta"]:checked');
|
||||
if(acabado.length > 0) {
|
||||
let labelText = '';
|
||||
const id = acabado.attr('id');
|
||||
|
||||
if (id) {
|
||||
labelText = $(`label[for="${id}"]`).text().trim();
|
||||
}
|
||||
|
||||
// Caso 2: input dentro de label
|
||||
if (!labelText) {
|
||||
labelText = acabado.closest('label').text().trim();
|
||||
}
|
||||
|
||||
$('#summary-acabado-cubierta').text(labelText);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateSobreCubierta() {
|
||||
|
||||
if($('#sobrecubierta').hasClass('active')) {
|
||||
$('#summary-sobrecubierta-papel-gramaje').text($('#papel-sobrecubierta option:selected').text());
|
||||
$('#summary-sobrecubierta-tamanio-solapa').text($('#tamanio-solapas-sobrecubierta').val() + ' mm');
|
||||
$('#summary-sobrecubierta-acabado').text($('#sobrecubierta-acabado option:selected').text());
|
||||
}
|
||||
}
|
||||
|
||||
export function updateFaja() {
|
||||
|
||||
if($('#faja').hasClass('active')) {
|
||||
$('#summary-faja-papel-gramaje').text($('#papel-faja option:selected').text());
|
||||
$('#summary-faja-alto-faja').text($('#alto-faja').val() + ' mm');
|
||||
$('#summary-faja-tamanio-solapa').text($('#tamanio-solapas-faja').val() + ' mm');
|
||||
$('#summary-faja-acabado').text($('#faja-acabado option:selected').text());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
// ===== Clase =====
|
||||
class TiradaCard {
|
||||
constructor({ id, titulo, unidades, precioUnidad, precioTotal, selected = false, moneda = '€', name = 'tirada',
|
||||
labels = {}, locale = (document.documentElement.lang || navigator.language || 'es-ES')
|
||||
}) {
|
||||
this.id = id;
|
||||
this.titulo = titulo;
|
||||
this.unidades = unidades;
|
||||
this.precioUnidad = precioUnidad;
|
||||
this.precioTotal = precioTotal;
|
||||
this.selected = selected;
|
||||
this.moneda = moneda;
|
||||
this.name = name;
|
||||
this.locale = locale;
|
||||
this.labels = Object.assign({
|
||||
perUnit: 'Precio por unidad',
|
||||
total: 'Total',
|
||||
select: 'Seleccionar tirada',
|
||||
selected: 'Seleccionada',
|
||||
units: 'UNIDADES'
|
||||
}, labels);
|
||||
this.$container = null; // se establece al renderizar
|
||||
}
|
||||
|
||||
#formatMoneyES(value, decimals = 2) {
|
||||
return new Intl.NumberFormat(this.locale, {
|
||||
minimumFractionDigits: decimals,
|
||||
maximumFractionDigits: decimals
|
||||
}).format(Number(value));
|
||||
}
|
||||
|
||||
#title() {
|
||||
if (this.titulo) return this.titulo;
|
||||
if (this.unidades != null) return `${this.unidades} ${this.labels.units}`;
|
||||
return '';
|
||||
}
|
||||
|
||||
// pásale el contenedor donde van todas las tarjetas de este grupo
|
||||
renderCol($container) {
|
||||
this.$container = $container ? $($container) : $(document.body);
|
||||
|
||||
const col = $(`
|
||||
<div class="col d-flex">
|
||||
<label class="tirada-card ${this.selected ? 'selected' : ''} w-100 h-100" for="tirada-${this.id}">
|
||||
<input type="radio" name="${this.name}" id="tirada-${this.id}" value="${this.unidades}" ${this.selected ? 'checked' : ''}>
|
||||
<div class="title">${this.#title()}</div>
|
||||
|
||||
<div class="per muted">${this.labels.perUnit}</div>
|
||||
<div class="price-big">${this.#formatMoneyES(this.precioUnidad, 4)} ${this.moneda}</div>
|
||||
|
||||
<div class="price-row">
|
||||
<div class="muted">${this.labels.total}</div>
|
||||
<div class="fw-bold">${this.#formatMoneyES(this.precioTotal, 2)} ${this.moneda}</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-select-tirada">${this.labels.select}</button>
|
||||
</label>
|
||||
</div>
|
||||
`);
|
||||
|
||||
// --- Delegación de eventos en el contenedor (una sola vez por grupo) ---
|
||||
const boundKey = `tirada-bound-${this.name}`;
|
||||
const groupName = this.name;
|
||||
const $group = this.$container;
|
||||
const labels = this.labels;
|
||||
|
||||
if (!$group.data(boundKey)) {
|
||||
$group.on('change', `input[type="radio"][name="${groupName}"]`, function () {
|
||||
// radios del grupo
|
||||
const $groupRadios = $group.find(`input[type="radio"][name="${groupName}"]`);
|
||||
|
||||
// resetear todas las tarjetas del grupo
|
||||
$groupRadios
|
||||
.closest('.tirada-card')
|
||||
.removeClass('selected')
|
||||
.find('.btn-select-tirada')
|
||||
.html(labels.select || 'Seleccionar tirada');
|
||||
|
||||
// marcar la tarjeta seleccionada y su botón
|
||||
const $card = $(this).closest('.tirada-card');
|
||||
$card.find('.btn-select-tirada')
|
||||
.html(labels.selected + ' <i class="mdi mdi-check-circle ms-1"></i>');
|
||||
$card.addClass('selected');
|
||||
|
||||
const $input = $(this);
|
||||
const detail = {
|
||||
id: $input.attr('id'),
|
||||
name: $input.attr('name'),
|
||||
unidades: Number($input.data('unidades') ?? $input.val()),
|
||||
precioUnidad: Number($input.data('precioUnidad')),
|
||||
precioTotal: Number($input.data('precioTotal'))
|
||||
};
|
||||
// jQuery: pasa 'detail' como segundo argumento del handler
|
||||
$group.trigger('tirada:changed', [detail]);
|
||||
|
||||
});
|
||||
|
||||
$group.data(boundKey, true);
|
||||
}
|
||||
|
||||
const $btnLocal = col.find('.btn-select-tirada');
|
||||
if (this.selected) {
|
||||
$btnLocal.html(labels.selected + ' <i class="mdi mdi-check-circle ms-1"></i>');
|
||||
}
|
||||
|
||||
// --- Comportamiento de la tarjeta/botón (local a esta tarjeta) ---
|
||||
const card = col.find('.tirada-card');
|
||||
|
||||
col.find('.btn-select-tirada').on('click', (e) => {
|
||||
e.preventDefault();
|
||||
card.find('input[type="radio"]').prop('checked', true).trigger('change');
|
||||
});
|
||||
|
||||
card.on('click', function (e) {
|
||||
if ($(e.target).is('.btn-select-tirada')) return;
|
||||
$(this).find('input[type="radio"]').prop('checked', true).trigger('change');
|
||||
});
|
||||
|
||||
return col;
|
||||
}
|
||||
}
|
||||
|
||||
export default TiradaCard;
|
||||
@ -247,7 +247,6 @@ if (document.getElementById("sa-position"))
|
||||
icon: 'success',
|
||||
title: 'Your work has been saved',
|
||||
showConfirmButton: false,
|
||||
timer: 1500,
|
||||
showCloseButton: true
|
||||
})
|
||||
});
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
/*
|
||||
Template Name: Velzon - Admin & Dashboard Template
|
||||
Author: Themesbrand
|
||||
Version: 2.4.0
|
||||
Website: https://Themesbrand.com/
|
||||
Contact: Themesbrand@gmail.com
|
||||
File: Common Plugins Js File
|
||||
*/
|
||||
(function () {
|
||||
const head = document.head || document.getElementsByTagName("head")[0];
|
||||
|
||||
//Common plugins
|
||||
if(document.querySelectorAll("[toast-list]") || document.querySelectorAll('[data-choices]') || document.querySelectorAll("[data-provider]")){
|
||||
document.writeln("<script type='text/javascript' src='https://cdn.jsdelivr.net/npm/toastify-js'></script>");
|
||||
document.writeln("<script type='text/javascript' src='/assets/libs/choices.js/public/assets/scripts/choices.min.js'></script>");
|
||||
document.writeln("<script type='text/javascript' src='/assets/libs/flatpickr/flatpickr.min.js'></script>");
|
||||
}
|
||||
const scripts = [
|
||||
"/assets/libs/toastify-js/src/toastify.js",
|
||||
"/assets/libs/choices.js/public/assets/scripts/choices.min.js",
|
||||
"/assets/libs/flatpickr/flatpickr.min.js",
|
||||
"/assets/libs/feather-icons/feather.min.js" // <- AÑADIMOS feather aquí
|
||||
];
|
||||
|
||||
scripts.forEach(src => {
|
||||
const script = document.createElement("script");
|
||||
script.src = src;
|
||||
script.type = "text/javascript";
|
||||
script.defer = true;
|
||||
head.appendChild(script);
|
||||
});
|
||||
})();
|
||||
|
||||
2
src/main/resources/static/assets/libs/jquery/jquery-3.6.0.min.js
vendored
Normal file
1
src/main/resources/static/assets/libs/select2/select2.min.css
vendored
Normal file
2
src/main/resources/static/assets/libs/select2/select2.min.js
vendored
Normal file
BIN
src/main/resources/templates/imprimelibros.zip
Normal file
46
src/main/resources/templates/imprimelibros/home.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!doctype html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{imprimelibros/layout}">
|
||||
|
||||
<head>
|
||||
<th:block layout:fragment="pagetitle" />
|
||||
<th:block th:replace="~{imprimelibros/partials/head-css :: head-css}" />
|
||||
<th:block layout:fragment="pagecss">
|
||||
<link th:href="@{/assets/css/presupuestador.css}" rel="stylesheet"
|
||||
th:unless="${#authorization.expression('isAuthenticated()')}" />
|
||||
</th:block>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
|
||||
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}" />
|
||||
</div>
|
||||
|
||||
<th:block layout:fragment="content">
|
||||
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||
<div class="container-fluid">
|
||||
<!-- contenido para usuario logueado -->
|
||||
</div>
|
||||
</div>
|
||||
<div th:unless="${#authorization.expression('isAuthenticated()')}">
|
||||
<div th:insert="~{imprimelibros/presupuestos/presupuestador :: presupuestador}"></div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="modal" />
|
||||
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||
<th:block layout:fragment="pagejs">
|
||||
<div th:unless="${#authorization.expression('isAuthenticated()')}">
|
||||
<script th:src="@{/assets/js/pages/imprimelibros/presupuestador/imagen-selector.js}"></script>
|
||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/presupuestador.js}"></script>
|
||||
</div>
|
||||
<script th:inline="javascript">
|
||||
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
||||
</script>
|
||||
</th:block>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
39
src/main/resources/templates/imprimelibros/layout.html
Normal file
@ -0,0 +1,39 @@
|
||||
<html th:lang="${#locale.language}" th:with="isAuth=${#authorization.expression('isAuthenticated()')}"
|
||||
th:attrappend="data-layout=${isAuth} ? 'semibox' : 'horizontal'" data-sidebar-visibility="show" data-topbar="light"
|
||||
data-sidebar="light" data-sidebar-size="lg" data-sidebar-image="none" data-preloader="disable"
|
||||
xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||
|
||||
<head>
|
||||
<th:block layout:fragment="pagetitle" />
|
||||
<th:block th:replace="~{imprimelibros/partials/head-css :: head-css}" />
|
||||
<link href="/assets/libs/sweetalert2/sweetalert2.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="/assets/libs/select2/select2.min.css" rel="stylesheet" />
|
||||
<th:block layout:fragment="pagecss" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
|
||||
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}" />
|
||||
</div>
|
||||
|
||||
<section class="main-content">
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
<section layout:fragment="content" th:remove="tag"></section>
|
||||
</div>
|
||||
</div>
|
||||
<div th:replace="~{imprimelibros/partials/footer :: footer}" />
|
||||
</section>
|
||||
|
||||
<th:block layout:fragment="modal" />
|
||||
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||
<script src="/assets/libs/jquery/jquery-3.6.0.min.js"></script>
|
||||
<script src="/assets/libs/sweetalert2/sweetalert2.min.js"></script>
|
||||
<script src="/assets/libs/select2/select2.min.js"></script>
|
||||
<th:block layout:fragment="pagejs" />
|
||||
<script th:src="@{/assets/js/app.js}"></script>
|
||||
<script th:src="@{/assets/js/pages/imprimelibros/languageBundle.js}"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -0,0 +1,16 @@
|
||||
<div th:fragment="footer" th:remove="tag">
|
||||
<footer class="footer">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<span th:text="${T(java.time.Year).now()}">2025</span> © Imprime Libros.
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="text-sm-end d-none d-sm-block">
|
||||
Design & Develop by IMN & JJO
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
@ -0,0 +1,14 @@
|
||||
<div th:fragment="head-css" th:remove="tag">
|
||||
<!-- Layout config Js -->
|
||||
<script src="/assets/js/layout.js"></script>
|
||||
<!-- Bootstrap Css -->
|
||||
<link href="/assets/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
<!-- Icons Css -->
|
||||
<link href="/assets/css/icons.min.css" rel="stylesheet" type="text/css" />
|
||||
<!-- App Css-->
|
||||
<link href="/assets/css/app.min.css" rel="stylesheet" type="text/css" />
|
||||
<!-- custom Css-->
|
||||
<link href="/assets/css/custom.min.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
<link href="/assets/css/imprimelibros.css" rel="stylesheet" type="text/css" />
|
||||
</div>
|
||||
@ -0,0 +1,26 @@
|
||||
<div th:fragment="page-title(title,pagetitle)" th:remove="tag">
|
||||
<!-- start page title -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="page-title-box d-sm-flex align-items-center justify-content-between">
|
||||
<h4 th:data-key="${keyTitle}" class="mb-sm-0" th:text="${currentPage}"></h4>
|
||||
|
||||
<div class="page-title-right">
|
||||
<ol class="breadcrumb m-0">
|
||||
<li th:each="item : ${breadcrumb}" class="breadcrumb-item"
|
||||
th:classappend="${item.url == null}? 'active' : ''">
|
||||
|
||||
<a th:if="${item.url != null}" th:data-key="${item.dataKey}" th:href="${item.url}"
|
||||
th:text="${item.label}"></a>
|
||||
|
||||
<span th:if="${item.url == null}" th:data-key="${item.dataKey}"
|
||||
th:text="${item.label}"></span>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end page title -->
|
||||
</div>
|
||||
@ -0,0 +1,53 @@
|
||||
<div th:fragment="sidebar" th:remove="tag">
|
||||
<!-- ========== App Menu ========== -->
|
||||
<div class="app-menu navbar-menu">
|
||||
<!-- LOGO -->
|
||||
<div class="navbar-brand-box">
|
||||
<!-- Dark Logo-->
|
||||
<a href="/" class="logo logo-dark">
|
||||
<span class="logo-sm">
|
||||
<img src="/assets/images/logo-sm.png" alt="" height="22">
|
||||
</span>
|
||||
<span class="logo-lg">
|
||||
<img src="/assets/images/logo-dark.png" alt="" height="17">
|
||||
</span>
|
||||
</a>
|
||||
<!-- Light Logo-->
|
||||
<a href="/" class="logo logo-light">
|
||||
<span class="logo-sm">
|
||||
<img src="/assets/images/logo-sm.png" alt="" height="22">
|
||||
</span>
|
||||
<span class="logo-lg">
|
||||
<img src="/assets/images/logo-light.png" alt="" height="17">
|
||||
</span>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm p-0 fs-20 header-item float-end btn-vertical-sm-hover"
|
||||
id="vertical-hover">
|
||||
<i class="ri-record-circle-line"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="scrollbar">
|
||||
<div class="container-fluid">
|
||||
|
||||
<div id="two-column-menu">
|
||||
</div>
|
||||
<li href="/" class="menu-title"><span data-key="t-menu">Menu</span></li>
|
||||
<ul class="navbar-nav" id="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link menu-link" href="/">
|
||||
<i class="ri-home-line"></i> <span data-key="t-home">Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- <div th:replace="~{printhub/partials/sidebarMenus/configurationMenu :: configuration}"></div> -->
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Sidebar -->
|
||||
</div>
|
||||
|
||||
<div class="sidebar-background"></div>
|
||||
</div>
|
||||
<!-- Left Sidebar End -->
|
||||
<!-- Vertical Overlay-->
|
||||
<div class="vertical-overlay"></div>
|
||||
</div>
|
||||
@ -0,0 +1,16 @@
|
||||
<div th:fragment="configuration" th:remove="tag">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link menu-link" href="#configurationMenu" data-bs-toggle="collapse" role="button"
|
||||
aria-expanded="false" aria-controls="configurationMenu">
|
||||
<i class="ri-tools-line"></i> <span data-key="t-menu-config">Configuración</span>
|
||||
</a>
|
||||
<div class="collapse menu-dropdown" id="configurationMenu">
|
||||
<ul class="nav nav-sm flex-column">
|
||||
<li class="nav-item">
|
||||
<a href="/configuration/printers" class="nav-link" data-key="t-menu-config-impresioras">
|
||||
Impresoras </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li> <!-- end Dashboard Menu -->
|
||||
</div>
|
||||