mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-24 09:40:21 +00:00
Merge branch 'feat/pedidos_system' into 'main'
vista de pedidos casi terminada (a falta de acciones delos botones, cambio de... See merge request jjimenez/erp-imprimelibros!25
This commit is contained in:
762
logs/erp.log
762
logs/erp.log
@ -1,23 +1,23 @@
|
|||||||
2025-11-15 08:19:06 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.8 with PID 13129 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
2025-12-28 11:38:17 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.9 with PID 10066 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
||||||
2025-11-15 08:19:06 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
2025-12-28 11:38:17 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
||||||
2025-11-15 08:19:10 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
2025-12-28 11:38:22 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||||
2025-11-15 08:19:10 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@ea50607
|
2025-12-28 11:38:22 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6ed6a1c8
|
||||||
2025-11-15 08:19:10 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
2025-12-28 11:38:22 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.util - Run: 0
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.util - Run: 0
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.util - Previously run: 51
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.util - Previously run: 62
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.util - Filtered out: 0
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.util - Filtered out: 0
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.util - -------------------------------
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.util - -------------------------------
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.util - Total change sets: 51
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.util - Total change sets: 62
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.util - Update summary generated
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.util - Update summary generated
|
||||||
2025-11-15 08:19:11 INFO [restartedMain] liquibase.command - Command execution complete
|
2025-12-28 11:38:23 INFO [restartedMain] liquibase.command - Command execution complete
|
||||||
2025-11-15 08:19:12 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
2025-12-28 11:38:24 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||||
2025-11-15 08:19:12 INFO [restartedMain] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
2025-12-28 11:38:24 INFO [restartedMain] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.39.Final
|
||||||
2025-11-15 08:19:12 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
2025-12-28 11:38:24 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
||||||
2025-11-15 08:19:13 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
2025-12-28 11:38:24 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
||||||
Database driver: undefined/unknown
|
Database driver: undefined/unknown
|
||||||
Database version: 8.0.43
|
Database version: 8.0.43
|
||||||
@ -25,33 +25,29 @@
|
|||||||
Isolation level: undefined/unknown
|
Isolation level: undefined/unknown
|
||||||
Minimum pool size: undefined/unknown
|
Minimum pool size: undefined/unknown
|
||||||
Maximum pool size: undefined/unknown
|
Maximum pool size: undefined/unknown
|
||||||
2025-11-15 08:19:14 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
2025-12-28 11:38:26 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
||||||
2025-11-15 08:19:19 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 13.772 seconds (process running for 14.889)
|
2025-12-28 11:38:31 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 14.361 seconds (process running for 15.893)
|
||||||
2025-11-15 08:24:35 WARN [http-nio-8080-exec-9] o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
|
2025-12-28 11:54:36 INFO [Thread-5] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||||
2025-11-15 08:24:35 ERROR [http-nio-8080-exec-9] o.h.e.jdbc.spi.SqlExceptionHelper - Duplicate entry '000000' for key 'payment_transactions.uq_tx_gateway_txid'
|
2025-12-28 11:54:36 INFO [Thread-5] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||||
2025-11-15 08:25:30 WARN [http-nio-8080-exec-8] o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
|
2025-12-28 11:54:36 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.9 with PID 10066 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
||||||
2025-11-15 08:25:30 ERROR [http-nio-8080-exec-8] o.h.e.jdbc.spi.SqlExceptionHelper - Duplicate entry '000000' for key 'payment_transactions.uq_tx_gateway_txid'
|
2025-12-28 11:54:36 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
||||||
2025-11-15 08:29:20 INFO [Thread-5] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
2025-12-28 11:54:38 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Starting...
|
||||||
2025-11-15 08:29:20 INFO [Thread-5] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
2025-12-28 11:54:38 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Added connection com.mysql.cj.jdbc.ConnectionImpl@4a1a1522
|
||||||
2025-11-15 08:29:20 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.8 with PID 13129 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
2025-12-28 11:54:38 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Start completed.
|
||||||
2025-11-15 08:29:20 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
2025-12-28 11:54:38 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Starting...
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Added connection com.mysql.cj.jdbc.ConnectionImpl@5582ad4
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Start completed.
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.util - Run: 0
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.util - Previously run: 62
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.util - Filtered out: 0
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.util - -------------------------------
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.util - Run: 0
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.util - Total change sets: 62
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.util - Previously run: 51
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.util - Update summary generated
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.util - Filtered out: 0
|
2025-12-28 11:54:39 INFO [restartedMain] liquibase.command - Command execution complete
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.util - -------------------------------
|
2025-12-28 11:54:39 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.util - Total change sets: 51
|
2025-12-28 11:54:39 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.util - Update summary generated
|
2025-12-28 11:54:39 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
||||||
2025-11-15 08:29:21 INFO [restartedMain] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 08:29:22 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 08:29:22 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 08:29:22 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-2)']
|
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-2)']
|
||||||
Database driver: undefined/unknown
|
Database driver: undefined/unknown
|
||||||
Database version: 8.0.43
|
Database version: 8.0.43
|
||||||
@ -59,673 +55,5 @@
|
|||||||
Isolation level: undefined/unknown
|
Isolation level: undefined/unknown
|
||||||
Minimum pool size: undefined/unknown
|
Minimum pool size: undefined/unknown
|
||||||
Maximum pool size: undefined/unknown
|
Maximum pool size: undefined/unknown
|
||||||
2025-11-15 08:29:22 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
2025-12-28 11:54:40 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
||||||
2025-11-15 08:29:23 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 3.113 seconds (process running for 618.77)
|
2025-12-28 11:54:41 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 5.124 seconds (process running for 986.129)
|
||||||
2025-11-15 08:29:39 WARN [http-nio-8080-exec-5] o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
|
|
||||||
2025-11-15 08:29:39 ERROR [http-nio-8080-exec-5] o.h.e.jdbc.spi.SqlExceptionHelper - Duplicate entry '091619' for key 'payment_transactions.uq_tx_gateway_txid'
|
|
||||||
2025-11-15 08:29:53 WARN [http-nio-8080-exec-6] o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
|
|
||||||
2025-11-15 08:29:53 ERROR [http-nio-8080-exec-6] o.h.e.jdbc.spi.SqlExceptionHelper - Duplicate entry '091606' for key 'payment_transactions.uq_tx_gateway_txid'
|
|
||||||
2025-11-15 08:35:22 INFO [Thread-7] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown initiated...
|
|
||||||
2025-11-15 08:35:22 INFO [Thread-7] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown completed.
|
|
||||||
2025-11-15 08:35:22 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.8 with PID 13129 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 08:35:22 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 08:35:22 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Starting...
|
|
||||||
2025-11-15 08:35:22 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-3 - Added connection com.mysql.cj.jdbc.ConnectionImpl@28cd0857
|
|
||||||
2025-11-15 08:35:22 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Start completed.
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.util - Run: 0
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.util - Previously run: 51
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.util - Total change sets: 51
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-3)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 08:35:23 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 08:35:24 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 2.476 seconds (process running for 979.836)
|
|
||||||
2025-11-15 08:36:03 INFO [Thread-11] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Shutdown initiated...
|
|
||||||
2025-11-15 08:36:03 INFO [Thread-11] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Shutdown completed.
|
|
||||||
2025-11-15 08:36:03 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.8 with PID 13129 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 08:36:03 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 08:36:03 ERROR [restartedMain] o.s.boot.SpringApplication - Application run failed
|
|
||||||
java.lang.NoClassDefFoundError: List
|
|
||||||
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
|
|
||||||
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3578) ~[na:na]
|
|
||||||
at java.base/java.lang.Class.privateGetPublicMethods(Class.java:3603) ~[na:na]
|
|
||||||
at java.base/java.lang.Class.getMethods(Class.java:2185) ~[na:na]
|
|
||||||
at org.springframework.data.util.ReactiveWrappers.usesReactiveType(ReactiveWrappers.java:141) ~[spring-data-commons-3.5.5.jar:3.5.5]
|
|
||||||
at org.springframework.data.repository.core.support.AbstractRepositoryMetadata.isReactiveRepository(AbstractRepositoryMetadata.java:135) ~[spring-data-commons-3.5.5.jar:3.5.5]
|
|
||||||
at org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport.useRepositoryConfiguration(RepositoryConfigurationExtensionSupport.java:344) ~[spring-data-commons-3.5.5.jar:3.5.5]
|
|
||||||
at org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport.getRepositoryConfigurations(RepositoryConfigurationExtensionSupport.java:98) ~[spring-data-commons-3.5.5.jar:3.5.5]
|
|
||||||
at org.springframework.data.repository.config.RepositoryConfigurationDelegate.registerRepositoriesIn(RepositoryConfigurationDelegate.java:170) ~[spring-data-commons-3.5.5.jar:3.5.5]
|
|
||||||
at org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport.registerBeanDefinitions(AbstractRepositoryConfigurationSourceSupport.java:62) ~[spring-boot-autoconfigure-3.5.7.jar:3.5.7]
|
|
||||||
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:409) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:986) ~[na:na]
|
|
||||||
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:408) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:430) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) ~[spring-context-6.2.12.jar:6.2.12]
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.5.7.jar:3.5.7]
|
|
||||||
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) ~[spring-boot-3.5.7.jar:3.5.7]
|
|
||||||
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.5.7.jar:3.5.7]
|
|
||||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[spring-boot-3.5.7.jar:3.5.7]
|
|
||||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) ~[spring-boot-3.5.7.jar:3.5.7]
|
|
||||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) ~[spring-boot-3.5.7.jar:3.5.7]
|
|
||||||
at com.imprimelibros.erp.ErpApplication.main(ErpApplication.java:14) ~[classes/:na]
|
|
||||||
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
|
|
||||||
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
|
|
||||||
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) ~[spring-boot-devtools-3.5.7.jar:3.5.7]
|
|
||||||
Caused by: java.lang.ClassNotFoundException: List
|
|
||||||
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na]
|
|
||||||
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
|
|
||||||
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[na:na]
|
|
||||||
at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
|
|
||||||
at java.base/java.lang.Class.forName(Class.java:534) ~[na:na]
|
|
||||||
at java.base/java.lang.Class.forName(Class.java:513) ~[na:na]
|
|
||||||
at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) ~[spring-boot-devtools-3.5.7.jar:3.5.7]
|
|
||||||
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[na:na]
|
|
||||||
... 31 common frames omitted
|
|
||||||
2025-11-15 08:40:59 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.8 with PID 36905 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 08:40:59 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 08:41:03 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 08:41:03 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7d70349b
|
|
||||||
2025-11-15 08:41:03 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.util - Run: 0
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.util - Previously run: 52
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.util - Total change sets: 52
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 08:41:04 INFO [restartedMain] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 08:41:05 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 08:41:05 INFO [restartedMain] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 08:41:05 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 08:41:05 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 08:41:07 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 08:41:13 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 13.881 seconds (process running for 15.488)
|
|
||||||
2025-11-15 08:50:21 INFO [Thread-5] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 08:50:21 INFO [Thread-5] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 08:50:21 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.8 with PID 36905 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 08:50:21 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 08:50:22 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Starting...
|
|
||||||
2025-11-15 08:50:22 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7a12d5f7
|
|
||||||
2025-11-15 08:50:22 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Start completed.
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.util - Run: 0
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.util - Previously run: 52
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.util - Total change sets: 52
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 08:50:23 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-2)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 08:50:24 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 08:50:25 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 3.873 seconds (process running for 567.976)
|
|
||||||
2025-11-15 08:50:39 INFO [Thread-7] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown initiated...
|
|
||||||
2025-11-15 08:50:39 INFO [Thread-7] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown completed.
|
|
||||||
2025-11-15 08:50:40 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.8 with PID 36905 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 08:50:40 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 08:50:40 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Starting...
|
|
||||||
2025-11-15 08:50:40 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-3 - Added connection com.mysql.cj.jdbc.ConnectionImpl@393c3fa3
|
|
||||||
2025-11-15 08:50:40 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Start completed.
|
|
||||||
2025-11-15 08:50:40 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.util - Run: 0
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.util - Previously run: 52
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.util - Total change sets: 52
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-3)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 08:50:41 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 08:50:42 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 2.774 seconds (process running for 585.241)
|
|
||||||
2025-11-15 08:51:28 WARN [http-nio-8080-exec-1] o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
|
|
||||||
2025-11-15 08:51:28 ERROR [http-nio-8080-exec-1] o.h.e.jdbc.spi.SqlExceptionHelper - Duplicate entry '341823' for key 'refunds.uq_refund_gateway_id'
|
|
||||||
2025-11-15 08:51:57 WARN [http-nio-8080-exec-2] o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
|
|
||||||
2025-11-15 08:51:57 ERROR [http-nio-8080-exec-2] o.h.e.jdbc.spi.SqlExceptionHelper - Duplicate entry '091606' for key 'refunds.uq_refund_gateway_id'
|
|
||||||
2025-11-15 08:56:05 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.8 with PID 53621 (/home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros/target/classes started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 08:56:05 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 08:56:09 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 08:56:09 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@40832d28
|
|
||||||
2025-11-15 08:56:09 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 08:56:10 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.util - Run: 0
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 08:56:11 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 08:56:13 INFO [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 08:56:19 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 14.185 seconds (process running for 15.876)
|
|
||||||
2025-11-15 09:22:03 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 81704 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 09:22:03 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 09:22:05 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 09:22:05 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@12214f2f
|
|
||||||
2025-11-15 09:22:05 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 09:22:06 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 09:22:07 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 09:22:07 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 09:22:07 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 09:22:07 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 09:22:08 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 09:22:10 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 09:22:15 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.758 seconds (process running for 14.115)
|
|
||||||
2025-11-15 09:22:22 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 09:22:22 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 09:28:43 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 89069 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 09:28:43 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 09:28:46 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 09:28:46 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7c421952
|
|
||||||
2025-11-15 09:28:46 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 09:28:47 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 09:28:48 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 09:28:48 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 09:28:48 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 09:28:48 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 09:28:48 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 09:28:50 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 09:28:56 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.922 seconds (process running for 14.229)
|
|
||||||
2025-11-15 09:29:00 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 09:29:00 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 09:36:40 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 97006 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 09:36:40 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 09:36:43 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 09:36:43 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7c421952
|
|
||||||
2025-11-15 09:36:43 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 09:36:44 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 09:36:44 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 09:36:44 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 09:36:44 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 09:36:45 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 09:36:47 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 09:36:52 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.437 seconds (process running for 13.709)
|
|
||||||
2025-11-15 09:36:55 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 09:36:55 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 09:40:43 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 101329 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 09:40:43 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 09:40:45 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 09:40:45 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@17d2b646
|
|
||||||
2025-11-15 09:40:45 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 09:40:46 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 09:40:47 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 09:40:47 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 09:40:47 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 09:40:47 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 09:40:48 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 09:40:50 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 09:40:55 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.997 seconds (process running for 14.277)
|
|
||||||
2025-11-15 09:40:58 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 09:40:58 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 09:42:43 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 103589 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 09:42:43 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 09:42:45 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 09:42:46 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6cd2cb2
|
|
||||||
2025-11-15 09:42:46 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 09:42:46 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 09:42:47 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 09:42:47 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 09:42:47 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 09:42:47 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 09:42:47 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 09:42:49 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 09:42:55 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.227 seconds (process running for 13.496)
|
|
||||||
2025-11-15 09:43:09 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 09:43:09 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 09:43:16 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 104336 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 09:43:16 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 09:43:18 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 09:43:19 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6f289728
|
|
||||||
2025-11-15 09:43:19 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 09:43:20 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 09:43:20 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 09:43:20 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 09:43:20 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 09:43:20 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 09:43:22 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 09:43:27 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.406 seconds (process running for 13.785)
|
|
||||||
2025-11-15 09:44:27 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 09:44:27 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 09:44:34 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 105914 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 09:44:34 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 09:44:36 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 09:44:37 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6f289728
|
|
||||||
2025-11-15 09:44:37 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 09:44:38 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 09:44:38 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 09:44:38 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 09:44:38 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 09:44:39 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 09:44:40 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 09:44:45 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.126 seconds (process running for 13.405)
|
|
||||||
2025-11-15 09:45:44 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 09:45:44 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 09:45:50 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 107469 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 09:45:50 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 09:45:52 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 09:45:53 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@33215ffb
|
|
||||||
2025-11-15 09:45:53 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 09:45:54 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 09:45:54 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 09:45:54 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 09:45:54 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 09:45:54 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 09:45:56 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 09:46:01 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.001 seconds (process running for 13.345)
|
|
||||||
2025-11-15 09:46:17 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 09:46:17 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 10:04:46 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 126526 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 10:04:46 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 10:04:49 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 10:04:49 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6cd2cb2
|
|
||||||
2025-11-15 10:04:49 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 10:04:50 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 10:04:50 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 10:04:50 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 10:04:50 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 10:04:51 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 10:04:53 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 10:04:58 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.902 seconds (process running for 14.284)
|
|
||||||
2025-11-15 10:05:48 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 10:05:48 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 10:09:03 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 131295 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 10:09:03 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 10:09:06 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 10:09:06 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7de6549d
|
|
||||||
2025-11-15 10:09:06 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 10:09:07 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 10:09:07 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 10:09:07 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 10:09:08 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 10:09:08 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 10:09:10 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 10:09:15 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.299 seconds (process running for 13.566)
|
|
||||||
2025-11-15 10:10:21 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 10:10:21 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 10:10:53 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 133492 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 10:10:53 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 10:10:55 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 10:10:55 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@33215ffb
|
|
||||||
2025-11-15 10:10:55 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 10:10:56 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 10:10:57 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 10:10:57 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 10:10:57 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 10:10:57 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 10:10:57 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 10:10:59 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 10:11:05 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.798 seconds (process running for 14.082)
|
|
||||||
2025-11-15 10:11:36 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 10:11:36 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 10:12:13 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 135015 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 10:12:13 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 10:12:16 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 10:12:16 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7ffcb232
|
|
||||||
2025-11-15 10:12:16 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 10:12:18 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 10:12:18 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 10:12:18 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 10:12:18 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 10:12:19 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 10:12:21 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 10:12:26 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 13.246 seconds (process running for 14.87)
|
|
||||||
2025-11-15 10:22:31 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 10:22:31 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 10:22:40 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 146328 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 10:22:40 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 10:22:43 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 10:22:43 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6f289728
|
|
||||||
2025-11-15 10:22:43 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 10:22:44 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 10:22:44 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 10:22:44 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 10:22:44 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 10:22:45 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 10:22:47 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 10:22:52 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.681 seconds (process running for 13.963)
|
|
||||||
2025-11-15 10:24:16 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 10:24:16 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 10:24:21 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 148308 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 10:24:21 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 10:24:24 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 10:24:24 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6cd2cb2
|
|
||||||
2025-11-15 10:24:24 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 10:24:25 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 10:24:25 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 10:24:25 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 10:24:25 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 10:24:26 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 10:24:28 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 10:24:33 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.138 seconds (process running for 13.409)
|
|
||||||
2025-11-15 10:24:42 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 10:24:42 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
2025-11-15 10:29:55 INFO [main] c.i.erp.cart.envioCarroTest - Starting envioCarroTest using Java 21.0.8 with PID 154243 (started by jjimenez in /home/jjimenez/DEVELOPMENT/01_PROGRAMMING/erp-imprimelibros)
|
|
||||||
2025-11-15 10:29:55 INFO [main] c.i.erp.cart.envioCarroTest - The following 1 profile is active: "dev"
|
|
||||||
2025-11-15 10:29:58 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
|
||||||
2025-11-15 10:29:58 INFO [main] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@33215ffb
|
|
||||||
2025-11-15 10:29:58 INFO [main] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.ui - Database is up to date, no changesets to execute
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.util - UPDATE SUMMARY
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.util - Run: 0
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.util - Previously run: 53
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.util - Filtered out: 0
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.util - -------------------------------
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.util - Total change sets: 53
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.util - Update summary generated
|
|
||||||
2025-11-15 10:29:59 INFO [main] liquibase.command - Command execution complete
|
|
||||||
2025-11-15 10:29:59 INFO [main] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
|
||||||
2025-11-15 10:29:59 INFO [main] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.33.Final
|
|
||||||
2025-11-15 10:29:59 INFO [main] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
|
|
||||||
2025-11-15 10:30:00 INFO [main] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
|
|
||||||
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
|
|
||||||
Database driver: undefined/unknown
|
|
||||||
Database version: 8.0.43
|
|
||||||
Autocommit mode: undefined/unknown
|
|
||||||
Isolation level: undefined/unknown
|
|
||||||
Minimum pool size: undefined/unknown
|
|
||||||
Maximum pool size: undefined/unknown
|
|
||||||
2025-11-15 10:30:02 INFO [main] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
|
||||||
2025-11-15 10:30:07 INFO [main] c.i.erp.cart.envioCarroTest - Started envioCarroTest in 12.337 seconds (process running for 13.593)
|
|
||||||
2025-11-15 10:30:11 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
|
||||||
2025-11-15 10:30:11 INFO [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.5.7</version>
|
<version>3.5.9</version>
|
||||||
<relativePath /> <!-- lookup parent from repository -->
|
<relativePath /> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.imprimelibros</groupId>
|
<groupId>com.imprimelibros</groupId>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -20,41 +21,41 @@ import com.imprimelibros.erp.cart.dto.DireccionCardDTO;
|
|||||||
import com.imprimelibros.erp.cart.dto.DireccionShipment;
|
import com.imprimelibros.erp.cart.dto.DireccionShipment;
|
||||||
import com.imprimelibros.erp.cart.dto.UpdateCartRequest;
|
import com.imprimelibros.erp.cart.dto.UpdateCartRequest;
|
||||||
import com.imprimelibros.erp.common.Utils;
|
import com.imprimelibros.erp.common.Utils;
|
||||||
|
import com.imprimelibros.erp.common.email.EmailService;
|
||||||
import com.imprimelibros.erp.direcciones.DireccionService;
|
import com.imprimelibros.erp.direcciones.DireccionService;
|
||||||
import com.imprimelibros.erp.externalApi.skApiClient;
|
import com.imprimelibros.erp.externalApi.skApiClient;
|
||||||
import com.imprimelibros.erp.pedidos.Pedido;
|
import com.imprimelibros.erp.pedidos.PedidoRepository;
|
||||||
import com.imprimelibros.erp.pedidos.PedidoService;
|
|
||||||
import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
|
import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CartService {
|
public class CartService {
|
||||||
|
|
||||||
|
private final EmailService emailService;
|
||||||
|
|
||||||
private final CartRepository cartRepo;
|
private final CartRepository cartRepo;
|
||||||
private final CartDireccionRepository cartDireccionRepo;
|
private final CartDireccionRepository cartDireccionRepo;
|
||||||
private final CartItemRepository itemRepo;
|
private final CartItemRepository itemRepo;
|
||||||
private final MessageSource messageSource;
|
private final MessageSource messageSource;
|
||||||
private final PresupuestoRepository presupuestoRepo;
|
private final PresupuestoRepository presupuestoRepo;
|
||||||
private final Utils utils;
|
|
||||||
private final DireccionService direccionService;
|
private final DireccionService direccionService;
|
||||||
private final skApiClient skApiClient;
|
private final skApiClient skApiClient;
|
||||||
private final PedidoService pedidoService;
|
|
||||||
private final PresupuestoService presupuestoService;
|
private final PresupuestoService presupuestoService;
|
||||||
|
private final PedidoRepository pedidoRepository;
|
||||||
|
|
||||||
public CartService(CartRepository cartRepo, CartItemRepository itemRepo,
|
public CartService(CartRepository cartRepo, CartItemRepository itemRepo,
|
||||||
CartDireccionRepository cartDireccionRepo, MessageSource messageSource,
|
CartDireccionRepository cartDireccionRepo, MessageSource messageSource,
|
||||||
PresupuestoFormatter presupuestoFormatter, PresupuestoRepository presupuestoRepo,
|
PresupuestoFormatter presupuestoFormatter, PresupuestoRepository presupuestoRepo, PedidoRepository pedidoRepository,
|
||||||
Utils utils, DireccionService direccionService, skApiClient skApiClient,
|
DireccionService direccionService, skApiClient skApiClient,PresupuestoService presupuestoService, EmailService emailService) {
|
||||||
PedidoService pedidoService, PresupuestoService presupuestoService) {
|
|
||||||
this.cartRepo = cartRepo;
|
this.cartRepo = cartRepo;
|
||||||
this.itemRepo = itemRepo;
|
this.itemRepo = itemRepo;
|
||||||
this.cartDireccionRepo = cartDireccionRepo;
|
this.cartDireccionRepo = cartDireccionRepo;
|
||||||
this.messageSource = messageSource;
|
this.messageSource = messageSource;
|
||||||
this.presupuestoRepo = presupuestoRepo;
|
this.presupuestoRepo = presupuestoRepo;
|
||||||
this.utils = utils;
|
|
||||||
this.direccionService = direccionService;
|
this.direccionService = direccionService;
|
||||||
this.skApiClient = skApiClient;
|
this.skApiClient = skApiClient;
|
||||||
this.pedidoService = pedidoService;
|
|
||||||
this.presupuestoService = presupuestoService;
|
this.presupuestoService = presupuestoService;
|
||||||
|
this.emailService = emailService;
|
||||||
|
this.pedidoRepository = pedidoRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cart findById(Long cartId) {
|
public Cart findById(Long cartId) {
|
||||||
@ -89,7 +90,7 @@ public class CartService {
|
|||||||
|
|
||||||
Presupuesto p = item.getPresupuesto();
|
Presupuesto p = item.getPresupuesto();
|
||||||
|
|
||||||
Map<String, Object> elemento = getElementoCart(p, locale);
|
Map<String, Object> elemento = presupuestoService.getPresupuestoInfoForCard(p, locale);
|
||||||
elemento.put("cartItemId", item.getId());
|
elemento.put("cartItemId", item.getId());
|
||||||
resultados.add(elemento);
|
resultados.add(elemento);
|
||||||
}
|
}
|
||||||
@ -159,38 +160,6 @@ public class CartService {
|
|||||||
return itemRepo.findByCartId(cart.getId()).size();
|
return itemRepo.findByCartId(cart.getId()).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> getElementoCart(Presupuesto presupuesto, Locale locale) {
|
|
||||||
|
|
||||||
Map<String, Object> resumen = new HashMap<>();
|
|
||||||
|
|
||||||
resumen.put("titulo", presupuesto.getTitulo());
|
|
||||||
|
|
||||||
resumen.put("imagen",
|
|
||||||
"/assets/images/imprimelibros/presupuestador/" + presupuesto.getTipoEncuadernacion() + ".png");
|
|
||||||
resumen.put("imagen_alt",
|
|
||||||
messageSource.getMessage("presupuesto." + presupuesto.getTipoEncuadernacion(), null, locale));
|
|
||||||
|
|
||||||
resumen.put("presupuestoId", presupuesto.getId());
|
|
||||||
|
|
||||||
if (presupuesto.getServiciosJson() != null && presupuesto.getServiciosJson().contains("ejemplar-prueba")) {
|
|
||||||
resumen.put("hasSample", true);
|
|
||||||
} else {
|
|
||||||
resumen.put("hasSample", false);
|
|
||||||
}
|
|
||||||
Map<String, Object> detalles = utils.getTextoPresupuesto(presupuesto, locale);
|
|
||||||
|
|
||||||
resumen.put("tirada", presupuesto.getSelectedTirada());
|
|
||||||
|
|
||||||
resumen.put("baseTotal", Utils.formatCurrency(presupuesto.getBaseImponible(), locale));
|
|
||||||
resumen.put("base", presupuesto.getBaseImponible());
|
|
||||||
resumen.put("iva4", presupuesto.getIvaImporte4());
|
|
||||||
resumen.put("iva21", presupuesto.getIvaImporte21());
|
|
||||||
|
|
||||||
resumen.put("resumen", detalles);
|
|
||||||
|
|
||||||
return resumen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> getCartSummaryRaw(Cart cart, Locale locale) {
|
public Map<String, Object> getCartSummaryRaw(Cart cart, Locale locale) {
|
||||||
|
|
||||||
double base = 0.0;
|
double base = 0.0;
|
||||||
@ -298,7 +267,7 @@ public class CartService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double totalBeforeDiscount = base + iva4 + iva21 + shipment;
|
double totalBeforeDiscount = base + iva4 + iva21 + shipment;
|
||||||
int fidelizacion = pedidoService.getDescuentoFidelizacion();
|
int fidelizacion = this.getDescuentoFidelizacion(cart.getUserId());
|
||||||
double descuento = totalBeforeDiscount * fidelizacion / 100.0;
|
double descuento = totalBeforeDiscount * fidelizacion / 100.0;
|
||||||
double total = totalBeforeDiscount - descuento;
|
double total = totalBeforeDiscount - descuento;
|
||||||
|
|
||||||
@ -325,6 +294,27 @@ public class CartService {
|
|||||||
return summary;
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDescuentoFidelizacion(Long userId) {
|
||||||
|
// descuento entre el 1% y el 6% para clientes fidelidad (mas de 1500€ en el
|
||||||
|
// ultimo año)
|
||||||
|
Instant haceUnAno = Instant.now().minusSeconds(365 * 24 * 60 * 60);
|
||||||
|
double totalGastado = pedidoRepository.sumTotalByCreatedByAndCreatedAtAfter(userId, haceUnAno);
|
||||||
|
if (totalGastado < 1200) {
|
||||||
|
return 0;
|
||||||
|
} else if (totalGastado >= 1200 && totalGastado < 1999) {
|
||||||
|
return 1;
|
||||||
|
} else if (totalGastado >= 2000 && totalGastado < 2999) {
|
||||||
|
return 2;
|
||||||
|
} else if (totalGastado >= 3000 && totalGastado < 3999) {
|
||||||
|
return 3;
|
||||||
|
} else if (totalGastado >= 4000 && totalGastado < 4999) {
|
||||||
|
return 4;
|
||||||
|
} else if (totalGastado >= 5000) {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> getCartSummary(Cart cart, Locale locale) {
|
public Map<String, Object> getCartSummary(Cart cart, Locale locale) {
|
||||||
Map<String, Object> raw = getCartSummaryRaw(cart, locale);
|
Map<String, Object> raw = getCartSummaryRaw(cart, locale);
|
||||||
|
|
||||||
@ -445,175 +435,6 @@ public class CartService {
|
|||||||
cartDireccionRepo.deleteByDireccionIdAndCartStatus(direccionId, Cart.Status.ACTIVE);
|
cartDireccionRepo.deleteByDireccionIdAndCartStatus(direccionId, Cart.Status.ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public Long crearPedido(Long cartId, Locale locale) {
|
|
||||||
|
|
||||||
Cart cart = this.getCartById(cartId);
|
|
||||||
List<CartItem> items = cart.getItems();
|
|
||||||
|
|
||||||
List<Map<String, Object>> presupuestoRequests = new ArrayList<>();
|
|
||||||
List<Long> presupuestoIds = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Integer i = 0; i < items.size(); i++) {
|
|
||||||
CartItem item = items.get(i);
|
|
||||||
Presupuesto pCart = item.getPresupuesto();
|
|
||||||
|
|
||||||
// Asegurarnos de trabajar con la entidad gestionada por JPA
|
|
||||||
Presupuesto p = presupuestoRepo.findById(pCart.getId())
|
|
||||||
.orElseThrow(() -> new IllegalStateException("Presupuesto no encontrado: " + pCart.getId()));
|
|
||||||
|
|
||||||
Map<String, Object> data_to_send = presupuestoService.toSkApiRequest(p, true);
|
|
||||||
data_to_send.put("createPedido", 0);
|
|
||||||
|
|
||||||
// Recuperar el mapa anidado datosCabecera
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, Object> datosCabecera = (Map<String, Object>) data_to_send.get("datosCabecera");
|
|
||||||
if (datosCabecera != null) {
|
|
||||||
Object tituloOriginal = datosCabecera.get("titulo");
|
|
||||||
datosCabecera.put(
|
|
||||||
"titulo",
|
|
||||||
"[" + (i + 1) + "/" + items.size() + "] " + (tituloOriginal != null ? tituloOriginal : ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> direcciones_presupuesto = this.getDireccionesPresupuesto(cart, p);
|
|
||||||
data_to_send.put("direcciones", direcciones_presupuesto.get("direcciones"));
|
|
||||||
data_to_send.put("direccionesFP1", direcciones_presupuesto.get("direccionesFP1"));
|
|
||||||
|
|
||||||
Map<String, Object> result = skApiClient.savePresupuesto(data_to_send);
|
|
||||||
|
|
||||||
if (result.containsKey("error")) {
|
|
||||||
System.out.println("Error al guardar presupuesto en SK");
|
|
||||||
System.out.println("-------------------------");
|
|
||||||
System.out.println(result.get("error"));
|
|
||||||
// decide si seguir con otros items o abortar:
|
|
||||||
// continue; o bien throw ...
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object dataObj = result.get("data");
|
|
||||||
if (!(dataObj instanceof Map<?, ?> dataRaw)) {
|
|
||||||
System.out.println("Formato inesperado de 'data' en savePresupuesto: " + result);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, Object> dataMap = (Map<String, Object>) dataRaw;
|
|
||||||
Long presId = ((Number) dataMap.get("id")).longValue();
|
|
||||||
String skin = ((String) dataMap.get("iskn")).toString();
|
|
||||||
p.setProveedor("Safekat");
|
|
||||||
p.setProveedorRef1(skin);
|
|
||||||
p.setProveedorRef2(presId);
|
|
||||||
p.setEstado(Presupuesto.Estado.aceptado);
|
|
||||||
presupuestoRepo.save(p);
|
|
||||||
|
|
||||||
presupuestoIds.add(p.getId());
|
|
||||||
|
|
||||||
presupuestoRequests.add(dataMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crear el pedido en base a los presupuestos guardados
|
|
||||||
if (presupuestoRequests.isEmpty()) {
|
|
||||||
throw new IllegalStateException("No se pudieron guardar los presupuestos en SK.");
|
|
||||||
} else {
|
|
||||||
ArrayList<Long> presupuestoSkIds = new ArrayList<>();
|
|
||||||
for (Map<String, Object> presData : presupuestoRequests) {
|
|
||||||
Long presId = ((Number) presData.get("id")).longValue();
|
|
||||||
presupuestoSkIds.add(presId);
|
|
||||||
}
|
|
||||||
Map<String, Object> ids = new HashMap<>();
|
|
||||||
ids.put("presupuesto_ids", presupuestoSkIds);
|
|
||||||
Long pedidoId = skApiClient.crearPedido(ids);
|
|
||||||
if (pedidoId == null) {
|
|
||||||
throw new IllegalStateException("No se pudo crear el pedido en SK.");
|
|
||||||
}
|
|
||||||
Pedido pedidoInterno = pedidoService.crearPedido(presupuestoIds, this.getCartSummaryRaw(cart, locale),
|
|
||||||
"Safekat", String.valueOf(pedidoId), cart.getUserId());
|
|
||||||
return pedidoInterno.getId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> getDireccionesPresupuesto(Cart cart, Presupuesto presupuesto) {
|
|
||||||
|
|
||||||
List<Map<String, Object>> direccionesPresupuesto = new ArrayList<>();
|
|
||||||
List<Map<String, Object>> direccionesPrueba = new ArrayList<>();
|
|
||||||
if (cart.getOnlyOneShipment()) {
|
|
||||||
List<CartDireccion> direcciones = cart.getDirecciones().stream().limit(1).toList();
|
|
||||||
if (!direcciones.isEmpty()) {
|
|
||||||
if (presupuesto.getServiciosJson() != null
|
|
||||||
&& presupuesto.getServiciosJson().contains("deposito-legal")) {
|
|
||||||
direccionesPresupuesto.add(direcciones.get(0).toSkMap(
|
|
||||||
presupuesto.getSelectedTirada()-4,
|
|
||||||
presupuesto.getPeso(),
|
|
||||||
direcciones.get(0).getIsPalets(),
|
|
||||||
false));
|
|
||||||
|
|
||||||
direccionesPresupuesto.add(direcciones.get(0).toSkMapDepositoLegal());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
direccionesPresupuesto.add(direcciones.get(0).toSkMap(
|
|
||||||
presupuesto.getSelectedTirada(),
|
|
||||||
presupuesto.getPeso(),
|
|
||||||
direcciones.get(0).getIsPalets(),
|
|
||||||
false));
|
|
||||||
}
|
|
||||||
if (presupuesto.getServiciosJson() != null
|
|
||||||
&& presupuesto.getServiciosJson().contains("ejemplar-prueba")) {
|
|
||||||
direccionesPrueba.add(direcciones.get(0).toSkMap(
|
|
||||||
1,
|
|
||||||
presupuesto.getPeso(),
|
|
||||||
false,
|
|
||||||
true));
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> direccionesRet = new HashMap<>();
|
|
||||||
direccionesRet.put("direcciones", direccionesPresupuesto);
|
|
||||||
if (!direccionesPrueba.isEmpty())
|
|
||||||
direccionesRet.put("direccionesFP1", direccionesPrueba.get(0));
|
|
||||||
else {
|
|
||||||
direccionesRet.put("direccionesFP1", new ArrayList<>());
|
|
||||||
}
|
|
||||||
return direccionesRet;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
List<CartDireccion> direcciones = cart.getDirecciones().stream()
|
|
||||||
.filter(d -> d.getPresupuesto() != null && d.getPresupuesto().getId().equals(presupuesto.getId()))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
for (CartDireccion cd : direcciones) {
|
|
||||||
|
|
||||||
// direccion de ejemplar de prueba
|
|
||||||
if (cd.getPresupuesto() == null || !cd.getPresupuesto().getId().equals(presupuesto.getId())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cd.getUnidades() == null || cd.getUnidades() <= 0) {
|
|
||||||
direccionesPrueba.add(cd.toSkMap(
|
|
||||||
1,
|
|
||||||
presupuesto.getPeso(),
|
|
||||||
false,
|
|
||||||
true));
|
|
||||||
} else {
|
|
||||||
direccionesPresupuesto.add(cd.toSkMap(
|
|
||||||
cd.getUnidades(),
|
|
||||||
presupuesto.getPeso(),
|
|
||||||
cd.getIsPalets(),
|
|
||||||
false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (presupuesto.getServiciosJson() != null
|
|
||||||
&& presupuesto.getServiciosJson().contains("deposito-legal")) {
|
|
||||||
CartDireccion cd = new CartDireccion();
|
|
||||||
direccionesPresupuesto.add(cd.toSkMapDepositoLegal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Map<String, Object> direccionesRet = new HashMap<>();
|
|
||||||
direccionesRet.put("direcciones", direccionesPresupuesto);
|
|
||||||
if (!direccionesPrueba.isEmpty())
|
|
||||||
direccionesRet.put("direccionesFP1", direccionesPrueba.get(0));
|
|
||||||
else {
|
|
||||||
direccionesRet.put("direccionesFP1", new ArrayList<>());
|
|
||||||
}
|
|
||||||
return direccionesRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************
|
/***************************************
|
||||||
* MÉTODOS PRIVADOS
|
* MÉTODOS PRIVADOS
|
||||||
|
|||||||
@ -4,7 +4,9 @@ import java.math.BigDecimal;
|
|||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -12,6 +14,7 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
@ -357,4 +360,62 @@ public class Utils {
|
|||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm", locale);
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm", locale);
|
||||||
return dateTime.format(formatter);
|
return dateTime.format(formatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String formatDate(LocalDateTime dateTime, Locale locale) {
|
||||||
|
if (dateTime == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy", locale);
|
||||||
|
return dateTime.format(formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatInstant(Instant instant, Locale locale) {
|
||||||
|
if (instant == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
ZoneId zone = zoneIdForLocale(locale);
|
||||||
|
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter
|
||||||
|
.ofPattern("dd/MM/yyyy HH:mm", locale)
|
||||||
|
.withZone(zone);
|
||||||
|
|
||||||
|
return formatter.format(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* Metodos auxiliares
|
||||||
|
*/
|
||||||
|
private static ZoneId zoneIdForLocale(Locale locale) {
|
||||||
|
if (locale == null || locale.getCountry().isEmpty()) {
|
||||||
|
return ZoneId.of("UTC");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar timezones cuyo ID termine con el country code
|
||||||
|
// Ej: ES -> Europe/Madrid
|
||||||
|
String country = locale.getCountry();
|
||||||
|
|
||||||
|
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
|
||||||
|
for (String id : zoneIds) {
|
||||||
|
// TimeZone# getID() no funciona por país, pero sí el prefijo + país
|
||||||
|
if (id.endsWith("/" + country) || id.contains("/" + country)) {
|
||||||
|
return ZoneId.of(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback por regiones comunes (manual pero muy útil)
|
||||||
|
Map<String, String> fallback = Map.of(
|
||||||
|
"ES", "Europe/Madrid",
|
||||||
|
"MX", "America/Mexico_City",
|
||||||
|
"AR", "America/Argentina/Buenos_Aires",
|
||||||
|
"US", "America/New_York",
|
||||||
|
"GB", "Europe/London",
|
||||||
|
"FR", "Europe/Paris");
|
||||||
|
|
||||||
|
if (fallback.containsKey(country)) {
|
||||||
|
return ZoneId.of(fallback.get(country));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ZoneId.systemDefault(); // último fallback
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -229,6 +229,7 @@ public class skApiClient {
|
|||||||
Long id = ((Integer) responseBody.get("id")).longValue();
|
Long id = ((Integer) responseBody.get("id")).longValue();
|
||||||
|
|
||||||
if (success != null && id != null && success) {
|
if (success != null && id != null && success) {
|
||||||
|
|
||||||
return Map.of("data", id);
|
return Map.of("data", id);
|
||||||
} else {
|
} else {
|
||||||
// Tu lógica actual: si success es true u otra cosa → error 2
|
// Tu lógica actual: si success es true u otra cosa → error 2
|
||||||
@ -247,7 +248,7 @@ public class skApiClient {
|
|||||||
return (Long) result.get("data");
|
return (Long) result.get("data");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getMaxSolapas(Map<String, Object> requestBody, Locale locale) {
|
public Map<String, Object> getMaxSolapas(Map<String, Object> requestBody, Locale locale) {
|
||||||
try {
|
try {
|
||||||
String jsonResponse = performWithRetry(() -> {
|
String jsonResponse = performWithRetry(() -> {
|
||||||
String url = this.skApiUrl + "api/calcular-solapas";
|
String url = this.skApiUrl + "api/calcular-solapas";
|
||||||
@ -288,7 +289,11 @@ public class skApiClient {
|
|||||||
messageSource.getMessage("presupuesto.errores.error-interior", new Object[] { 1 }, locale));
|
messageSource.getMessage("presupuesto.errores.error-interior", new Object[] { 1 }, locale));
|
||||||
}
|
}
|
||||||
|
|
||||||
return root.get("data").asInt();
|
Integer maxSolapas = root.get("data").asInt();
|
||||||
|
Double lomo = root.get("lomo").asDouble();
|
||||||
|
return Map.of(
|
||||||
|
"maxSolapas", maxSolapas,
|
||||||
|
"lomo", lomo);
|
||||||
|
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
// Fallback al 80% del ancho
|
// Fallback al 80% del ancho
|
||||||
@ -301,7 +306,9 @@ public class skApiClient {
|
|||||||
throw new RuntimeException("Tamaño no válido en la solicitud: " + requestBody);
|
throw new RuntimeException("Tamaño no válido en la solicitud: " + requestBody);
|
||||||
else {
|
else {
|
||||||
int ancho = (int) tamanio.get("ancho");
|
int ancho = (int) tamanio.get("ancho");
|
||||||
return (int) Math.floor(ancho * 0.8); // 80% del ancho
|
return Map.of(
|
||||||
|
"maxSolapas", (int) (ancho * 0.8),
|
||||||
|
"lomo", 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,6 +395,187 @@ public class skApiClient {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> checkPedidoEstado(Long presupuestoId, Locale locale) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
String jsonResponse = performWithRetry(() -> {
|
||||||
|
String url = this.skApiUrl + "api/estado-pedido/" + presupuestoId;
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.setBearerAuth(authService.getToken()); // token actualizado
|
||||||
|
|
||||||
|
HttpEntity<Void> entity = new HttpEntity<>(headers);
|
||||||
|
|
||||||
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
|
url,
|
||||||
|
HttpMethod.GET,
|
||||||
|
entity,
|
||||||
|
String.class);
|
||||||
|
|
||||||
|
return response.getBody();
|
||||||
|
});
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
JsonNode root = mapper.readTree(jsonResponse);
|
||||||
|
|
||||||
|
if (root.get("data") == null) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Sin respuesta desde el servidor del proveedor");
|
||||||
|
}
|
||||||
|
|
||||||
|
String estado = root.get("data").asText();
|
||||||
|
return Map.of(
|
||||||
|
"estado", estado);
|
||||||
|
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
// Fallback al 80% del ancho
|
||||||
|
return Map.of(
|
||||||
|
"estado", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getFilesTypes(Long presupuestoId, Locale locale) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
Map<String, Object> result = performWithRetryMap(() -> {
|
||||||
|
String url = this.skApiUrl + "api/files-presupuesto/" + presupuestoId;
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.setBearerAuth(authService.getToken()); // token actualizado
|
||||||
|
|
||||||
|
HttpEntity<Void> entity = new HttpEntity<>(headers);
|
||||||
|
|
||||||
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
|
url,
|
||||||
|
HttpMethod.GET,
|
||||||
|
entity,
|
||||||
|
String.class);
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, Object> responseBody = mapper.readValue(
|
||||||
|
response.getBody(),
|
||||||
|
new TypeReference<Map<String, Object>>() {
|
||||||
|
});
|
||||||
|
|
||||||
|
// Si la API devuelve "error" a nivel raíz
|
||||||
|
if (responseBody.get("error") != null) {
|
||||||
|
// Devolvemos un mapa con sólo el error para que el caller decida
|
||||||
|
return Map.of("error", responseBody.get("error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean hasError = (Boolean) (responseBody.get("error") == null
|
||||||
|
|| responseBody.get("error") == "null" ? false : true);
|
||||||
|
Map<String, Boolean> files = (Map<String, Boolean>) responseBody.get("data");
|
||||||
|
|
||||||
|
if (files != null && !hasError) {
|
||||||
|
return Map.of("data", files);
|
||||||
|
} else {
|
||||||
|
// Tu lógica actual: si success es true u otra cosa → error 2
|
||||||
|
return Map.of("error", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return Map.of("error", 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.get("error") != null) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
messageSource.getMessage("pedido.errors.connecting-server-error", null, locale));
|
||||||
|
}
|
||||||
|
Map<String, Object> data = (Map<String, Object>) result.get("data");
|
||||||
|
return data;
|
||||||
|
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
messageSource.getMessage("pedido.errors.connecting-server-error", null, locale));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] downloadFile(Long presupuestoId, String fileType, Locale locale) {
|
||||||
|
return performWithRetryBytes(() -> {
|
||||||
|
|
||||||
|
String normalized = (fileType == null) ? "" : fileType.trim().toLowerCase();
|
||||||
|
|
||||||
|
String endpoint = switch (normalized) {
|
||||||
|
case "ferro" -> "api/get-ferro/" + presupuestoId;
|
||||||
|
case "cubierta" -> "api/get-cubierta/" + presupuestoId;
|
||||||
|
case "tapa" -> "api/get-tapa/" + presupuestoId;
|
||||||
|
default -> throw new IllegalArgumentException("Tipo de fichero no soportado: " + fileType);
|
||||||
|
};
|
||||||
|
|
||||||
|
// OJO: skApiUrl debería terminar en "/" para que concatene bien
|
||||||
|
String url = this.skApiUrl + endpoint;
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
// Si tu CI4 requiere Bearer, mantenlo. Si NO lo requiere, puedes quitar esta
|
||||||
|
// línea.
|
||||||
|
headers.setBearerAuth(authService.getToken());
|
||||||
|
headers.setAccept(List.of(MediaType.APPLICATION_PDF, MediaType.APPLICATION_OCTET_STREAM));
|
||||||
|
|
||||||
|
try {
|
||||||
|
ResponseEntity<byte[]> response = restTemplate.exchange(
|
||||||
|
url,
|
||||||
|
HttpMethod.GET,
|
||||||
|
new HttpEntity<>(headers),
|
||||||
|
byte[].class);
|
||||||
|
|
||||||
|
if (response.getStatusCode().is2xxSuccessful()) {
|
||||||
|
return response.getBody(); // bytes del PDF
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (HttpClientErrorException.NotFound e) {
|
||||||
|
// CI4 no tiene ese fichero
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean aceptarFerro(Long presupuestoId, Locale locale) {
|
||||||
|
|
||||||
|
String result = performWithRetry(() -> {
|
||||||
|
String url = this.skApiUrl + "api/aceptar-ferro/" + presupuestoId;
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.setBearerAuth(authService.getToken());
|
||||||
|
|
||||||
|
HttpEntity<Void> entity = new HttpEntity<>(headers);
|
||||||
|
|
||||||
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
|
url,
|
||||||
|
HttpMethod.POST,
|
||||||
|
entity,
|
||||||
|
String.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, Object> responseBody = new ObjectMapper().readValue(
|
||||||
|
response.getBody(),
|
||||||
|
new TypeReference<Map<String, Object>>() {
|
||||||
|
});
|
||||||
|
|
||||||
|
Boolean success = (Boolean) (responseBody.get("success") != null ? responseBody.get("success") : false);
|
||||||
|
|
||||||
|
return success.toString();
|
||||||
|
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "false"; // Fallback en caso de error
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Boolean.parseBoolean(result);
|
||||||
|
}
|
||||||
|
|
||||||
/******************
|
/******************
|
||||||
* PRIVATE METHODS
|
* PRIVATE METHODS
|
||||||
******************/
|
******************/
|
||||||
@ -419,6 +607,19 @@ public class skApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] performWithRetryBytes(Supplier<byte[]> request) {
|
||||||
|
try {
|
||||||
|
return request.get();
|
||||||
|
} catch (HttpClientErrorException.Unauthorized e) {
|
||||||
|
authService.invalidateToken();
|
||||||
|
try {
|
||||||
|
return request.get();
|
||||||
|
} catch (HttpClientErrorException ex) {
|
||||||
|
throw new RuntimeException("La autenticación ha fallado tras renovar el token.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static BigDecimal calcularMargen(
|
private static BigDecimal calcularMargen(
|
||||||
BigDecimal importe, BigDecimal importeMin, BigDecimal importeMax,
|
BigDecimal importe, BigDecimal importeMin, BigDecimal importeMax,
|
||||||
BigDecimal margenMax, BigDecimal margenMin) {
|
BigDecimal margenMax, BigDecimal margenMin) {
|
||||||
|
|||||||
@ -69,4 +69,18 @@ public class PaisesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPaisNombrePorCode3(String code3, Locale locale) {
|
||||||
|
if (code3 == null || code3.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Optional<Paises> opt = repo.findByCode3(code3);
|
||||||
|
if (opt.isPresent()) {
|
||||||
|
Paises pais = opt.get();
|
||||||
|
String key = pais.getKeyword();
|
||||||
|
return messageSource.getMessage("paises." + key, null, key, locale);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.imprimelibros.erp.common.Utils;
|
import com.imprimelibros.erp.common.Utils;
|
||||||
import com.imprimelibros.erp.datatables.DataTable;
|
import com.imprimelibros.erp.datatables.DataTable;
|
||||||
import com.imprimelibros.erp.datatables.DataTablesParser;
|
import com.imprimelibros.erp.datatables.DataTablesParser;
|
||||||
@ -98,7 +100,7 @@ public class PaymentController {
|
|||||||
Specification<PaymentTransaction> base = Specification.allOf(
|
Specification<PaymentTransaction> base = Specification.allOf(
|
||||||
(root, query, cb) -> cb.equal(root.get("status"), PaymentTransactionStatus.succeeded));
|
(root, query, cb) -> cb.equal(root.get("status"), PaymentTransactionStatus.succeeded));
|
||||||
base = base.and((root, query, cb) -> cb.equal(root.get("type"), PaymentTransactionType.CAPTURE));
|
base = base.and((root, query, cb) -> cb.equal(root.get("type"), PaymentTransactionType.CAPTURE));
|
||||||
|
base = base.and((root, query, cb) -> cb.notEqual(root.join("payment").get("gateway"), "bank_transfer"));
|
||||||
String clientSearch = dt.getColumnSearch("client");
|
String clientSearch = dt.getColumnSearch("client");
|
||||||
|
|
||||||
// 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification
|
// 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification
|
||||||
@ -229,10 +231,12 @@ public class PaymentController {
|
|||||||
})
|
})
|
||||||
.add("transfer_id", pago -> {
|
.add("transfer_id", pago -> {
|
||||||
if (pago.getPayment() != null) {
|
if (pago.getPayment() != null) {
|
||||||
return "TRANSF-" + pago.getPayment().getOrderId();
|
Long pedido = pago.getPayment().getOrderId();
|
||||||
} else {
|
if (pedido != null) {
|
||||||
return "";
|
return "TRANSF-" + pedido;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return "";
|
||||||
})
|
})
|
||||||
.add("order_id", pago -> {
|
.add("order_id", pago -> {
|
||||||
if (pago.getStatus() != PaymentTransactionStatus.pending) {
|
if (pago.getStatus() != PaymentTransactionStatus.pending) {
|
||||||
|
|||||||
@ -13,9 +13,13 @@ import com.imprimelibros.erp.redsys.RedsysService.RedsysNotification;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import com.imprimelibros.erp.payments.repo.WebhookEventRepository;
|
import com.imprimelibros.erp.payments.repo.WebhookEventRepository;
|
||||||
|
import com.imprimelibros.erp.pedidos.Pedido;
|
||||||
|
import com.imprimelibros.erp.pedidos.PedidoLinea;
|
||||||
|
import com.imprimelibros.erp.pedidos.PedidoService;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ -28,18 +32,61 @@ public class PaymentService {
|
|||||||
private final WebhookEventRepository webhookEventRepo;
|
private final WebhookEventRepository webhookEventRepo;
|
||||||
private final ObjectMapper om = new ObjectMapper();
|
private final ObjectMapper om = new ObjectMapper();
|
||||||
private final CartService cartService;
|
private final CartService cartService;
|
||||||
|
private final PedidoService pedidoService;
|
||||||
|
|
||||||
public PaymentService(PaymentRepository payRepo,
|
public PaymentService(PaymentRepository payRepo,
|
||||||
PaymentTransactionRepository txRepo,
|
PaymentTransactionRepository txRepo,
|
||||||
RefundRepository refundRepo,
|
RefundRepository refundRepo,
|
||||||
RedsysService redsysService,
|
RedsysService redsysService,
|
||||||
WebhookEventRepository webhookEventRepo, CartService cartService) {
|
WebhookEventRepository webhookEventRepo,
|
||||||
|
CartService cartService,
|
||||||
|
PedidoService pedidoService) {
|
||||||
this.payRepo = payRepo;
|
this.payRepo = payRepo;
|
||||||
this.txRepo = txRepo;
|
this.txRepo = txRepo;
|
||||||
this.refundRepo = refundRepo;
|
this.refundRepo = refundRepo;
|
||||||
this.redsysService = redsysService;
|
this.redsysService = redsysService;
|
||||||
this.webhookEventRepo = webhookEventRepo;
|
this.webhookEventRepo = webhookEventRepo;
|
||||||
this.cartService = cartService;
|
this.cartService = cartService;
|
||||||
|
this.pedidoService = pedidoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Payment findFailedPaymentByOrderId(Long orderId) {
|
||||||
|
return payRepo.findFirstByOrderIdAndStatusOrderByIdDesc(orderId, PaymentStatus.failed)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Long> getPaymentTransactionData(Long paymentId) {
|
||||||
|
PaymentTransaction tx = txRepo.findByPaymentIdAndType(
|
||||||
|
paymentId,
|
||||||
|
PaymentTransactionType.CAPTURE)
|
||||||
|
.orElse(null);
|
||||||
|
if (tx == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String resp_payload = tx.getResponsePayload();
|
||||||
|
try {
|
||||||
|
ObjectMapper om = new ObjectMapper();
|
||||||
|
var node = om.readTree(resp_payload);
|
||||||
|
Long cartId = null;
|
||||||
|
Long dirFactId = null;
|
||||||
|
if (node.has("Ds_MerchantData")) {
|
||||||
|
// format: "Ds_MerchantData": "{"dirFactId":3,"cartId":90}"
|
||||||
|
String merchantData = node.get("Ds_MerchantData").asText();
|
||||||
|
merchantData = merchantData.replace(""", "\"");
|
||||||
|
var mdNode = om.readTree(merchantData);
|
||||||
|
if (mdNode.has("cartId")) {
|
||||||
|
cartId = mdNode.get("cartId").asLong();
|
||||||
|
}
|
||||||
|
if (mdNode.has("dirFactId")) {
|
||||||
|
dirFactId = mdNode.get("dirFactId").asLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Map.of(
|
||||||
|
"cartId", cartId,
|
||||||
|
"dirFactId", dirFactId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,14 +94,15 @@ public class PaymentService {
|
|||||||
* oficial (ApiMacSha256).
|
* oficial (ApiMacSha256).
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public FormPayload createRedsysPayment(Long cartId, long amountCents, String currency, String method)
|
public FormPayload createRedsysPayment(Long cartId, Long dirFactId, Long amountCents, String currency, String method, Long orderId)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Payment p = new Payment();
|
Payment p = new Payment();
|
||||||
p.setOrderId(null);
|
p.setOrderId(orderId);
|
||||||
|
|
||||||
Cart cart = this.cartService.findById(cartId);
|
Cart cart = this.cartService.findById(cartId);
|
||||||
if (cart != null && cart.getUserId() != null) {
|
if (cart != null && cart.getUserId() != null) {
|
||||||
p.setUserId(cart.getUserId());
|
p.setUserId(cart.getUserId());
|
||||||
|
this.cartService.lockCartById(cartId);
|
||||||
}
|
}
|
||||||
p.setCurrency(currency);
|
p.setCurrency(currency);
|
||||||
p.setAmountTotalCents(amountCents);
|
p.setAmountTotalCents(amountCents);
|
||||||
@ -62,10 +110,6 @@ public class PaymentService {
|
|||||||
p.setStatus(PaymentStatus.requires_payment_method);
|
p.setStatus(PaymentStatus.requires_payment_method);
|
||||||
p = payRepo.saveAndFlush(p);
|
p = payRepo.saveAndFlush(p);
|
||||||
|
|
||||||
// ANTES:
|
|
||||||
// String dsOrder = String.format("%012d", p.getId());
|
|
||||||
|
|
||||||
// AHORA: timestamp
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
String dsOrder = String.format("%012d", now % 1_000_000_000_000L);
|
String dsOrder = String.format("%012d", now % 1_000_000_000_000L);
|
||||||
|
|
||||||
@ -73,7 +117,7 @@ public class PaymentService {
|
|||||||
payRepo.save(p);
|
payRepo.save(p);
|
||||||
|
|
||||||
RedsysService.PaymentRequest req = new RedsysService.PaymentRequest(dsOrder, amountCents,
|
RedsysService.PaymentRequest req = new RedsysService.PaymentRequest(dsOrder, amountCents,
|
||||||
"Compra en Imprimelibros", cartId);
|
"Compra en Imprimelibros", cartId, dirFactId);
|
||||||
|
|
||||||
if ("bizum".equalsIgnoreCase(method)) {
|
if ("bizum".equalsIgnoreCase(method)) {
|
||||||
return redsysService.buildRedirectFormBizum(req);
|
return redsysService.buildRedirectFormBizum(req);
|
||||||
@ -207,13 +251,12 @@ public class PaymentService {
|
|||||||
p.setAmountCapturedCents(p.getAmountCapturedCents() + notif.amountCents);
|
p.setAmountCapturedCents(p.getAmountCapturedCents() + notif.amountCents);
|
||||||
p.setAuthorizedAt(LocalDateTime.now());
|
p.setAuthorizedAt(LocalDateTime.now());
|
||||||
p.setCapturedAt(LocalDateTime.now());
|
p.setCapturedAt(LocalDateTime.now());
|
||||||
|
pedidoService.setOrderAsPaid(p.getOrderId());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
p.setStatus(PaymentStatus.failed);
|
p.setStatus(PaymentStatus.failed);
|
||||||
p.setFailedAt(LocalDateTime.now());
|
p.setFailedAt(LocalDateTime.now());
|
||||||
}
|
pedidoService.markPedidoAsPaymentDenied(p.getOrderId());
|
||||||
|
|
||||||
if (authorized) {
|
|
||||||
processOrder(notif.cartId, locale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payRepo.save(p);
|
payRepo.save(p);
|
||||||
@ -308,15 +351,13 @@ public class PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Payment createBankTransferPayment(Long cartId, long amountCents, String currency) {
|
public Payment createBankTransferPayment(Long cartId, Long dirFactId, long amountCents, String currency, Locale locale, Long orderId) {
|
||||||
Payment p = new Payment();
|
Payment p = new Payment();
|
||||||
p.setOrderId(null);
|
p.setOrderId(null);
|
||||||
|
|
||||||
Cart cart = this.cartService.findById(cartId);
|
Cart cart = this.cartService.findById(cartId);
|
||||||
if (cart != null && cart.getUserId() != null) {
|
if (cart != null && cart.getUserId() != null) {
|
||||||
p.setUserId(cart.getUserId());
|
p.setUserId(cart.getUserId());
|
||||||
// En el orderId de la transferencia pendiente guardamos el ID del carrito
|
|
||||||
p.setOrderId(cartId);
|
|
||||||
// Se bloquea el carrito para evitar modificaciones mientras se procesa el pago
|
// Se bloquea el carrito para evitar modificaciones mientras se procesa el pago
|
||||||
this.cartService.lockCartById(cartId);
|
this.cartService.lockCartById(cartId);
|
||||||
}
|
}
|
||||||
@ -325,6 +366,9 @@ public class PaymentService {
|
|||||||
p.setAmountTotalCents(amountCents);
|
p.setAmountTotalCents(amountCents);
|
||||||
p.setGateway("bank_transfer");
|
p.setGateway("bank_transfer");
|
||||||
p.setStatus(PaymentStatus.requires_action); // pendiente de ingreso
|
p.setStatus(PaymentStatus.requires_action); // pendiente de ingreso
|
||||||
|
if (orderId != null) {
|
||||||
|
p.setOrderId(orderId);
|
||||||
|
}
|
||||||
p = payRepo.save(p);
|
p = payRepo.save(p);
|
||||||
|
|
||||||
// Crear transacción pendiente
|
// Crear transacción pendiente
|
||||||
@ -334,6 +378,18 @@ public class PaymentService {
|
|||||||
tx.setStatus(PaymentTransactionStatus.pending);
|
tx.setStatus(PaymentTransactionStatus.pending);
|
||||||
tx.setAmountCents(amountCents);
|
tx.setAmountCents(amountCents);
|
||||||
tx.setCurrency(currency);
|
tx.setCurrency(currency);
|
||||||
|
String payload = "";
|
||||||
|
if (cartId != null) {
|
||||||
|
payload = "{\"cartId\":" + cartId + "}";
|
||||||
|
}
|
||||||
|
if (dirFactId != null) {
|
||||||
|
if (!payload.isEmpty()) {
|
||||||
|
payload = payload.substring(0, payload.length() - 1) + ",\"dirFactId\":" + dirFactId + "}";
|
||||||
|
} else {
|
||||||
|
payload = "{\"dirFactId\":" + dirFactId + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx.setResponsePayload(payload);
|
||||||
// tx.setProcessedAt(null); // la dejas nula hasta que se confirme
|
// tx.setProcessedAt(null); // la dejas nula hasta que se confirme
|
||||||
txRepo.save(tx);
|
txRepo.save(tx);
|
||||||
|
|
||||||
@ -374,12 +430,37 @@ public class PaymentService {
|
|||||||
p.setAmountCapturedCents(p.getAmountTotalCents());
|
p.setAmountCapturedCents(p.getAmountTotalCents());
|
||||||
p.setCapturedAt(LocalDateTime.now());
|
p.setCapturedAt(LocalDateTime.now());
|
||||||
p.setStatus(PaymentStatus.captured);
|
p.setStatus(PaymentStatus.captured);
|
||||||
payRepo.save(p);
|
|
||||||
|
|
||||||
// 4) Procesar el pedido asociado al carrito (si existe)
|
Long cartId = null;
|
||||||
if (p.getOrderId() != null) {
|
Long dirFactId = null;
|
||||||
processOrder(p.getOrderId(), locale);
|
try {
|
||||||
|
// Intentar extraer cartId del payload de la transacción
|
||||||
|
if (tx.getResponsePayload() != null && !tx.getResponsePayload().isBlank()) {
|
||||||
|
ObjectMapper om = new ObjectMapper();
|
||||||
|
var node = om.readTree(tx.getResponsePayload());
|
||||||
|
if (node.has("cartId")) {
|
||||||
|
cartId = node.get("cartId").asLong();
|
||||||
|
}
|
||||||
|
if (node.has("dirFactId")) {
|
||||||
|
dirFactId = node.get("dirFactId").asLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignorar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4) Procesar el pedido asociado al carrito (si existe) o marcar el pedido como pagado
|
||||||
|
if(p.getOrderId() != null) {
|
||||||
|
pedidoService.setOrderAsPaid(p.getOrderId());
|
||||||
|
}
|
||||||
|
/*else if (cartId != null) {
|
||||||
|
// Se procesa el pedido dejando el estado calculado en processOrder
|
||||||
|
Long orderId = processOrder(cartId, dirFactId, locale, null);
|
||||||
|
if (orderId != null) {
|
||||||
|
p.setOrderId(orderId);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
payRepo.save(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -474,29 +555,5 @@ public class PaymentService {
|
|||||||
return code >= 0 && code <= 99;
|
return code >= 0 && code <= 99;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Procesa el pedido asociado al carrito:
|
|
||||||
* - bloquea el carrito
|
|
||||||
* - crea el pedido a partir del carrito
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Transactional
|
|
||||||
private Boolean processOrder(Long cartId, Locale locale) {
|
|
||||||
|
|
||||||
Cart cart = this.cartService.findById(cartId);
|
|
||||||
if (cart != null) {
|
|
||||||
// Bloqueamos el carrito
|
|
||||||
this.cartService.lockCartById(cart.getId());
|
|
||||||
// Creamos el pedido
|
|
||||||
Long orderId = this.cartService.crearPedido(cart.getId(), locale);
|
|
||||||
if (orderId == null) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// envio de correo de confirmacion de pedido podria ir aqui
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,13 @@
|
|||||||
package com.imprimelibros.erp.payments.repo;
|
package com.imprimelibros.erp.payments.repo;
|
||||||
|
|
||||||
import com.imprimelibros.erp.payments.model.Payment;
|
import com.imprimelibros.erp.payments.model.Payment;
|
||||||
|
import com.imprimelibros.erp.payments.model.PaymentStatus;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface PaymentRepository extends JpaRepository<Payment, Long> {
|
public interface PaymentRepository extends JpaRepository<Payment, Long> {
|
||||||
Optional<Payment> findByGatewayAndGatewayOrderId(String gateway, String gatewayOrderId);
|
Optional<Payment> findByGatewayAndGatewayOrderId(String gateway, String gatewayOrderId);
|
||||||
|
Optional<Payment> findFirstByOrderIdAndStatusOrderByIdDesc(Long orderId, PaymentStatus status);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,10 @@ import java.util.Optional;
|
|||||||
public interface PaymentTransactionRepository extends JpaRepository<PaymentTransaction, Long>, JpaSpecificationExecutor<PaymentTransaction> {
|
public interface PaymentTransactionRepository extends JpaRepository<PaymentTransaction, Long>, JpaSpecificationExecutor<PaymentTransaction> {
|
||||||
List<PaymentTransaction> findByGatewayTransactionId(String gatewayTransactionId);
|
List<PaymentTransaction> findByGatewayTransactionId(String gatewayTransactionId);
|
||||||
Optional<PaymentTransaction> findByIdempotencyKey(String idempotencyKey);
|
Optional<PaymentTransaction> findByIdempotencyKey(String idempotencyKey);
|
||||||
|
Optional<PaymentTransaction> findByPaymentIdAndType(
|
||||||
|
Long paymentId,
|
||||||
|
PaymentTransactionType type
|
||||||
|
);
|
||||||
Optional<PaymentTransaction> findFirstByPaymentIdAndTypeAndStatusOrderByIdDesc(
|
Optional<PaymentTransaction> findFirstByPaymentIdAndTypeAndStatusOrderByIdDesc(
|
||||||
Long paymentId,
|
Long paymentId,
|
||||||
PaymentTransactionType type,
|
PaymentTransactionType type,
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
package com.imprimelibros.erp.pedidos;
|
package com.imprimelibros.erp.pedidos;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.imprimelibros.erp.common.jpa.AbstractAuditedEntity;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "pedidos")
|
@Table(name = "pedidos")
|
||||||
public class Pedido {
|
public class Pedido extends AbstractAuditedEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
@ -37,27 +41,8 @@ public class Pedido {
|
|||||||
@Column(name = "proveedor_ref", length = 100)
|
@Column(name = "proveedor_ref", length = 100)
|
||||||
private String proveedorRef;
|
private String proveedorRef;
|
||||||
|
|
||||||
// Auditoría básica (coincidiendo con las columnas que se ven en la captura)
|
@OneToMany(mappedBy = "pedido", cascade = CascadeType.ALL, orphanRemoval = false)
|
||||||
@Column(name = "created_by")
|
private List<PedidoLinea> lineas = new ArrayList<>();
|
||||||
private Long createdBy;
|
|
||||||
|
|
||||||
@Column(name = "updated_by")
|
|
||||||
private Long updatedBy;
|
|
||||||
|
|
||||||
@Column(name = "deleted_by")
|
|
||||||
private Long deletedBy;
|
|
||||||
|
|
||||||
@Column(name = "deleted", nullable = false)
|
|
||||||
private boolean deleted = false;
|
|
||||||
|
|
||||||
@Column(name = "created_at", updatable = false)
|
|
||||||
private LocalDateTime createdAt;
|
|
||||||
|
|
||||||
@Column(name = "updated_at")
|
|
||||||
private LocalDateTime updatedAt;
|
|
||||||
|
|
||||||
@Column(name = "deleted_at")
|
|
||||||
private LocalDateTime deletedAt;
|
|
||||||
|
|
||||||
// --- Getters y setters ---
|
// --- Getters y setters ---
|
||||||
|
|
||||||
@ -132,60 +117,4 @@ public class Pedido {
|
|||||||
public void setProveedorRef(String proveedorRef) {
|
public void setProveedorRef(String proveedorRef) {
|
||||||
this.proveedorRef = proveedorRef;
|
this.proveedorRef = proveedorRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getCreatedBy() {
|
|
||||||
return createdBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCreatedBy(Long createdBy) {
|
|
||||||
this.createdBy = createdBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getUpdatedBy() {
|
|
||||||
return updatedBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUpdatedBy(Long updatedBy) {
|
|
||||||
this.updatedBy = updatedBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getDeletedBy() {
|
|
||||||
return deletedBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeletedBy(Long deletedBy) {
|
|
||||||
this.deletedBy = deletedBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDeleted() {
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeleted(boolean deleted) {
|
|
||||||
this.deleted = deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getCreatedAt() {
|
|
||||||
return createdAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCreatedAt(LocalDateTime createdAt) {
|
|
||||||
this.createdAt = createdAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getUpdatedAt() {
|
|
||||||
return updatedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
|
||||||
this.updatedAt = updatedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getDeletedAt() {
|
|
||||||
return deletedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
|
||||||
this.deletedAt = deletedAt;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
308
src/main/java/com/imprimelibros/erp/pedidos/PedidoDireccion.java
Normal file
308
src/main/java/com/imprimelibros/erp/pedidos/PedidoDireccion.java
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
package com.imprimelibros.erp.pedidos;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
|
import com.imprimelibros.erp.direcciones.Direccion.TipoIdentificacionFiscal;
|
||||||
|
import com.imprimelibros.erp.paises.Paises;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "pedidos_direcciones")
|
||||||
|
public class PedidoDireccion {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
// FK a pedidos_lineas.id (nullable, on delete set null)
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "pedido_linea_id")
|
||||||
|
private PedidoLinea pedidoLinea;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "pedido_id")
|
||||||
|
private Pedido pedido;
|
||||||
|
|
||||||
|
@Column(name = "unidades")
|
||||||
|
private Integer unidades;
|
||||||
|
|
||||||
|
@Column(name = "is_facturacion", nullable = false)
|
||||||
|
private boolean facturacion = false;
|
||||||
|
|
||||||
|
@Column(name = "is_ejemplar_prueba", nullable = false)
|
||||||
|
private boolean ejemplarPrueba = false;
|
||||||
|
|
||||||
|
@Column(name = "email", length = 255)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Column(name = "att", nullable = false, length = 150)
|
||||||
|
private String att;
|
||||||
|
|
||||||
|
@Column(name = "direccion", nullable = false, length = 255)
|
||||||
|
private String direccion;
|
||||||
|
|
||||||
|
@Column(name = "cp", nullable = false)
|
||||||
|
private Integer cp;
|
||||||
|
|
||||||
|
@Column(name = "ciudad", nullable = false, length = 100)
|
||||||
|
private String ciudad;
|
||||||
|
|
||||||
|
@Column(name = "provincia", nullable = false, length = 100)
|
||||||
|
private String provincia;
|
||||||
|
|
||||||
|
@Column(name = "pais_code3", nullable = false, length = 3)
|
||||||
|
private String paisCode3 = "esp";
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "pais_code3", referencedColumnName = "code3", insertable = false, updatable = false)
|
||||||
|
private Paises pais;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private String paisNombre;
|
||||||
|
|
||||||
|
@Column(name = "telefono", nullable = false, length = 30)
|
||||||
|
private String telefono;
|
||||||
|
|
||||||
|
@Column(name = "instrucciones", length = 255)
|
||||||
|
private String instrucciones;
|
||||||
|
|
||||||
|
@Column(name = "razon_social", length = 150)
|
||||||
|
private String razonSocial;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "tipo_identificacion_fiscal", nullable = false, length = 20)
|
||||||
|
private TipoIdentificacionFiscal tipoIdentificacionFiscal = TipoIdentificacionFiscal.DNI;
|
||||||
|
|
||||||
|
@Column(name = "identificacion_fiscal", length = 50)
|
||||||
|
private String identificacionFiscal;
|
||||||
|
|
||||||
|
@Column(name = "is_palets", nullable = false)
|
||||||
|
private boolean palets = false;
|
||||||
|
|
||||||
|
@CreationTimestamp
|
||||||
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
// ===== GETTERS & SETTERS =====
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PedidoLinea getPedidoLinea() {
|
||||||
|
return pedidoLinea;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPedidoLinea(PedidoLinea pedidoLinea) {
|
||||||
|
this.pedidoLinea = pedidoLinea;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pedido getPedido() {
|
||||||
|
return pedido;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPedido(Pedido pedido) {
|
||||||
|
this.pedido = pedido;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getUnidades() {
|
||||||
|
return unidades;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnidades(Integer unidades) {
|
||||||
|
this.unidades = unidades;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFacturacion() {
|
||||||
|
return facturacion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFacturacion(boolean facturacion) {
|
||||||
|
this.facturacion = facturacion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEjemplarPrueba() {
|
||||||
|
return ejemplarPrueba;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEjemplarPrueba(boolean ejemplarPrueba) {
|
||||||
|
this.ejemplarPrueba = ejemplarPrueba;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAtt() {
|
||||||
|
return att;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAtt(String att) {
|
||||||
|
this.att = att;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDireccion() {
|
||||||
|
return direccion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDireccion(String direccion) {
|
||||||
|
this.direccion = direccion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCp() {
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCp(Integer cp) {
|
||||||
|
this.cp = cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCiudad() {
|
||||||
|
return ciudad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCiudad(String ciudad) {
|
||||||
|
this.ciudad = ciudad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProvincia() {
|
||||||
|
return provincia;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvincia(String provincia) {
|
||||||
|
this.provincia = provincia;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPaisCode3() {
|
||||||
|
return paisCode3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaisCode3(String paisCode3) {
|
||||||
|
this.paisCode3 = paisCode3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Paises getPais() {
|
||||||
|
return pais;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPais(Paises pais) {
|
||||||
|
this.pais = pais;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTelefono() {
|
||||||
|
return telefono;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTelefono(String telefono) {
|
||||||
|
this.telefono = telefono;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstrucciones() {
|
||||||
|
return instrucciones;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstrucciones(String instrucciones) {
|
||||||
|
this.instrucciones = instrucciones;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRazonSocial() {
|
||||||
|
return razonSocial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRazonSocial(String razonSocial) {
|
||||||
|
this.razonSocial = razonSocial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TipoIdentificacionFiscal getTipoIdentificacionFiscal() {
|
||||||
|
return tipoIdentificacionFiscal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTipoIdentificacionFiscal(TipoIdentificacionFiscal tipoIdentificacionFiscal) {
|
||||||
|
this.tipoIdentificacionFiscal = tipoIdentificacionFiscal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentificacionFiscal() {
|
||||||
|
return identificacionFiscal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdentificacionFiscal(String identificacionFiscal) {
|
||||||
|
this.identificacionFiscal = identificacionFiscal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPalets() {
|
||||||
|
return palets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPalets(boolean palets) {
|
||||||
|
this.palets = palets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPaisNombre() {
|
||||||
|
return paisNombre;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaisNombre(String paisNombre) {
|
||||||
|
this.paisNombre = paisNombre;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Map<String, Object> toSkMap(Double pesoKg) {
|
||||||
|
|
||||||
|
Map<String, Object> direccion = new HashMap<>();
|
||||||
|
direccion.put("cantidad", this.getUnidades());
|
||||||
|
direccion.put("peso", pesoKg);
|
||||||
|
direccion.put("att", this.getAtt());
|
||||||
|
direccion.put("email", this.getEmail());
|
||||||
|
direccion.put("direccion", this.getDireccion());
|
||||||
|
direccion.put("pais_code3", this.getPaisCode3());
|
||||||
|
direccion.put("cp", this.getCp());
|
||||||
|
direccion.put("municipio", this.getCiudad());
|
||||||
|
direccion.put("provincia", this.getProvincia());
|
||||||
|
direccion.put("telefono", this.getTelefono());
|
||||||
|
direccion.put("entregaPieCalle", this.isPalets() ? 1 : 0);
|
||||||
|
direccion.put("is_ferro_prototipo", this.isEjemplarPrueba() ? 1 : 0);
|
||||||
|
direccion.put("num_ferro_prototipo", this.isEjemplarPrueba() ? 1 : 0);
|
||||||
|
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("direccion", direccion);
|
||||||
|
map.put("unidades", this.getUnidades());
|
||||||
|
map.put("entregaPalets", this.isPalets() ? 1 : 0);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> toSkMapDepositoLegal() {
|
||||||
|
Map<String, Object> direccion = new HashMap<>();
|
||||||
|
direccion.put("cantidad", 4);
|
||||||
|
direccion.put("peso", 0);
|
||||||
|
direccion.put("att", "Unidades para Depósito Legal (sin envío)");
|
||||||
|
direccion.put("email", "");
|
||||||
|
direccion.put("direccion", "");
|
||||||
|
direccion.put("pais_code3", "esp");
|
||||||
|
direccion.put("cp", "");
|
||||||
|
direccion.put("municipio", "");
|
||||||
|
direccion.put("provincia", "");
|
||||||
|
direccion.put("telefono", "");
|
||||||
|
direccion.put("entregaPieCalle", 0);
|
||||||
|
direccion.put("is_ferro_prototipo", 0);
|
||||||
|
direccion.put("num_ferro_prototipo", 0);
|
||||||
|
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("direccion", direccion);
|
||||||
|
map.put("unidades", 4);
|
||||||
|
map.put("entregaPalets", 0);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.imprimelibros.erp.pedidos;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface PedidoDireccionRepository extends JpaRepository<PedidoDireccion, Long> {
|
||||||
|
|
||||||
|
// Todas las direcciones de una línea de pedido
|
||||||
|
List<PedidoDireccion> findByPedidoLinea_Id(Long pedidoLineaId);
|
||||||
|
|
||||||
|
// Si en tu código sueles trabajar con el objeto:
|
||||||
|
List<PedidoDireccion> findByPedidoLinea(PedidoLinea pedidoLinea);
|
||||||
|
|
||||||
|
PedidoDireccion findByPedidoIdAndFacturacionTrue(Long pedidoId);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
select distinct d
|
||||||
|
from PedidoDireccion d
|
||||||
|
join d.pedidoLinea pl
|
||||||
|
where d.pedidoLinea.id = :pedidoLineaId
|
||||||
|
""")
|
||||||
|
List<PedidoDireccion> findByPedidoLineaId(Long pedidoLineaId);
|
||||||
|
|
||||||
|
}
|
||||||
@ -9,6 +9,36 @@ import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
|||||||
@Table(name = "pedidos_lineas")
|
@Table(name = "pedidos_lineas")
|
||||||
public class PedidoLinea {
|
public class PedidoLinea {
|
||||||
|
|
||||||
|
public enum Estado {
|
||||||
|
pendiente_pago("pedido.estado.pendiente_pago", 1),
|
||||||
|
procesando_pago("pedido.estado.procesando_pago", 2),
|
||||||
|
denegado_pago("pedido.estado.denegado_pago", 3),
|
||||||
|
aprobado("pedido.estado.aprobado", 4),
|
||||||
|
maquetacion("pedido.estado.maquetacion", 5),
|
||||||
|
haciendo_ferro("pedido.estado.haciendo_ferro", 6),
|
||||||
|
esperando_aceptacion_ferro("pedido.estado.esperando_aceptacion_ferro", 7),
|
||||||
|
ferro_cliente("pedido.estado.ferro_cliente", 8),
|
||||||
|
produccion("pedido.estado.produccion", 9),
|
||||||
|
terminado("pedido.estado.terminado", 10),
|
||||||
|
cancelado("pedido.estado.cancelado", 11);
|
||||||
|
|
||||||
|
private final String messageKey;
|
||||||
|
private final int priority;
|
||||||
|
|
||||||
|
Estado(String messageKey, int priority) {
|
||||||
|
this.messageKey = messageKey;
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageKey() {
|
||||||
|
return messageKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Long id;
|
private Long id;
|
||||||
@ -21,6 +51,16 @@ public class PedidoLinea {
|
|||||||
@JoinColumn(name = "presupuesto_id", nullable = false)
|
@JoinColumn(name = "presupuesto_id", nullable = false)
|
||||||
private Presupuesto presupuesto;
|
private Presupuesto presupuesto;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "estado", nullable = false)
|
||||||
|
private Estado estado = Estado.aprobado;
|
||||||
|
|
||||||
|
@Column(name = "estado_manual", nullable = false)
|
||||||
|
private Boolean estadoManual;
|
||||||
|
|
||||||
|
@Column(name = "fecha_entrega")
|
||||||
|
private LocalDateTime fechaEntrega;
|
||||||
|
|
||||||
@Column(name = "created_at")
|
@Column(name = "created_at")
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
@ -53,6 +93,30 @@ public class PedidoLinea {
|
|||||||
this.presupuesto = presupuesto;
|
this.presupuesto = presupuesto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Estado getEstado() {
|
||||||
|
return estado;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEstado(Estado estado) {
|
||||||
|
this.estado = estado;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getEstadoManual() {
|
||||||
|
return estadoManual;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEstadoManual(Boolean estadoManual) {
|
||||||
|
this.estadoManual = estadoManual;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getFechaEntrega() {
|
||||||
|
return fechaEntrega;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFechaEntrega(LocalDateTime fechaEntrega) {
|
||||||
|
this.fechaEntrega = fechaEntrega;
|
||||||
|
}
|
||||||
|
|
||||||
public LocalDateTime getCreatedAt() {
|
public LocalDateTime getCreatedAt() {
|
||||||
return createdAt;
|
return createdAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import java.util.List;
|
|||||||
public interface PedidoLineaRepository extends JpaRepository<PedidoLinea, Long> {
|
public interface PedidoLineaRepository extends JpaRepository<PedidoLinea, Long> {
|
||||||
|
|
||||||
List<PedidoLinea> findByPedidoId(Long pedidoId);
|
List<PedidoLinea> findByPedidoId(Long pedidoId);
|
||||||
|
List<PedidoLinea> findByPedidoIdOrderByIdAsc(Long pedidoId);
|
||||||
|
|
||||||
List<PedidoLinea> findByPresupuestoId(Long presupuestoId);
|
List<PedidoLinea> findByPresupuestoId(Long presupuestoId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,23 @@
|
|||||||
package com.imprimelibros.erp.pedidos;
|
package com.imprimelibros.erp.pedidos;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface PedidoRepository extends JpaRepository<Pedido, Long> {
|
public interface PedidoRepository extends JpaRepository<Pedido, Long>, JpaSpecificationExecutor<Pedido> {
|
||||||
// aquí podrás añadir métodos tipo:
|
|
||||||
// List<Pedido> findByDeletedFalse();
|
// Suma de "total" para un "createdBy" desde una fecha dada
|
||||||
|
@Query("""
|
||||||
|
SELECT COALESCE(SUM(p.total), 0)
|
||||||
|
FROM Pedido p
|
||||||
|
WHERE p.createdBy.id = :userId
|
||||||
|
AND p.createdAt >= :afterDate
|
||||||
|
""")
|
||||||
|
Double sumTotalByCreatedByAndCreatedAtAfter(@Param("userId") Long userId,
|
||||||
|
@Param("afterDate") Instant afterDate);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,30 @@
|
|||||||
package com.imprimelibros.erp.pedidos;
|
package com.imprimelibros.erp.pedidos;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.imprimelibros.erp.cart.Cart;
|
||||||
|
import com.imprimelibros.erp.cart.CartDireccion;
|
||||||
|
import com.imprimelibros.erp.cart.CartItem;
|
||||||
|
import com.imprimelibros.erp.cart.CartService;
|
||||||
|
import com.imprimelibros.erp.common.Utils;
|
||||||
|
import com.imprimelibros.erp.direcciones.Direccion;
|
||||||
import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
|
import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
|
||||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
||||||
|
import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
|
||||||
|
import com.imprimelibros.erp.users.UserService;
|
||||||
|
import com.imprimelibros.erp.direcciones.DireccionService;
|
||||||
|
import com.imprimelibros.erp.externalApi.skApiClient;
|
||||||
|
import com.imprimelibros.erp.pedidos.PedidoLinea.Estado;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class PedidoService {
|
public class PedidoService {
|
||||||
@ -16,50 +32,45 @@ public class PedidoService {
|
|||||||
private final PedidoRepository pedidoRepository;
|
private final PedidoRepository pedidoRepository;
|
||||||
private final PedidoLineaRepository pedidoLineaRepository;
|
private final PedidoLineaRepository pedidoLineaRepository;
|
||||||
private final PresupuestoRepository presupuestoRepository;
|
private final PresupuestoRepository presupuestoRepository;
|
||||||
|
private final PedidoDireccionRepository pedidoDireccionRepository;
|
||||||
|
private final DireccionService direccionService;
|
||||||
|
private final UserService userService;
|
||||||
|
private final PresupuestoService presupuestoService;
|
||||||
|
private final CartService cartService;
|
||||||
|
private final skApiClient skApiClient;
|
||||||
|
private final PresupuestoRepository presupuestoRepo;
|
||||||
|
private final MessageSource messageSource;
|
||||||
|
|
||||||
public PedidoService(PedidoRepository pedidoRepository, PedidoLineaRepository pedidoLineaRepository,
|
public PedidoService(PedidoRepository pedidoRepository, PedidoLineaRepository pedidoLineaRepository,
|
||||||
PresupuestoRepository presupuestoRepository) {
|
PresupuestoRepository presupuestoRepository, PedidoDireccionRepository pedidoDireccionRepository,
|
||||||
|
DireccionService direccionService, UserService userService, PresupuestoService presupuestoService,
|
||||||
|
CartService cartService, skApiClient skApiClient, PresupuestoRepository presupuestoRepo,
|
||||||
|
MessageSource messageSource) {
|
||||||
this.pedidoRepository = pedidoRepository;
|
this.pedidoRepository = pedidoRepository;
|
||||||
this.pedidoLineaRepository = pedidoLineaRepository;
|
this.pedidoLineaRepository = pedidoLineaRepository;
|
||||||
this.presupuestoRepository = presupuestoRepository;
|
this.presupuestoRepository = presupuestoRepository;
|
||||||
|
this.pedidoDireccionRepository = pedidoDireccionRepository;
|
||||||
|
this.direccionService = direccionService;
|
||||||
|
this.userService = userService;
|
||||||
|
this.presupuestoService = presupuestoService;
|
||||||
|
this.cartService = cartService;
|
||||||
|
this.skApiClient = skApiClient;
|
||||||
|
this.presupuestoRepo = presupuestoRepo;
|
||||||
|
this.messageSource = messageSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDescuentoFidelizacion() {
|
|
||||||
// descuento entre el 1% y el 6% para clientes fidelidad (mas de 1500€ en el
|
|
||||||
// ultimo año)
|
|
||||||
double totalGastado = 1600.0; // Ejemplo, deberías obtenerlo del historial del cliente
|
|
||||||
if (totalGastado < 1200) {
|
|
||||||
return 0;
|
|
||||||
} else if (totalGastado >= 1200 && totalGastado < 1999) {
|
|
||||||
return 1;
|
|
||||||
} else if (totalGastado >= 2000 && totalGastado < 2999) {
|
|
||||||
return 2;
|
|
||||||
} else if (totalGastado >= 3000 && totalGastado < 3999) {
|
|
||||||
return 3;
|
|
||||||
} else if (totalGastado >= 4000 && totalGastado < 4999) {
|
|
||||||
return 4;
|
|
||||||
} else if (totalGastado >= 5000) {
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Crea un pedido a partir de:
|
|
||||||
* - lista de IDs de presupuesto
|
|
||||||
* - resumen numérico del carrito (getCartSummaryRaw)
|
|
||||||
* - datos de proveedor
|
|
||||||
* - usuario que crea el pedido
|
|
||||||
*/
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Pedido crearPedido(List<Long> presupuestoIds,
|
public Pedido crearPedido(
|
||||||
Map<String, Object> cartSummaryRaw,
|
Long cartId,
|
||||||
|
Long direccionFacturacionId,
|
||||||
String proveedor,
|
String proveedor,
|
||||||
String proveedorRef,
|
String proveedorRef) {
|
||||||
Long userId) {
|
|
||||||
|
|
||||||
Pedido pedido = new Pedido();
|
Pedido pedido = new Pedido();
|
||||||
|
|
||||||
|
Cart cart = cartService.getCartById(cartId);
|
||||||
|
Map<String, Object> cartSummaryRaw = cartService.getCartSummaryRaw(cart, Locale.getDefault());
|
||||||
|
|
||||||
// Datos económicos (ojo con las claves, son las del summaryRaw)
|
// Datos económicos (ojo con las claves, son las del summaryRaw)
|
||||||
pedido.setBase((Double) cartSummaryRaw.getOrDefault("base", 0.0d));
|
pedido.setBase((Double) cartSummaryRaw.getOrDefault("base", 0.0d));
|
||||||
pedido.setEnvio((Double) cartSummaryRaw.getOrDefault("shipment", 0.0d));
|
pedido.setEnvio((Double) cartSummaryRaw.getOrDefault("shipment", 0.0d));
|
||||||
@ -69,33 +80,567 @@ public class PedidoService {
|
|||||||
pedido.setTotal((Double) cartSummaryRaw.getOrDefault("total", 0.0d));
|
pedido.setTotal((Double) cartSummaryRaw.getOrDefault("total", 0.0d));
|
||||||
|
|
||||||
// Proveedor
|
// Proveedor
|
||||||
pedido.setProveedor(proveedor);
|
if (proveedor != null && proveedorRef != null) {
|
||||||
pedido.setProveedorRef(proveedorRef);
|
pedido.setProveedor(proveedor);
|
||||||
|
pedido.setProveedorRef(proveedorRef);
|
||||||
|
}
|
||||||
|
|
||||||
// Auditoría mínima
|
// Auditoría mínima
|
||||||
pedido.setCreatedBy(userId);
|
Long userId = cart.getUserId();
|
||||||
pedido.setCreatedAt(LocalDateTime.now());
|
pedido.setCreatedBy(userService.findById(userId));
|
||||||
|
pedido.setCreatedAt(Instant.now());
|
||||||
pedido.setDeleted(false);
|
pedido.setDeleted(false);
|
||||||
pedido.setUpdatedAt(LocalDateTime.now());
|
pedido.setUpdatedAt(Instant.now());
|
||||||
pedido.setUpdatedBy(userId);
|
pedido.setUpdatedBy(userService.findById(userId));
|
||||||
|
|
||||||
// Guardamos el pedido
|
// Guardamos el pedido
|
||||||
Pedido saved = pedidoRepository.save(pedido);
|
Pedido pedidoGuardado = pedidoRepository.save(pedido);
|
||||||
|
|
||||||
// Crear líneas del pedido
|
List<CartItem> items = cart.getItems();
|
||||||
for (Long presupuestoId : presupuestoIds) {
|
|
||||||
Presupuesto presupuesto = presupuestoRepository.getReferenceById(presupuestoId);
|
for (Integer i = 0; i < items.size(); i++) {
|
||||||
|
CartItem item = items.get(i);
|
||||||
|
Presupuesto pCart = item.getPresupuesto();
|
||||||
|
|
||||||
|
// Asegurarnos de trabajar con la entidad gestionada por JPA
|
||||||
|
Presupuesto p = presupuestoRepository.findById(pCart.getId())
|
||||||
|
.orElseThrow(() -> new IllegalStateException("Presupuesto no encontrado: " + pCart.getId()));
|
||||||
|
p.setEstado(Presupuesto.Estado.aceptado);
|
||||||
|
presupuestoRepository.save(p);
|
||||||
|
|
||||||
PedidoLinea linea = new PedidoLinea();
|
PedidoLinea linea = new PedidoLinea();
|
||||||
linea.setPedido(saved);
|
linea.setPedido(pedidoGuardado);
|
||||||
linea.setPresupuesto(presupuesto);
|
linea.setPresupuesto(p);
|
||||||
linea.setCreatedBy(userId);
|
linea.setCreatedBy(userId);
|
||||||
linea.setCreatedAt(LocalDateTime.now());
|
linea.setCreatedAt(LocalDateTime.now());
|
||||||
|
linea.setEstado(PedidoLinea.Estado.pendiente_pago);
|
||||||
|
linea.setEstadoManual(false);
|
||||||
|
pedidoLineaRepository.save(linea);
|
||||||
|
|
||||||
|
// Guardar las direcciones asociadas a la línea del pedido
|
||||||
|
Map<String, Object> direcciones_presupuesto = this.getDireccionesPresupuesto(cart, p);
|
||||||
|
saveDireccionesPedidoLinea(direcciones_presupuesto, pedidoGuardado, linea, direccionFacturacionId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return pedidoGuardado;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean markPedidoAsProcesingPayment(Long pedidoId) {
|
||||||
|
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
|
||||||
|
if (pedido == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
|
||||||
|
for (PedidoLinea linea : lineas) {
|
||||||
|
linea.setEstado(PedidoLinea.Estado.procesando_pago);
|
||||||
pedidoLineaRepository.save(linea);
|
pedidoLineaRepository.save(linea);
|
||||||
}
|
}
|
||||||
|
|
||||||
return saved;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean markPedidoAsPaymentDenied(Long pedidoId) {
|
||||||
|
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
|
||||||
|
if (pedido == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
|
||||||
|
for (PedidoLinea linea : lineas) {
|
||||||
|
linea.setEstado(PedidoLinea.Estado.denegado_pago);
|
||||||
|
pedidoLineaRepository.save(linea);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pedido findById(Long pedidoId) {
|
||||||
|
return pedidoRepository.findById(pedidoId).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lista de los items del pedido preparados para la vista */
|
||||||
|
@Transactional
|
||||||
|
public List<Map<String, Object>> getLineas(Long pedidoId, Locale locale) {
|
||||||
|
Pedido p = pedidoRepository.findById(pedidoId).orElse(null);
|
||||||
|
if (p == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Object>> resultados = new ArrayList<>();
|
||||||
|
List<PedidoLinea> items = pedidoLineaRepository.findByPedidoIdOrderByIdAsc(p.getId());
|
||||||
|
for (PedidoLinea item : items) {
|
||||||
|
|
||||||
|
Presupuesto presupuesto = item.getPresupuesto();
|
||||||
|
Map<String, Object> elemento = presupuestoService.getPresupuestoInfoForCard(presupuesto, locale);
|
||||||
|
elemento.put("estado", item.getEstado());
|
||||||
|
elemento.put("fechaEntrega",
|
||||||
|
item.getFechaEntrega() != null ? Utils.formatDate(item.getFechaEntrega(), locale) : "");
|
||||||
|
elemento.put("lineaId", item.getId());
|
||||||
|
resultados.add(elemento);
|
||||||
|
}
|
||||||
|
return resultados;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PedidoDireccion getDireccionFacturacionPedido(Long pedidoId) {
|
||||||
|
return pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PedidoDireccion> getDireccionesEntregaPedidoLinea(Long pedidoLineaId) {
|
||||||
|
return pedidoDireccionRepository.findByPedidoLinea_Id(pedidoLineaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean setOrderAsPaid(Long pedidoId) {
|
||||||
|
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
|
||||||
|
if (pedido == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
|
||||||
|
List<Map<String, Object>> referenciasProveedor = new ArrayList<>();
|
||||||
|
Integer total = lineas.size();
|
||||||
|
Integer counter = 1;
|
||||||
|
for (PedidoLinea linea : lineas) {
|
||||||
|
if (linea.getEstado() == Estado.pendiente_pago
|
||||||
|
|| linea.getEstado() == Estado.denegado_pago) {
|
||||||
|
|
||||||
|
Presupuesto presupuesto = linea.getPresupuesto();
|
||||||
|
linea.setEstado(getEstadoInicial(presupuesto));
|
||||||
|
pedidoLineaRepository.save(linea);
|
||||||
|
|
||||||
|
// Save presupuesto in SK
|
||||||
|
Map<String, Object> result = savePresupuestoSK(linea.getId(), presupuesto, counter, total);
|
||||||
|
if (result == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
referenciasProveedor.add(result);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (referenciasProveedor.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save pedido in SK
|
||||||
|
ArrayList<Long> presupuestoSkIds = new ArrayList<>();
|
||||||
|
for (Map<String, Object> presData : referenciasProveedor) {
|
||||||
|
Long presId = ((Number) presData.get("id")).longValue();
|
||||||
|
presupuestoSkIds.add(presId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> ids = new HashMap<>();
|
||||||
|
ids.put("presupuesto_ids", presupuestoSkIds);
|
||||||
|
Long skPedidoId = skApiClient.crearPedido(ids);
|
||||||
|
if (skPedidoId == null) {
|
||||||
|
System.out.println("No se pudo crear el pedido en SK.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pedido.setProveedor("Safekat");
|
||||||
|
pedido.setProveedorRef(skPedidoId.toString());
|
||||||
|
pedidoRepository.save(pedido);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> actualizarEstado(Long pedidoLineaId, Locale locale) {
|
||||||
|
PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null);
|
||||||
|
if (pedidoLinea == null) {
|
||||||
|
return Map.of("success", false,
|
||||||
|
"message", messageSource.getMessage("pedido.errors.linea-not-found", null, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pedidoLinea.getEstado().getPriority() >= PedidoLinea.Estado.haciendo_ferro.getPriority() &&
|
||||||
|
pedidoLinea.getEstado().getPriority() < PedidoLinea.Estado.terminado.getPriority()) {
|
||||||
|
PedidoLinea.Estado estadoOld = pedidoLinea.getEstado();
|
||||||
|
Map<String, Object> result = skApiClient.checkPedidoEstado(
|
||||||
|
Long.valueOf(pedidoLinea.getPresupuesto().getProveedorRef2().toString()), locale);
|
||||||
|
if (result == null || !result.containsKey("estado")) {
|
||||||
|
return Map.of(
|
||||||
|
"success", false,
|
||||||
|
"message", messageSource.getMessage("pedido.errors.update-server-error", null, locale));
|
||||||
|
}
|
||||||
|
PedidoLinea.Estado estadoSk = PedidoLinea.Estado.valueOf((String) result.get("estado"));
|
||||||
|
if (estadoOld == estadoSk) {
|
||||||
|
return Map.of(
|
||||||
|
"success", true,
|
||||||
|
"state", messageSource.getMessage("pedido.estado." + estadoSk.name(), null, locale),
|
||||||
|
"stateKey", estadoSk.name(),
|
||||||
|
"message", messageSource.getMessage("pedido.success.same-estado", null, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
pedidoLinea.setEstado(estadoSk);
|
||||||
|
pedidoLineaRepository.save(pedidoLinea);
|
||||||
|
return Map.of(
|
||||||
|
"success", true,
|
||||||
|
"state", messageSource.getMessage("pedido.estado." + estadoSk.name(), null, locale),
|
||||||
|
"stateKey", estadoSk.name(),
|
||||||
|
"message", messageSource.getMessage("pedido.success.estado-actualizado", null, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Map.of(
|
||||||
|
"success", false,
|
||||||
|
"message", messageSource.getMessage("pedido.errors.cannot-update", null, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean markPedidoAsMaquetacionDone(Long pedidoId) {
|
||||||
|
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
|
||||||
|
if (pedido == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
|
||||||
|
for (PedidoLinea linea : lineas) {
|
||||||
|
if (linea.getEstado() == Estado.maquetacion) {
|
||||||
|
linea.setEstado(Estado.haciendo_ferro);
|
||||||
|
pedidoLineaRepository.save(linea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getFilesType(Long pedidoLineaId, Locale locale) {
|
||||||
|
PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null);
|
||||||
|
if (pedidoLinea == null) {
|
||||||
|
return Map.of("success", false, "message", "Línea de pedido no encontrada.");
|
||||||
|
}
|
||||||
|
Map<String, Object> files = skApiClient.getFilesTypes(
|
||||||
|
Long.valueOf(pedidoLinea.getPresupuesto().getProveedorRef2().toString()), locale);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public byte[] getFerroFileContent(Long pedidoLineaId, Locale locale) {
|
||||||
|
return downloadFile(pedidoLineaId, "ferro", locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getCubiertaFileContent(Long pedidoLineaId, Locale locale) {
|
||||||
|
return downloadFile(pedidoLineaId, "cubierta", locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getTapaFileContent(Long pedidoLineaId, Locale locale) {
|
||||||
|
return downloadFile(pedidoLineaId, "tapa", locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean aceptarFerro(Long pedidoLineaId, Locale locale) {
|
||||||
|
PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null);
|
||||||
|
if (pedidoLinea == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pedidoLinea.getEstado() != PedidoLinea.Estado.esperando_aceptacion_ferro) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Boolean result = skApiClient.aceptarFerro(
|
||||||
|
Long.valueOf(pedidoLinea.getPresupuesto().getProveedorRef2().toString()), locale);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pedidoLinea.setEstado(PedidoLinea.Estado.produccion);
|
||||||
|
pedidoLineaRepository.save(pedidoLinea);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/***************************
|
||||||
|
* MÉTODOS PRIVADOS
|
||||||
|
***************************/
|
||||||
|
private byte[] downloadFile(Long pedidoLineaId, String fileType, Locale locale) {
|
||||||
|
PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null);
|
||||||
|
if (pedidoLinea == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] fileData = skApiClient.downloadFile(
|
||||||
|
Long.valueOf(pedidoLinea.getPresupuesto().getProveedorRef2().toString()),
|
||||||
|
fileType,
|
||||||
|
locale);
|
||||||
|
return fileData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
private Map<String, Object> savePresupuestoSK(Long pedidoLineaId, Presupuesto presupuesto, Integer counter,
|
||||||
|
Integer total) {
|
||||||
|
|
||||||
|
Map<String, Object> data_to_send = presupuestoService.toSkApiRequest(presupuesto, true);
|
||||||
|
data_to_send.put("createPedido", 0);
|
||||||
|
|
||||||
|
// Recuperar el mapa anidado datosCabecera
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> datosCabecera = (Map<String, Object>) data_to_send.get("datosCabecera");
|
||||||
|
if (datosCabecera != null) {
|
||||||
|
Object tituloOriginal = datosCabecera.get("titulo");
|
||||||
|
datosCabecera.put(
|
||||||
|
"titulo",
|
||||||
|
"[" + (counter) + "/" + total + "] " + (tituloOriginal != null ? tituloOriginal : ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PedidoDireccion> direccionesPedidoLinea = pedidoDireccionRepository
|
||||||
|
.findByPedidoLineaId(pedidoLineaId);
|
||||||
|
List<Map<String, Object>> direccionesPresupuesto = new ArrayList<>();
|
||||||
|
List<Map<String, Object>> direccionEjemplarPrueba = new ArrayList<>();
|
||||||
|
|
||||||
|
for (PedidoDireccion pd : direccionesPedidoLinea) {
|
||||||
|
if (pd.isEjemplarPrueba()) {
|
||||||
|
direccionEjemplarPrueba.add(
|
||||||
|
pd.toSkMap(presupuesto.getPeso()));
|
||||||
|
} else {
|
||||||
|
direccionesPresupuesto.add(
|
||||||
|
pd.toSkMap(presupuesto.getPeso() * pd.getUnidades()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (presupuesto.getServiciosJson() != null && presupuesto.getServiciosJson().contains("deposito-legal")) {
|
||||||
|
direccionesPresupuesto.add(
|
||||||
|
PedidoDireccion.toSkMapDepositoLegal());
|
||||||
|
}
|
||||||
|
data_to_send.put("direcciones", direccionesPresupuesto);
|
||||||
|
if (direccionEjemplarPrueba.size() > 0)
|
||||||
|
data_to_send.put("direccionesFP1", direccionEjemplarPrueba.get(0));
|
||||||
|
else {
|
||||||
|
data_to_send.put("direccionesFP1", new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = skApiClient.savePresupuesto(data_to_send);
|
||||||
|
|
||||||
|
if (result.containsKey("error")) {
|
||||||
|
System.out.println("Error al guardar presupuesto en SK");
|
||||||
|
System.out.println("-------------------------");
|
||||||
|
System.out.println(result.get("error"));
|
||||||
|
// decide si seguir con otros items o abortar:
|
||||||
|
// continue; o bien throw ...
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object dataObj = result.get("data");
|
||||||
|
if (!(dataObj instanceof Map<?, ?> dataRaw)) {
|
||||||
|
System.out.println("Formato inesperado de 'data' en savePresupuesto: " + result);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> dataMap = (Map<String, Object>) dataRaw;
|
||||||
|
Long presId = ((Number) dataMap.get("id")).longValue();
|
||||||
|
String skin = ((String) dataMap.get("iskn")).toString();
|
||||||
|
presupuesto.setProveedor("Safekat");
|
||||||
|
presupuesto.setProveedorRef1(skin);
|
||||||
|
presupuesto.setProveedorRef2(presId);
|
||||||
|
presupuesto.setEstado(Presupuesto.Estado.aceptado);
|
||||||
|
presupuestoRepo.save(presupuesto);
|
||||||
|
|
||||||
|
return dataMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener las direcciones de envío asociadas a un presupuesto en el carrito
|
||||||
|
private Map<String, Object> getDireccionesPresupuesto(Cart cart, Presupuesto presupuesto) {
|
||||||
|
|
||||||
|
List<Map<String, Object>> direccionesPresupuesto = new ArrayList<>();
|
||||||
|
List<Map<String, Object>> direccionesPrueba = new ArrayList<>();
|
||||||
|
if (cart.getOnlyOneShipment()) {
|
||||||
|
List<CartDireccion> direcciones = cart.getDirecciones().stream().limit(1).toList();
|
||||||
|
if (!direcciones.isEmpty()) {
|
||||||
|
if (presupuesto.getServiciosJson() != null
|
||||||
|
&& presupuesto.getServiciosJson().contains("deposito-legal")) {
|
||||||
|
direccionesPresupuesto.add(direcciones.get(0).toSkMap(
|
||||||
|
presupuesto.getSelectedTirada(),
|
||||||
|
presupuesto.getPeso(),
|
||||||
|
direcciones.get(0).getIsPalets(),
|
||||||
|
false));
|
||||||
|
|
||||||
|
direccionesPresupuesto.add(direcciones.get(0).toSkMapDepositoLegal());
|
||||||
|
} else {
|
||||||
|
direccionesPresupuesto.add(direcciones.get(0).toSkMap(
|
||||||
|
presupuesto.getSelectedTirada(),
|
||||||
|
presupuesto.getPeso(),
|
||||||
|
direcciones.get(0).getIsPalets(),
|
||||||
|
false));
|
||||||
|
}
|
||||||
|
if (presupuesto.getServiciosJson() != null
|
||||||
|
&& presupuesto.getServiciosJson().contains("ejemplar-prueba")) {
|
||||||
|
direccionesPrueba.add(direcciones.get(0).toSkMap(
|
||||||
|
1,
|
||||||
|
presupuesto.getPeso(),
|
||||||
|
false,
|
||||||
|
true));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> direccionesRet = new HashMap<>();
|
||||||
|
direccionesRet.put("direcciones", direccionesPresupuesto);
|
||||||
|
if (!direccionesPrueba.isEmpty())
|
||||||
|
direccionesRet.put("direccionesFP1", direccionesPrueba.get(0));
|
||||||
|
else {
|
||||||
|
direccionesRet.put("direccionesFP1", new ArrayList<>());
|
||||||
|
}
|
||||||
|
return direccionesRet;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List<CartDireccion> direcciones = cart.getDirecciones().stream()
|
||||||
|
.filter(d -> d.getPresupuesto() != null && d.getPresupuesto().getId().equals(presupuesto.getId()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
for (CartDireccion cd : direcciones) {
|
||||||
|
|
||||||
|
// direccion de ejemplar de prueba
|
||||||
|
if (cd.getPresupuesto() == null || !cd.getPresupuesto().getId().equals(presupuesto.getId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cd.getUnidades() == null || cd.getUnidades() <= 0) {
|
||||||
|
direccionesPrueba.add(cd.toSkMap(
|
||||||
|
1,
|
||||||
|
presupuesto.getPeso(),
|
||||||
|
false,
|
||||||
|
true));
|
||||||
|
} else {
|
||||||
|
direccionesPresupuesto.add(cd.toSkMap(
|
||||||
|
cd.getUnidades(),
|
||||||
|
presupuesto.getPeso(),
|
||||||
|
cd.getIsPalets(),
|
||||||
|
false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (presupuesto.getServiciosJson() != null
|
||||||
|
&& presupuesto.getServiciosJson().contains("deposito-legal")) {
|
||||||
|
CartDireccion cd = new CartDireccion();
|
||||||
|
direccionesPresupuesto.add(cd.toSkMapDepositoLegal());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, Object> direccionesRet = new HashMap<>();
|
||||||
|
direccionesRet.put("direcciones", direccionesPresupuesto);
|
||||||
|
if (!direccionesPrueba.isEmpty())
|
||||||
|
direccionesRet.put("direccionesFP1", direccionesPrueba.get(0));
|
||||||
|
else {
|
||||||
|
direccionesRet.put("direccionesFP1", new ArrayList<>());
|
||||||
|
}
|
||||||
|
return direccionesRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
private void saveDireccionesPedidoLinea(
|
||||||
|
Map<String, Object> direcciones,
|
||||||
|
Pedido pedido,
|
||||||
|
PedidoLinea linea, Long direccionFacturacionId) {
|
||||||
|
|
||||||
|
String email = pedido.getCreatedBy().getUserName();
|
||||||
|
|
||||||
|
// direccion prueba
|
||||||
|
if (direcciones.containsKey("direccionesFP1")) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> fp1 = (Map<String, Object>) direcciones.get("direccionesFP1");
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
PedidoDireccion direccion = saveDireccion(
|
||||||
|
email,
|
||||||
|
false,
|
||||||
|
(HashMap<String, Object>) fp1.get("direccion"),
|
||||||
|
pedido,
|
||||||
|
linea,
|
||||||
|
true,
|
||||||
|
false);
|
||||||
|
pedidoDireccionRepository.save(direccion);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Viene vacio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direcciones.containsKey("direcciones")) {
|
||||||
|
List<?> dirs = (List<?>) direcciones.get("direcciones");
|
||||||
|
for (Object dir : dirs) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
HashMap<String, Object> direccionEnvio = (HashMap<String, Object>) ((HashMap<String, Object>) dir)
|
||||||
|
.get("direccion");
|
||||||
|
if (direccionEnvio.get("cantidad") != null && (Integer) direccionEnvio.get("cantidad") == 4
|
||||||
|
&& direccionEnvio.get("att").toString().contains("Depósito Legal")) {
|
||||||
|
continue; // Saltar la dirección de depósito legal
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
PedidoDireccion direccion = saveDireccion(
|
||||||
|
email,
|
||||||
|
((Number) ((HashMap<String, Object>) dir)
|
||||||
|
.getOrDefault("entregaPalets", 0))
|
||||||
|
.intValue() == 1,
|
||||||
|
(HashMap<String, Object>) ((HashMap<String, Object>) dir).get("direccion"),
|
||||||
|
pedido,
|
||||||
|
linea, false,
|
||||||
|
false);
|
||||||
|
pedidoDireccionRepository.save(direccion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direccionFacturacionId != null) {
|
||||||
|
Direccion dirFact = direccionService.findById(direccionFacturacionId).orElse(null);
|
||||||
|
if (dirFact != null) {
|
||||||
|
HashMap<String, Object> dirFactMap = new HashMap<>();
|
||||||
|
dirFactMap.put("att", dirFact.getAtt());
|
||||||
|
dirFactMap.put("direccion", dirFact.getDireccion());
|
||||||
|
dirFactMap.put("cp", dirFact.getCp());
|
||||||
|
dirFactMap.put("municipio", dirFact.getCiudad());
|
||||||
|
dirFactMap.put("provincia", dirFact.getProvincia());
|
||||||
|
dirFactMap.put("pais_code3", dirFact.getPaisCode3());
|
||||||
|
dirFactMap.put("telefono", dirFact.getTelefono());
|
||||||
|
dirFactMap.put("instrucciones", dirFact.getInstrucciones());
|
||||||
|
dirFactMap.put("razon_social", dirFact.getRazonSocial());
|
||||||
|
dirFactMap.put("tipo_identificacion_fiscal", dirFact.getTipoIdentificacionFiscal().name());
|
||||||
|
dirFactMap.put("identificacion_fiscal", dirFact.getIdentificacionFiscal());
|
||||||
|
|
||||||
|
PedidoDireccion direccion = saveDireccion(
|
||||||
|
email,
|
||||||
|
false,
|
||||||
|
dirFactMap,
|
||||||
|
pedido,
|
||||||
|
linea,
|
||||||
|
false,
|
||||||
|
true);
|
||||||
|
pedidoDireccionRepository.save(direccion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PedidoDireccion saveDireccion(
|
||||||
|
String email,
|
||||||
|
Boolean palets,
|
||||||
|
HashMap<String, Object> dir,
|
||||||
|
Pedido pedido,
|
||||||
|
PedidoLinea linea,
|
||||||
|
Boolean isEjemplarPrueba,
|
||||||
|
Boolean isFacturacion) {
|
||||||
|
|
||||||
|
PedidoDireccion direccion = new PedidoDireccion();
|
||||||
|
direccion.setEmail(email);
|
||||||
|
direccion.setPalets(isEjemplarPrueba || isFacturacion ? false : palets);
|
||||||
|
direccion.setPedidoLinea(isFacturacion ? null : linea);
|
||||||
|
if (isFacturacion) {
|
||||||
|
direccion.setUnidades(null);
|
||||||
|
direccion.setFacturacion(true);
|
||||||
|
direccion.setPedido(pedido);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (isEjemplarPrueba) {
|
||||||
|
direccion.setUnidades(1);
|
||||||
|
direccion.setEjemplarPrueba(true);
|
||||||
|
} else {
|
||||||
|
direccion.setUnidades((Integer) dir.getOrDefault("cantidad", 1));
|
||||||
|
direccion.setEjemplarPrueba(false);
|
||||||
|
}
|
||||||
|
direccion.setFacturacion(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
direccion.setAtt((String) dir.getOrDefault("att", ""));
|
||||||
|
direccion.setDireccion((String) dir.getOrDefault("direccion", ""));
|
||||||
|
direccion.setCp((Integer) dir.getOrDefault("cp", 0));
|
||||||
|
direccion.setCiudad((String) dir.getOrDefault("municipio", ""));
|
||||||
|
direccion.setProvincia((String) dir.getOrDefault("provincia", ""));
|
||||||
|
direccion.setPaisCode3((String) dir.getOrDefault("pais_code3", "esp"));
|
||||||
|
direccion.setTelefono((String) dir.getOrDefault("telefono", ""));
|
||||||
|
direccion.setInstrucciones((String) dir.getOrDefault("instrucciones", ""));
|
||||||
|
direccion.setRazonSocial((String) dir.getOrDefault("razon_social", ""));
|
||||||
|
direccion.setTipoIdentificacionFiscal(Direccion.TipoIdentificacionFiscal
|
||||||
|
.valueOf((String) dir.getOrDefault("tipo_identificacion_fiscal",
|
||||||
|
Direccion.TipoIdentificacionFiscal.DNI.name())));
|
||||||
|
direccion.setIdentificacionFiscal((String) dir.getOrDefault("identificacion_fiscal", ""));
|
||||||
|
|
||||||
|
return direccion;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Estado getEstadoInicial(Presupuesto p) {
|
||||||
|
|
||||||
|
if (presupuestoService.hasMaquetacion(p)) {
|
||||||
|
return Estado.maquetacion;
|
||||||
|
} else {
|
||||||
|
return Estado.haciendo_ferro;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,402 @@
|
|||||||
|
package com.imprimelibros.erp.pedidos;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
|
||||||
|
import com.imprimelibros.erp.common.Utils;
|
||||||
|
import com.imprimelibros.erp.datatables.DataTable;
|
||||||
|
import com.imprimelibros.erp.datatables.DataTablesParser;
|
||||||
|
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
||||||
|
import com.imprimelibros.erp.datatables.DataTablesResponse;
|
||||||
|
import com.imprimelibros.erp.i18n.TranslationService;
|
||||||
|
import com.imprimelibros.erp.paises.PaisesService;
|
||||||
|
import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
|
||||||
|
import com.imprimelibros.erp.users.UserDao;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Join;
|
||||||
|
import jakarta.persistence.criteria.JoinType;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/pedidos")
|
||||||
|
public class PedidosController {
|
||||||
|
|
||||||
|
private final PresupuestoService presupuestoService;
|
||||||
|
|
||||||
|
private final PedidoRepository repoPedido;
|
||||||
|
private final PedidoService pedidoService;
|
||||||
|
private final UserDao repoUser;
|
||||||
|
private final MessageSource messageSource;
|
||||||
|
private final PedidoLineaRepository repoPedidoLinea;
|
||||||
|
private final PaisesService paisesService;
|
||||||
|
private final TranslationService translationService;
|
||||||
|
|
||||||
|
public PedidosController(PedidoRepository repoPedido, PedidoService pedidoService, UserDao repoUser,
|
||||||
|
MessageSource messageSource, TranslationService translationService,
|
||||||
|
PedidoLineaRepository repoPedidoLinea, PaisesService paisesService, PresupuestoService presupuestoService) {
|
||||||
|
this.repoPedido = repoPedido;
|
||||||
|
this.pedidoService = pedidoService;
|
||||||
|
this.repoUser = repoUser;
|
||||||
|
this.messageSource = messageSource;
|
||||||
|
this.translationService = translationService;
|
||||||
|
this.repoPedidoLinea = repoPedidoLinea;
|
||||||
|
this.paisesService = paisesService;
|
||||||
|
this.presupuestoService = presupuestoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public String listarPedidos(Model model, Locale locale) {
|
||||||
|
|
||||||
|
List<String> keys = List.of(
|
||||||
|
"app.cancelar",
|
||||||
|
"app.seleccionar",
|
||||||
|
"app.yes",
|
||||||
|
"checkout.payment.card",
|
||||||
|
"checkout.payment.bizum",
|
||||||
|
"checkout.payment.bank-transfer",
|
||||||
|
"checkout.error.select-method");
|
||||||
|
|
||||||
|
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||||
|
model.addAttribute("languageBundle", translations);
|
||||||
|
|
||||||
|
if (Utils.isCurrentUserAdmin()) {
|
||||||
|
return "imprimelibros/pedidos/pedidos-list";
|
||||||
|
}
|
||||||
|
return "imprimelibros/pedidos/pedidos-list-cliente";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "datatable", produces = "application/json")
|
||||||
|
@ResponseBody
|
||||||
|
public DataTablesResponse<Map<String, Object>> getDatatable(
|
||||||
|
HttpServletRequest request,
|
||||||
|
Principal principal,
|
||||||
|
Locale locale) {
|
||||||
|
|
||||||
|
DataTablesRequest dt = DataTablesParser.from(request);
|
||||||
|
|
||||||
|
Boolean isAdmin = Utils.isCurrentUserAdmin();
|
||||||
|
Long currentUserId = Utils.currentUserId(principal);
|
||||||
|
|
||||||
|
List<String> searchable = List.of(
|
||||||
|
"id");
|
||||||
|
|
||||||
|
// Campos ordenables
|
||||||
|
List<String> orderable = List.of(
|
||||||
|
"id",
|
||||||
|
"createdBy.fullName",
|
||||||
|
"createdAt",
|
||||||
|
"total",
|
||||||
|
"estado");
|
||||||
|
|
||||||
|
Specification<Pedido> base = (root, query, cb) -> cb.conjunction();
|
||||||
|
if (!isAdmin) {
|
||||||
|
base = base.and((root, query, cb) -> cb.equal(root.get("createdBy").get("id"), currentUserId));
|
||||||
|
}
|
||||||
|
String clientSearch = dt.getColumnSearch("cliente");
|
||||||
|
String estadoSearch = dt.getColumnSearch("estado");
|
||||||
|
|
||||||
|
// 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification
|
||||||
|
if (clientSearch != null) {
|
||||||
|
List<Long> userIds = repoUser.findIdsByFullNameLike(clientSearch.trim());
|
||||||
|
|
||||||
|
if (userIds.isEmpty()) {
|
||||||
|
// Ningún usuario coincide → forzamos 0 resultados
|
||||||
|
base = base.and((root, query, cb) -> cb.disjunction());
|
||||||
|
} else {
|
||||||
|
base = base.and((root, query, cb) -> root.get("createdBy").in(userIds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (estadoSearch != null && !estadoSearch.isBlank()) {
|
||||||
|
try {
|
||||||
|
PedidoLinea.Estado estadoEnum = PedidoLinea.Estado.valueOf(estadoSearch.trim());
|
||||||
|
|
||||||
|
base = base.and((root, query, cb) -> {
|
||||||
|
// Evitar duplicados de pedidos si el provider usa joins
|
||||||
|
if (Pedido.class.equals(query.getResultType())) {
|
||||||
|
query.distinct(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Join<Pedido, PedidoLinea> lineas = root.join("lineas", JoinType.INNER);
|
||||||
|
return cb.equal(lineas.get("estado"), estadoEnum);
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
// Valor de estado no válido → forzamos 0 resultados
|
||||||
|
base = base.and((root, query, cb) -> cb.disjunction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Long total = repoPedido.count(base);
|
||||||
|
|
||||||
|
return DataTable
|
||||||
|
.of(repoPedido, Pedido.class, dt, searchable)
|
||||||
|
.orderable(orderable)
|
||||||
|
.add("id", Pedido::getId)
|
||||||
|
.add("created_at", pedido -> Utils.formatInstant(pedido.getCreatedAt(), locale))
|
||||||
|
.add("cliente", pedido -> {
|
||||||
|
if (pedido.getCreatedBy() != null) {
|
||||||
|
return pedido.getCreatedBy().getFullName();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
})
|
||||||
|
.add("total", pedido -> {
|
||||||
|
if (pedido.getTotal() != null) {
|
||||||
|
return Utils.formatCurrency(pedido.getTotal(), locale);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.add("estado", pedido -> {
|
||||||
|
List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoId(pedido.getId());
|
||||||
|
if (lineas.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// concatenar los estados de las líneas, ordenados por prioridad
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
lineas.stream()
|
||||||
|
.map(PedidoLinea::getEstado)
|
||||||
|
.distinct()
|
||||||
|
.sorted(Comparator.comparingInt(PedidoLinea.Estado::getPriority))
|
||||||
|
.forEach(estado -> {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(messageSource.getMessage(estado.getMessageKey(), null, locale));
|
||||||
|
});
|
||||||
|
String text = sb.toString();
|
||||||
|
return text;
|
||||||
|
})
|
||||||
|
.add("actions", pedido -> {
|
||||||
|
String data = "<span class=\'badge bg-success btn-view \' data-id=\'" + pedido.getId()
|
||||||
|
+ "\' style=\'cursor: pointer;\'>"
|
||||||
|
+ messageSource.getMessage("app.view", null, locale) + "</span>";
|
||||||
|
List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoId(pedido.getId());
|
||||||
|
boolean hasDenegadoPago = lineas.stream()
|
||||||
|
.anyMatch(linea -> PedidoLinea.Estado.denegado_pago.equals(linea.getEstado()));
|
||||||
|
if (hasDenegadoPago) {
|
||||||
|
data += " <span class='badge bg-danger btn-pay' data-amount='" + (int) (pedido.getTotal() * 100)
|
||||||
|
+ "' data-id='" + pedido.getId() + "' style='cursor: pointer;'>"
|
||||||
|
+ messageSource.getMessage("app.pay", null, locale) + "</span>";
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
.where(base)
|
||||||
|
.toJson(total);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/view/{id}")
|
||||||
|
public String verPedido(
|
||||||
|
@PathVariable(name = "id", required = true) Long id,
|
||||||
|
Model model, Locale locale) {
|
||||||
|
|
||||||
|
Boolean isAdmin = Utils.isCurrentUserAdmin();
|
||||||
|
if (isAdmin) {
|
||||||
|
model.addAttribute("isAdmin", true);
|
||||||
|
} else {
|
||||||
|
model.addAttribute("isAdmin", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
PedidoDireccion direccionFacturacion = pedidoService.getDireccionFacturacionPedido(id);
|
||||||
|
if (direccionFacturacion != null) {
|
||||||
|
String paisNombre = paisesService.getPaisNombrePorCode3(direccionFacturacion.getPaisCode3(), locale);
|
||||||
|
direccionFacturacion.setPaisNombre(paisNombre);
|
||||||
|
}
|
||||||
|
|
||||||
|
model.addAttribute("direccionFacturacion", direccionFacturacion);
|
||||||
|
|
||||||
|
List<Map<String, Object>> lineas = pedidoService.getLineas(id, locale);
|
||||||
|
for (Map<String, Object> linea : lineas) {
|
||||||
|
|
||||||
|
PedidoLinea pedidoLinea = repoPedidoLinea.findById(
|
||||||
|
((Number) linea.get("lineaId")).longValue()).orElse(null);
|
||||||
|
if (pedidoLinea != null) {
|
||||||
|
Map<String, Boolean> buttons = new HashMap<>();
|
||||||
|
if (pedidoLinea.getEstado().getPriority() >= PedidoLinea.Estado.esperando_aceptacion_ferro.getPriority()
|
||||||
|
&& pedidoLinea.getEstado().getPriority() <= PedidoLinea.Estado.produccion.getPriority()) {
|
||||||
|
|
||||||
|
if (pedidoLinea.getEstado() == PedidoLinea.Estado.esperando_aceptacion_ferro) {
|
||||||
|
buttons.put("aceptar_ferro", true);
|
||||||
|
} else {
|
||||||
|
buttons.put("aceptar_ferro", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> filesType = pedidoService.getFilesType(pedidoLinea.getId(), locale);
|
||||||
|
if (filesType == null || filesType.get("error") != null) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
messageSource.getMessage("pedido.errors.update-server-error", null, locale));
|
||||||
|
}
|
||||||
|
for (String key : filesType.keySet()) {
|
||||||
|
buttons.put(key, (Integer) filesType.get(key) == 1 ? true : false);
|
||||||
|
}
|
||||||
|
linea.put("buttons", buttons);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PedidoDireccion> dirEntrega = pedidoService.getDireccionesEntregaPedidoLinea(
|
||||||
|
((Number) linea.get("lineaId")).longValue());
|
||||||
|
|
||||||
|
if (dirEntrega != null && !dirEntrega.isEmpty()) {
|
||||||
|
for (PedidoDireccion direccion : dirEntrega) {
|
||||||
|
String paisNombre = paisesService.getPaisNombrePorCode3(direccion.getPaisCode3(), locale);
|
||||||
|
direccion.setPaisNombre(paisNombre);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
linea.put("direccionesEntrega", dirEntrega);
|
||||||
|
|
||||||
|
}
|
||||||
|
model.addAttribute("lineas", lineas);
|
||||||
|
model.addAttribute("id", id);
|
||||||
|
return "imprimelibros/pedidos/pedidos-view";
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------
|
||||||
|
// Acciones sobre las lineas de pedido
|
||||||
|
// -------------------------------------
|
||||||
|
@PostMapping("/linea/{id}/update-status")
|
||||||
|
@ResponseBody
|
||||||
|
public Map<String, Object> updateStatus(
|
||||||
|
@PathVariable(name = "id", required = true) Long id, Locale locale) {
|
||||||
|
|
||||||
|
Map<String, Object> result = pedidoService.actualizarEstado(id, locale);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/linea/{id}/update-maquetacion")
|
||||||
|
@ResponseBody
|
||||||
|
public Map<String, Object> updateMaquetacion(
|
||||||
|
@PathVariable(name = "id", required = true) Long id,
|
||||||
|
Locale locale) {
|
||||||
|
|
||||||
|
PedidoLinea entity = repoPedidoLinea.findById(id).orElse(null);
|
||||||
|
if (entity == null) {
|
||||||
|
String errorMsg = messageSource.getMessage("pedido.errors.linea-not-found", null, locale);
|
||||||
|
return Map.of(
|
||||||
|
"success", false,
|
||||||
|
"message", errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.getEstado() != PedidoLinea.Estado.maquetacion) {
|
||||||
|
String errorMsg = messageSource.getMessage("pedido.errors.state-error", null, locale);
|
||||||
|
return Map.of(
|
||||||
|
"success", false,
|
||||||
|
"message", errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setEstado(PedidoLinea.Estado.haciendo_ferro);
|
||||||
|
repoPedidoLinea.save(entity);
|
||||||
|
String successMsg = messageSource.getMessage("pedido.success.estado-actualizado", null, locale);
|
||||||
|
return Map.of(
|
||||||
|
"success", true,
|
||||||
|
"message", successMsg,
|
||||||
|
"state", messageSource.getMessage(entity.getEstado().getMessageKey(), null, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/linea/{id}/download-ferro")
|
||||||
|
public ResponseEntity<Resource> downloadFerro(@PathVariable(name = "id", required = true) Long id, Locale locale) {
|
||||||
|
|
||||||
|
byte[] ferroFileContent = pedidoService.getFerroFileContent(id, locale);
|
||||||
|
if (ferroFileContent == null) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayResource resource = new ByteArrayResource(ferroFileContent);
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=ferro_" + id + ".pdf")
|
||||||
|
.contentType(MediaType.APPLICATION_PDF)
|
||||||
|
.body(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/linea/{id}/download-cub")
|
||||||
|
public ResponseEntity<Resource> downloadCubierta(@PathVariable(name = "id", required = true) Long id,
|
||||||
|
Locale locale) {
|
||||||
|
|
||||||
|
byte[] cubFileContent = pedidoService.getCubiertaFileContent(id, locale);
|
||||||
|
if (cubFileContent == null) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayResource resource = new ByteArrayResource(cubFileContent);
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=cubierta_" + id + ".pdf")
|
||||||
|
.contentType(MediaType.APPLICATION_PDF)
|
||||||
|
.body(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/linea/{id}/download-tapa")
|
||||||
|
public ResponseEntity<Resource> downloadTapa(@PathVariable(name = "id", required = true) Long id, Locale locale) {
|
||||||
|
|
||||||
|
byte[] tapaFileContent = pedidoService.getTapaFileContent(id, locale);
|
||||||
|
if (tapaFileContent == null) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayResource resource = new ByteArrayResource(tapaFileContent);
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=tapa_" + id + ".pdf")
|
||||||
|
.contentType(MediaType.APPLICATION_PDF)
|
||||||
|
.body(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/linea/{id}/aceptar-ferro")
|
||||||
|
@ResponseBody
|
||||||
|
public Map<String, Object> aceptarFerro(@PathVariable(name = "id", required = true) Long id,
|
||||||
|
Locale locale) {
|
||||||
|
|
||||||
|
PedidoLinea entity = repoPedidoLinea.findById(id).orElse(null);
|
||||||
|
if (entity == null) {
|
||||||
|
String errorMsg = messageSource.getMessage("pedido.errors.linea-not-found", null, locale);
|
||||||
|
return Map.of(
|
||||||
|
"success", false,
|
||||||
|
"message", errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.getEstado() != PedidoLinea.Estado.esperando_aceptacion_ferro) {
|
||||||
|
String errorMsg = messageSource.getMessage("pedido.errors.state-error", null, locale);
|
||||||
|
return Map.of(
|
||||||
|
"success", false,
|
||||||
|
"message", errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean result = pedidoService.aceptarFerro(id, locale);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
String successMsg = messageSource.getMessage("pedido.success.estado-actualizado", null, locale);
|
||||||
|
return Map.of(
|
||||||
|
"success", true,
|
||||||
|
"message", successMsg,
|
||||||
|
"state", messageSource.getMessage(entity.getEstado().getMessageKey(), null, locale));
|
||||||
|
} else {
|
||||||
|
String errorMsg = messageSource.getMessage("pedido.errors.update-server-error", null, locale);
|
||||||
|
return Map.of(
|
||||||
|
"success", false,
|
||||||
|
"message", errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -147,7 +147,9 @@ public class PresupuestoController {
|
|||||||
return ResponseEntity.badRequest().body(errores);
|
return ResponseEntity.badRequest().body(errores);
|
||||||
}
|
}
|
||||||
Map<String, Object> resultado = new HashMap<>();
|
Map<String, Object> resultado = new HashMap<>();
|
||||||
resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale));
|
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale);
|
||||||
|
resultado.put("solapas", datosInterior.get("maxSolapas"));
|
||||||
|
resultado.put("lomo", datosInterior.get("lomo"));
|
||||||
resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale));
|
resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale));
|
||||||
return ResponseEntity.ok(resultado);
|
return ResponseEntity.ok(resultado);
|
||||||
}
|
}
|
||||||
@ -267,7 +269,10 @@ public class PresupuestoController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale));
|
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale);
|
||||||
|
resultado.put("solapas", datosInterior.get("maxSolapas"));
|
||||||
|
resultado.put("lomo", datosInterior.get("lomo"));
|
||||||
|
|
||||||
return ResponseEntity.ok(resultado);
|
return ResponseEntity.ok(resultado);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +305,10 @@ public class PresupuestoController {
|
|||||||
presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción
|
presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale));
|
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale);
|
||||||
|
resultado.put("solapas", datosInterior.get("maxSolapas"));
|
||||||
|
resultado.put("lomo", datosInterior.get("lomo"));
|
||||||
|
|
||||||
return ResponseEntity.ok(resultado);
|
return ResponseEntity.ok(resultado);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +331,10 @@ public class PresupuestoController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> resultado = new HashMap<>();
|
Map<String, Object> resultado = new HashMap<>();
|
||||||
resultado.put("solapas", apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale));
|
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale);
|
||||||
|
resultado.put("solapas", datosInterior.get("maxSolapas"));
|
||||||
|
resultado.put("lomo", datosInterior.get("lomo"));
|
||||||
|
|
||||||
return ResponseEntity.ok(resultado);
|
return ResponseEntity.ok(resultado);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,7 +503,8 @@ public class PresupuestoController {
|
|||||||
String sessionId = request.getSession(true).getId();
|
String sessionId = request.getSession(true).getId();
|
||||||
String ip = IpUtils.getClientIp(request);
|
String ip = IpUtils.getClientIp(request);
|
||||||
|
|
||||||
var resumen = presupuestoService.getResumen(p, serviciosList, datosMaquetacion, datosMarcapaginas, save, mode, locale, sessionId, ip);
|
var resumen = presupuestoService.getResumen(p, serviciosList, datosMaquetacion, datosMarcapaginas, save, mode,
|
||||||
|
locale, sessionId, ip);
|
||||||
|
|
||||||
return ResponseEntity.ok(resumen);
|
return ResponseEntity.ok(resumen);
|
||||||
}
|
}
|
||||||
@ -519,7 +531,27 @@ public class PresupuestoController {
|
|||||||
"presupuesto.add.cancel",
|
"presupuesto.add.cancel",
|
||||||
"presupuesto.add.select-client",
|
"presupuesto.add.select-client",
|
||||||
"presupuesto.add.error.options",
|
"presupuesto.add.error.options",
|
||||||
"presupuesto.add.error.options-client");
|
"presupuesto.add.error.options-client",
|
||||||
|
"presupuesto.duplicar.title",
|
||||||
|
"presupuesto.duplicar.text",
|
||||||
|
"presupuesto.duplicar.confirm",
|
||||||
|
"presupuesto.duplicar.cancelar",
|
||||||
|
"presupuesto.duplicar.aceptar",
|
||||||
|
"presupuesto.duplicar.required",
|
||||||
|
"presupuesto.duplicar.success.title",
|
||||||
|
"presupuesto.duplicar.success.text",
|
||||||
|
"presupuesto.duplicar.error.title",
|
||||||
|
"presupuesto.duplicar.error.internal",
|
||||||
|
"presupuesto.reimprimir.title",
|
||||||
|
"presupuesto.reimprimir.text",
|
||||||
|
"presupuesto.reimprimir.confirm",
|
||||||
|
"presupuesto.reimprimir.cancelar",
|
||||||
|
"presupuesto.reimprimir.aceptar",
|
||||||
|
"presupuesto.reimprimir.success.title",
|
||||||
|
"presupuesto.reimprimir.success.text",
|
||||||
|
"presupuesto.reimprimir.error.title",
|
||||||
|
"presupuesto.reimprimir.error.internal"
|
||||||
|
);
|
||||||
|
|
||||||
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||||
model.addAttribute("languageBundle", translations);
|
model.addAttribute("languageBundle", translations);
|
||||||
@ -543,7 +575,26 @@ public class PresupuestoController {
|
|||||||
"presupuesto.exito.guardado",
|
"presupuesto.exito.guardado",
|
||||||
"presupuesto.add.error.save.title",
|
"presupuesto.add.error.save.title",
|
||||||
"presupuesto.iva-reducido",
|
"presupuesto.iva-reducido",
|
||||||
"presupuesto.iva-reducido-descripcion");
|
"presupuesto.iva-reducido-descripcion",
|
||||||
|
"presupuesto.duplicar.title",
|
||||||
|
"presupuesto.duplicar.text",
|
||||||
|
"presupuesto.duplicar.confirm",
|
||||||
|
"presupuesto.duplicar.cancelar",
|
||||||
|
"presupuesto.duplicar.aceptar",
|
||||||
|
"presupuesto.duplicar.required",
|
||||||
|
"presupuesto.duplicar.success.title",
|
||||||
|
"presupuesto.duplicar.success.text",
|
||||||
|
"presupuesto.duplicar.error.title",
|
||||||
|
"presupuesto.duplicar.error.internal",
|
||||||
|
"presupuesto.reimprimir.title",
|
||||||
|
"presupuesto.reimprimir.text",
|
||||||
|
"presupuesto.reimprimir.confirm",
|
||||||
|
"presupuesto.reimprimir.cancelar",
|
||||||
|
"presupuesto.reimprimir.aceptar",
|
||||||
|
"presupuesto.reimprimir.success.title",
|
||||||
|
"presupuesto.reimprimir.success.text",
|
||||||
|
"presupuesto.reimprimir.error.title",
|
||||||
|
"presupuesto.reimprimir.error.internal");
|
||||||
|
|
||||||
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||||
model.addAttribute("languageBundle", translations);
|
model.addAttribute("languageBundle", translations);
|
||||||
@ -562,15 +613,15 @@ public class PresupuestoController {
|
|||||||
return "redirect:/presupuesto";
|
return "redirect:/presupuesto";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(presupuestoOpt.get().getEstado() == Presupuesto.Estado.aceptado){
|
if (presupuestoOpt.get().getEstado() == Presupuesto.Estado.aceptado) {
|
||||||
|
|
||||||
Map<String, Object> resumen = presupuestoService.getTextosResumen(
|
Map<String, Object> resumen = presupuestoService.getTextosResumen(
|
||||||
presupuestoOpt.get(),
|
presupuestoOpt.get(),
|
||||||
Utils.decodeJsonList(presupuestoOpt.get().getServiciosJson()),
|
Utils.decodeJsonList(presupuestoOpt.get().getServiciosJson()),
|
||||||
Utils.decodeJsonMap(presupuestoOpt.get().getDatosMaquetacionJson()),
|
Utils.decodeJsonMap(presupuestoOpt.get().getDatosMaquetacionJson()),
|
||||||
Utils.decodeJsonMap(presupuestoOpt.get().getDatosMarcapaginasJson()),
|
Utils.decodeJsonMap(presupuestoOpt.get().getDatosMarcapaginasJson()),
|
||||||
locale);
|
locale);
|
||||||
|
|
||||||
model.addAttribute("resumen", resumen);
|
model.addAttribute("resumen", resumen);
|
||||||
model.addAttribute("presupuesto", presupuestoOpt.get());
|
model.addAttribute("presupuesto", presupuestoOpt.get());
|
||||||
return "imprimelibros/presupuestos/presupuestador-view";
|
return "imprimelibros/presupuestos/presupuestador-view";
|
||||||
@ -595,6 +646,7 @@ public class PresupuestoController {
|
|||||||
model.addAttribute("appMode", "edit");
|
model.addAttribute("appMode", "edit");
|
||||||
}
|
}
|
||||||
model.addAttribute("id", presupuestoOpt.get().getId());
|
model.addAttribute("id", presupuestoOpt.get().getId());
|
||||||
|
model.addAttribute("presupuesto", presupuestoOpt.get());
|
||||||
return "imprimelibros/presupuestos/presupuesto-form";
|
return "imprimelibros/presupuestos/presupuesto-form";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -780,4 +832,30 @@ public class PresupuestoController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/comentario")
|
||||||
|
@ResponseBody
|
||||||
|
public String actualizarComentario(@PathVariable Long id,
|
||||||
|
@RequestParam String comentario) {
|
||||||
|
presupuestoService.updateComentario(id, comentario);
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/api/duplicar/{id}")
|
||||||
|
@ResponseBody
|
||||||
|
public Map<String, Object> duplicarPresupuesto(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam(name = "titulo", defaultValue = "") String titulo) {
|
||||||
|
|
||||||
|
Long entity = presupuestoService.duplicarPresupuesto(id, titulo);
|
||||||
|
return Map.of("id", entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/api/reimprimir/{id}")
|
||||||
|
@ResponseBody
|
||||||
|
public Map<String, Object> reimprimirPresupuesto(@PathVariable Long id) {
|
||||||
|
|
||||||
|
Long entity = presupuestoService.reimprimirPresupuesto(id);
|
||||||
|
return Map.of("id", entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,7 +86,7 @@ public class PresupuestoDatatableService {
|
|||||||
.addIf(publico, "ciudad", Presupuesto::getCiudad)
|
.addIf(publico, "ciudad", Presupuesto::getCiudad)
|
||||||
.add("updatedAt", p -> formatDate(p.getUpdatedAt(), locale))
|
.add("updatedAt", p -> formatDate(p.getUpdatedAt(), locale))
|
||||||
.addIf(!publico, "user", p -> p.getUser() != null ? p.getUser().getFullName() : "")
|
.addIf(!publico, "user", p -> p.getUser() != null ? p.getUser().getFullName() : "")
|
||||||
.add("actions", this::generarBotones)
|
.add("actions", p -> generarBotones(p, locale))
|
||||||
.where(base)
|
.where(base)
|
||||||
.toJson(count);
|
.toJson(count);
|
||||||
}
|
}
|
||||||
@ -115,18 +115,27 @@ public class PresupuestoDatatableService {
|
|||||||
return df.format(instant);
|
return df.format(instant);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generarBotones(Presupuesto p) {
|
private String generarBotones(Presupuesto p, Locale locale) {
|
||||||
boolean borrador = p.getEstado() == Presupuesto.Estado.borrador;
|
boolean borrador = p.getEstado() == Presupuesto.Estado.borrador;
|
||||||
String id = String.valueOf(p.getId());
|
String id = String.valueOf(p.getId());
|
||||||
String editBtn = "<a href=\"javascript:void(0);\" data-id=\"" + id + "\" class=\"link-success btn-edit-" +
|
String editBtn = "<a href=\"javascript:void(0);\" data-id=\"" + id + "\" class=\"link-success btn-edit-" +
|
||||||
(p.getOrigen().equals(Presupuesto.Origen.publico) ? "anonimo" : "privado") + " fs-15\"><i class=\"ri-" +
|
(p.getOrigen().equals(Presupuesto.Origen.publico) ? "anonimo" : "privado") + " fs-15\"><i class=\"ri-" +
|
||||||
(p.getOrigen().equals(Presupuesto.Origen.publico) || p.getEstado() == Presupuesto.Estado.aceptado ? "eye" : "pencil") + "-line\"></i></a>";
|
(p.getOrigen().equals(Presupuesto.Origen.publico) || p.getEstado() == Presupuesto.Estado.aceptado ? "eye" : "pencil") + "-line\" " +
|
||||||
|
"data-bs-toggle=\"tooltip\" data-bs-placement=\"top\" title=\"" +
|
||||||
|
msg(p.getEstado() == Presupuesto.Estado.aceptado ? "presupuesto.ver" : "presupuesto.editar", locale) + "\"></i></a>";
|
||||||
|
|
||||||
|
String duplicarBtn = !p.getOrigen().equals(Presupuesto.Origen.publico) ? "<a href=\"javascript:void(0);\" data-id=\"" + id
|
||||||
|
+ "\" class=\"link-success btn-duplicate-privado fs-15\"><i class=\"ri-file-copy-2-line\" data-bs-toggle=\"tooltip\" data-bs-placement=\"top\" title=\"" +
|
||||||
|
msg("presupuesto.duplicar", locale) + "\"></i></a>" : "";
|
||||||
|
String reimprimirBtn = p.getEstado() == Presupuesto.Estado.aceptado && !p.getOrigen().equals(Presupuesto.Origen.publico) ? "<a href=\"javascript:void(0);\" data-id=\"" + id
|
||||||
|
+ "\" class=\"link-success btn-reprint-privado fs-15\"><i class=\"ri-printer-line\" data-bs-toggle=\"tooltip\" data-bs-placement=\"top\" title=\"" +
|
||||||
|
msg("presupuesto.reimprimir", locale) + "\"></i></a>" : "";
|
||||||
String deleteBtn = borrador ? "<a href=\"javascript:void(0);\" data-id=\"" + id
|
String deleteBtn = borrador ? "<a href=\"javascript:void(0);\" data-id=\"" + id
|
||||||
+ "\" class=\"link-danger btn-delete-"
|
+ "\" class=\"link-danger btn-delete-"
|
||||||
+ (p.getOrigen().equals(Presupuesto.Origen.publico) ? "anonimo" : "privado")
|
+ (p.getOrigen().equals(Presupuesto.Origen.publico) ? "anonimo" : "privado")
|
||||||
+ " fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>" : "";
|
+ " fs-15\"><i class=\"ri-delete-bin-5-line\" data-bs-toggle=\"tooltip\" data-bs-placement=\"top\" title=\"" +
|
||||||
|
msg("presupuesto.borrar", locale) + "\"></i></a>" : "";
|
||||||
|
|
||||||
return "<div class=\"hstack gap-3 flex-wrap\">" + editBtn + deleteBtn + "</div>";
|
return "<div class=\"hstack gap-3 flex-wrap\">" + editBtn + duplicarBtn + reimprimirBtn + deleteBtn + "</div>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ public class PresupuestoPapeles {
|
|||||||
);
|
);
|
||||||
|
|
||||||
private static final Map<String, String> CABEZADA_COLOR_KEYS = Map.of(
|
private static final Map<String, String> CABEZADA_COLOR_KEYS = Map.of(
|
||||||
|
"NOCABE", "presupuesto.cabezada-sin-cabezada",
|
||||||
"WHI", "presupuesto.cabezada-blanca",
|
"WHI", "presupuesto.cabezada-blanca",
|
||||||
"GRE", "presupuesto.cabezada-verde",
|
"GRE", "presupuesto.cabezada-verde",
|
||||||
"BLUE", "presupuesto.cabezada-azul",
|
"BLUE", "presupuesto.cabezada-azul",
|
||||||
|
|||||||
@ -387,6 +387,9 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
|
|||||||
@Column(name = "proveedor_ref2")
|
@Column(name = "proveedor_ref2")
|
||||||
private Long proveedorRef2;
|
private Long proveedorRef2;
|
||||||
|
|
||||||
|
@Column(name = "is_reimpresion", nullable = false)
|
||||||
|
private Boolean isReimpresion = false;
|
||||||
|
|
||||||
// ====== MÉTODOS AUX ======
|
// ====== MÉTODOS AUX ======
|
||||||
|
|
||||||
public String resumenPresupuesto() {
|
public String resumenPresupuesto() {
|
||||||
@ -965,6 +968,14 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
|
|||||||
this.proveedorRef2 = proveedorRef2;
|
this.proveedorRef2 = proveedorRef2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getIsReimpresion() {
|
||||||
|
return isReimpresion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsReimpresion(Boolean isReimpresion) {
|
||||||
|
this.isReimpresion = isReimpresion;
|
||||||
|
}
|
||||||
|
|
||||||
public void setId(Long id) {
|
public void setId(Long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
|
import com.imprimelibros.erp.common.Utils;
|
||||||
import com.imprimelibros.erp.common.web.IpUtils;
|
import com.imprimelibros.erp.common.web.IpUtils;
|
||||||
import com.imprimelibros.erp.configurationERP.VariableService;
|
import com.imprimelibros.erp.configurationERP.VariableService;
|
||||||
import com.imprimelibros.erp.presupuesto.GeoIpService;
|
import com.imprimelibros.erp.presupuesto.GeoIpService;
|
||||||
@ -71,14 +72,16 @@ public class PresupuestoService {
|
|||||||
private final skApiClient apiClient;
|
private final skApiClient apiClient;
|
||||||
private final GeoIpService geoIpService;
|
private final GeoIpService geoIpService;
|
||||||
private final UserDao userRepo;
|
private final UserDao userRepo;
|
||||||
|
private final Utils utils;
|
||||||
|
|
||||||
public PresupuestoService(PresupuestadorItems presupuestadorItems, PresupuestoFormatter presupuestoFormatter,
|
public PresupuestoService(PresupuestadorItems presupuestadorItems, PresupuestoFormatter presupuestoFormatter,
|
||||||
skApiClient apiClient, GeoIpService geoIpService, UserDao userRepo) {
|
skApiClient apiClient, GeoIpService geoIpService, UserDao userRepo, Utils utils) {
|
||||||
this.presupuestadorItems = presupuestadorItems;
|
this.presupuestadorItems = presupuestadorItems;
|
||||||
this.presupuestoFormatter = presupuestoFormatter;
|
this.presupuestoFormatter = presupuestoFormatter;
|
||||||
this.apiClient = apiClient;
|
this.apiClient = apiClient;
|
||||||
this.geoIpService = geoIpService;
|
this.geoIpService = geoIpService;
|
||||||
this.userRepo = userRepo;
|
this.userRepo = userRepo;
|
||||||
|
this.utils = utils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateDatosGenerales(int[] tiradas) {
|
public boolean validateDatosGenerales(int[] tiradas) {
|
||||||
@ -348,6 +351,11 @@ public class PresupuestoService {
|
|||||||
body.put("interior", interior);
|
body.put("interior", interior);
|
||||||
body.put("cubierta", cubierta);
|
body.put("cubierta", cubierta);
|
||||||
body.put("guardas", null);
|
body.put("guardas", null);
|
||||||
|
// Para las reimpresiones
|
||||||
|
if(presupuesto.getIsReimpresion() != null && presupuesto.getIsReimpresion()) {
|
||||||
|
body.put("reimpresion", 1);
|
||||||
|
body.put("iskn", presupuesto.getProveedorRef1());
|
||||||
|
}
|
||||||
if (presupuesto.getSobrecubierta()) {
|
if (presupuesto.getSobrecubierta()) {
|
||||||
Map<String, Object> sobrecubierta = new HashMap<>();
|
Map<String, Object> sobrecubierta = new HashMap<>();
|
||||||
sobrecubierta.put("papel", presupuesto.getPapelSobrecubiertaId());
|
sobrecubierta.put("papel", presupuesto.getPapelSobrecubiertaId());
|
||||||
@ -1020,6 +1028,7 @@ public class PresupuestoService {
|
|||||||
resumen.put("iva_importe_4", presupuesto.getIvaImporte4());
|
resumen.put("iva_importe_4", presupuesto.getIvaImporte4());
|
||||||
resumen.put("iva_importe_21", presupuesto.getIvaImporte21());
|
resumen.put("iva_importe_21", presupuesto.getIvaImporte21());
|
||||||
resumen.put("total_con_iva", presupuesto.getTotalConIva());
|
resumen.put("total_con_iva", presupuesto.getTotalConIva());
|
||||||
|
resumen.put("isReimpresion", presupuesto.getIsReimpresion());
|
||||||
|
|
||||||
return resumen;
|
return resumen;
|
||||||
}
|
}
|
||||||
@ -1143,6 +1152,7 @@ public class PresupuestoService {
|
|||||||
if(presupuesto.getSelectedTirada() != null && presupuesto.getSelectedTirada().equals(tirada))
|
if(presupuesto.getSelectedTirada() != null && presupuesto.getSelectedTirada().equals(tirada))
|
||||||
presupuesto.setServiciosJson(new ObjectMapper().writeValueAsString(servicios));
|
presupuesto.setServiciosJson(new ObjectMapper().writeValueAsString(servicios));
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
|
System.out.println("Error guardando servicios JSON: " + ignore.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1222,6 +1232,18 @@ public class PresupuestoService {
|
|||||||
HashMap<String, Object> result = new HashMap<>();
|
HashMap<String, Object> result = new HashMap<>();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
Presupuesto presupuestoExistente = null;
|
||||||
|
if (id != null) {
|
||||||
|
presupuestoExistente = presupuestoRepository.findById(id).orElse(null);
|
||||||
|
}
|
||||||
|
if (presupuestoExistente != null) {
|
||||||
|
// merge de datos que no están en el formulario
|
||||||
|
presupuesto.setIsReimpresion(presupuestoExistente.getIsReimpresion());
|
||||||
|
presupuesto.setProveedor(presupuestoExistente.getProveedor());
|
||||||
|
presupuesto.setProveedorRef1(presupuestoExistente.getProveedorRef1());
|
||||||
|
presupuesto.setProveedorRef2(presupuestoExistente.getProveedorRef2());
|
||||||
|
}
|
||||||
|
|
||||||
presupuesto.setDatosMaquetacionJson(
|
presupuesto.setDatosMaquetacionJson(
|
||||||
datosMaquetacion != null ? new ObjectMapper().writeValueAsString(datosMaquetacion) : null);
|
datosMaquetacion != null ? new ObjectMapper().writeValueAsString(datosMaquetacion) : null);
|
||||||
presupuesto.setDatosMarcapaginasJson(
|
presupuesto.setDatosMarcapaginasJson(
|
||||||
@ -1310,6 +1332,93 @@ public class PresupuestoService {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean hasMaquetacion(Presupuesto presupuesto) {
|
||||||
|
if (presupuesto.getServiciosJson() != null && !presupuesto.getServiciosJson().isEmpty()) {
|
||||||
|
if(presupuesto.getServiciosJson().contains("maquetacion")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getPresupuestoInfoForCard(Presupuesto presupuesto, Locale locale) {
|
||||||
|
|
||||||
|
Map<String, Object> resumen = new HashMap<>();
|
||||||
|
|
||||||
|
resumen.put("titulo", presupuesto.getTitulo());
|
||||||
|
|
||||||
|
resumen.put("imagen",
|
||||||
|
"/assets/images/imprimelibros/presupuestador/" + presupuesto.getTipoEncuadernacion() + ".png");
|
||||||
|
resumen.put("imagen_alt",
|
||||||
|
messageSource.getMessage("presupuesto." + presupuesto.getTipoEncuadernacion(), null, locale));
|
||||||
|
|
||||||
|
resumen.put("presupuestoId", presupuesto.getId());
|
||||||
|
|
||||||
|
if (presupuesto.getServiciosJson() != null && presupuesto.getServiciosJson().contains("ejemplar-prueba")) {
|
||||||
|
resumen.put("hasSample", true);
|
||||||
|
} else {
|
||||||
|
resumen.put("hasSample", false);
|
||||||
|
}
|
||||||
|
Map<String, Object> detalles = utils.getTextoPresupuesto(presupuesto, locale);
|
||||||
|
|
||||||
|
resumen.put("tirada", presupuesto.getSelectedTirada());
|
||||||
|
|
||||||
|
resumen.put("baseTotal", Utils.formatCurrency(presupuesto.getBaseImponible(), locale));
|
||||||
|
resumen.put("base", presupuesto.getBaseImponible());
|
||||||
|
resumen.put("iva4", presupuesto.getIvaImporte4());
|
||||||
|
resumen.put("iva21", presupuesto.getIvaImporte21());
|
||||||
|
resumen.put("total", Utils.formatCurrency(presupuesto.getTotalConIva(), locale));
|
||||||
|
|
||||||
|
resumen.put("resumen", detalles);
|
||||||
|
|
||||||
|
return resumen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Presupuesto findPresupuestoById(Long id) {
|
||||||
|
return presupuestoRepository.findById(id).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateComentario(Long presupuestoId, String comentario) {
|
||||||
|
Presupuesto presupuesto = presupuestoRepository.findById(presupuestoId).orElse(null);
|
||||||
|
if (presupuesto != null) {
|
||||||
|
presupuesto.setComentario(comentario);
|
||||||
|
presupuestoRepository.saveAndFlush(presupuesto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long duplicarPresupuesto(Long presupuestoId, String titulo) {
|
||||||
|
|
||||||
|
Presupuesto presupuesto = presupuestoRepository.findById(presupuestoId).orElse(null);
|
||||||
|
if (presupuesto != null) {
|
||||||
|
Presupuesto nuevo = presupuesto.clone();
|
||||||
|
nuevo.setId(null); // para que se genere uno nuevo
|
||||||
|
nuevo.setEstado(Presupuesto.Estado.borrador);
|
||||||
|
nuevo.setTitulo(titulo != null && !titulo.isEmpty() ? titulo : "[D] " + presupuesto.getTitulo());
|
||||||
|
nuevo.setIsReimpresion(false);
|
||||||
|
nuevo.setProveedor(null);
|
||||||
|
nuevo.setProveedorRef1(null);
|
||||||
|
nuevo.setProveedorRef2(null);
|
||||||
|
presupuestoRepository.saveAndFlush(nuevo);
|
||||||
|
return nuevo.getId();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long reimprimirPresupuesto(Long presupuestoId) {
|
||||||
|
|
||||||
|
Presupuesto presupuesto = presupuestoRepository.findById(presupuestoId).orElse(null);
|
||||||
|
if (presupuesto != null) {
|
||||||
|
Presupuesto nuevo = presupuesto.clone();
|
||||||
|
nuevo.setId(null); // para que se genere uno nuevo
|
||||||
|
nuevo.setEstado(Presupuesto.Estado.borrador);
|
||||||
|
nuevo.setTitulo("[R] " + presupuesto.getTitulo());
|
||||||
|
nuevo.setIsReimpresion(true);
|
||||||
|
presupuestoRepository.saveAndFlush(nuevo);
|
||||||
|
return nuevo.getId();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// =======================================================================
|
// =======================================================================
|
||||||
// Métodos privados
|
// Métodos privados
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
package com.imprimelibros.erp.redsys;
|
package com.imprimelibros.erp.redsys;
|
||||||
|
|
||||||
|
import com.imprimelibros.erp.cart.Cart;
|
||||||
import com.imprimelibros.erp.common.Utils;
|
import com.imprimelibros.erp.common.Utils;
|
||||||
import com.imprimelibros.erp.payments.PaymentService;
|
import com.imprimelibros.erp.payments.PaymentService;
|
||||||
import com.imprimelibros.erp.payments.model.Payment;
|
import com.imprimelibros.erp.payments.model.Payment;
|
||||||
|
import com.imprimelibros.erp.payments.repo.PaymentTransactionRepository;
|
||||||
|
import com.imprimelibros.erp.pedidos.Pedido;
|
||||||
|
import com.imprimelibros.erp.pedidos.PedidoService;
|
||||||
import com.imprimelibros.erp.redsys.RedsysService.FormPayload;
|
import com.imprimelibros.erp.redsys.RedsysService.FormPayload;
|
||||||
|
|
||||||
|
import groovy.util.logging.Log;
|
||||||
import jakarta.servlet.ServletContext;
|
import jakarta.servlet.ServletContext;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
@ -27,6 +32,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
|||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@ -37,26 +43,37 @@ public class RedsysController {
|
|||||||
private final MessageSource messageSource;
|
private final MessageSource messageSource;
|
||||||
private final SpringTemplateEngine templateEngine;
|
private final SpringTemplateEngine templateEngine;
|
||||||
private final ServletContext servletContext;
|
private final ServletContext servletContext;
|
||||||
|
private final PedidoService pedidoService;
|
||||||
|
|
||||||
public RedsysController(PaymentService paymentService, MessageSource messageSource,
|
public RedsysController(PaymentService paymentService, MessageSource messageSource,
|
||||||
SpringTemplateEngine templateEngine, ServletContext servletContext) {
|
SpringTemplateEngine templateEngine, ServletContext servletContext,
|
||||||
|
PedidoService pedidoService) {
|
||||||
this.paymentService = paymentService;
|
this.paymentService = paymentService;
|
||||||
this.messageSource = messageSource;
|
this.messageSource = messageSource;
|
||||||
this.templateEngine = templateEngine;
|
this.templateEngine = templateEngine;
|
||||||
this.servletContext = servletContext;
|
this.servletContext = servletContext;
|
||||||
|
this.pedidoService = pedidoService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/crear", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
@PostMapping(value = "/crear", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<byte[]> crearPago(@RequestParam("amountCents") Long amountCents,
|
public ResponseEntity<byte[]> crearPago(@RequestParam("amountCents") Long amountCents,
|
||||||
@RequestParam("method") String method, @RequestParam("cartId") Long cartId,
|
@RequestParam("method") String method, @RequestParam("cartId") Long cartId,
|
||||||
|
@RequestParam(value = "dirFactId", required = false) Long dirFactId,
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response, Locale locale)
|
HttpServletResponse response, Locale locale)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
|
// Creamos el pedido inteno
|
||||||
|
Pedido order = pedidoService.crearPedido(cartId, dirFactId, null, null);
|
||||||
|
|
||||||
if ("bank-transfer".equalsIgnoreCase(method)) {
|
if ("bank-transfer".equalsIgnoreCase(method)) {
|
||||||
|
|
||||||
// 1) Creamos el Payment interno SIN orderId (null)
|
// 1) Creamos el Payment interno SIN orderId (null)
|
||||||
Payment p = paymentService.createBankTransferPayment(cartId, amountCents, "EUR");
|
Payment p = paymentService.createBankTransferPayment(cartId, dirFactId, amountCents, "EUR", locale,
|
||||||
|
order.getId());
|
||||||
|
|
||||||
|
pedidoService.markPedidoAsProcesingPayment(order.getId());
|
||||||
|
|
||||||
// 1️⃣ Crear la "aplicación" web de Thymeleaf (Jakarta)
|
// 1️⃣ Crear la "aplicación" web de Thymeleaf (Jakarta)
|
||||||
JakartaServletWebApplication app = JakartaServletWebApplication.buildApplication(servletContext);
|
JakartaServletWebApplication app = JakartaServletWebApplication.buildApplication(servletContext);
|
||||||
@ -88,7 +105,104 @@ public class RedsysController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tarjeta o Bizum (Redsys)
|
// Tarjeta o Bizum (Redsys)
|
||||||
FormPayload form = paymentService.createRedsysPayment(cartId, amountCents, "EUR", method);
|
FormPayload form = paymentService.createRedsysPayment(cartId, dirFactId, amountCents, "EUR", method,
|
||||||
|
order.getId());
|
||||||
|
|
||||||
|
String html = """
|
||||||
|
<html><head><meta charset="utf-8"><title>Redirigiendo a Redsys…</title></head>
|
||||||
|
<body onload="document.forms[0].submit()">
|
||||||
|
<form action="%s" method="post">
|
||||||
|
<input type="hidden" name="Ds_SignatureVersion" value="%s"/>
|
||||||
|
<input type="hidden" name="Ds_MerchantParameters" value="%s"/>
|
||||||
|
<input type="hidden" name="Ds_Signature" value="%s"/>
|
||||||
|
<input type="hidden" name="cartId" value="%d"/>
|
||||||
|
<noscript>
|
||||||
|
<p>Haz clic en pagar para continuar</p>
|
||||||
|
<button type="submit">Pagar</button>
|
||||||
|
</noscript>
|
||||||
|
</form>
|
||||||
|
</body></html>
|
||||||
|
""".formatted(
|
||||||
|
form.action(),
|
||||||
|
form.signatureVersion(),
|
||||||
|
form.merchantParameters(),
|
||||||
|
form.signature(), cartId);
|
||||||
|
|
||||||
|
byte[] body = html.getBytes(StandardCharsets.UTF_8);
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.contentType(MediaType.TEXT_HTML)
|
||||||
|
.body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/reintentar", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity<byte[]> reintentarPago(@RequestParam("amountCents") Long amountCents,
|
||||||
|
@RequestParam("method") String method, @RequestParam("orderId") Long orderId,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response, Locale locale)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
// Creamos el pedido inteno
|
||||||
|
Pedido order = pedidoService.findById(orderId);
|
||||||
|
|
||||||
|
// Find the payment with orderId = order.getId() and status = failed
|
||||||
|
Payment failedPayment = paymentService.findFailedPaymentByOrderId(order.getId());
|
||||||
|
if (failedPayment == null) {
|
||||||
|
throw new Exception("No se encontró un pago fallido para el pedido " + order.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
Long cartId = null;
|
||||||
|
Long dirFactId = null;
|
||||||
|
// Find payment transaction details from failedPayment if needed
|
||||||
|
try {
|
||||||
|
Map<String, Long> transactionDetails = paymentService.getPaymentTransactionData(failedPayment.getId());
|
||||||
|
cartId = transactionDetails.get("cartId");
|
||||||
|
dirFactId = transactionDetails.get("dirFactId");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new Exception(
|
||||||
|
"No se pudieron obtener los detalles de la transacción para el pago " + failedPayment.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("bank-transfer".equalsIgnoreCase(method)) {
|
||||||
|
|
||||||
|
// 1) Creamos el Payment interno SIN orderId (null)
|
||||||
|
Payment p = paymentService.createBankTransferPayment(cartId, dirFactId, amountCents, "EUR", locale,
|
||||||
|
order.getId());
|
||||||
|
|
||||||
|
pedidoService.markPedidoAsProcesingPayment(order.getId());
|
||||||
|
|
||||||
|
// 1️⃣ Crear la "aplicación" web de Thymeleaf (Jakarta)
|
||||||
|
JakartaServletWebApplication app = JakartaServletWebApplication.buildApplication(servletContext);
|
||||||
|
|
||||||
|
// 2️⃣ Construir el intercambio web desde request/response
|
||||||
|
response.setContentType("text/html;charset=UTF-8");
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
IWebExchange exchange = app.buildExchange(request, response);
|
||||||
|
|
||||||
|
// 3️⃣ Crear el contexto WebContext con Locale
|
||||||
|
WebContext ctx = new WebContext(exchange, locale);
|
||||||
|
|
||||||
|
String importeFormateado = Utils.formatCurrency(amountCents / 100.0, locale);
|
||||||
|
ctx.setVariable("importe", importeFormateado);
|
||||||
|
ctx.setVariable("concepto", "TRANSF-" + p.getOrderId());
|
||||||
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
boolean isAuth = auth != null
|
||||||
|
&& auth.isAuthenticated()
|
||||||
|
&& !(auth instanceof AnonymousAuthenticationToken);
|
||||||
|
ctx.setVariable("isAuth", isAuth);
|
||||||
|
|
||||||
|
// 3) Renderizamos la plantilla a HTML
|
||||||
|
String html = templateEngine.process("imprimelibros/pagos/transfer", ctx);
|
||||||
|
|
||||||
|
byte[] body = html.getBytes(StandardCharsets.UTF_8);
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.contentType(MediaType.TEXT_HTML)
|
||||||
|
.body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tarjeta o Bizum (Redsys)
|
||||||
|
FormPayload form = paymentService.createRedsysPayment(cartId, dirFactId, amountCents, "EUR", method,
|
||||||
|
order.getId());
|
||||||
|
|
||||||
String html = """
|
String html = """
|
||||||
<html><head><meta charset="utf-8"><title>Redirigiendo a Redsys…</title></head>
|
<html><head><meta charset="utf-8"><title>Redirigiendo a Redsys…</title></head>
|
||||||
|
|||||||
@ -49,7 +49,7 @@ public class RedsysService {
|
|||||||
|
|
||||||
// ---------- RECORDS ----------
|
// ---------- RECORDS ----------
|
||||||
// Pedido a Redsys
|
// Pedido a Redsys
|
||||||
public record PaymentRequest(String order, long amountCents, String description, Long cartId) {
|
public record PaymentRequest(String order, long amountCents, String description, Long cartId, Long dirFactId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payload para el formulario
|
// Payload para el formulario
|
||||||
@ -84,7 +84,10 @@ public class RedsysService {
|
|||||||
// Si tu PaymentRequest no lo lleva todavía, puedes pasarlo en description o
|
// Si tu PaymentRequest no lo lleva todavía, puedes pasarlo en description o
|
||||||
// crear otro campo.
|
// crear otro campo.
|
||||||
JSONObject ctx = new JSONObject();
|
JSONObject ctx = new JSONObject();
|
||||||
ctx.put("cartId", req.cartId()); // o req.cartId() si decides añadirlo al record
|
ctx.put("cartId", req.cartId());
|
||||||
|
if (req.dirFactId() != null) {
|
||||||
|
ctx.put("dirFactId", req.dirFactId());
|
||||||
|
}
|
||||||
api.setParameter("DS_MERCHANT_MERCHANTDATA", ctx.toString());
|
api.setParameter("DS_MERCHANT_MERCHANTDATA", ctx.toString());
|
||||||
|
|
||||||
if (req.description() != null && !req.description().isBlank()) {
|
if (req.description() != null && !req.description().isBlank()) {
|
||||||
@ -195,6 +198,7 @@ public class RedsysService {
|
|||||||
public final long amountCents;
|
public final long amountCents;
|
||||||
public final String currency;
|
public final String currency;
|
||||||
public final Long cartId;
|
public final Long cartId;
|
||||||
|
public final Long dirFactId;
|
||||||
public final String processedPayMethod; // Ds_ProcessedPayMethod
|
public final String processedPayMethod; // Ds_ProcessedPayMethod
|
||||||
public final String bizumIdOper; // Ds_Bizum_IdOper
|
public final String bizumIdOper; // Ds_Bizum_IdOper
|
||||||
public final String authorisationCode; // Ds_AuthorisationCode
|
public final String authorisationCode; // Ds_AuthorisationCode
|
||||||
@ -206,6 +210,7 @@ public class RedsysService {
|
|||||||
this.currency = str(raw.get("Ds_Currency"));
|
this.currency = str(raw.get("Ds_Currency"));
|
||||||
this.amountCents = parseLongSafe(raw.get("Ds_Amount"));
|
this.amountCents = parseLongSafe(raw.get("Ds_Amount"));
|
||||||
this.cartId = extractCartId(raw.get("Ds_MerchantData"));
|
this.cartId = extractCartId(raw.get("Ds_MerchantData"));
|
||||||
|
this.dirFactId = extractDirFactId(raw.get("Ds_MerchantData"));
|
||||||
this.processedPayMethod = str(raw.get("Ds_ProcessedPayMethod"));
|
this.processedPayMethod = str(raw.get("Ds_ProcessedPayMethod"));
|
||||||
this.bizumIdOper = str(raw.get("Ds_Bizum_IdOper"));
|
this.bizumIdOper = str(raw.get("Ds_Bizum_IdOper"));
|
||||||
this.authorisationCode = str(raw.get("Ds_AuthorisationCode"));
|
this.authorisationCode = str(raw.get("Ds_AuthorisationCode"));
|
||||||
@ -228,6 +233,24 @@ public class RedsysService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Long extractDirFactId(Object merchantDataObj) {
|
||||||
|
if (merchantDataObj == null)
|
||||||
|
return null;
|
||||||
|
try {
|
||||||
|
String json = String.valueOf(merchantDataObj);
|
||||||
|
|
||||||
|
// 👇 DES-ESCAPAR las comillas HTML que vienen de Redsys
|
||||||
|
json = json.replace(""", "\"");
|
||||||
|
|
||||||
|
org.json.JSONObject ctx = new org.json.JSONObject(json);
|
||||||
|
long v = ctx.optLong("dirFactId", 0L);
|
||||||
|
return v != 0L ? v : null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(); // te ayudará si vuelve a fallar
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean authorized() {
|
public boolean authorized() {
|
||||||
try {
|
try {
|
||||||
int r = Integer.parseInt(response);
|
int r = Integer.parseInt(response);
|
||||||
|
|||||||
@ -17,4 +17,5 @@ public interface UserService extends UserDetailsService {
|
|||||||
* @return página de usuarios
|
* @return página de usuarios
|
||||||
*/
|
*/
|
||||||
Page<User> findByRoleAndSearch(String role, String query, Pageable pageable);
|
Page<User> findByRoleAndSearch(String role, String query, Pageable pageable);
|
||||||
|
User findById(Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,4 +31,8 @@ public class UserServiceImpl implements UserService {
|
|||||||
if (query == null || query.isBlank()) query = null;
|
if (query == null || query.isBlank()) query = null;
|
||||||
return userDao.searchUsers(role, query, pageable);
|
return userDao.searchUsers(role, query, pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public User findById(Long id) {
|
||||||
|
return userDao.findById(id).orElse(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,151 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 0014-create-pedidos-direcciones
|
||||||
|
author: jjo
|
||||||
|
changes:
|
||||||
|
- createTable:
|
||||||
|
tableName: pedidos_direcciones
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
name: id
|
||||||
|
type: BIGINT AUTO_INCREMENT
|
||||||
|
constraints:
|
||||||
|
primaryKey: true
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: pedido_linea_id
|
||||||
|
type: BIGINT
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: pedido_id
|
||||||
|
type: BIGINT
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: unidades
|
||||||
|
type: MEDIUMINT UNSIGNED
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: is_facturacion
|
||||||
|
type: TINYINT(1)
|
||||||
|
defaultValueNumeric: 0
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: is_ejemplar_prueba
|
||||||
|
type: TINYINT(1)
|
||||||
|
defaultValueNumeric: 0
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: att
|
||||||
|
type: VARCHAR(150)
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: direccion
|
||||||
|
type: VARCHAR(255)
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: cp
|
||||||
|
type: MEDIUMINT UNSIGNED
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: ciudad
|
||||||
|
type: VARCHAR(100)
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: provincia
|
||||||
|
type: VARCHAR(100)
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: pais_code3
|
||||||
|
type: CHAR(3)
|
||||||
|
defaultValue: esp
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: telefono
|
||||||
|
type: VARCHAR(30)
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: instrucciones
|
||||||
|
type: VARCHAR(255)
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: razon_social
|
||||||
|
type: VARCHAR(150)
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: tipo_identificacion_fiscal
|
||||||
|
type: ENUM('DNI','NIE','CIF','Pasaporte','VAT_ID')
|
||||||
|
defaultValue: DNI
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: identificacion_fiscal
|
||||||
|
type: VARCHAR(50)
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: created_at
|
||||||
|
type: TIMESTAMP
|
||||||
|
defaultValueComputed: CURRENT_TIMESTAMP
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
- addForeignKeyConstraint:
|
||||||
|
baseTableName: pedidos_direcciones
|
||||||
|
baseColumnNames: pedido_linea_id
|
||||||
|
referencedTableName: pedidos_lineas
|
||||||
|
referencedColumnNames: id
|
||||||
|
constraintName: fk_pedidos_direcciones_pedido_linea
|
||||||
|
onDelete: SET NULL
|
||||||
|
onUpdate: CASCADE
|
||||||
|
|
||||||
|
- addForeignKeyConstraint:
|
||||||
|
baseTableName: pedidos_direcciones
|
||||||
|
baseColumnNames: pedido_id
|
||||||
|
referencedTableName: pedidos
|
||||||
|
referencedColumnNames: id
|
||||||
|
constraintName: fk_pedidos_direcciones_pedidos
|
||||||
|
onDelete: SET NULL
|
||||||
|
onUpdate: CASCADE
|
||||||
|
|
||||||
|
- createIndex:
|
||||||
|
tableName: pedidos_direcciones
|
||||||
|
indexName: idx_pedidos_direcciones_pedido_linea_id
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
name: pedido_linea_id
|
||||||
|
|
||||||
|
rollback:
|
||||||
|
- dropTable:
|
||||||
|
tableName: pedidos_direcciones
|
||||||
|
cascadeConstraints: true
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 0015-alter-pedidos-lineas-and-presupuesto-estados
|
||||||
|
author: jjo
|
||||||
|
changes:
|
||||||
|
# Añadir columnas a pedidos_lineas
|
||||||
|
- addColumn:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
name: estado
|
||||||
|
type: "ENUM('aprobado','maquetación','haciendo_ferro','producción','terminado','cancelado')"
|
||||||
|
defaultValue: aprobado
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
afterColumn: presupuesto_id
|
||||||
|
|
||||||
|
- column:
|
||||||
|
name: estado_manual
|
||||||
|
type: TINYINT(1)
|
||||||
|
defaultValueNumeric: 0
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
afterColumn: estado
|
||||||
|
|
||||||
|
# Añadir columna a presupuesto
|
||||||
|
- addColumn:
|
||||||
|
tableName: presupuesto
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
name: is_reimpresion
|
||||||
|
type: TINYINT(1)
|
||||||
|
defaultValueNumeric: 0
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
|
||||||
|
rollback:
|
||||||
|
- dropColumn:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
|
||||||
|
- dropColumn:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado_manual
|
||||||
|
|
||||||
|
- dropColumn:
|
||||||
|
tableName: presupuesto
|
||||||
|
columnName: is_reimpresion
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 0016-fix-enum-estado-pedidos-lineas
|
||||||
|
author: jjo
|
||||||
|
changes:
|
||||||
|
|
||||||
|
# 1) Convertir valores existentes "maquetación" → "maquetacion"
|
||||||
|
- update:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
name: estado
|
||||||
|
value: "maquetacion"
|
||||||
|
where: "estado = 'maquetación'"
|
||||||
|
|
||||||
|
# 2) Cambiar ENUM quitando tilde
|
||||||
|
- modifyDataType:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
newDataType: "ENUM('aprobado','maquetacion','haciendo_ferro','producción','terminado','cancelado')"
|
||||||
|
|
||||||
|
rollback:
|
||||||
|
|
||||||
|
# 1) Volver a convertir "maquetacion" → "maquetación"
|
||||||
|
- update:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
name: estado
|
||||||
|
value: "maquetación"
|
||||||
|
where: "estado = 'maquetacion'"
|
||||||
|
|
||||||
|
# 2) Restaurar ENUM original
|
||||||
|
- modifyDataType:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
newDataType: "ENUM('aprobado','maquetación','haciendo_ferro','producción','terminado','cancelado')"
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: add-fecha-entrega-to-pedidos-lineas
|
||||||
|
author: jjo
|
||||||
|
changes:
|
||||||
|
- addColumn:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
name: fecha_entrega
|
||||||
|
type: datetime
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
afterColumn: estado_manual
|
||||||
|
|
||||||
|
rollback:
|
||||||
|
- dropColumn:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: fecha_entrega
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 0018-change-presupuesto-ch-3
|
||||||
|
author: jjo
|
||||||
|
preConditions:
|
||||||
|
- dbms:
|
||||||
|
type: mysql
|
||||||
|
|
||||||
|
changes:
|
||||||
|
- sql:
|
||||||
|
splitStatements: false
|
||||||
|
stripComments: true
|
||||||
|
sql: |
|
||||||
|
ALTER TABLE presupuesto
|
||||||
|
DROP CHECK presupuesto_chk_3;
|
||||||
|
|
||||||
|
rollback:
|
||||||
|
- sql:
|
||||||
|
splitStatements: false
|
||||||
|
stripComments: true
|
||||||
|
sql: |
|
||||||
|
ALTER TABLE presupuesto
|
||||||
|
ADD CONSTRAINT presupuesto_chk_3
|
||||||
|
CHECK (tipo_cubierta BETWEEN 0 AND 2);
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 0019-add-estados-pago-to-pedidos-lineas
|
||||||
|
author: jjo
|
||||||
|
changes:
|
||||||
|
- modifyDataType:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
newDataType: >
|
||||||
|
enum(
|
||||||
|
'pendiente_pago',
|
||||||
|
'procesando_pago',
|
||||||
|
'aprobado',
|
||||||
|
'maquetacion',
|
||||||
|
'haciendo_ferro',
|
||||||
|
'produccion',
|
||||||
|
'terminado',
|
||||||
|
'cancelado'
|
||||||
|
)
|
||||||
|
rollback:
|
||||||
|
- modifyDataType:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
newDataType: >
|
||||||
|
enum(
|
||||||
|
'aprobado',
|
||||||
|
'maquetacion',
|
||||||
|
'haciendo_ferro',
|
||||||
|
'produccion',
|
||||||
|
'terminado',
|
||||||
|
'cancelado'
|
||||||
|
)
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 0020-add-estados-pago-to-pedidos-lineas-2
|
||||||
|
author: jjo
|
||||||
|
changes:
|
||||||
|
- modifyDataType:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
newDataType: >
|
||||||
|
enum(
|
||||||
|
'pendiente_pago',
|
||||||
|
'procesando_pago',
|
||||||
|
'denegado_pago',
|
||||||
|
'aprobado',
|
||||||
|
'maquetacion',
|
||||||
|
'haciendo_ferro',
|
||||||
|
'produccion',
|
||||||
|
'terminado',
|
||||||
|
'cancelado'
|
||||||
|
)
|
||||||
|
rollback:
|
||||||
|
- modifyDataType:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
newDataType: >
|
||||||
|
enum(
|
||||||
|
'pendiente_pago',
|
||||||
|
'procesando_pago',
|
||||||
|
'aprobado',
|
||||||
|
'maquetacion',
|
||||||
|
'haciendo_ferro',
|
||||||
|
'produccion',
|
||||||
|
'terminado',
|
||||||
|
'cancelado'
|
||||||
|
)
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 0021-add-email-and-is-palets-to-pedidos-direcciones
|
||||||
|
author: jjo
|
||||||
|
changes:
|
||||||
|
- sql:
|
||||||
|
dbms: mysql
|
||||||
|
splitStatements: false
|
||||||
|
stripComments: true
|
||||||
|
sql: >
|
||||||
|
ALTER TABLE pedidos_direcciones
|
||||||
|
ADD COLUMN is_palets TINYINT(1) NOT NULL DEFAULT 0 AFTER identificacion_fiscal,
|
||||||
|
ADD COLUMN email VARCHAR(255) NULL AFTER is_ejemplar_prueba;
|
||||||
|
|
||||||
|
rollback:
|
||||||
|
- sql:
|
||||||
|
dbms: mysql
|
||||||
|
splitStatements: false
|
||||||
|
stripComments: true
|
||||||
|
sql: >
|
||||||
|
ALTER TABLE pedidos_direcciones
|
||||||
|
DROP COLUMN is_palets,
|
||||||
|
DROP COLUMN email;
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 0022-add-estados-pago-to-pedidos-lineas-3
|
||||||
|
author: jjo
|
||||||
|
changes:
|
||||||
|
- modifyDataType:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
newDataType: >
|
||||||
|
enum(
|
||||||
|
'pendiente_pago',
|
||||||
|
'procesando_pago',
|
||||||
|
'denegado_pago',
|
||||||
|
'aprobado',
|
||||||
|
'maquetacion',
|
||||||
|
'haciendo_ferro',
|
||||||
|
'esperando_aceptacion_ferro',
|
||||||
|
'produccion',
|
||||||
|
'terminado',
|
||||||
|
'cancelado'
|
||||||
|
)
|
||||||
|
rollback:
|
||||||
|
- modifyDataType:
|
||||||
|
tableName: pedidos_lineas
|
||||||
|
columnName: estado
|
||||||
|
newDataType: >
|
||||||
|
enum(
|
||||||
|
'pendiente_pago',
|
||||||
|
'procesando_pago',
|
||||||
|
'denegado_pago',
|
||||||
|
'aprobado',
|
||||||
|
'maquetacion',
|
||||||
|
'haciendo_ferro',
|
||||||
|
'produccion',
|
||||||
|
'terminado',
|
||||||
|
'cancelado'
|
||||||
|
)
|
||||||
@ -24,4 +24,22 @@ databaseChangeLog:
|
|||||||
- include:
|
- include:
|
||||||
file: db/changelog/changesets/0012--drop-unique-gateway-txid-2.yml
|
file: db/changelog/changesets/0012--drop-unique-gateway-txid-2.yml
|
||||||
- include:
|
- include:
|
||||||
file: db/changelog/changesets/0013-drop-unique-refund-gateway-id.yml
|
file: db/changelog/changesets/0013-drop-unique-refund-gateway-id.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0014-create-pedidos-direcciones.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0015-alter-pedidos-lineas-and-presupuesto-estados.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0016-fix-enum-estado-pedidos-lineas.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0017-add-fecha-entrega-to-pedidos-lineas.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0018-change-presupuesto-ch-3.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0019-add-estados-pago-to-pedidos-lineas.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0020-add-estados-pago-to-pedidos-lineas-2.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0021-add-email-and-is-palets-to-pedidos-direcciones.yml
|
||||||
|
- include:
|
||||||
|
file: db/changelog/changesets/0022-add-estados-pago-to-pedidos-lineas-3.yml
|
||||||
@ -10,6 +10,8 @@ app.add=Añadir
|
|||||||
app.back=Volver
|
app.back=Volver
|
||||||
app.eliminar=Eliminar
|
app.eliminar=Eliminar
|
||||||
app.imprimir=Imprimir
|
app.imprimir=Imprimir
|
||||||
|
app.view=Ver
|
||||||
|
app.pay=Pagar
|
||||||
app.acciones.siguiente=Siguiente
|
app.acciones.siguiente=Siguiente
|
||||||
app.acciones.anterior=Anterior
|
app.acciones.anterior=Anterior
|
||||||
|
|
||||||
@ -20,6 +22,7 @@ app.logout=Cerrar sesión
|
|||||||
|
|
||||||
app.sidebar.inicio=Inicio
|
app.sidebar.inicio=Inicio
|
||||||
app.sidebar.presupuestos=Presupuestos
|
app.sidebar.presupuestos=Presupuestos
|
||||||
|
app.sidebar.pedidos=Pedidos
|
||||||
app.sidebar.configuracion=Configuración
|
app.sidebar.configuracion=Configuración
|
||||||
app.sidebar.usuarios=Usuarios
|
app.sidebar.usuarios=Usuarios
|
||||||
app.sidebar.direcciones=Mis Direcciones
|
app.sidebar.direcciones=Mis Direcciones
|
||||||
|
|||||||
@ -35,6 +35,8 @@ direcciones.pasaporte=Pasaporte
|
|||||||
direcciones.cif=C.I.F.
|
direcciones.cif=C.I.F.
|
||||||
direcciones.vat_id=VAT ID
|
direcciones.vat_id=VAT ID
|
||||||
|
|
||||||
|
direcciones.direccionFacturacion=Dirección de facturación
|
||||||
|
|
||||||
direcciones.delete.title=Eliminar dirección
|
direcciones.delete.title=Eliminar dirección
|
||||||
direcciones.delete.button=Si, ELIMINAR
|
direcciones.delete.button=Si, ELIMINAR
|
||||||
direcciones.delete.text=¿Está seguro de que desea eliminar esta dirección?<br>Esta acción no se puede deshacer.
|
direcciones.delete.text=¿Está seguro de que desea eliminar esta dirección?<br>Esta acción no se puede deshacer.
|
||||||
|
|||||||
@ -27,6 +27,7 @@ pdf.datos-maquetacion=Datos de maquetación:
|
|||||||
pdf.datos-marcapaginas=Datos de marcapáginas:
|
pdf.datos-marcapaginas=Datos de marcapáginas:
|
||||||
|
|
||||||
pdf.incluye-envio=El presupuesto incluye el envío a una dirección de la península.
|
pdf.incluye-envio=El presupuesto incluye el envío a una dirección de la península.
|
||||||
|
pdf.presupuesto-validez=Validez del presupuesto: 30 días desde la fecha de emisión.
|
||||||
|
|
||||||
pdf.politica-privacidad=Política de privacidad
|
pdf.politica-privacidad=Política de privacidad
|
||||||
pdf.politica-privacidad.responsable=Responsable: Impresión Imprime Libros - CIF: B04998886 - Teléfono de contacto: 910052574
|
pdf.politica-privacidad.responsable=Responsable: Impresión Imprime Libros - CIF: B04998886 - Teléfono de contacto: 910052574
|
||||||
|
|||||||
@ -13,6 +13,54 @@ checkout.payment.bizum=Bizum
|
|||||||
checkout.payment.bank-transfer=Transferencia bancaria
|
checkout.payment.bank-transfer=Transferencia bancaria
|
||||||
checkout.error.payment=Error al procesar el pago: el pago ha sido cancelado o rechazado Por favor, inténtelo de nuevo.
|
checkout.error.payment=Error al procesar el pago: el pago ha sido cancelado o rechazado Por favor, inténtelo de nuevo.
|
||||||
checkout.success.payment=Pago realizado con éxito. Gracias por su compra.
|
checkout.success.payment=Pago realizado con éxito. Gracias por su compra.
|
||||||
|
checkout.error.select-method=Por favor, seleccione un método de pago.
|
||||||
|
|
||||||
checkout.make-payment=Realizar el pago
|
checkout.make-payment=Realizar el pago
|
||||||
checkout.authorization-required=Certifico que tengo los derechos para imprimir los archivos incluidos en mi pedido y me hago responsable en caso de reclamación de los mismos
|
checkout.authorization-required=Certifico que tengo los derechos para imprimir los archivos incluidos en mi pedido y me hago responsable en caso de reclamación de los mismos
|
||||||
|
|
||||||
|
pedido.estado.pendiente_pago=Pendiente de pago
|
||||||
|
pedido.estado.procesando_pago=Procesando pago
|
||||||
|
pedido.estado.denegado_pago=Pago denegado
|
||||||
|
pedido.estado.aprobado=Aprobado
|
||||||
|
pedido.estado.maquetacion=Maquetación
|
||||||
|
pedido.estado.haciendo_ferro=Haciendo ferro
|
||||||
|
pedido.estado.esperando_aceptacion_ferro=Esperando aceptación de ferro
|
||||||
|
pedido.estado.ferro_cliente=Esperando aprobación de ferro
|
||||||
|
pedido.estado.produccion=Producción
|
||||||
|
pedido.estado.terminado=Terminado
|
||||||
|
pedido.estado.cancelado=Cancelado
|
||||||
|
|
||||||
|
pedido.module-title=Pedidos
|
||||||
|
pedido.pedido=Pedido
|
||||||
|
pedido.fecha-entrega=Fecha de entrega
|
||||||
|
pedido.cancelar=Cancelar pedido
|
||||||
|
pedido.update-estado=Actualizar estado
|
||||||
|
pedido.maquetacion_finalizada=Maquetación finalizada
|
||||||
|
pedido.ferro=Ferro
|
||||||
|
pedido.cubierta=Cubierta
|
||||||
|
pedido.tapa=Tapa
|
||||||
|
pedido.aceptar_ferro=Aceptar ferro
|
||||||
|
pedido.shipping-addresses=Direcciones de envío
|
||||||
|
pedido.prueba=Prueba
|
||||||
|
|
||||||
|
pedido.table.id=Num. Pedido
|
||||||
|
pedido.table.cliente=Cliente
|
||||||
|
pedido.table.fecha=Fecha
|
||||||
|
pedido.table.importe=Importe
|
||||||
|
pedido.table.estado=Estado
|
||||||
|
pedido.table.acciones=Acciones
|
||||||
|
|
||||||
|
pedido.view.tirada=Tirada
|
||||||
|
pedido.view.view-presupuesto=Ver presupuesto
|
||||||
|
pedido.view.aceptar-ferro=Aceptar ferro
|
||||||
|
pedido.view.ferro-download=Descargar ferro
|
||||||
|
pedido.view.cub-download=Descargar cubierta
|
||||||
|
pedido.view.tapa-download=Descargar tapa
|
||||||
|
|
||||||
|
pedido.errors.linea-not-found=No se ha encontrado la línea de pedido.
|
||||||
|
pedido.errors.state-error=Estado de línea no válido.
|
||||||
|
pedido.errors.update-server-error=Error al actualizar el estado desde el servidor externo.
|
||||||
|
pedido.errors.connecting-server-error=Error al conectar con el servidor externo.
|
||||||
|
pedido.errors.cannot-update=No se puede actualizar el estado de una línea con ese estado inicial.
|
||||||
|
pedido.success.estado-actualizado=Estado del pedido actualizado correctamente.
|
||||||
|
pedido.success.same-estado=Sin cambios en el estado.
|
||||||
@ -11,6 +11,12 @@ presupuesto.add-to-presupuesto=Añadir al presupuesto
|
|||||||
presupuesto.calcular=Calcular
|
presupuesto.calcular=Calcular
|
||||||
presupuesto.add=Añadir presupuesto
|
presupuesto.add=Añadir presupuesto
|
||||||
presupuesto.guardar=Guardar
|
presupuesto.guardar=Guardar
|
||||||
|
presupuesto.duplicar=Duplicar
|
||||||
|
presupuesto.reimprimir=Reimprimir
|
||||||
|
presupuesto.reimpresion=Reimpresión
|
||||||
|
presupuesto.editar=Editar
|
||||||
|
presupuesto.ver=Ver
|
||||||
|
presupuesto.borrar=Eliminar
|
||||||
presupuesto.add-to-cart=Añadir a la cesta
|
presupuesto.add-to-cart=Añadir a la cesta
|
||||||
|
|
||||||
presupuesto.nav.presupuestos-cliente=Presupuestos cliente
|
presupuesto.nav.presupuestos-cliente=Presupuestos cliente
|
||||||
@ -37,6 +43,8 @@ presupuesto.tabla.region=Región
|
|||||||
presupuesto.tabla.ciudad=Ciudad
|
presupuesto.tabla.ciudad=Ciudad
|
||||||
presupuesto.tabla.acciones=Acciones
|
presupuesto.tabla.acciones=Acciones
|
||||||
|
|
||||||
|
presupuesto.comentario-administrador=Comentarios
|
||||||
|
|
||||||
# Pestaña datos generales de presupuesto
|
# Pestaña datos generales de presupuesto
|
||||||
presupuesto.informacion-libro=Información del libro
|
presupuesto.informacion-libro=Información del libro
|
||||||
presupuesto.datos-generales-descripcion=Datos generales del presupuesto
|
presupuesto.datos-generales-descripcion=Datos generales del presupuesto
|
||||||
@ -130,6 +138,7 @@ presupuesto.papel-guardas=Papel de guardas
|
|||||||
presupuesto.guardas-impresas=Guardas impresas
|
presupuesto.guardas-impresas=Guardas impresas
|
||||||
presupuesto.no=No
|
presupuesto.no=No
|
||||||
presupuesto.cabezada=Cabezada
|
presupuesto.cabezada=Cabezada
|
||||||
|
presupuesto.cabezada-sin-cabezada=Sin cabezada
|
||||||
presupuesto.cabezada-blanca=Blanca
|
presupuesto.cabezada-blanca=Blanca
|
||||||
presupuesto.cabezada-verde=Verde
|
presupuesto.cabezada-verde=Verde
|
||||||
presupuesto.cabezada-azul=Azul
|
presupuesto.cabezada-azul=Azul
|
||||||
@ -294,6 +303,30 @@ presupuesto.error.delete-permission-denied=No se puede eliminar: permiso denegad
|
|||||||
presupuesto.error.delete-not-found=No se puede eliminar: presupuesto no encontrado.
|
presupuesto.error.delete-not-found=No se puede eliminar: presupuesto no encontrado.
|
||||||
presupuesto.error.delete-not-draft=Solo se pueden eliminar presupuestos en estado Borrador.
|
presupuesto.error.delete-not-draft=Solo se pueden eliminar presupuestos en estado Borrador.
|
||||||
|
|
||||||
|
# Mensajes de duplicar presupuesto
|
||||||
|
presupuesto.duplicar.title=Duplicar presupuesto
|
||||||
|
presupuesto.duplicar.confirm=Si, DUPLICAR
|
||||||
|
presupuesto.duplicar.cancelar=Cancelar
|
||||||
|
presupuesto.duplicar.text=¿Está seguro de que desea duplicar este presupuesto?<br>Se creará una copia exacta del mismo en estado Borrador con el título introducido a continuación.
|
||||||
|
presupuesto.duplicar.required=El título es obligatorio.
|
||||||
|
presupuesto.duplicar.success.title=Presupuesto duplicado
|
||||||
|
presupuesto.duplicar.success.text=El presupuesto ha sido duplicado con éxito.
|
||||||
|
presupuesto.duplicar.aceptar=Aceptar
|
||||||
|
presupuesto.duplicar.error.title=Error al duplicar presupuesto
|
||||||
|
presupuesto.duplicar.error.internal=No se puede duplicar: error interno.
|
||||||
|
|
||||||
|
# Mensajes de reimprimir presupuesto
|
||||||
|
presupuesto.reimprimir.title=Reimprimir presupuesto
|
||||||
|
presupuesto.reimprimir.confirm=Si, REIMPRIMIR
|
||||||
|
presupuesto.reimprimir.cancelar=Cancelar
|
||||||
|
presupuesto.reimprimir.text=¿Está seguro de que desea reimprimir este presupuesto?<br>Se generará una nuevo presupuesto usando los mismos ficheros para su impresión.
|
||||||
|
presupuesto.reimprimir.success.title=Presupuesto generado
|
||||||
|
presupuesto.reimprimir.success.text=El presupuesto ha sido generado con éxito.
|
||||||
|
presupuesto.reimprimir.aceptar=Aceptar
|
||||||
|
presupuesto.reimprimir.error.title=Error al generar el presupuesto
|
||||||
|
presupuesto.reimprimir.error.internal=No se puede generar el nuevo presupuesto: error interno.
|
||||||
|
|
||||||
|
|
||||||
# Añadir presupuesto
|
# Añadir presupuesto
|
||||||
presupuesto.add.tipo=Tipo de presupuesto
|
presupuesto.add.tipo=Tipo de presupuesto
|
||||||
presupuesto.add.anonimo=Anónimo
|
presupuesto.add.anonimo=Anónimo
|
||||||
|
|||||||
BIN
src/main/resources/static/assets/images/billin_address.gif
Normal file
BIN
src/main/resources/static/assets/images/billin_address.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 629 KiB |
BIN
src/main/resources/static/assets/images/billing_address2.gif
Normal file
BIN
src/main/resources/static/assets/images/billing_address2.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 986 KiB |
BIN
src/main/resources/static/assets/images/billing_address2.gif.gif
Normal file
BIN
src/main/resources/static/assets/images/billing_address2.gif.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 750 KiB |
BIN
src/main/resources/static/assets/images/delivery-truck.gif
Normal file
BIN
src/main/resources/static/assets/images/delivery-truck.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 847 KiB |
@ -21,6 +21,7 @@ $(() => {
|
|||||||
$(this).find('.direccion-id').attr('name', 'direcciones[' + i + '].id');
|
$(this).find('.direccion-id').attr('name', 'direcciones[' + i + '].id');
|
||||||
$(this).find('.direccion-cp').attr('name', 'direcciones[' + i + '].cp');
|
$(this).find('.direccion-cp').attr('name', 'direcciones[' + i + '].cp');
|
||||||
$(this).find('.direccion-pais-code3').attr('name', 'direcciones[' + i + '].paisCode3');
|
$(this).find('.direccion-pais-code3').attr('name', 'direcciones[' + i + '].paisCode3');
|
||||||
|
$(this).find('.is-palets').attr('name', 'direcciones[' + i + '].isPalets');
|
||||||
if ($(this).find('.presupuesto-id').length > 0 && $(this).find('.presupuesto-id').val() !== null
|
if ($(this).find('.presupuesto-id').length > 0 && $(this).find('.presupuesto-id').val() !== null
|
||||||
&& $(this).find('.presupuesto-id').val() !== "")
|
&& $(this).find('.presupuesto-id').val() !== "")
|
||||||
$(this).find('.presupuesto-id').attr('name', 'direcciones[' + i + '].presupuestoId');
|
$(this).find('.presupuesto-id').attr('name', 'direcciones[' + i + '].presupuestoId');
|
||||||
|
|||||||
@ -140,6 +140,7 @@ $(() => {
|
|||||||
let uri = `/checkout/get-address/${direccionId}`;
|
let uri = `/checkout/get-address/${direccionId}`;
|
||||||
const response = await fetch(uri);
|
const response = await fetch(uri);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
$('#dirFactId').val(direccionId);
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
$('#direccion-div').append(html);
|
$('#direccion-div').append(html);
|
||||||
$('#addBillingAddressBtn').addClass('d-none');
|
$('#addBillingAddressBtn').addClass('d-none');
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
import { normalizeNumericFilter } from '../utils.js';
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
|
||||||
|
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.getAttribute('content');
|
||||||
|
if (window.$ && csrfToken && csrfHeader) {
|
||||||
|
$.ajaxSetup({
|
||||||
|
beforeSend: function (xhr) {
|
||||||
|
xhr.setRequestHeader(csrfHeader, csrfToken);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const language = document.documentElement.lang || 'es-ES';
|
||||||
|
|
||||||
|
const tablePedidos = $('#pedidos-datatable').DataTable({
|
||||||
|
processing: true,
|
||||||
|
serverSide: true,
|
||||||
|
orderCellsTop: true,
|
||||||
|
pageLength: 50,
|
||||||
|
lengthMenu: [10, 25, 50, 100, 500],
|
||||||
|
order: [[5, 'desc']], // Ordena por fecha por defecto
|
||||||
|
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
|
||||||
|
responsive: true,
|
||||||
|
dom: 'lBrtip',
|
||||||
|
buttons: {
|
||||||
|
dom: {
|
||||||
|
button: {
|
||||||
|
className: 'btn btn-sm btn-outline-primary me-1'
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{ extend: 'copy' },
|
||||||
|
{ extend: 'csv' },
|
||||||
|
{ extend: 'excel' },
|
||||||
|
{ extend: 'pdf' },
|
||||||
|
{ extend: 'print' },
|
||||||
|
{ extend: 'colvis' }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ajax: {
|
||||||
|
url: '/pedidos/datatable',
|
||||||
|
method: 'GET',
|
||||||
|
},
|
||||||
|
order: [[0, 'desc']],
|
||||||
|
columns: [
|
||||||
|
{ data: 'id', name: 'id', orderable: true },
|
||||||
|
{ data: 'cliente', name: 'createdBy.fullName', orderable: true },
|
||||||
|
{ data: 'created_at', name: 'createdAt', orderable: true },
|
||||||
|
{ data: 'total', name: 'total', orderable: true },
|
||||||
|
{ data: 'estado', name: 'estado', orderable: true },
|
||||||
|
{ data: 'actions', name: 'actions', orderable: false, searchable: false }
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
tablePedidos.on("keyup change", ".input-filter", function () {
|
||||||
|
const colName = $(this).data("col");
|
||||||
|
const colIndex = tablePedidos.settings()[0].aoColumns.findIndex(c => c.name === colName);
|
||||||
|
|
||||||
|
if (colIndex >= 0) {
|
||||||
|
tablePedidos
|
||||||
|
.column(colIndex)
|
||||||
|
.search(normalizeNumericFilter(this.value))
|
||||||
|
.draw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
$(() => {
|
||||||
|
$(document).on('click', '.btn-view', function () {
|
||||||
|
let pedidoId = $(this).data('id');
|
||||||
|
let url = `/pedidos/view/${pedidoId}`;
|
||||||
|
window.location.href = url;
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.btn-pay', async function () {
|
||||||
|
const pedidoId = parseInt($(this).data('id'));
|
||||||
|
const amount = parseInt($(this).data('amount'));
|
||||||
|
|
||||||
|
const result = await swalMetodoPago();
|
||||||
|
if (!result.isConfirmed) return;
|
||||||
|
|
||||||
|
const method = result.value;
|
||||||
|
|
||||||
|
// crear y enviar un form normal (NO ajax)
|
||||||
|
const form = document.createElement('form');
|
||||||
|
form.method = 'POST';
|
||||||
|
form.action = '/pagos/redsys/reintentar';
|
||||||
|
|
||||||
|
// CSRF (Spring Security)
|
||||||
|
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
|
||||||
|
const csrfParam = document.querySelector('meta[name="_csrf_parameter"]')?.getAttribute('content') || '_csrf';
|
||||||
|
|
||||||
|
const add = (name, value) => {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'hidden';
|
||||||
|
input.name = name;
|
||||||
|
input.value = String(value);
|
||||||
|
form.appendChild(input);
|
||||||
|
};
|
||||||
|
|
||||||
|
add('amountCents', amount);
|
||||||
|
add('orderId', pedidoId);
|
||||||
|
add('method', method);
|
||||||
|
if (csrfToken) add(csrfParam, csrfToken);
|
||||||
|
|
||||||
|
document.body.appendChild(form);
|
||||||
|
form.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function swalMetodoPago() {
|
||||||
|
return Swal.fire({
|
||||||
|
title: window.languageBundle['checkout.payment'] || 'Método de pago',
|
||||||
|
width: '32rem',
|
||||||
|
html: `
|
||||||
|
<div style="width: 100%;" class="g-3 text-start">
|
||||||
|
|
||||||
|
<div class="form-check card-radio">
|
||||||
|
<input id="swalPaymentCard" name="paymentMethod" type="radio" class="form-check-input" value="card" checked>
|
||||||
|
<label class="form-check-label" for="swalPaymentCard">
|
||||||
|
${window.languageBundle['checkout.payment.card'] || 'Tarjeta'}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check card-radio">
|
||||||
|
<input id="swalPaymentBizum" name="paymentMethod" type="radio" class="form-check-input" value="bizum">
|
||||||
|
<label class="form-check-label" for="swalPaymentBizum">
|
||||||
|
${window.languageBundle['checkout.payment.bizum'] || 'Bizum'}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check card-radio">
|
||||||
|
<input id="swalPaymentTransfer" name="paymentMethod" type="radio" class="form-check-input" value="bank-transfer">
|
||||||
|
<label class="form-check-label" for="swalPaymentTransfer">
|
||||||
|
${window.languageBundle['checkout.payment.bank-transfer'] || 'Transferencia bancaria'}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
focusConfirm: false,
|
||||||
|
showCancelButton: true,
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2',
|
||||||
|
cancelButton: 'btn btn-light'
|
||||||
|
},
|
||||||
|
confirmButtonText: window.languageBundle['app.aceptar'] || 'Aceptar',
|
||||||
|
cancelButtonText: window.languageBundle['app.cancelar'] || 'Cancelar',
|
||||||
|
|
||||||
|
preConfirm: () => {
|
||||||
|
const selected = document.querySelector('input[name="paymentMethod"]:checked');
|
||||||
|
if (!selected) {
|
||||||
|
Swal.showValidationMessage(
|
||||||
|
window.languageBundle['checkout.error.select-method'] || 'Selecciona un método de pago'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return selected.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
@ -0,0 +1,146 @@
|
|||||||
|
$(() => {
|
||||||
|
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
|
||||||
|
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.getAttribute('content');
|
||||||
|
if (window.$ && csrfToken && csrfHeader) {
|
||||||
|
$.ajaxSetup({
|
||||||
|
beforeSend: function (xhr) {
|
||||||
|
xhr.setRequestHeader(csrfHeader, csrfToken);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const language = document.documentElement.lang || 'es-ES';
|
||||||
|
|
||||||
|
|
||||||
|
$(document).on('click', '.update-status-item', function () {
|
||||||
|
const lineaId = $(this).data('linea-id');
|
||||||
|
if (!lineaId) {
|
||||||
|
console.error('No se ha encontrado el ID de la línea del pedido.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Llamada AJAX para actualizar el estado del pedido
|
||||||
|
$.ajax({
|
||||||
|
url: `/pedidos/linea/${lineaId}/update-status`,
|
||||||
|
type: 'POST',
|
||||||
|
success: function (response) {
|
||||||
|
if (!response || !response.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: response.message || "Error",
|
||||||
|
timer: 1800,
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2',
|
||||||
|
cancelButton: 'btn btn-light'
|
||||||
|
},
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const estadoSpan = $(`.estado-linea[data-linea-id='${lineaId}']`);
|
||||||
|
if (estadoSpan.length) {
|
||||||
|
estadoSpan.text(response.state);
|
||||||
|
}
|
||||||
|
if (response.stateKey === 'terminado' || response.stateKey === 'cancelado') {
|
||||||
|
$(`.update-estado-button[data-linea-id='${lineaId}']`)
|
||||||
|
.closest('.update-estado-button')
|
||||||
|
.addClass('d-none');
|
||||||
|
}
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: response.message || "Exito",
|
||||||
|
timer: 1800,
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2',
|
||||||
|
cancelButton: 'btn btn-light'
|
||||||
|
},
|
||||||
|
showConfirmButton: false
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.dismiss === Swal.DismissReason.timer) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error('Error al actualizar el estado del pedido:', error);
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: xhr.responseJSON?.message || 'Error',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar
|
||||||
|
cancelButton: 'btn btn-light' // clases para cancelar
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.maquetacion-ok', function () {
|
||||||
|
|
||||||
|
const lineaId = $(this).data('linea-id');
|
||||||
|
if (!lineaId) {
|
||||||
|
console.error('No se ha encontrado el ID de la línea del pedido.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Llamada AJAX para marcar la maquetación como OK
|
||||||
|
$.ajax({
|
||||||
|
url: `/pedidos/linea/${lineaId}/update-maquetacion`,
|
||||||
|
type: 'POST',
|
||||||
|
success: function (response) {
|
||||||
|
if (!response || !response.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: response.message || "Error",
|
||||||
|
timer: 1800,
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2',
|
||||||
|
cancelButton: 'btn btn-light'
|
||||||
|
},
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const estadoSpan = $(`.estado-linea[data-linea-id='${lineaId}']`);
|
||||||
|
if (estadoSpan.length) {
|
||||||
|
estadoSpan.text(response.state);
|
||||||
|
// hide the maquetacion-ok button
|
||||||
|
$(`.maquetacion-ok[data-linea-id='${lineaId}']`)
|
||||||
|
.closest('.maquetacion-ok-button')
|
||||||
|
.addClass('d-none');
|
||||||
|
}
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: response.message || "Exito",
|
||||||
|
timer: 1800,
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2',
|
||||||
|
cancelButton: 'btn btn-light'
|
||||||
|
},
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error('Error al actualizar la maquetación del pedido:', error);
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: xhr.responseJSON?.message || 'Error',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar
|
||||||
|
cancelButton: 'btn btn-light' // clases para cancelar
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
})
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
$(() => {
|
||||||
|
if ($(".btn-download-ferro").length) {
|
||||||
|
$(document).on('click', '.btn-download-ferro', function () {
|
||||||
|
const lineaId = $(this).data('linea-id');
|
||||||
|
|
||||||
|
window.open(`/pedidos/linea/${lineaId}/download-ferro`, '_blank');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($(".btn-download-cub").length) {
|
||||||
|
$(document).on('click', '.btn-download-cub', function () {
|
||||||
|
const lineaId = $(this).data('linea-id');
|
||||||
|
|
||||||
|
window.open(`/pedidos/linea/${lineaId}/download-cub`, '_blank');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($(".btn-download-tapa").length) {
|
||||||
|
$(document).on('click', '.btn-download-tapa', function () {
|
||||||
|
const lineaId = $(this).data('linea-id');
|
||||||
|
|
||||||
|
window.open(`/pedidos/linea/${lineaId}/download-tapa`, '_blank');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($(".btn-aceptar-ferro").length) {
|
||||||
|
$(document).on('click', '.btn-aceptar-ferro', function () {
|
||||||
|
const lineaId = $(this).data('linea-id');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: `/pedidos/linea/${lineaId}/aceptar-ferro`,
|
||||||
|
type: 'POST',
|
||||||
|
success: function (response) {
|
||||||
|
if (!response || !response.success) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: response.message || "Error",
|
||||||
|
timer: 1800,
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2',
|
||||||
|
cancelButton: 'btn btn-light'
|
||||||
|
},
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const estadoSpan = $(`.estado-linea[data-linea-id='${lineaId}']`);
|
||||||
|
if (estadoSpan.length) {
|
||||||
|
estadoSpan.text(response.state);
|
||||||
|
}
|
||||||
|
$(`.btn-aceptar-ferro[data-linea-id='${lineaId}']`)
|
||||||
|
.closest('.btn-aceptar-ferro')
|
||||||
|
.addClass('d-none');
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: response.message || "Exito",
|
||||||
|
timer: 1800,
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2',
|
||||||
|
cancelButton: 'btn btn-light'
|
||||||
|
},
|
||||||
|
showConfirmButton: false
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.dismiss === Swal.DismissReason.timer) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error('Error al aceptar el ferro del pedido:', error);
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: xhr.responseJSON?.message || 'Error',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar
|
||||||
|
cancelButton: 'btn btn-light' // clases para cancelar
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
import { normalizeNumericFilter } from '../utils.js';
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
|
||||||
|
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.getAttribute('content');
|
||||||
|
if (window.$ && csrfToken && csrfHeader) {
|
||||||
|
$.ajaxSetup({
|
||||||
|
beforeSend: function (xhr) {
|
||||||
|
xhr.setRequestHeader(csrfHeader, csrfToken);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const language = document.documentElement.lang || 'es-ES';
|
||||||
|
|
||||||
|
const tablePedidos = $('#pedidos-datatable').DataTable({
|
||||||
|
processing: true,
|
||||||
|
serverSide: true,
|
||||||
|
orderCellsTop: true,
|
||||||
|
pageLength: 50,
|
||||||
|
lengthMenu: [10, 25, 50, 100, 500],
|
||||||
|
order: [[5, 'desc']], // Ordena por fecha por defecto
|
||||||
|
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
|
||||||
|
responsive: true,
|
||||||
|
dom: 'lBrtip',
|
||||||
|
buttons: {
|
||||||
|
dom: {
|
||||||
|
button: {
|
||||||
|
className: 'btn btn-sm btn-outline-primary me-1'
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{ extend: 'copy' },
|
||||||
|
{ extend: 'csv' },
|
||||||
|
{ extend: 'excel' },
|
||||||
|
{ extend: 'pdf' },
|
||||||
|
{ extend: 'print' },
|
||||||
|
{ extend: 'colvis' }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ajax: {
|
||||||
|
url: '/pedidos/datatable',
|
||||||
|
method: 'GET',
|
||||||
|
},
|
||||||
|
order: [[0, 'desc']],
|
||||||
|
columns: [
|
||||||
|
{ data: 'id', name: 'id', orderable: true },
|
||||||
|
{ data: 'created_at', name: 'createdAt', orderable: true },
|
||||||
|
{ data: 'total', name: 'total', orderable: true },
|
||||||
|
{ data: 'estado', name: 'estado', orderable: true },
|
||||||
|
{ data: 'actions', name: 'actions', orderable: false, searchable: false }
|
||||||
|
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
tablePedidos.on("keyup change", ".input-filter", function () {
|
||||||
|
const colName = $(this).data("col");
|
||||||
|
const colIndex = tablePedidos.settings()[0].aoColumns.findIndex(c => c.name === colName);
|
||||||
|
|
||||||
|
if (colIndex >= 0) {
|
||||||
|
tablePedidos
|
||||||
|
.column(colIndex)
|
||||||
|
.search(normalizeNumericFilter(this.value))
|
||||||
|
.draw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
$(() => {
|
||||||
|
var snowEditor = document.querySelectorAll(".snow-editor");
|
||||||
|
if (snowEditor) {
|
||||||
|
Array.from(snowEditor).forEach(function (item) {
|
||||||
|
var snowEditorData = {};
|
||||||
|
var issnowEditorVal = item.classList.contains("snow-editor");
|
||||||
|
if (issnowEditorVal == true) {
|
||||||
|
snowEditorData.theme = 'snow',
|
||||||
|
snowEditorData.modules = {
|
||||||
|
'toolbar': [
|
||||||
|
[{
|
||||||
|
'font': []
|
||||||
|
}, {
|
||||||
|
'size': []
|
||||||
|
}],
|
||||||
|
['bold', 'italic', 'underline', 'strike'],
|
||||||
|
[{
|
||||||
|
'color': []
|
||||||
|
}, {
|
||||||
|
'background': []
|
||||||
|
}],
|
||||||
|
[{
|
||||||
|
'script': 'super'
|
||||||
|
}, {
|
||||||
|
'script': 'sub'
|
||||||
|
}],
|
||||||
|
[{
|
||||||
|
'header': [false, 1, 2, 3, 4, 5, 6]
|
||||||
|
}, 'blockquote', 'code-block'],
|
||||||
|
[{
|
||||||
|
'list': 'ordered'
|
||||||
|
}, {
|
||||||
|
'list': 'bullet'
|
||||||
|
}, {
|
||||||
|
'indent': '-1'
|
||||||
|
}, {
|
||||||
|
'indent': '+1'
|
||||||
|
}],
|
||||||
|
['direction', {
|
||||||
|
'align': []
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var quill = new Quill(item, snowEditorData);
|
||||||
|
|
||||||
|
var initialContent = item.dataset.contenido || "";
|
||||||
|
// Contenido inicial desde Thymeleaf
|
||||||
|
var initialContent = item.dataset.contenido || "";
|
||||||
|
if (initialContent) {
|
||||||
|
if(initialContent.trim() !== "" && initialContent.trim() !== "<p><br></p>")
|
||||||
|
$('.badge-comentario').removeClass('d-none');
|
||||||
|
quill.clipboard.dangerouslyPasteHTML(initialContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
quill.root.addEventListener("blur", function () {
|
||||||
|
|
||||||
|
let contenido = quill.root.innerHTML;
|
||||||
|
if(contenido.trim() !== "" && contenido.trim() !== "<p><br></p>"){
|
||||||
|
$('.badge-comentario').removeClass('d-none');
|
||||||
|
} else {
|
||||||
|
$('.badge-comentario').addClass('d-none');
|
||||||
|
}
|
||||||
|
if ($('#presupuesto_id').length > 0 && $('#presupuesto_id').val() !== "") {
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/presupuesto/" + $('#presupuesto_id').val() + "/comentario",
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
comentario: contenido
|
||||||
|
},
|
||||||
|
success: function () {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
@ -402,8 +402,6 @@ export default class PresupuestoWizard {
|
|||||||
...this.#getInteriorData(),
|
...this.#getInteriorData(),
|
||||||
...this.#getCubiertaData(),
|
...this.#getCubiertaData(),
|
||||||
selectedTirada: this.formData.selectedTirada
|
selectedTirada: this.formData.selectedTirada
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sobrecubierta = data.sobrecubierta;
|
const sobrecubierta = data.sobrecubierta;
|
||||||
@ -927,6 +925,7 @@ export default class PresupuestoWizard {
|
|||||||
this.#changeTab('pills-general-data');
|
this.#changeTab('pills-general-data');
|
||||||
} else {
|
} else {
|
||||||
const maxSolapas = data.solapas ?? 120;
|
const maxSolapas = data.solapas ?? 120;
|
||||||
|
const lomo = data.lomo ?? 0;
|
||||||
$('.solapas-presupuesto').attr('max', maxSolapas);
|
$('.solapas-presupuesto').attr('max', maxSolapas);
|
||||||
$('.max-solapa-text').text(function (_, textoActual) {
|
$('.max-solapa-text').text(function (_, textoActual) {
|
||||||
return textoActual.replace(/\d+/, maxSolapas);
|
return textoActual.replace(/\d+/, maxSolapas);
|
||||||
@ -951,6 +950,20 @@ export default class PresupuestoWizard {
|
|||||||
this.acabadoSobrecubierta.val(this.formData.cubierta.sobrecubierta.acabado);
|
this.acabadoSobrecubierta.val(this.formData.cubierta.sobrecubierta.acabado);
|
||||||
this.fajaSobrecubierta.val(this.formData.cubierta.faja.acabado);
|
this.fajaSobrecubierta.val(this.formData.cubierta.faja.acabado);
|
||||||
|
|
||||||
|
if(lomo < 10){
|
||||||
|
this.formData.cubierta.cabezada = "NOCAB";
|
||||||
|
this.cabezada.val("NOCAB");
|
||||||
|
this.cabezada.prop("disabled", true);
|
||||||
|
if(this.formData.cubierta.tipoCubierta === 'tapaDuraLomoRedondo'){
|
||||||
|
this.formData.cubierta.tipoCubierta = 'tapaDura';
|
||||||
|
}
|
||||||
|
$("#tapaDuraLomoRedondo").addClass("d-none");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
this.cabezada.prop("disabled", false);
|
||||||
|
$("#tapaDuraLomoRedondo").removeClass("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
this.#loadCubiertaData();
|
this.#loadCubiertaData();
|
||||||
this.summaryTableCubierta.removeClass('d-none');
|
this.summaryTableCubierta.removeClass('d-none');
|
||||||
if (this.sobrecubierta.hasClass('active')) {
|
if (this.sobrecubierta.hasClass('active')) {
|
||||||
@ -1683,7 +1696,7 @@ export default class PresupuestoWizard {
|
|||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
presupuesto: this.#getPresupuestoData(),
|
presupuesto: this.#getPresupuestoData(),
|
||||||
save: !this.opts.canSave,
|
save: this.opts.canSave,
|
||||||
mode: this.opts.mode,
|
mode: this.opts.mode,
|
||||||
servicios: servicios,
|
servicios: servicios,
|
||||||
datosMaquetacion: this.formData.servicios.datosMaquetacion,
|
datosMaquetacion: this.formData.servicios.datosMaquetacion,
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { duplicar, reimprimir } from './presupuesto-utils.js';
|
||||||
|
|
||||||
|
$(()=> {
|
||||||
|
$('.duplicar-btn').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const id = $('#presupuesto_id').val();
|
||||||
|
const tituloOriginal = $('#titulo').text().trim() == '' ? $('#titulo').val().trim() : $('#titulo').text().trim();
|
||||||
|
duplicar(id, tituloOriginal);
|
||||||
|
})
|
||||||
|
|
||||||
|
$('.reimprimir-btn').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const id = $('#presupuesto_id').val();
|
||||||
|
reimprimir(id);
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { preguntarTipoPresupuesto } from './presupuesto-utils.js';
|
import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-utils.js';
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
// si jQuery está cargado, añade CSRF a AJAX
|
// si jQuery está cargado, añade CSRF a AJAX
|
||||||
@ -200,6 +200,26 @@ import { preguntarTipoPresupuesto } from './presupuesto-utils.js';
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#presupuestos-clientes-datatable').on('click', '.btn-duplicate-privado', function (e) {
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
const id = $(this).data('id');
|
||||||
|
let data = table_clientes.row($(this).parents('tr')).data();
|
||||||
|
const tituloOriginal = data.titulo;
|
||||||
|
|
||||||
|
duplicar(id, tituloOriginal);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#presupuestos-clientes-datatable').on('click', '.btn-reprint-privado', function (e) {
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
const id = $(this).data('id');
|
||||||
|
let data = table_clientes.row($(this).parents('tr')).data();
|
||||||
|
const tituloOriginal = data.titulo;
|
||||||
|
|
||||||
|
reimprimir(id, tituloOriginal);
|
||||||
|
});
|
||||||
|
|
||||||
$('#presupuestos-clientes-datatable').on('click', '.btn-delete-privado', function (e) {
|
$('#presupuestos-clientes-datatable').on('click', '.btn-delete-privado', function (e) {
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@ -75,3 +75,171 @@ export async function preguntarTipoPresupuesto() {
|
|||||||
}
|
}
|
||||||
}).then((r) => (r.isConfirmed ? r.value : null));
|
}).then((r) => (r.isConfirmed ? r.value : null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function duplicar(id, titulo) {
|
||||||
|
|
||||||
|
// swal with input
|
||||||
|
Swal.fire({
|
||||||
|
title: window.languageBundle.get(['presupuesto.duplicar.title']) || 'Duplicar presupuesto',
|
||||||
|
html: window.languageBundle.get(['presupuesto.duplicar.text']) || `¿Deseas duplicar el presupuesto "${titulo}"?`,
|
||||||
|
icon: 'question',
|
||||||
|
input: 'text',
|
||||||
|
inputValue: `[D] ${titulo}`,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: window.languageBundle.get(['presupuesto.duplicar.confirm']) || 'Sí, duplicar',
|
||||||
|
cancelButtonText: window.languageBundle.get(['presupuesto.duplicar.cancelar']) || 'Cancelar',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar
|
||||||
|
cancelButton: 'btn btn-light' // clases para cancelar
|
||||||
|
},
|
||||||
|
inputValidator: (value) => {
|
||||||
|
if (!value) {
|
||||||
|
return window.languageBundle.get(['presupuesto.duplicar.required']) || 'El título no puede estar vacío';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
|
||||||
|
const tituloNuevo = result.value;
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
|
||||||
|
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.getAttribute('content');
|
||||||
|
if (window.$ && csrfToken && csrfHeader) {
|
||||||
|
$.ajaxSetup({
|
||||||
|
beforeSend: function (xhr) {
|
||||||
|
xhr.setRequestHeader(csrfHeader, csrfToken);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: `/presupuesto/api/duplicar/${id}`,
|
||||||
|
data: {
|
||||||
|
titulo: tituloNuevo,
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
success: function (response) {
|
||||||
|
|
||||||
|
if (response.id && response.id > 0) {
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: window.languageBundle.get(['presupuesto.duplicar.success.title']) || 'Presupuesto duplicado',
|
||||||
|
text: window.languageBundle.get(['presupuesto.duplicar.success.text']) || `El presupuesto "${titulo}" ha sido duplicado correctamente.`,
|
||||||
|
icon: 'success',
|
||||||
|
confirmButtonText: window.languageBundle.get(['presupuesto.duplicar.aceptar']) || 'Aceptar',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary' // clases para el botón confirmar
|
||||||
|
},
|
||||||
|
}).then(() => {
|
||||||
|
// Recargar la página o redirigir a la lista de presupuestos
|
||||||
|
window.location.href = '/presupuesto/edit/' + response.id;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: window.languageBundle.get(['presupuesto.duplicar.error.title']) || 'Error al duplicar',
|
||||||
|
text: response.message || window.languageBundle.get(['presupuesto.duplicar.error.internal']) || 'Ha ocurrido un error al duplicar el presupuesto.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: window.languageBundle.get(['presupuesto.duplicar.aceptar']) || 'Aceptar',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary' // clases para el botón confirmar
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
Swal.fire({
|
||||||
|
title: window.languageBundle.get(['presupuesto.duplicar.error.title']) || 'Error al duplicar',
|
||||||
|
text: xhr.responseJSON?.message || window.languageBundle.get(['presupuesto.duplicar.error.internal']) || 'Ha ocurrido un error al duplicar el presupuesto.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: window.languageBundle.get(['presupuesto.duplicar.aceptar']) || 'Aceptar',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary' // clases para el botón confirmar
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reimprimir(id) {
|
||||||
|
Swal.fire({
|
||||||
|
title: window.languageBundle.get(['presupuesto.reimprimir.title']) || 'Reimprimir presupuesto',
|
||||||
|
html: window.languageBundle.get(['presupuesto.reimprimir.text']) || `¿Deseas reimprimir el presupuesto?`,
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: window.languageBundle.get(['presupuesto.reimprimir.confirm']) || 'Sí, reimprimir',
|
||||||
|
cancelButtonText: window.languageBundle.get(['presupuesto.reimprimir.cancelar']) || 'Cancelar',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar
|
||||||
|
cancelButton: 'btn btn-light' // clases para cancelar
|
||||||
|
},
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
|
||||||
|
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.getAttribute('content');
|
||||||
|
if (window.$ && csrfToken && csrfHeader) {
|
||||||
|
$.ajaxSetup({
|
||||||
|
beforeSend: function (xhr) {
|
||||||
|
xhr.setRequestHeader(csrfHeader, csrfToken);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: `/presupuesto/api/reimprimir/${id}`,
|
||||||
|
method: 'POST',
|
||||||
|
success: function (response) {
|
||||||
|
|
||||||
|
if (response.id && response.id > 0) {
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: window.languageBundle.get(['presupuesto.reimprimir.success.title']) || 'Presupuesto reimpreso',
|
||||||
|
text: window.languageBundle.get(['presupuesto.reimprimir.success.text']) || `El presupuesto ha sido reimpreso correctamente.`,
|
||||||
|
icon: 'success',
|
||||||
|
confirmButtonText: window.languageBundle.get(['presupuesto.reimprimir.aceptar']) || 'Aceptar',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary' // clases para el botón confirmar
|
||||||
|
},
|
||||||
|
}).then(() => {
|
||||||
|
// Recargar la página o redirigir a la lista de presupuestos
|
||||||
|
window.location.href = '/presupuesto/edit/' + response.id;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: window.languageBundle.get(['presupuesto.reimprimir.error.title']) || 'Error al reimprimir',
|
||||||
|
text: response.message || window.languageBundle.get(['presupuesto.reimprimir.error.internal']) || 'Ha ocurrido un error al reimprimir el presupuesto.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: window.languageBundle.get(['presupuesto.reimprimir.aceptar']) || 'Aceptar',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary' // clases para el botón confirmar
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
Swal.fire({
|
||||||
|
title: window.languageBundle.get(['presupuesto.reimprimir.error.title']) || 'Error al reimprimir',
|
||||||
|
text: xhr.responseJSON?.message || window.languageBundle.get(['presupuesto.reimprimir.error.internal']) || 'Ha ocurrido un error al reimprimir el presupuesto.',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: window.languageBundle.get(['presupuesto.reimprimir.aceptar']) || 'Aceptar',
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-secondary' // clases para el botón confirmar
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ $(() => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// obtén el id de donde lo tengas (data-attr o variable global)
|
// obtén el id de donde lo tengas (data-attr o variable global)
|
||||||
const id = $('#presupuesto-id').val();
|
const id = $('#presupuesto_id').val();
|
||||||
|
|
||||||
const url = `/api/pdf/presupuesto/${id}?mode=download`;
|
const url = `/api/pdf/presupuesto/${id}?mode=download`;
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ $(() => {
|
|||||||
|
|
||||||
$('.add-cart-btn').on('click', async () => {
|
$('.add-cart-btn').on('click', async () => {
|
||||||
|
|
||||||
const presupuestoId = $('#presupuesto-id').val();
|
const presupuestoId = $('#presupuesto_id').val();
|
||||||
const res = await $.ajax({
|
const res = await $.ajax({
|
||||||
url: `/cart/add/${presupuestoId}`,
|
url: `/cart/add/${presupuestoId}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@ -50,6 +50,7 @@
|
|||||||
<input type="hidden" name="amountCents" th:value="${summary.amountCents}" />
|
<input type="hidden" name="amountCents" th:value="${summary.amountCents}" />
|
||||||
<input type="hidden" name="method" value="card" />
|
<input type="hidden" name="method" value="card" />
|
||||||
<input type="hidden" name="cartId" th:value="${summary.cartId}" />
|
<input type="hidden" name="cartId" th:value="${summary.cartId}" />
|
||||||
|
<input type="hidden" id="dirFactId" name="dirFactId" value="" />
|
||||||
<button id="btn-checkout" type="submit" class="btn btn-secondary w-100 mt-2"
|
<button id="btn-checkout" type="submit" class="btn btn-secondary w-100 mt-2"
|
||||||
th:text="#{checkout.make-payment}" disabled>Checkout</button>
|
th:text="#{checkout.make-payment}" disabled>Checkout</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<input type="hidden" class="direccion-cp" th:value="${direccion.cp}" />
|
<input type="hidden" class="direccion-cp" th:value="${direccion.cp}" />
|
||||||
<input type="hidden" class="direccion-pais-code3" th:value="${direccion.pais.code3}" />
|
<input type="hidden" class="direccion-pais-code3" th:value="${direccion.pais.code3}" />
|
||||||
<input type="hidden" class="item-tirada" th:value="${unidades != null ? unidades : ''}" />
|
<input type="hidden" class="item-tirada" th:value="${unidades != null ? unidades : ''}" />
|
||||||
<input type="hidden" class="is-palets" th:value="${isPalets != null ? isPalets : ''}" />
|
<input type="hidden" class="is-palets" th:value="${isPalets != null and (isPalets==1 or isPalets==true)? 1 : 0}" />
|
||||||
|
|
||||||
<div class="row g-3 align-items-start flex-nowrap">
|
<div class="row g-3 align-items-start flex-nowrap">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
<div th:fragment="direccionEnvioCard(direccion, pais)" name="direccion"
|
||||||
|
class="card card border mb-3 direccion-card mx-2">
|
||||||
|
<div class="card-body position-relative">
|
||||||
|
|
||||||
|
<th:block th:if="${direccion.unidades != null}">
|
||||||
|
<div class="position-absolute top-0 end-0 mt-2 me-3 text-end">
|
||||||
|
<div th:if="${direccion.isEjemplarPrueba}">
|
||||||
|
<span class="mb-2 fw-semibold d-block text-muted text-uppercase"
|
||||||
|
th:text="#{pedido.prueba}"></span>
|
||||||
|
</div>
|
||||||
|
<div th:if="${!direccion.isEjemplarPrueba}">
|
||||||
|
<!-- singular -->
|
||||||
|
<span class="mb-2 fw-semibold d-block text-muted text-uppercase"
|
||||||
|
th:if="${direccion.unidades == 1}"
|
||||||
|
th:text="|${direccion.unidades} #{cart.shipping.ud}|"></span>
|
||||||
|
|
||||||
|
<!-- plural -->
|
||||||
|
<span class="mb-2 fw-semibold d-block text-muted text-uppercase"
|
||||||
|
th:unless="${direccion.unidades == 1}"
|
||||||
|
th:text="|${direccion.unidades} #{cart.shipping.uds}|"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<!-- TEXTO: usa TODO el ancho -->
|
||||||
|
<span class="fs-14 mb-1 d-block text-break" th:text="${direccion.att}"></span>
|
||||||
|
<span class="text-muted fw-normal text-wrap mb-1 d-block text-break"
|
||||||
|
th:text="${direccion.direccion}"></span>
|
||||||
|
<span class="text-muted fw-normal text-wrap mb-1 d-block text-break"
|
||||||
|
th:text="${direccion.cp} + ', ' + ${direccion.ciudad} + ', (' + ${direccion.provincia} + ')'">
|
||||||
|
</span>
|
||||||
|
<span class="text-muted fw-normal d-block text-break" th:text="${pais}"></span>
|
||||||
|
<span class="text-muted fw-normal d-block text-break"
|
||||||
|
th:text="#{'direcciones.telefono'} + ': ' + ${direccion.telefono}"></span>
|
||||||
|
<span class="fw-normal d-block text-break" th:text="${direccion.razonSocial}"></span>
|
||||||
|
<span class="fw-normal d-block text-break" th:text="${direccion.identificacionFiscal}"></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
<div th:fragment="direccionFacturacionCard(direccion, pais)" name="direccionFacturacion"
|
||||||
|
class="card card border mb-3 direccion-facturacion-card">
|
||||||
|
<div class="card-header bg-light">
|
||||||
|
<span class="fs-16" th:text="#{'direcciones.direccionFacturacion'}"></span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-start g-3 flex-nowrap w-auto">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<img src="/assets/images/billing_address2.gif" style="width: 100px; height: auto;"
|
||||||
|
alt="Billing Address">
|
||||||
|
</div>
|
||||||
|
<div class="flex-nowrap">
|
||||||
|
<span class="fs-14 mb-1 d-block text-break" th:text="${direccion.att}"></span>
|
||||||
|
<span class="text-muted fw-normal text-wrap mb-1 d-block text-break"
|
||||||
|
th:text="${direccion.direccion}"></span>
|
||||||
|
<span class="text-muted fw-normal text-wrap mb-1 d-block text-break"
|
||||||
|
th:text="${direccion.cp} + ', ' + ${direccion.ciudad} + ', (' + ${direccion.provincia} + ')'">
|
||||||
|
</span>
|
||||||
|
<span class="text-muted fw-normal d-block text-break" th:text="${pais}"></span>
|
||||||
|
<span class="text-muted fw-normal d-block text-break"
|
||||||
|
th:text="#{'direcciones.telefono'} + ': ' + ${direccion.telefono}"></span>
|
||||||
|
<span class="fw-normal d-block text-break" th:text="${direccion.razonSocial}"></span>
|
||||||
|
<span class="fw-normal d-block text-break" th:text="${direccion.identificacionFiscal}"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
@ -43,6 +43,11 @@
|
|||||||
<i class="ri-file-paper-2-line"></i> <span th:text="#{app.sidebar.presupuestos}">Presupuestos</span>
|
<i class="ri-file-paper-2-line"></i> <span th:text="#{app.sidebar.presupuestos}">Presupuestos</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link menu-link" href="/pedidos">
|
||||||
|
<i class="ri-book-3-line"></i> <span th:text="#{app.sidebar.pedidos}">Pedidos</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link menu-link" href="/direcciones">
|
<a class="nav-link menu-link" href="/direcciones">
|
||||||
<i class="ri-truck-line"></i>
|
<i class="ri-truck-line"></i>
|
||||||
|
|||||||
@ -139,6 +139,7 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="fw-bold fs-6 mb-1" th:text="#{pdf.incluye-envio}">El presupuesto incluye el envío a una dirección de la
|
<div class="fw-bold fs-6 mb-1" th:text="#{pdf.incluye-envio}">El presupuesto incluye el envío a una dirección de la
|
||||||
península.</div>
|
península.</div>
|
||||||
|
<div class="fw-bold fs-6 mb-1" th:text="#{pdf.presupuesto-validez}">Validez del presupuesto: 30 días desde la fecha de emisión.</div>
|
||||||
<div class="privacy">
|
<div class="privacy">
|
||||||
<div class="pv-title" th:text="#{pdf.politica-privacidad}">Política de privacidad</div>
|
<div class="pv-title" th:text="#{pdf.politica-privacidad}">Política de privacidad</div>
|
||||||
<div class="pv-text" th:text="#{pdf.politica-privacidad.responsable}">Responsable: Impresión Imprime Libros -
|
<div class="pv-text" th:text="#{pdf.politica-privacidad.responsable}">Responsable: Impresión Imprime Libros -
|
||||||
|
|||||||
@ -0,0 +1,167 @@
|
|||||||
|
<div th:fragment="pedido-linea (item, isAdmin)">
|
||||||
|
<input type="hidden" id="lineaId" th:value="${item.lineaId}" />
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="card p-3 mb-0">
|
||||||
|
<div class="row g-3 align-items-start">
|
||||||
|
<!-- Col 1: imagen -->
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="avatar-lg bg-light rounded p-1">
|
||||||
|
<img th:src="${item.imagen != null ? item.imagen : '/assets/images/products/placeholder.png'}"
|
||||||
|
alt="portada" class="img-fluid d-block rounded">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Col 2: detalles -->
|
||||||
|
<div class="col">
|
||||||
|
<h5 class="fs-18 text-truncate mb-1">
|
||||||
|
<span class="text-dark"
|
||||||
|
th:text="${item.titulo != null ? item.titulo : 'Presupuesto #'}">Presupuesto</span>
|
||||||
|
</h5>
|
||||||
|
<h5 class="fs-14 text-truncate mb-1">
|
||||||
|
<span th:text="#{cart.item.presupuesto-numero}">Presupuesto #</span>
|
||||||
|
<span th:text="${item.presupuestoId != null ? item.presupuestoId : ''}">#</span>
|
||||||
|
<a th:href="@{|/presupuesto/edit/${item.presupuestoId}|}"
|
||||||
|
th:text="#{pedido.view.view-presupuesto}" class="badge bg-secondary">Ver presupuesto</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<ul class="list-unstyled text-muted mb-1 ps-0">
|
||||||
|
<li th:each="linea : ${item.resumen.lineas}" class="mb-1">
|
||||||
|
<span th:utext="${linea['descripcion']}"></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="list-unstyled text-muted mb-1" th:if="${item.resumen.servicios != null}">
|
||||||
|
<li>
|
||||||
|
<span th:utext="#{pdf.servicios-adicionales}">Servicios adicionales:</span>
|
||||||
|
<span class="spec-label" th:text="${item.resumen.servicios}"></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="list-unstyled text-muted mb-1"
|
||||||
|
th:if="${item.resumen != null and #maps.containsKey(item.resumen,'datosMaquetacion') and item.resumen['datosMaquetacion'] != null}">
|
||||||
|
<li class="spec-row mb-1">
|
||||||
|
<span th:text="#{pdf.datos-maquetacion}">Datos de maquetación:</span>
|
||||||
|
<span th:utext="${item.resumen.datosMaquetacion}"></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="list-unstyled text-muted mb-1"
|
||||||
|
th:if="${item.resumen != null and #maps.containsKey(item.resumen,'datosMarcapaginas') and item.resumen['datosMarcapaginas'] != null}">
|
||||||
|
<li class="spec-row mb-1">
|
||||||
|
<span th:text="#{pdf.datos-marcapaginas}">Datos de marcapáginas:</span>
|
||||||
|
<span th:utext="${item.resumen.datosMarcapaginas}"></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Col 3: precio -->
|
||||||
|
<div class="col-auto ms-auto text-end">
|
||||||
|
<p class="text-muted mb-1" th:text="#{cart.precio}">Precio</p>
|
||||||
|
<h5 class="fs-14 mb-0">
|
||||||
|
<span th:text="${item.baseTotal != null ? item.total : '-'}">0,00</span>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted mt-4 mb-1" th:text="#{pedido.table.estado}">Estado</p>
|
||||||
|
<h5 class="fs-14 mb-0">
|
||||||
|
<span class="estado-linea" th:attr="data-linea-id=${item.lineaId}" th:text="${item.estado != null} ?
|
||||||
|
#{__${'pedido.estado.' + item.estado}__} : '-'">
|
||||||
|
</span>
|
||||||
|
</h5>
|
||||||
|
<div class="col-12 d-grid gap-2 mt-2">
|
||||||
|
<button th:if="${item.estado.name == 'esperando_aceptacion_ferro'}" type="button"
|
||||||
|
class="btn btn-secondary w-100 btn-aceptar-ferro" th:text="#{pedido.view.aceptar-ferro}"
|
||||||
|
th:attr="data-linea-id=${item.lineaId}">
|
||||||
|
Aceptar ferro
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button th:if="${item.estado.priority >= 7 && item.estado.priority < 11 && item.buttons.ferro}"
|
||||||
|
type="button" class="btn btn-light w-100 btn-download-ferro"
|
||||||
|
th:text="#{pedido.view.ferro-download}"
|
||||||
|
th:attr="data-linea-id=${item.lineaId}">
|
||||||
|
Descargar ferro
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button th:if="${item.estado.priority >= 7 && item.estado.priority < 11 && item.buttons.cub}"
|
||||||
|
type="button" class="btn btn-light w-100 btn-download-cub"
|
||||||
|
th:text="#{pedido.view.cub-download}"
|
||||||
|
th:attr="data-linea-id=${item.lineaId}">
|
||||||
|
Descargar cubierta
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button th:if="${item.estado.priority >= 7 && item.estado.priority < 11 && item.buttons.tapa}"
|
||||||
|
type="button" class="btn btn-light w-100 btn-download-tapa"
|
||||||
|
th:text="#{pedido.view.tapa-download}"
|
||||||
|
th:attr="data-linea-id=${item.lineaId}">
|
||||||
|
Descargar tapa
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<th:block th:if="${item.fechaEntrega != null and item.fechaEntrega != ''}">
|
||||||
|
<p class="text-muted mt-4 mb-1" th:text="#{pedido.fecha-entrega}">Fecha de entrega</p>
|
||||||
|
<h5 class="fs-14 mb-0">
|
||||||
|
<span th:text="${item.fechaEntrega}">-</span>
|
||||||
|
</h5>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div card class="mt-3">
|
||||||
|
<div class="card-header bg-light p-3">
|
||||||
|
<span class="mb-0 fs-16" th:text="#{pedido.shipping-addresses}">Direcciones de envío</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-3">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-auto d-none d-md-block">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<img src="/assets/images/delivery-truck.gif" style="width: 120px; height: auto;"
|
||||||
|
alt="delivery">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div th:each="direccionEnvio : ${item.direccionesEntrega}"
|
||||||
|
class="mb-3 col-xl-4 col-lg-6 col-md-12 col-sm-12">
|
||||||
|
<div th:insert="~{imprimelibros/direcciones/direccionEnvioCard :: direccionEnvioCard(
|
||||||
|
direccion=${direccionEnvio},
|
||||||
|
pais=${direccionEnvio.paisNombre}
|
||||||
|
)}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:if="${isAdmin}" class="card-footer bg-light p-3">
|
||||||
|
<div class="row align-items-center gy-3">
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="d-flex flex-wrap my-n1">
|
||||||
|
<!-- Botón cancelar -->
|
||||||
|
<div th:if="${item.estado.name != 'cancelado' && item.estado.name != 'terminado'}">
|
||||||
|
<a href="javascript:void(0);" class="d-block text-body p-1 px-2 cancel-item"
|
||||||
|
th:attr="data-linea-id=${item.lineaId}">
|
||||||
|
<i class="ri-delete-bin-fill text-muted align-bottom me-1"><span
|
||||||
|
th:text="#{pedido.cancelar}">Cancelar Pedido</span></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<!-- Actualizar estado-->
|
||||||
|
<div class="update-estado-button"
|
||||||
|
th:if="${item.estado.name != 'cancelado' && item.estado.name != 'maquetacion' && item.estado.name != 'terminado'}">
|
||||||
|
<a href="javascript:void(0);" class="d-block text-body p-1 px-2 update-status-item"
|
||||||
|
th:attr="data-linea-id=${item.lineaId}">
|
||||||
|
<i class="ri-refresh-line text-muted align-bottom me-1"><span
|
||||||
|
th:text="#{pedido.update-estado}">Actualizar estado</span></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="maquetacion-ok-button" th:if="${item.estado.name == 'maquetacion'}">
|
||||||
|
<a href="javascript:void(0);" class="d-block text-body p-1 px-2 maquetacion-ok"
|
||||||
|
th:attr="data-linea-id=${item.lineaId}">
|
||||||
|
<i class="ri-check-double-line text-muted align-bottom me-1"><span
|
||||||
|
th:text="#{pedido.maquetacion_finalizada}"> Maquetación finalizada</span></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
<!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/libs/datatables/dataTables.bootstrap5.min.css}" rel="stylesheet" />
|
||||||
|
</th:block>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
|
||||||
|
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}"
|
||||||
|
sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')">
|
||||||
|
|
||||||
|
<th:block layout:fragment="content">
|
||||||
|
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page" th:text="#{pedido.module-title}">
|
||||||
|
Pedidos</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
|
||||||
|
<table id="pedidos-datatable" class="table table-striped table-nowrap responsive w-100">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.id}">Num. Pedido</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.fecha}">Fecha</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.importe}">Importe</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.estado}">Estado</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><input type="text" class="form-control form-control-sm input-filter" data-col="id" /></th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
<th>
|
||||||
|
<select class="form-select form-select-sm input-filter" data-col="estado">
|
||||||
|
<option value=""></option>
|
||||||
|
<option th:text="#{pedido.estado.aprobado}" value="aprobado"></option>
|
||||||
|
<option th:text="#{pedido.estado.maquetacion}" value="maquetacion"></option>
|
||||||
|
<option th:text="#{pedido.estado.haciendo_ferro}" value="haciendo_ferro"></option>
|
||||||
|
<option th:text="#{pedido.estado.produccion}" value="produccion"></option>
|
||||||
|
<option th:text="#{pedido.estado.terminado}" value="terminado"></option>
|
||||||
|
<option th:text="#{pedido.estado.cancelado}" value="cancelado"></option>
|
||||||
|
</select>
|
||||||
|
</th>
|
||||||
|
<th></th> <!-- Acciones (sin filtro) -->
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||||
|
<th:block layout:fragment="pagejs">
|
||||||
|
<script th:inline="javascript">
|
||||||
|
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script th:src="@{/assets/libs/datatables/datatables.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/dataTables.bootstrap5.min.js}"></script>
|
||||||
|
|
||||||
|
<!-- JS de Buttons y dependencias -->
|
||||||
|
<script th:src="@{/assets/libs/datatables/dataTables.buttons.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/jszip/jszip.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/pdfmake/pdfmake.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/pdfmake/vfs_fonts.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.html5.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.print.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.colVis.min.js}"></script>
|
||||||
|
|
||||||
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/pedidos/pedidos-common.js}"></script>
|
||||||
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/pedidos/pedidos.js}"></script>
|
||||||
|
</th:block>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
<!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/libs/datatables/dataTables.bootstrap5.min.css}" rel="stylesheet" />
|
||||||
|
</th:block>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
|
||||||
|
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}"
|
||||||
|
sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')">
|
||||||
|
|
||||||
|
<th:block layout:fragment="content">
|
||||||
|
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page" th:text="#{pedido.module-title}">
|
||||||
|
Pedidos</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
|
||||||
|
<table id="pedidos-datatable" class="table table-striped table-nowrap responsive w-100">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.id}">Num. Pedido</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.cliente}">Cliente</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.fecha}">Fecha</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.importe}">Importe</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.estado}">Estado</th>
|
||||||
|
<th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><input type="text" class="form-control form-control-sm input-filter" data-col="id" /></th>
|
||||||
|
<th><input type="text" class="form-control form-control-sm input-filter" data-col="createdBy.fullName" /></th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
<th>
|
||||||
|
<select class="form-select form-select-sm input-filter" data-col="estado">
|
||||||
|
<option value=""></option>
|
||||||
|
<option th:text="#{pedido.estado.aprobado}" value="aprobado"></option>
|
||||||
|
<option th:text="#{pedido.estado.maquetacion}" value="maquetacion"></option>
|
||||||
|
<option th:text="#{pedido.estado.haciendo_ferro}" value="haciendo_ferro"></option>
|
||||||
|
<option th:text="#{pedido.estado.produccion}" value="produccion"></option>
|
||||||
|
<option th:text="#{pedido.estado.terminado}" value="terminado"></option>
|
||||||
|
<option th:text="#{pedido.estado.cancelado}" value="cancelado"></option>
|
||||||
|
</select>
|
||||||
|
</th>
|
||||||
|
<th></th> <!-- Acciones (sin filtro) -->
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||||
|
<th:block layout:fragment="pagejs">
|
||||||
|
<script th:inline="javascript">
|
||||||
|
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script th:src="@{/assets/libs/datatables/datatables.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/dataTables.bootstrap5.min.js}"></script>
|
||||||
|
|
||||||
|
<!-- JS de Buttons y dependencias -->
|
||||||
|
<script th:src="@{/assets/libs/datatables/dataTables.buttons.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/jszip/jszip.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/pdfmake/pdfmake.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/pdfmake/vfs_fonts.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.html5.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.print.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.colVis.min.js}"></script>
|
||||||
|
|
||||||
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/pedidos/pedidos-common.js}"></script>
|
||||||
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/pedidos/pedidos-admin.js}"></script>
|
||||||
|
</th:block>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
<!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/libs/datatables/dataTables.bootstrap5.min.css}" rel="stylesheet" />
|
||||||
|
</th:block>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
|
||||||
|
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}"
|
||||||
|
sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')">
|
||||||
|
|
||||||
|
<th:block layout:fragment="content">
|
||||||
|
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||||
|
<li class="breadcrumb-item" aria-current="page" th:text="#{pedido.module-title}"><a
|
||||||
|
href="/pedidos"></a>
|
||||||
|
Pedidos</li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">
|
||||||
|
<span th:text="#{pedido.pedido} + ' '">Pedido </span><span th:text="${id}"></span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-auto">
|
||||||
|
<div th:insert="~{imprimelibros/direcciones/direccionFacturacionCard ::
|
||||||
|
direccionFacturacionCard(
|
||||||
|
direccion=${direccionFacturacion},
|
||||||
|
pais=${direccionFacturacion != null ? direccionFacturacion.paisNombre : ''}
|
||||||
|
)}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<th:block th:each="linea: ${lineas}">
|
||||||
|
<div
|
||||||
|
th:insert="~{imprimelibros/pedidos/pedidos-linea :: pedido-linea (item=${linea}, isAdmin=${isAdmin})}">
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||||
|
<th:block layout:fragment="pagejs">
|
||||||
|
<script th:inline="javascript">
|
||||||
|
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script th:src="@{/assets/libs/datatables/datatables.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/dataTables.bootstrap5.min.js}"></script>
|
||||||
|
|
||||||
|
<!-- JS de Buttons y dependencias -->
|
||||||
|
<script th:src="@{/assets/libs/datatables/dataTables.buttons.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/jszip/jszip.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/pdfmake/pdfmake.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/pdfmake/vfs_fonts.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.html5.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.print.min.js}"></script>
|
||||||
|
<script th:src="@{/assets/libs/datatables/buttons.colVis.min.js}"></script>
|
||||||
|
|
||||||
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/pedidos/pedidos-view.js}"></script>
|
||||||
|
<script th:if="${isAdmin}" type="module" th:src="@{/assets/js/pages/imprimelibros/pedidos/pedidos-view-admin.js}"></script>
|
||||||
|
</th:block>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -13,6 +13,12 @@
|
|||||||
<span th:text="#{app.imprimir}">Imprimir</span>
|
<span th:text="#{app.imprimir}">Imprimir</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button th:if="${appMode == 'edit'}" type="button"
|
||||||
|
class="btn btn-secondary d-flex align-items-center mx-2 duplicar-btn">
|
||||||
|
<i class="ri-file-copy-2-line me-2"></i>
|
||||||
|
<span th:text="#{presupuesto.duplicar}">Duplicar</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<button th:if="${appMode == 'add' or appMode == 'edit'}" type="button"
|
<button th:if="${appMode == 'add' or appMode == 'edit'}" type="button"
|
||||||
class="btn btn-secondary d-flex align-items-center mx-2 add-cart-btn">
|
class="btn btn-secondary d-flex align-items-center mx-2 add-cart-btn">
|
||||||
<i class="ri-shopping-cart-line me-2"></i>
|
<i class="ri-shopping-cart-line me-2"></i>
|
||||||
|
|||||||
@ -151,6 +151,7 @@
|
|||||||
<div class="col-auto mb-3">
|
<div class="col-auto mb-3">
|
||||||
<label for="cabezada" class="form-label" th:text="#{presupuesto.cabezada}">Cabezada</label>
|
<label for="cabezada" class="form-label" th:text="#{presupuesto.cabezada}">Cabezada</label>
|
||||||
<select class="form-select select2 datos-cubierta tapa-cubierta-summary" id="cabezada">
|
<select class="form-select select2 datos-cubierta tapa-cubierta-summary" id="cabezada">
|
||||||
|
<option value="NOCAB" th:text="#{presupuesto.cabezada-sin-cabezada}">Sin cabezada</option>
|
||||||
<option value="WHI" th:text="#{presupuesto.cabezada-blanca}" selected>Blanca</option>
|
<option value="WHI" th:text="#{presupuesto.cabezada-blanca}" selected>Blanca</option>
|
||||||
<option value="GRE" th:text="#{presupuesto.cabezada-verde}">Verde</option>
|
<option value="GRE" th:text="#{presupuesto.cabezada-verde}">Verde</option>
|
||||||
<option value="BLUE" th:text="#{presupuesto.cabezada-azul}">Azul</option>
|
<option value="BLUE" th:text="#{presupuesto.cabezada-azul}">Azul</option>
|
||||||
|
|||||||
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
<div class="col-9 mx-auto mt-4">
|
<div class="col-9 mx-auto mt-4">
|
||||||
<h5 id="resumen-titulo" class="text-center"></h5>
|
<h5 id="resumen-titulo" class="text-center"></h5>
|
||||||
|
<h6 th:if="${presupuesto?.isReimpresion}" th:text="#{presupuesto.reimpresion}"
|
||||||
|
class="text-uppercase bg-danger text-white text-center">REIMPRESION</h6>
|
||||||
<table id="resumen-tabla-final" class="table table-borderless table-striped mt-3"
|
<table id="resumen-tabla-final" class="table table-borderless table-striped mt-3"
|
||||||
th:data-currency="#{app.currency}">
|
th:data-currency="#{app.currency}">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<h5 th:text="#{presupuesto.resumen-presupuesto}">Resumen presupuesto</h5>
|
<h5 th:text="#{presupuesto.resumen-presupuesto}">Resumen presupuesto</h5>
|
||||||
|
<h6 th:if="${presupuesto?.isReimpresion}" th:text="#{presupuesto.reimpresion}"
|
||||||
|
class="text-uppercase bg-danger text-white text-center">REIMPRESION</h6>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -42,18 +42,21 @@
|
|||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
|
||||||
<input type="hidden" id="presupuesto-id" th:value="${presupuesto.id}" />
|
<input type="hidden" id="presupuesto_id" th:value="${presupuesto.id}" />
|
||||||
|
|
||||||
<div class="row" id="card presupuesto-row animate-fadeInUpBounce">
|
<div class="row" id="card presupuesto-row animate-fadeInUpBounce">
|
||||||
|
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h4 class="card-title mb-0 text-uppercase" th:text="${resumen.titulo}">Resumen del
|
<h4 id="titulo" class="card-title mb-0 text-uppercase" th:text="${resumen.titulo}">Resumen del
|
||||||
presupuesto</h4>
|
presupuesto</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
<div th:if="${presupuesto.isReimpresion}" class="row">
|
||||||
|
<h6 th:text="#{presupuesto.reimpresion}" class="bg-danger py-2 text-center text-white text-uppercase">REIMPRESION</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card col-12 col-sm-9 mx-auto">
|
<div class="card col-12 col-sm-9 mx-auto">
|
||||||
<h5 id="resumen-titulo" class="text-center"></h5>
|
<h5 id="resumen-titulo" class="text-center"></h5>
|
||||||
@ -77,7 +80,9 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr th:if="${resumen['linea0']}">
|
<tr th:if="${resumen['linea0']}">
|
||||||
<td><img style="max-width: 60px; height: auto;" th:src="${resumen['imagen']}" th:alt="${resumen['imagen_alt']}" class="img-fluid" /></td>
|
<td><img style="max-width: 60px; height: auto;"
|
||||||
|
th:src="${resumen['imagen']}" th:alt="${resumen['imagen_alt']}"
|
||||||
|
class="img-fluid" /></td>
|
||||||
<td class="text-start" th:utext="${resumen['linea0'].descripcion}">
|
<td class="text-start" th:utext="${resumen['linea0'].descripcion}">
|
||||||
Descripción 1</td>
|
Descripción 1</td>
|
||||||
<td class="text-end" th:text="${resumen['linea0'].cantidad}">1</td>
|
<td class="text-end" th:text="${resumen['linea0'].cantidad}">1</td>
|
||||||
@ -159,12 +164,50 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-secondary d-flex align-items-center mx-2 add-cart-btn">
|
class="btn btn-secondary d-flex align-items-center mx-2 duplicar-btn">
|
||||||
<i class="ri-shopping-cart-line me-2"></i>
|
<i class="ri-file-copy-2-line me-2"></i>
|
||||||
<span th:text="#{presupuesto.add-to-cart}">Añadir a la cesta</span>
|
<span th:text="#{presupuesto.duplicar}">Duplicar</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-secondary d-flex align-items-center mx-2 reimprimir-btn">
|
||||||
|
<i class="ri-printer-line me-2"></i>
|
||||||
|
<span th:text="#{presupuesto.reimprimir}">Reimprimir</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')">
|
||||||
|
<div class="accordion lefticon-accordion custom-accordionwithicon accordion-border-box mt-3"
|
||||||
|
id="accordionlefticon">
|
||||||
|
<div class="accordion-item material-shadow">
|
||||||
|
<h2 class="accordion-header" id="accordionComentario">
|
||||||
|
<button class="accordion-button collapsed" type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#accor_accordionComentario" aria-expanded="false"
|
||||||
|
aria-controls="accor_accordionComentario">
|
||||||
|
<span
|
||||||
|
th:text="#{presupuesto.comentario-administrador}">Comentario</span>
|
||||||
|
<span class="d-none badge badge-comentario bg-danger ms-1">!</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div id="accor_accordionComentario" class="accordion-collapse collapse"
|
||||||
|
aria-labelledby="accordionComentario"
|
||||||
|
data-bs-parent="#accordionlefticon">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div class="snow-editor" id="comentario" name="comentario"
|
||||||
|
th:attr="data-contenido=${presupuesto.comentario} "
|
||||||
|
style=" height: 300px;">
|
||||||
|
</div> <!-- end Snow-editor-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -189,6 +232,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestos/resumen-view.js}"></script>
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestos/resumen-view.js}"></script>
|
||||||
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestos/duplicate-reprint.js}"></script>
|
||||||
|
|
||||||
</th:block>
|
</th:block>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -33,7 +33,8 @@
|
|||||||
aria-controls="pills-general-data" aria-selected="true">
|
aria-controls="pills-general-data" aria-selected="true">
|
||||||
<i
|
<i
|
||||||
class="ri-information-line fs-5 p-1 bg-soft-primary text-primary rounded-circle align-middle me-2"></i>
|
class="ri-information-line fs-5 p-1 bg-soft-primary text-primary rounded-circle align-middle me-2"></i>
|
||||||
<label class="fs-13 my-2" th:text="#{presupuesto.datos-generales}">Datos Generales</label>
|
<label class="fs-13 my-2" th:text="#{presupuesto.datos-generales}">Datos
|
||||||
|
Generales</label>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
@ -60,7 +61,8 @@
|
|||||||
aria-controls="pills-seleccion-tirada" aria-selected="false">
|
aria-controls="pills-seleccion-tirada" aria-selected="false">
|
||||||
<i
|
<i
|
||||||
class="ri-add-box-line fs-5 p-1 bg-soft-primary text-primary rounded-circle align-middle me-2"></i>
|
class="ri-add-box-line fs-5 p-1 bg-soft-primary text-primary rounded-circle align-middle me-2"></i>
|
||||||
<label class="fs-13 my-2" th:text="#{presupuesto.seleccion-tirada}">Seleccion de tirada</label>
|
<label class="fs-13 my-2" th:text="#{presupuesto.seleccion-tirada}">Seleccion de
|
||||||
|
tirada</label>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
@ -137,6 +139,34 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- end tab content -->
|
<!-- end tab content -->
|
||||||
|
|
||||||
|
<div sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')">
|
||||||
|
<div class="accordion lefticon-accordion custom-accordionwithicon accordion-border-box mt-3"
|
||||||
|
id="accordionlefticon">
|
||||||
|
<div class="accordion-item material-shadow">
|
||||||
|
<h2 class="accordion-header" id="accordionComentario">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#accor_accordionComentario" aria-expanded="false"
|
||||||
|
aria-controls="accor_accordionComentario">
|
||||||
|
<span th:text="#{presupuesto.comentario-administrador}">Comentario</span>
|
||||||
|
<span class="d-none badge badge-comentario bg-danger ms-1">!</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div id="accor_accordionComentario" class="accordion-collapse collapse"
|
||||||
|
aria-labelledby="accordionComentario" data-bs-parent="#accordionlefticon">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div class="snow-editor" id="comentario" name="comentario"
|
||||||
|
th:attr="data-contenido=${presupuesto.comentario} "
|
||||||
|
style=" height: 300px;">
|
||||||
|
</div> <!-- end Snow-editor-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<!-- end card body -->
|
<!-- end card body -->
|
||||||
|
|||||||
@ -10,6 +10,8 @@
|
|||||||
</th:block>
|
</th:block>
|
||||||
<th:block layout:fragment="pagecss">
|
<th:block layout:fragment="pagecss">
|
||||||
<link th:href="@{/assets/css/presupuestador.css}" rel="stylesheet" />
|
<link th:href="@{/assets/css/presupuestador.css}" rel="stylesheet" />
|
||||||
|
<link sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')"
|
||||||
|
th:href="@{/assets/libs/quill/quill.snow.css}" rel="stylesheet" type="text/css" />
|
||||||
</th:block>
|
</th:block>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -19,62 +21,77 @@
|
|||||||
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}"
|
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}"
|
||||||
sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')">
|
sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')">
|
||||||
|
|
||||||
<th:block layout:fragment="content">
|
<th:block layout:fragment="content">
|
||||||
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||||
|
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<nav aria-label="breadcrumb">
|
<nav aria-label="breadcrumb">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||||
<li class="breadcrumb-item"><a href="/presupuesto" th:text="#{presupuesto.title}"></a></li>
|
<li class="breadcrumb-item"><a href="/presupuesto" th:text="#{presupuesto.title}"></a></li>
|
||||||
<li class="breadcrumb-item active" aria-current="page" th:if="${appMode == 'add'}" th:text="#{presupuesto.add}">
|
<li class="breadcrumb-item active" aria-current="page" th:if="${appMode == 'add'}"
|
||||||
|
th:text="#{presupuesto.add}">
|
||||||
Nuevo presupuesto
|
Nuevo presupuesto
|
||||||
</li>
|
</li>
|
||||||
<li class="breadcrumb-item active" aria-current="page" th:text="#{presupuesto.editar.title}" th:if="${appMode == 'edit'}">
|
<li class="breadcrumb-item active" aria-current="page" th:text="#{presupuesto.editar.title}"
|
||||||
|
th:if="${appMode == 'edit'}">
|
||||||
Editar presupuesto
|
Editar presupuesto
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container-fluid">
|
|
||||||
|
|
||||||
<!-- alert info -->
|
|
||||||
<div th:if="${appMode} == 'view'" class="alert alert-warning fade show" role="alert">
|
|
||||||
<i class="ri-information-fill me-1 align-middle"></i>
|
|
||||||
<span th:text="#{presupuesto.info.presupuestos-anonimos-view}"></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div th:insert="~{imprimelibros/presupuestos/presupuestador :: presupuestador}"></div>
|
<div class="container-fluid">
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</th:block>
|
|
||||||
|
|
||||||
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
<!-- alert info -->
|
||||||
<th:block layout:fragment="pagejs">
|
<div th:if="${appMode} == 'view'" class="alert alert-warning fade show" role="alert">
|
||||||
<script th:inline="javascript">
|
<i class="ri-information-fill me-1 align-middle"></i>
|
||||||
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
<span th:text="#{presupuesto.info.presupuestos-anonimos-view}"></span>
|
||||||
</script>
|
</div>
|
||||||
|
|
||||||
<!-- JS de Buttons y dependencias -->
|
<div th:insert="~{imprimelibros/presupuestos/presupuestador :: presupuestador}"></div>
|
||||||
<div th:if="${appMode} == 'view'">
|
</div>
|
||||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-publicos.js}"></script>
|
|
||||||
</div>
|
|
||||||
<div th:if="${appMode} == 'edit'">
|
|
||||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-privado.js}"></script>
|
|
||||||
</div>
|
|
||||||
<div th:if="${appMode} == 'add'">
|
|
||||||
<div th:if="${mode} == 'public'">
|
|
||||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-publicos-add.js}"></script>
|
|
||||||
</div>
|
</div>
|
||||||
<div th:if="${mode} != 'public'">
|
</th:block>
|
||||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-privado.js}"></script>
|
|
||||||
|
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||||
|
<th:block layout:fragment="pagejs">
|
||||||
|
<script th:inline="javascript">
|
||||||
|
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- JS de Buttons y dependencias -->
|
||||||
|
<div th:if="${appMode} == 'view'">
|
||||||
|
<script type="module"
|
||||||
|
th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-publicos.js}"></script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div th:if="${appMode} == 'edit'">
|
||||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/presupuesto-maquetacion.js}"></script>
|
<script type="module"
|
||||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/presupuesto-marcapaginas.js}"></script>
|
th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-privado.js}"></script>
|
||||||
</th:block>
|
</div>
|
||||||
|
<div th:if="${appMode} == 'add'">
|
||||||
|
<div th:if="${mode} == 'public'">
|
||||||
|
<script type="module"
|
||||||
|
th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-publicos-add.js}"></script>
|
||||||
|
</div>
|
||||||
|
<div th:if="${mode} != 'public'">
|
||||||
|
<script type="module"
|
||||||
|
th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-privado.js}"></script>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="module"
|
||||||
|
th:src="@{/assets/js/pages/imprimelibros/presupuestador/presupuesto-maquetacion.js}"></script>
|
||||||
|
<script type="module"
|
||||||
|
th:src="@{/assets/js/pages/imprimelibros/presupuestador/presupuesto-marcapaginas.js}"></script>
|
||||||
|
|
||||||
|
<script sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')"
|
||||||
|
th:src="@{/assets/libs/quill/quill.min.js}"></script>
|
||||||
|
<script sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')"
|
||||||
|
th:src="@{/assets/js/pages/imprimelibros/presupuestador/text-editor.js}"></script>
|
||||||
|
|
||||||
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestos/duplicate-reprint.js}"></script>
|
||||||
|
</th:block>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<th scope="col" th:text="#{presupuesto.tabla.estado}">Estado</th>
|
<th scope="col" th:text="#{presupuesto.tabla.estado}">Estado</th>
|
||||||
<th scope="col" th:text="#{presupuesto.tabla.total-iva}">Total con IVA</th>
|
<th scope="col" th:text="#{presupuesto.tabla.total-iva}">Total con IVA</th>
|
||||||
<th scope="col" th:text="#{presupuesto.tabla.updated-at}">Actualizado el</th>
|
<th scope="col" th:text="#{presupuesto.tabla.updated-at}">Actualizado el</th>
|
||||||
<th scope="col" th:text="#{presupuesto.tabla.acciones}">Acciones</th>
|
<th style="min-width: 100px;" scope="col" th:text="#{presupuesto.tabla.acciones}">Acciones</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="id" /></th>
|
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="id" /></th>
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
<th scope="col" th:text="#{presupuesto.tabla.estado}">Estado</th>
|
<th scope="col" th:text="#{presupuesto.tabla.estado}">Estado</th>
|
||||||
<th scope="col" th:text="#{presupuesto.tabla.total-iva}">Total con IVA</th>
|
<th scope="col" th:text="#{presupuesto.tabla.total-iva}">Total con IVA</th>
|
||||||
<th scope="col" th:text="#{presupuesto.tabla.updated-at}">Actualizado el</th>
|
<th scope="col" th:text="#{presupuesto.tabla.updated-at}">Actualizado el</th>
|
||||||
<th scope="col" th:text="#{presupuesto.tabla.acciones}">Acciones</th>
|
<th style="min-width: 100px;" scope="col" th:text="#{presupuesto.tabla.acciones}">Acciones</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="id" /></th>
|
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="id" /></th>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ public class envioCarroTest {
|
|||||||
void addPedido(){
|
void addPedido(){
|
||||||
|
|
||||||
Locale locale = Locale.forLanguageTag("es-ES");
|
Locale locale = Locale.forLanguageTag("es-ES");
|
||||||
cartService.crearPedido(carritoId, locale);
|
//cartService.crearPedido(carritoId, null, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -17,10 +17,10 @@ public class savePresupuestosTest {
|
|||||||
@Test
|
@Test
|
||||||
void testGuardarPresupuesto() {
|
void testGuardarPresupuesto() {
|
||||||
Locale locale = new Locale("es", "ES");
|
Locale locale = new Locale("es", "ES");
|
||||||
Long resultado = cartService.crearPedido(9L, locale);
|
//Long resultado = cartService.crearPedido(9L, null, locale);
|
||||||
|
|
||||||
System.out.println("📦 Presupuesto guardado:");
|
System.out.println("📦 Presupuesto guardado:");
|
||||||
System.out.println(resultado);
|
//System.out.println(resultado);
|
||||||
|
|
||||||
// Aquí irían las aserciones para verificar que el presupuesto se guardó correctamente
|
// Aquí irían las aserciones para verificar que el presupuesto se guardó correctamente
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user