Compare commits

41 Commits

Author SHA1 Message Date
07380cf319 Merge branch 'fix/guardas' into 'main'
arreglado

See merge request jjimenez/erp-imprimelibros!49
2026-02-26 16:26:05 +00:00
1f11e4cd83 arreglado 2026-02-26 17:24:49 +01:00
efb3a5e833 Merge branch 'mod/modificaciones_problemas_varios' into 'main'
solucionado problema con cargar solapas de cubierta y formato. También añadido...

See merge request jjimenez/erp-imprimelibros!48
2026-02-26 11:11:52 +00:00
bc09810d30 terminados primeras modificaciones 2026-02-26 12:11:21 +01:00
27eabde40f solucionado problema con cargar solapas de cubierta y formato. También añadido los lomos desde sk al guardar 2026-02-25 21:09:04 +01:00
5286f12d76 trabajando en obtener lomos 2026-02-25 15:08:30 +01:00
355c5b6019 trabajando2 2026-02-24 20:57:51 +01:00
e4c1692ef0 añadido lomo 2026-02-24 14:50:27 +01:00
f3d96361a0 corregidos varios problemas 2026-02-24 14:32:47 +01:00
b7453b1138 trabajando 2026-02-24 13:24:39 +01:00
1c5d501e6d Merge branch 'feat/titulos_en_pedidoslist' into 'main'
Feat/titulos en pedidoslist

See merge request jjimenez/erp-imprimelibros!47
2026-02-15 11:54:13 +00:00
87368c434a hecho 2026-02-15 12:53:47 +01:00
a9cdc6d5d6 hecho 2026-02-15 12:53:19 +01:00
1a04d5ace1 Merge branch 'fix/listado_usuarios_infinito' into 'main'
arreglado, no se estaba mandando paginación

See merge request jjimenez/erp-imprimelibros!46
2026-02-15 10:25:48 +00:00
cc8d1b8d44 arreglado, no se estaba mandando paginación 2026-02-15 11:24:50 +01:00
6121562f9b Merge branch 'mod/order_user_select_by_name' into 'main'
hecho

See merge request jjimenez/erp-imprimelibros!45
2026-02-14 10:50:28 +00:00
f73f108c71 hecho 2026-02-14 11:50:06 +01:00
60080eb89e Merge branch 'fix/problema_dir_fact_multipedido' into 'main'
solucionado

See merge request jjimenez/erp-imprimelibros!44
2026-02-14 10:44:24 +00:00
e9f75b85c5 solucionado 2026-02-14 11:44:05 +01:00
ce6f4085a0 Merge branch 'mod/listados_cartItem' into 'main'
se modifican los item de los listados para que se abran en una nueva pestaña....

See merge request jjimenez/erp-imprimelibros!43
2026-02-14 10:19:32 +00:00
97ee53cefd se modifican los item de los listados para que se abran en una nueva pestaña. Se añade un badge al carrito item para editar el presupuesto 2026-02-14 11:19:07 +01:00
014079cf04 Merge branch 'fix/tipo_entrega' into 'main'
quitado tipo de entrega de todos sitios. Trabajando en el iva dependiendo de...

See merge request jjimenez/erp-imprimelibros!42
2026-02-14 10:06:36 +00:00
8acd0bb890 terminado. Ahora se mira la direcicón de facturación para el iva. modificado, el valor en pedido, factura, presupuesto 2026-02-14 11:06:12 +01:00
fffc2b91c1 quitado tipo de entrega de todos sitios. Trabajando en el iva dependiendo de la dirección de facturación 2026-02-13 21:19:25 +01:00
cef0af1bd2 Merge branch 'fix/problema_impersonate' into 'main'
arreglado el problema http. el superadmin no puede ser impersonate

See merge request jjimenez/erp-imprimelibros!41
2026-02-09 11:19:53 +00:00
8282c92419 arreglado el problema http. el superadmin no puede ser impersonate 2026-02-09 12:19:13 +01:00
433a055b14 Merge branch 'fix/actualizar_estado_pedido' into 'main'
Fix/actualizar estado pedido

See merge request jjimenez/erp-imprimelibros!40
2026-02-08 17:51:14 +00:00
fe4d180e2d terminado 2026-02-08 18:50:48 +01:00
cc2d2ef193 vamos a corregir el error 2026-02-08 18:12:08 +01:00
11a5918c37 Merge branch 'feat/add_local_perfil' into 'main'
hecho

See merge request jjimenez/erp-imprimelibros!39
2026-02-08 12:01:14 +00:00
88769ddaeb hecho 2026-02-08 13:00:51 +01:00
9acb105127 Merge branch 'jjimenez-main-patch-54099' into 'main'
Edit .gitignore

See merge request jjimenez/erp-imprimelibros!38
2026-02-08 11:46:19 +00:00
6dab15afbc Edit .gitignore 2026-02-08 11:46:08 +00:00
a0783c2062 Merge branch 'feat/add_presup_public_to_client' into 'main'
hehco

See merge request jjimenez/erp-imprimelibros!37
2026-02-08 11:44:40 +00:00
d0ccfb5626 hehco 2026-02-08 12:44:17 +01:00
2e569a7ffd Merge branch 'feat/home_fidelidad' into 'main'
Feat/home fidelidad

See merge request jjimenez/erp-imprimelibros!36
2026-02-08 11:34:00 +00:00
bc8ce4fa81 Merge branch 'main' into 'feat/home_fidelidad'
# Conflicts:
#   logs/erp.log
2026-02-08 11:33:53 +00:00
1bfe0cf3a2 terminado banner fidelidad 2026-02-08 12:32:44 +01:00
61e55e014f trabajando en el home 2026-02-04 20:36:08 +01:00
06a3521f6b Merge branch 'feat/perfil' into 'main'
añadido perfil a IL

See merge request jjimenez/erp-imprimelibros!35
2026-02-04 18:27:18 +00:00
ecf1472f58 añadido perfil a IL 2026-02-04 19:26:44 +01:00
68 changed files with 4754 additions and 1027 deletions

4
.gitignore vendored
View File

@ -33,4 +33,6 @@ build/
.vscode/ .vscode/
### Logs ### ### Logs ###
erp-*.log /Logs/
erp.log
erp*.log

View File

@ -1,750 +0,0 @@
2026-02-04 18:35:33 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:35:33 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:35:33 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Starting...
2026-02-04 18:35:33 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Added connection com.mysql.cj.jdbc.ConnectionImpl@599698cb
2026-02-04 18:35:33 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Start completed.
2026-02-04 18:35:34 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:35:34 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:35:34 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:35:34 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:35:34 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:35:34 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:35:34 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:35:34 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:35:34 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:35:34 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:35:34 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:35:34 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:35:34 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:35:34 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.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:35:34 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)
2026-02-04 18:35:35 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 2.346 seconds (process running for 37.495)
2026-02-04 18:43:19 INFO [Thread-7] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown initiated...
2026-02-04 18:43:19 INFO [Thread-7] com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown completed.
2026-02-04 18:43:19 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:43:19 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:43:20 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Starting...
2026-02-04 18:43:20 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-3 - Added connection com.mysql.cj.jdbc.ConnectionImpl@ba8f976
2026-02-04 18:43:20 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Start completed.
2026-02-04 18:43:20 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:43:20 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:43:20 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:43:20 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:43:20 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:43:20 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:43:20 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:43:20 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:43:20 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:43:20 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:43:20 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:43:20 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:43:20 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:43:20 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.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:43:21 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)
2026-02-04 18:43:22 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 2.456 seconds (process running for 504.275)
2026-02-04 18:43:54 INFO [Thread-11] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Shutdown initiated...
2026-02-04 18:43:54 INFO [Thread-11] com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Shutdown completed.
2026-02-04 18:43:54 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:43:54 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:43:54 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-4 - Starting...
2026-02-04 18:43:54 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-4 - Added connection com.mysql.cj.jdbc.ConnectionImpl@12309f24
2026-02-04 18:43:54 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-4 - Start completed.
2026-02-04 18:43:55 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:43:55 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:43:55 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:43:55 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:43:55 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:43:55 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:43:55 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:43:55 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:43:55 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:43:55 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:43:55 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:43:55 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:43:55 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:43:55 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-4)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:43:55 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)
2026-02-04 18:43:56 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.963 seconds (process running for 538.089)
2026-02-04 18:44:05 INFO [Thread-15] com.zaxxer.hikari.HikariDataSource - HikariPool-4 - Shutdown initiated...
2026-02-04 18:44:05 INFO [Thread-15] com.zaxxer.hikari.HikariDataSource - HikariPool-4 - Shutdown completed.
2026-02-04 18:44:05 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:44:05 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:44:05 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-5 - Starting...
2026-02-04 18:44:05 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-5 - Added connection com.mysql.cj.jdbc.ConnectionImpl@66edfae8
2026-02-04 18:44:05 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-5 - Start completed.
2026-02-04 18:44:05 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:44:05 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:44:05 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:44:05 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:44:05 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:44:05 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:44:05 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:44:05 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:44:05 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:44:05 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:44:05 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:44:05 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:44:05 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:44:05 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-5)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:44:06 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)
2026-02-04 18:44:06 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.791 seconds (process running for 548.815)
2026-02-04 18:44:26 INFO [Thread-19] com.zaxxer.hikari.HikariDataSource - HikariPool-5 - Shutdown initiated...
2026-02-04 18:44:26 INFO [Thread-19] com.zaxxer.hikari.HikariDataSource - HikariPool-5 - Shutdown completed.
2026-02-04 18:44:26 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:44:26 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:44:27 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-6 - Starting...
2026-02-04 18:44:27 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-6 - Added connection com.mysql.cj.jdbc.ConnectionImpl@61394b
2026-02-04 18:44:27 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-6 - Start completed.
2026-02-04 18:44:27 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:44:27 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:44:27 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:44:27 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:44:27 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:44:27 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:44:27 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:44:27 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:44:27 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:44:27 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:44:27 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:44:27 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:44:27 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:44:27 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-6)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:44:27 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)
2026-02-04 18:44:28 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.652 seconds (process running for 570.098)
2026-02-04 18:44:55 INFO [Thread-23] com.zaxxer.hikari.HikariDataSource - HikariPool-6 - Shutdown initiated...
2026-02-04 18:44:55 INFO [Thread-23] com.zaxxer.hikari.HikariDataSource - HikariPool-6 - Shutdown completed.
2026-02-04 18:44:55 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:44:55 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:44:56 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-7 - Starting...
2026-02-04 18:44:56 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-7 - Added connection com.mysql.cj.jdbc.ConnectionImpl@5d7dccf0
2026-02-04 18:44:56 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-7 - Start completed.
2026-02-04 18:44:56 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:44:56 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:44:56 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:44:56 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:44:56 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:44:56 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:44:56 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:44:56 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:44:56 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:44:56 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:44:56 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:44:56 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:44:56 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:44:56 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-7)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:44:56 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)
2026-02-04 18:44:57 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.656 seconds (process running for 599.118)
2026-02-04 18:47:33 INFO [Thread-27] com.zaxxer.hikari.HikariDataSource - HikariPool-7 - Shutdown initiated...
2026-02-04 18:47:33 INFO [Thread-27] com.zaxxer.hikari.HikariDataSource - HikariPool-7 - Shutdown completed.
2026-02-04 18:47:33 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:47:33 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:47:34 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-8 - Starting...
2026-02-04 18:47:34 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-8 - Added connection com.mysql.cj.jdbc.ConnectionImpl@60a2a425
2026-02-04 18:47:34 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-8 - Start completed.
2026-02-04 18:47:34 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:47:34 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:47:34 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:47:34 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:47:34 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:47:34 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:47:34 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:47:34 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:47:34 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:47:34 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:47:34 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:47:34 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:47:34 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:47:34 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-8)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:47:35 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)
2026-02-04 18:47:35 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.992 seconds (process running for 757.809)
2026-02-04 18:47:45 INFO [Thread-31] com.zaxxer.hikari.HikariDataSource - HikariPool-8 - Shutdown initiated...
2026-02-04 18:47:45 INFO [Thread-31] com.zaxxer.hikari.HikariDataSource - HikariPool-8 - Shutdown completed.
2026-02-04 18:47:45 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:47:45 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:47:45 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-9 - Starting...
2026-02-04 18:47:45 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-9 - Added connection com.mysql.cj.jdbc.ConnectionImpl@e8288e7
2026-02-04 18:47:45 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-9 - Start completed.
2026-02-04 18:47:45 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:47:45 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:47:45 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:47:45 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:47:45 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:47:45 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:47:45 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:47:45 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:47:45 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:47:45 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:47:45 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:47:45 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:47:45 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:47:46 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-9)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:47:46 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)
2026-02-04 18:47:47 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.921 seconds (process running for 769.005)
2026-02-04 18:48:25 INFO [Thread-35] com.zaxxer.hikari.HikariDataSource - HikariPool-9 - Shutdown initiated...
2026-02-04 18:48:25 INFO [Thread-35] com.zaxxer.hikari.HikariDataSource - HikariPool-9 - Shutdown completed.
2026-02-04 18:48:25 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:48:25 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:48:26 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-10 - Starting...
2026-02-04 18:48:26 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-10 - Added connection com.mysql.cj.jdbc.ConnectionImpl@43261ed1
2026-02-04 18:48:26 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-10 - Start completed.
2026-02-04 18:48:26 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:48:26 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:48:26 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:48:26 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:48:26 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:48:26 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:48:26 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:48:26 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:48:26 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:48:26 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:48:26 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:48:26 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:48:26 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:48:26 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-10)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:48: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)
2026-02-04 18:48:27 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.642 seconds (process running for 809.265)
2026-02-04 18:49:01 ERROR [http-nio-8080-exec-6] o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.context.NoSuchMessageException: No message found under code 'usuarios.delete.title' for locale 'es_ES'.] with root cause
org.springframework.context.NoSuchMessageException: No message found under code 'usuarios.delete.title' for locale 'es_ES'.
at org.springframework.context.support.AbstractMessageSource.getMessage(AbstractMessageSource.java:166) ~[spring-context-6.2.15.jar:6.2.15]
at com.imprimelibros.erp.i18n.TranslationService.getTranslations(TranslationService.java:22) ~[classes/:na]
at com.imprimelibros.erp.users.UserController.list(UserController.java:90) ~[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.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:360) ~[spring-aop-6.2.15.jar:6.2.15]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.2.15.jar:6.2.15]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.2.15.jar:6.2.15]
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.proceed(AuthorizationManagerBeforeMethodInterceptor.java:268) ~[spring-security-core-6.5.7.jar:6.5.7]
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.attemptAuthorization(AuthorizationManagerBeforeMethodInterceptor.java:263) ~[spring-security-core-6.5.7.jar:6.5.7]
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.invoke(AuthorizationManagerBeforeMethodInterceptor.java:196) ~[spring-security-core-6.5.7.jar:6.5.7]
at org.springframework.security.config.annotation.method.configuration.DeferringMethodInterceptor.invoke(DeferringMethodInterceptor.java:44) ~[spring-security-config-6.5.7.jar:6.5.7]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.15.jar:6.2.15]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:728) ~[spring-aop-6.2.15.jar:6.2.15]
at com.imprimelibros.erp.users.UserController$$SpringCGLIB$$0.list(<generated>) ~[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.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:991) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:896) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.2.15.jar:6.2.15]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.50.jar:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.2.15.jar:6.2.15]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.50.jar:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter.doFilter(ResourceUrlEncodingFilter.java:66) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:490) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:351) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:83) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:129) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:114) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:105) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:151) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:129) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:235) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:229) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:334) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:225) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.5.7.jar:6.5.7]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) ~[spring-security-config-6.5.7.jar:6.5.7]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) ~[spring-webmvc-6.2.15.jar:6.2.15]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) ~[spring-security-config-6.5.7.jar:6.5.7]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) ~[spring-web-6.2.15.jar:6.2.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:91) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:110) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.2.15.jar:6.2.15]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.15.jar:6.2.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.2.15.jar:6.2.15]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:165) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:88) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:113) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:83) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:72) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1774) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:973) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:491) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[tomcat-embed-core-10.1.50.jar:10.1.50]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
2026-02-04 18:49:10 INFO [Thread-39] com.zaxxer.hikari.HikariDataSource - HikariPool-10 - Shutdown initiated...
2026-02-04 18:49:10 INFO [Thread-39] com.zaxxer.hikari.HikariDataSource - HikariPool-10 - Shutdown completed.
2026-02-04 18:49:11 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:49:11 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:49:11 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-11 - Starting...
2026-02-04 18:49:11 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-11 - Added connection com.mysql.cj.jdbc.ConnectionImpl@24d7a4bc
2026-02-04 18:49:11 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-11 - Start completed.
2026-02-04 18:49:12 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:49:12 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:49:12 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:49:12 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:49:12 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:49:12 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:49:12 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:49:12 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:49:12 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:49:12 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:49:12 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:49:12 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:49:12 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:49:12 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-11)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:49:12 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)
2026-02-04 18:49:13 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 2.177 seconds (process running for 855.092)
2026-02-04 18:49:15 WARN [http-nio-8080-exec-8] org.hibernate.orm.query - HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory
2026-02-04 18:49:27 INFO [Thread-43] com.zaxxer.hikari.HikariDataSource - HikariPool-11 - Shutdown initiated...
2026-02-04 18:49:27 INFO [Thread-43] com.zaxxer.hikari.HikariDataSource - HikariPool-11 - Shutdown completed.
2026-02-04 18:49:27 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:49:27 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:49:28 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-12 - Starting...
2026-02-04 18:49:28 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-12 - Added connection com.mysql.cj.jdbc.ConnectionImpl@67beba86
2026-02-04 18:49:28 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-12 - Start completed.
2026-02-04 18:49:28 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:49:28 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:49:28 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:49:28 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:49:28 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:49:28 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:49:28 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:49:28 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:49:28 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:49:28 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:49:28 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:49:28 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:49:28 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:49:28 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-12)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:49:28 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)
2026-02-04 18:49:29 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.497 seconds (process running for 871.145)
2026-02-04 18:49:37 INFO [Thread-47] com.zaxxer.hikari.HikariDataSource - HikariPool-12 - Shutdown initiated...
2026-02-04 18:49:37 INFO [Thread-47] com.zaxxer.hikari.HikariDataSource - HikariPool-12 - Shutdown completed.
2026-02-04 18:49:38 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:49:38 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:49:38 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-13 - Starting...
2026-02-04 18:49:38 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-13 - Added connection com.mysql.cj.jdbc.ConnectionImpl@182d31b7
2026-02-04 18:49:38 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-13 - Start completed.
2026-02-04 18:49:38 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:49:38 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:49:38 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:49:38 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:49:38 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:49:38 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:49:38 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:49:38 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:49:38 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:49:38 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:49:38 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:49:38 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:49:38 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:49:38 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-13)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:49:38 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)
2026-02-04 18:49:39 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.514 seconds (process running for 881.484)
2026-02-04 18:49:54 INFO [Thread-51] com.zaxxer.hikari.HikariDataSource - HikariPool-13 - Shutdown initiated...
2026-02-04 18:49:54 INFO [Thread-51] com.zaxxer.hikari.HikariDataSource - HikariPool-13 - Shutdown completed.
2026-02-04 18:49:54 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:49:54 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:49:55 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-14 - Starting...
2026-02-04 18:49:55 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-14 - Added connection com.mysql.cj.jdbc.ConnectionImpl@1462c521
2026-02-04 18:49:55 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-14 - Start completed.
2026-02-04 18:49:55 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:49:55 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:49:55 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:49:55 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:49:55 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:49:55 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:49:55 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:49:55 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:49:55 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:49:55 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:49:55 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:49:55 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:49:55 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:49:55 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-14)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:49:56 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)
2026-02-04 18:49:56 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.989 seconds (process running for 898.859)
2026-02-04 18:51:14 WARN [http-nio-8080-exec-8] org.hibernate.orm.query - HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory
2026-02-04 18:52:23 INFO [Thread-55] com.zaxxer.hikari.HikariDataSource - HikariPool-14 - Shutdown initiated...
2026-02-04 18:52:23 INFO [Thread-55] com.zaxxer.hikari.HikariDataSource - HikariPool-14 - Shutdown completed.
2026-02-04 18:52:23 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:52:23 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:52:24 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-15 - Starting...
2026-02-04 18:52:24 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-15 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6ab79f28
2026-02-04 18:52:24 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-15 - Start completed.
2026-02-04 18:52:24 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:52:24 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:52:24 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:52:24 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:52:24 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:52:24 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:52:24 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:52:24 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:52:24 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:52:24 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:52:24 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:52:24 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:52:24 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:52:24 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-15)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:52: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)
2026-02-04 18:52:25 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.403 seconds (process running for 1047.183)
2026-02-04 18:52:34 INFO [Thread-59] com.zaxxer.hikari.HikariDataSource - HikariPool-15 - Shutdown initiated...
2026-02-04 18:52:34 INFO [Thread-59] com.zaxxer.hikari.HikariDataSource - HikariPool-15 - Shutdown completed.
2026-02-04 18:52:34 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:52:34 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:52:34 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-16 - Starting...
2026-02-04 18:52:34 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-16 - Added connection com.mysql.cj.jdbc.ConnectionImpl@36aac189
2026-02-04 18:52:34 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-16 - Start completed.
2026-02-04 18:52:34 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:52:34 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:52:34 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:52:34 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:52:34 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:52:34 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:52:34 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:52:34 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:52:34 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:52:34 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:52:34 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:52:34 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:52:34 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:52:34 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-16)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:52:35 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)
2026-02-04 18:52:35 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.438 seconds (process running for 1057.515)
2026-02-04 18:52:47 INFO [Thread-63] com.zaxxer.hikari.HikariDataSource - HikariPool-16 - Shutdown initiated...
2026-02-04 18:52:47 INFO [Thread-63] com.zaxxer.hikari.HikariDataSource - HikariPool-16 - Shutdown completed.
2026-02-04 18:52:47 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:52:47 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:52:48 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-17 - Starting...
2026-02-04 18:52:48 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-17 - Added connection com.mysql.cj.jdbc.ConnectionImpl@2cd22f84
2026-02-04 18:52:48 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-17 - Start completed.
2026-02-04 18:52:48 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:52:48 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:52:48 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:52:48 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:52:48 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:52:48 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:52:48 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:52:48 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:52:48 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:52:48 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:52:48 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:52:48 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:52:48 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:52:48 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-17)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:52:48 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)
2026-02-04 18:52:49 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.469 seconds (process running for 1070.921)
2026-02-04 18:52:57 INFO [Thread-67] com.zaxxer.hikari.HikariDataSource - HikariPool-17 - Shutdown initiated...
2026-02-04 18:52:57 INFO [Thread-67] com.zaxxer.hikari.HikariDataSource - HikariPool-17 - Shutdown completed.
2026-02-04 18:52:57 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:52:57 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:52:58 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-18 - Starting...
2026-02-04 18:52:58 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-18 - Added connection com.mysql.cj.jdbc.ConnectionImpl@2e97a257
2026-02-04 18:52:58 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-18 - Start completed.
2026-02-04 18:52:58 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:52:58 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:52:58 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:52:58 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:52:58 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:52:58 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:52:58 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:52:58 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:52:58 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:52:58 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:52:58 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:52:58 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:52:58 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:52:58 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-18)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:52:58 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)
2026-02-04 18:52:59 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.435 seconds (process running for 1081.278)
2026-02-04 18:53:23 INFO [Thread-71] com.zaxxer.hikari.HikariDataSource - HikariPool-18 - Shutdown initiated...
2026-02-04 18:53:23 INFO [Thread-71] com.zaxxer.hikari.HikariDataSource - HikariPool-18 - Shutdown completed.
2026-02-04 18:53:23 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:53:23 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:53:24 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-19 - Starting...
2026-02-04 18:53:24 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-19 - Added connection com.mysql.cj.jdbc.ConnectionImpl@5f5e4685
2026-02-04 18:53:24 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-19 - Start completed.
2026-02-04 18:53:24 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:53:24 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:53:24 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:53:24 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:53:24 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:53:24 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:53:24 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:53:24 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:53:24 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:53:24 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:53:24 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:53:24 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:53:24 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:53:24 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-19)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:53: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)
2026-02-04 18:53:25 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.498 seconds (process running for 1106.977)
2026-02-04 18:53:27 INFO [Thread-75] com.zaxxer.hikari.HikariDataSource - HikariPool-19 - Shutdown initiated...
2026-02-04 18:53:27 INFO [Thread-75] com.zaxxer.hikari.HikariDataSource - HikariPool-19 - Shutdown completed.
2026-02-04 18:53:27 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Starting ErpApplication using Java 21.0.10 with PID 14776 (C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros\target\classes started by jjime in C:\Users\jjime\Documents\00_DESIGN_NOTEBOOK\PROGRAMMING\erp-imprimelibros)
2026-02-04 18:53:27 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - The following 1 profile is active: "dev"
2026-02-04 18:53:28 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-20 - Starting...
2026-02-04 18:53:28 INFO [restartedMain] com.zaxxer.hikari.pool.HikariPool - HikariPool-20 - Added connection com.mysql.cj.jdbc.ConnectionImpl@5ebd6ac7
2026-02-04 18:53:28 INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-20 - Start completed.
2026-02-04 18:53:28 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:53:28 INFO [restartedMain] liquibase.ui - Database is up to date, no changesets to execute
2026-02-04 18:53:28 INFO [restartedMain] liquibase.changelog - Reading from imprimelibros.DATABASECHANGELOG
2026-02-04 18:53:28 INFO [restartedMain] liquibase.util - UPDATE SUMMARY
2026-02-04 18:53:28 INFO [restartedMain] liquibase.util - Run: 0
2026-02-04 18:53:28 INFO [restartedMain] liquibase.util - Previously run: 69
2026-02-04 18:53:28 INFO [restartedMain] liquibase.util - Filtered out: 0
2026-02-04 18:53:28 INFO [restartedMain] liquibase.util - -------------------------------
2026-02-04 18:53:28 INFO [restartedMain] liquibase.util - Total change sets: 69
2026-02-04 18:53:28 INFO [restartedMain] liquibase.util - Update summary generated
2026-02-04 18:53:28 INFO [restartedMain] liquibase.command - Command execution complete
2026-02-04 18:53:28 INFO [restartedMain] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2026-02-04 18:53:28 INFO [restartedMain] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
2026-02-04 18:53:28 INFO [restartedMain] o.hibernate.orm.connections.pooling - HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-20)']
Database driver: undefined/unknown
Database version: 8.0.45
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-04 18:53:28 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)
2026-02-04 18:53:29 INFO [restartedMain] c.imprimelibros.erp.ErpApplication - Started ErpApplication in 1.379 seconds (process running for 1110.923)
2026-02-04 18:53:33 WARN [http-nio-8080-exec-7] org.hibernate.orm.query - HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory
2026-02-04 18:53:50 WARN [http-nio-8080-exec-7] org.hibernate.orm.query - HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory
2026-02-04 18:54:32 WARN [http-nio-8080-exec-7] org.hibernate.orm.query - HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory
2026-02-04 18:54:56 WARN [http-nio-8080-exec-3] org.hibernate.orm.query - HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory
2026-02-04 18:55:47 WARN [http-nio-8080-exec-8] org.hibernate.orm.query - HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory

10
pom.xml
View File

@ -191,6 +191,16 @@
<systemPath>${project.basedir}/src/main/resources/lib/org.json.jar</systemPath> <systemPath>${project.basedir}/src/main/resources/lib/org.json.jar</systemPath>
</dependency> </dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.17</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.17</version>
</dependency>
</dependencies> </dependencies>

View File

@ -80,7 +80,7 @@ public class CartController {
else if (direcciones != null && direcciones.containsKey("direcciones")) else if (direcciones != null && direcciones.containsKey("direcciones"))
model.addAttribute("direcciones", direcciones.get("direcciones")); model.addAttribute("direcciones", direcciones.get("direcciones"));
var summary = service.getCartSummary(cart, locale); var summary = service.getCartSummary(cart, locale, true);
model.addAttribute("cartSummary", summary); model.addAttribute("cartSummary", summary);
if (summary.get("errorShipmentCost") != null && (Boolean) summary.get("errorShipmentCost")) if (summary.get("errorShipmentCost") != null && (Boolean) summary.get("errorShipmentCost"))
model.addAttribute("errorEnvio", true); model.addAttribute("errorEnvio", true);
@ -165,7 +165,7 @@ public class CartController {
try { try {
service.updateCart(id, updateRequest); service.updateCart(id, updateRequest);
var cartSummary = service.getCartSummary(service.getCartById(id), locale); var cartSummary = service.getCartSummary(service.getCartById(id), locale, true);
model.addAttribute("cartSummary", cartSummary); model.addAttribute("cartSummary", cartSummary);
return "imprimelibros/cart/_cartSummary :: cartSummary(summary=${cartSummary})"; return "imprimelibros/cart/_cartSummary :: cartSummary(summary=${cartSummary})";

View File

@ -163,7 +163,7 @@ public class CartService {
return itemRepo.findByCartId(cart.getId()).size(); return itemRepo.findByCartId(cart.getId()).size();
} }
public Map<String, Object> getCartSummaryRaw(Cart cart, Locale locale) { public Map<String, Object> getCartSummaryRaw(Cart cart, Locale locale, Boolean hasTaxes) {
double base = 0.0; double base = 0.0;
double iva4 = 0.0; double iva4 = 0.0;
@ -269,6 +269,11 @@ public class CartService {
} }
} }
if(!hasTaxes) {
iva4 = 0.0;
iva21 = 0.0;
}
double totalBeforeDiscount = base + iva4 + iva21 + shipment; double totalBeforeDiscount = base + iva4 + iva21 + shipment;
int fidelizacion = this.getDescuentoFidelizacion(cart.getUserId()); int fidelizacion = this.getDescuentoFidelizacion(cart.getUserId());
double descuento = totalBeforeDiscount * fidelizacion / 100.0; double descuento = totalBeforeDiscount * fidelizacion / 100.0;
@ -318,8 +323,8 @@ public class CartService {
return 0; return 0;
} }
public Map<String, Object> getCartSummary(Cart cart, Locale locale) { public Map<String, Object> getCartSummary(Cart cart, Locale locale, Boolean hasTaxes) {
Map<String, Object> raw = getCartSummaryRaw(cart, locale); Map<String, Object> raw = getCartSummaryRaw(cart, locale, hasTaxes);
double base = (Double) raw.get("base"); double base = (Double) raw.get("base");
double iva4 = (Double) raw.get("iva4"); double iva4 = (Double) raw.get("iva4");

View File

@ -58,10 +58,28 @@ public class CheckoutController {
Long userId = Utils.currentUserId(principal); Long userId = Utils.currentUserId(principal);
Cart cart = cartService.getOrCreateActiveCart(userId); Cart cart = cartService.getOrCreateActiveCart(userId);
model.addAttribute("summary", cartService.getCartSummary(cart, locale)); model.addAttribute("summary", cartService.getCartSummary(cart, locale, true));
return "imprimelibros/checkout/checkout"; // crea esta vista si quieres (tabla simple) return "imprimelibros/checkout/checkout"; // crea esta vista si quieres (tabla simple)
} }
@GetMapping({ "/get-summary", "/get-summary/{direccionId}/{method}" })
public String getCheckoutSummary(@PathVariable(required = false) Long direccionId,
@PathVariable(required = false) String method, Principal principal, Model model, Locale locale) {
Long userId = Utils.currentUserId(principal);
Cart cart = cartService.getOrCreateActiveCart(userId);
Boolean hasTaxes = true;
if (direccionId != null) {
hasTaxes = direccionService.hasTaxes(direccionId);
}
Map<String, Object> summary = cartService.getCartSummary(cart, locale, hasTaxes);
model.addAttribute("summary", summary);
if (method != null) {
model.addAttribute("method", method);
}
return "imprimelibros/checkout/_summary :: checkoutSummary(summary=${summary})";
}
@GetMapping("/get-address/{id}") @GetMapping("/get-address/{id}")
public String getDireccionCard(@PathVariable Long id, Model model, Locale locale) { public String getDireccionCard(@PathVariable Long id, Model model, Locale locale) {
Direccion dir = direccionService.findById(id) Direccion dir = direccionService.findById(id)

View File

@ -153,4 +153,26 @@ public class DireccionService {
return false; return false;
} }
public Boolean hasTaxes(Long direccionId) {
if(direccionId == null) {
return true; // Si no hay dirección, asumimos que sí tiene impuestos
}
Optional<Direccion> dir = repo.findById(direccionId);
if (dir == null || dir.isEmpty()) {
throw new RuntimeException("Dirección no encontrada");
}
if(dir.get().getPaisCode3().toLowerCase().equals("esp")) {
int provincia = dir.get().getCp() / 1000;
if (provincia == 35 || provincia == 38 ) {
return false; // Canarias (sin IVA)lñ.
}
return true; // España (todas las provincias)
}
else{
// Fuera de España, asumimos que no tiene impuestos (puedes ajustar esto según tus necesidades)
return false;
}
}
} }

View File

@ -0,0 +1,47 @@
package com.imprimelibros.erp.error;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.imprimelibros.erp.common.Utils;
import java.time.ZonedDateTime;
@Controller
public class ErrorPageController implements ErrorController {
@RequestMapping("/error")
public String handleError(HttpServletRequest request, Model model) {
Object statusObj = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
Integer statusCode = statusObj != null ? Integer.valueOf(statusObj.toString()) : 500;
HttpStatus status = HttpStatus.resolve(statusCode);
if (status == null) status = HttpStatus.INTERNAL_SERVER_ERROR;
Object message = request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
Object exception = request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
Object path = request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);
model.addAttribute("status", status.value());
model.addAttribute("error", status.getReasonPhrase());
model.addAttribute("message", message != null ? message : "");
model.addAttribute("path", path != null ? path : "");
model.addAttribute("timestamp", ZonedDateTime.now());
// Puedes usar esto para cambiar iconos/texto según status
model.addAttribute("is404", status == HttpStatus.NOT_FOUND);
model.addAttribute("is403", status == HttpStatus.FORBIDDEN);
model.addAttribute("is500", status.is5xxServerError());
if(Utils.isCurrentUserAdmin())
// una sola vista para todos los errores
return "imprimelibros/error/error";
else
return "redirect:/"; // redirige a home para usuarios no admin
}
}

View File

@ -128,6 +128,62 @@ public class skApiClient {
}); });
} }
public Map<String, Object> getLomos(Map<String, Object> requestBody) {
try {
String jsonResponse = performWithRetry(() -> {
String url = this.skApiUrl + "api/get-lomos";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(authService.getToken()); // token actualizado
Map<String, Object> data = new HashMap<>();
data.put("clienteId", requestBody.get("clienteId"));
data.put("tamanio", requestBody.get("tamanio"));
data.put("tirada", requestBody.get("tirada"));
data.put("paginas", requestBody.get("paginas"));
data.put("paginasColor", requestBody.get("paginasColor"));
data.put("papelInteriorDiferente", 0);
data.put("paginasCuadernillo", requestBody.get("paginasCuadernillo"));
data.put("tipo", requestBody.get("tipo"));
data.put("isColor", requestBody.get("isColor"));
data.put("isHq", requestBody.get("isHq"));
data.put("interior", requestBody.get("interior"));
data.put("cubierta", requestBody.get("cubierta"));
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(data, headers);
ResponseEntity<String> response = restTemplate.exchange(
url,
HttpMethod.POST,
entity,
String.class);
return response.getBody();
});
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonResponse);
if (root.get("lomoInterior") == null || !root.get("lomoInterior").isDouble()) {
throw new RuntimeException();
}
Double lomoInterior = root.get("lomoInterior").asDouble();
Double lomoCubierta = root.get("lomoCubierta").asDouble();
return Map.of(
"lomoInterior", lomoInterior,
"lomoCubierta", lomoCubierta);
} catch (JsonProcessingException e) {
// Fallback al 80% del ancho
return Map.of(
"lomoInterior", 0.0,
"lomoCubierta", 0.0);
}
}
public Map<String, Object> savePresupuesto(Map<String, Object> requestBody) { public Map<String, Object> savePresupuesto(Map<String, Object> requestBody) {
return performWithRetryMap(() -> { return performWithRetryMap(() -> {
String url = this.skApiUrl + "api/guardar"; String url = this.skApiUrl + "api/guardar";
@ -398,13 +454,12 @@ public class skApiClient {
public Map<String, Object> checkPedidoEstado(Long presupuestoId, Locale locale) { public Map<String, Object> checkPedidoEstado(Long presupuestoId, Locale locale) {
try { try {
String jsonResponse = performWithRetry(() -> { String jsonResponse = performWithRetry(() -> {
String url = this.skApiUrl + "api/estado-pedido/" + presupuestoId; String url = this.skApiUrl + "api/estado-pedido/" + presupuestoId;
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(authService.getToken());
headers.setBearerAuth(authService.getToken()); // token actualizado headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON));
HttpEntity<Void> entity = new HttpEntity<>(headers); HttpEntity<Void> entity = new HttpEntity<>(headers);
@ -420,19 +475,34 @@ public class skApiClient {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonResponse); JsonNode root = mapper.readTree(jsonResponse);
if (root.get("data") == null) { // ✅ Si falta data, devolvemos mapa sin "estado" (o con estado=null pero con
throw new RuntimeException( // HashMap)
"Sin respuesta desde el servidor del proveedor"); if (root == null || root.get("data") == null || root.get("data").isNull()) {
Map<String, Object> out = new HashMap<>();
out.put("message", "Respuesta sin campo 'data' desde el servidor del proveedor");
return out;
} }
String estado = root.get("data").asText(); String estado = root.get("data").asText();
return Map.of( return Map.of("estado", estado); // aquí NO es null, así que Map.of OK
"estado", estado);
} catch (HttpClientErrorException ex) {
if (ex.getStatusCode() == HttpStatus.NOT_FOUND) {
// ✅ 404: devolvemos mapa sin "estado" para evitar null en Map.of
Map<String, Object> out = new HashMap<>();
out.put("notFound", true);
out.put("message", "Orden de trabajo no encontrada para presupuestoId=" + presupuestoId);
return out;
}
throw ex;
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
// Fallback al 80% del ancho // ✅ no parseable (HTML, debugbar, etc.)
return Map.of( Map<String, Object> out = new HashMap<>();
"estado", null); out.put("message", "Respuesta no-JSON o JSON inválido desde el proveedor");
return out;
} }
} }
@ -576,7 +646,6 @@ public class skApiClient {
return Boolean.parseBoolean(result); return Boolean.parseBoolean(result);
} }
public Boolean cancelarPedido(Long pedidoId) { public Boolean cancelarPedido(Long pedidoId) {
String result = performWithRetry(() -> { String result = performWithRetry(() -> {
@ -618,12 +687,21 @@ public class skApiClient {
private String performWithRetry(Supplier<String> request) { private String performWithRetry(Supplier<String> request) {
try { try {
return request.get(); return request.get();
} catch (HttpClientErrorException.Unauthorized e) { } catch (HttpClientErrorException.Unauthorized e) {
// Token expirado, renovar y reintentar // Token expirado, renovar y reintentar
authService.invalidateToken(); authService.invalidateToken();
try { try {
return request.get(); // segundo intento return request.get(); // segundo intento
} catch (HttpClientErrorException ex) { } catch (HttpClientErrorException ex) {
// ✅ IMPORTANTe: si el segundo intento es 404, NO lo envuelvas
if (ex.getStatusCode() == HttpStatus.NOT_FOUND) {
throw ex;
}
// Si es otro 4xx/5xx, sí lo envolvemos
throw new RuntimeException("La autenticación ha fallado tras renovar el token.", ex); throw new RuntimeException("La autenticación ha fallado tras renovar el token.", ex);
} }
} }

View File

@ -8,7 +8,6 @@ import com.imprimelibros.erp.facturacion.dto.FacturaDireccionMapper;
import com.imprimelibros.erp.facturacion.dto.FacturaGuardarDto; import com.imprimelibros.erp.facturacion.dto.FacturaGuardarDto;
import com.imprimelibros.erp.facturacion.dto.FacturaLineaUpsertDto; import com.imprimelibros.erp.facturacion.dto.FacturaLineaUpsertDto;
import com.imprimelibros.erp.facturacion.dto.FacturaPagoUpsertDto; import com.imprimelibros.erp.facturacion.dto.FacturaPagoUpsertDto;
import com.imprimelibros.erp.facturacion.repo.FacturaDireccionRepository;
import com.imprimelibros.erp.facturacion.repo.FacturaLineaRepository; import com.imprimelibros.erp.facturacion.repo.FacturaLineaRepository;
import com.imprimelibros.erp.facturacion.repo.FacturaPagoRepository; import com.imprimelibros.erp.facturacion.repo.FacturaPagoRepository;
import com.imprimelibros.erp.facturacion.repo.FacturaRepository; import com.imprimelibros.erp.facturacion.repo.FacturaRepository;

View File

@ -7,13 +7,18 @@ import org.springframework.web.bind.annotation.GetMapping;
import com.imprimelibros.erp.configurationERP.VariableService; import com.imprimelibros.erp.configurationERP.VariableService;
import com.imprimelibros.erp.i18n.TranslationService; import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.pedidos.Pedido;
import com.imprimelibros.erp.pedidos.PedidoRepository;
import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import java.security.Principal;
import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import com.imprimelibros.erp.common.Utils;
@Controller @Controller
public class HomeController { public class HomeController {
@ -22,9 +27,10 @@ public class HomeController {
private TranslationService translationService; private TranslationService translationService;
@Autowired @Autowired
private VariableService variableService; private VariableService variableService;
@Autowired PedidoRepository pedidoRepository;
@GetMapping("/") @GetMapping("/")
public String index(Model model, Authentication authentication, Locale locale) { public String index(Model model, Authentication authentication, Principal principal,Locale locale) {
boolean isAuthenticated = authentication != null && authentication.isAuthenticated() boolean isAuthenticated = authentication != null && authentication.isAuthenticated()
&& !(authentication instanceof AnonymousAuthenticationToken); && !(authentication instanceof AnonymousAuthenticationToken);
@ -37,7 +43,8 @@ public class HomeController {
"presupuesto.impresion-cubierta", "presupuesto.impresion-cubierta",
"presupuesto.impresion-cubierta-help", "presupuesto.impresion-cubierta-help",
"presupuesto.iva-reducido", "presupuesto.iva-reducido",
"presupuesto.iva-reducido-descripcion"); "presupuesto.iva-reducido-descripcion",
"pedido.gasto-anual");
Map<String, String> translations = translationService.getTranslations(locale, keys); Map<String, String> translations = translationService.getTranslations(locale, keys);
model.addAttribute("languageBundle", translations); model.addAttribute("languageBundle", translations);
@ -51,6 +58,11 @@ public class HomeController {
// empty translations for authenticated users // empty translations for authenticated users
Map<String, String> translations = Map.of(); Map<String, String> translations = Map.of();
model.addAttribute("languageBundle", translations); model.addAttribute("languageBundle", translations);
Instant haceUnAno = Instant.now().minusSeconds(365 * 24 * 60 * 60);
Long userId = Utils.currentUserId(principal);
double totalGastado = pedidoRepository.sumTotalByCreatedByAndCreatedAtAfter(userId, haceUnAno);
model.addAttribute("totalGastado", totalGastado);
} }
return "imprimelibros/home/home"; return "imprimelibros/home/home";
} }

View File

@ -184,11 +184,14 @@ public class PaymentController {
// Campos ordenables // Campos ordenables
List<String> orderable = List.of( List<String> orderable = List.of(
"transferId", "client",
"transfer_id",
"status", "status",
"amountCents", "amountCents",
"payment.amountRefundedCents", "amountCentsRefund",
"createdAt", "updatedAt"); "payment.orderId",
"createdAt",
"processedAt");
Specification<PaymentTransaction> base = (root, query, cb) -> cb.or( Specification<PaymentTransaction> base = (root, query, cb) -> cb.or(
cb.equal(root.get("status"), PaymentTransactionStatus.pending), cb.equal(root.get("status"), PaymentTransactionStatus.pending),
@ -215,6 +218,26 @@ public class PaymentController {
return DataTable return DataTable
.of(repoPaymentTransaction, PaymentTransaction.class, dt, searchable) .of(repoPaymentTransaction, PaymentTransaction.class, dt, searchable)
.orderable(orderable) .orderable(orderable)
.orderable("client", (root, query, cb) -> {
var sq = query.subquery(String.class);
var u = sq.from(User.class);
sq.select(u.get("fullName"));
sq.where(cb.equal(u.get("id"), root.join("payment").get("userId")));
return sq;
})
.orderable("transfer_id", (root, query, cb) -> {
var orderId = root.join("payment").get("orderId").as(Long.class);
return cb.<Long>selectCase()
.when(cb.isNull(orderId), Long.MAX_VALUE)
.otherwise(orderId);
})
.orderable("payment.orderId", (root, query, cb) -> {
var orderId = root.join("payment").get("orderId").as(Long.class);
return cb.<Long>selectCase()
.when(cb.isNull(orderId), Long.MAX_VALUE)
.otherwise(orderId);
})
.orderable("amountCentsRefund", (root, query, cb) -> root.join("payment").get("amountRefundedCents"))
.add("created_at", pago -> Utils.formatDateTime(pago.getCreatedAt(), locale)) .add("created_at", pago -> Utils.formatDateTime(pago.getCreatedAt(), locale))
.add("processed_at", pago -> Utils.formatDateTime(pago.getProcessedAt(), locale)) .add("processed_at", pago -> Utils.formatDateTime(pago.getProcessedAt(), locale))
.add("client", pago -> { .add("client", pago -> {
@ -237,14 +260,14 @@ public class PaymentController {
return ""; return "";
}) })
.add("order_id", pago -> { .add("order_id", pago -> {
if (pago.getStatus() != PaymentTransactionStatus.pending) { //if (pago.getStatus() != PaymentTransactionStatus.pending) {
if (pago.getPayment() != null && pago.getPayment().getOrderId() != null) { if (pago.getPayment() != null && pago.getPayment().getOrderId() != null) {
return pago.getPayment().getOrderId().toString(); return pago.getPayment().getOrderId().toString();
} else { } else {
return ""; return "";
} }
} //}
return messageSource.getMessage("pagos.transferencia.no-pedido", null, "Pendiente", locale); //return messageSource.getMessage("pagos.transferencia.no-pedido", null, "Pendiente", locale);
}).add("amount_cents", pago -> Utils.formatCurrency(pago.getAmountCents() / 100.0, locale)) }).add("amount_cents", pago -> Utils.formatCurrency(pago.getAmountCents() / 100.0, locale))
.add("amount_cents_refund", pago -> .add("amount_cents_refund", pago ->

View File

@ -13,7 +13,7 @@ public interface PedidoDireccionRepository extends JpaRepository<PedidoDireccion
// Si en tu código sueles trabajar con el objeto: // Si en tu código sueles trabajar con el objeto:
List<PedidoDireccion> findByPedidoLinea(PedidoLinea pedidoLinea); List<PedidoDireccion> findByPedidoLinea(PedidoLinea pedidoLinea);
PedidoDireccion findByPedidoIdAndFacturacionTrue(Long pedidoId); PedidoDireccion findFirstByPedidoIdAndFacturacionTrue(Long pedidoId);
@Query(""" @Query("""
select distinct d select distinct d

View File

@ -3,6 +3,7 @@ package com.imprimelibros.erp.pedidos;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
@ -25,18 +26,34 @@ public class PedidoEstadoService {
/** /**
* Ejecuta cada noche a las 4:00 AM * Ejecuta cada noche a las 4:00 AM
*/ */
@Transactional
// test @Scheduled(cron = "0 * * * * *")
@Scheduled(cron = "0 0 4 * * *") @Scheduled(cron = "0 0 4 * * *")
public void actualizarEstadosPedidos() { public void actualizarEstadosPedidos() {
log.info("JOB actualizarEstadosPedidos iniciado");
List<PedidoLinea> pedidosLineas = pedidoLineaRepository.findPedidosLineasParaActualizarEstado(); List<PedidoLinea> pedidosLineas = pedidoLineaRepository.findPedidosLineasParaActualizarEstado();
log.info("Pedidos líneas a procesar: {}", pedidosLineas.size());
for (PedidoLinea linea : pedidosLineas) { for (PedidoLinea linea : pedidosLineas) {
log.info("Actualizando estado pedidoLineaId={}", linea.getId());
try { try {
Map<String, Object> resultado = pedidoService.actualizarEstado(linea.getId(), Locale.getDefault()); Map<String, Object> resultado = pedidoService.actualizarEstado(linea.getId(), Locale.getDefault());
if (!Boolean.TRUE.equals(resultado.get("success"))) { if (!Boolean.TRUE.equals(resultado.get("success"))) {
log.error("Error al actualizar estado. pedidoLineaId={} message={}", log.error("Error al actualizar estado. pedidoLineaId={} message={}",
linea.getId(), resultado.get("message")); linea.getId(), resultado.get("message"));
} else {
String msg = String.valueOf(resultado.get("message"));
if (msg != null && msg.contains("Orden de trabajo no encontrada")) {
log.warn("OT no encontrada. pedidoLineaId={} message={}", linea.getId(), msg);
}
} }
} catch (Exception ex) { } catch (Exception ex) {
log.error("Excepción actualizando estado. pedidoLineaId={}", linea.getId(), ex); log.error("Excepción actualizando estado. pedidoLineaId={}", linea.getId(), ex);

View File

@ -14,14 +14,16 @@ public class PedidoLinea {
procesando_pago("pedido.estado.procesando_pago", 2), procesando_pago("pedido.estado.procesando_pago", 2),
denegado_pago("pedido.estado.denegado_pago", 3), denegado_pago("pedido.estado.denegado_pago", 3),
aprobado("pedido.estado.aprobado", 4), aprobado("pedido.estado.aprobado", 4),
maquetacion("pedido.estado.maquetacion", 5), procesando_pedido("pedido.estado.procesando_pedido", 5),
haciendo_ferro("pedido.estado.haciendo_ferro", 6), maquetacion("pedido.estado.maquetacion", 6),
esperando_aceptacion_ferro("pedido.estado.esperando_aceptacion_ferro", 7), haciendo_ferro_digital("pedido.estado.haciendo_ferro_digital", 7),
ferro_cliente("pedido.estado.ferro_cliente", 8), esperando_aceptacion_ferro_digital("pedido.estado.esperando_aceptacion_ferro_digital", 8),
produccion("pedido.estado.produccion", 9), haciendo_ferro("pedido.estado.haciendo_ferro", 9),
terminado("pedido.estado.terminado", 10), esperando_aceptacion_ferro("pedido.estado.esperando_aceptacion_ferro", 10),
enviado("pedido.estado.enviado", 11), produccion("pedido.estado.produccion", 11),
cancelado("pedido.estado.cancelado", 12); terminado("pedido.estado.terminado", 12),
enviado("pedido.estado.enviado", 13),
cancelado("pedido.estado.cancelado", 14);
private final String messageKey; private final String messageKey;
private final int priority; private final int priority;

View File

@ -1,5 +1,6 @@
package com.imprimelibros.erp.pedidos; package com.imprimelibros.erp.pedidos;
import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
@ -24,10 +25,12 @@ import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
import com.imprimelibros.erp.users.UserService; import com.imprimelibros.erp.users.UserService;
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.facturacion.FacturaDireccion;
import com.imprimelibros.erp.facturacion.dto.DireccionFacturacionDto; import com.imprimelibros.erp.facturacion.dto.DireccionFacturacionDto;
import com.imprimelibros.erp.pedidos.PedidoLinea.Estado; import com.imprimelibros.erp.pedidos.PedidoLinea.Estado;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.HttpClientErrorException;
@Service @Service
public class PedidoService { public class PedidoService {
@ -66,7 +69,7 @@ public class PedidoService {
} }
public PedidoDireccion getPedidoDireccionFacturacionByPedidoId(Long pedidoId) { public PedidoDireccion getPedidoDireccionFacturacionByPedidoId(Long pedidoId) {
return pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId); return pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
} }
@Transactional @Transactional
@ -79,7 +82,8 @@ public class PedidoService {
Pedido pedido = new Pedido(); Pedido pedido = new Pedido();
Cart cart = cartService.getCartById(cartId); Cart cart = cartService.getCartById(cartId);
Map<String, Object> cartSummaryRaw = cartService.getCartSummaryRaw(cart, Locale.getDefault()); Boolean hasTaxes = direccionService.hasTaxes(direccionFacturacionId);
Map<String, Object> cartSummaryRaw = cartService.getCartSummaryRaw(cart, Locale.getDefault(), hasTaxes);
// 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));
@ -135,6 +139,11 @@ public class PedidoService {
Presupuesto p = presupuestoRepository.findById(pCart.getId()) Presupuesto p = presupuestoRepository.findById(pCart.getId())
.orElseThrow(() -> new IllegalStateException("Presupuesto no encontrado: " + pCart.getId())); .orElseThrow(() -> new IllegalStateException("Presupuesto no encontrado: " + pCart.getId()));
p.setEstado(Presupuesto.Estado.aceptado); p.setEstado(Presupuesto.Estado.aceptado);
if(!hasTaxes){
p.setIvaImporte21(BigDecimal.ZERO);
p.setIvaImporte4(BigDecimal.ZERO);
p.setTotalConIva(p.getBaseImponible());
}
presupuestoRepository.save(p); presupuestoRepository.save(p);
PedidoLinea linea = new PedidoLinea(); PedidoLinea linea = new PedidoLinea();
@ -148,9 +157,12 @@ public class PedidoService {
// Guardar las direcciones asociadas a la línea del pedido // Guardar las direcciones asociadas a la línea del pedido
Map<String, Object> direcciones_presupuesto = this.getDireccionesPresupuesto(cart, p); Map<String, Object> direcciones_presupuesto = this.getDireccionesPresupuesto(cart, p);
saveDireccionesPedidoLinea(direcciones_presupuesto, pedidoGuardado, linea, direccionFacturacionId); saveDireccionesPedidoLinea(direcciones_presupuesto, pedidoGuardado, linea);
} }
if(direccionFacturacionId != null && pedidoGuardado != null && pedidoGuardado.getId() != null){
saveDireccionFacturacionPedido(pedidoGuardado, direccionFacturacionId);
}
return pedidoGuardado; return pedidoGuardado;
} }
@ -194,7 +206,7 @@ public class PedidoService {
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null); Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
if (pedido != null) { if (pedido != null) {
PedidoDireccion direccionPedido = pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId); PedidoDireccion direccionPedido = pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
if (direccionPedido == null) { if (direccionPedido == null) {
// crear // crear
@ -241,7 +253,7 @@ public class PedidoService {
} }
public PedidoDireccion getDireccionFacturacionPedido(Long pedidoId) { public PedidoDireccion getDireccionFacturacionPedido(Long pedidoId) {
return pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId); return pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
} }
public List<PedidoDireccion> getDireccionesEntregaPedidoLinea(Long pedidoLineaId) { public List<PedidoDireccion> getDireccionesEntregaPedidoLinea(Long pedidoLineaId) {
@ -259,7 +271,7 @@ public class PedidoService {
Integer counter = 1; Integer counter = 1;
for (PedidoLinea linea : lineas) { for (PedidoLinea linea : lineas) {
if (linea.getEstado() == Estado.pendiente_pago if (linea.getEstado() == Estado.pendiente_pago
|| linea.getEstado() == Estado.denegado_pago) { || linea.getEstado() == Estado.denegado_pago || linea.getEstado() == Estado.procesando_pago) {
Presupuesto presupuesto = linea.getPresupuesto(); Presupuesto presupuesto = linea.getPresupuesto();
linea.setEstado(getEstadoInicial(presupuesto)); linea.setEstado(getEstadoInicial(presupuesto));
@ -299,6 +311,7 @@ public class PedidoService {
return true; return true;
} }
@Transactional
public Map<String, Object> actualizarEstado(Long pedidoLineaId, Locale locale) { public Map<String, Object> actualizarEstado(Long pedidoLineaId, Locale locale) {
PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null); PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null);
@ -315,8 +328,8 @@ public class PedidoService {
"message", messageSource.getMessage("pedido.errors.cannot-update", null, locale)); "message", messageSource.getMessage("pedido.errors.cannot-update", null, locale));
} }
// Rango: >= haciendo_ferro y < enviado // Rango: >= procesando_pedido y < enviado
if (estadoOld.getPriority() < PedidoLinea.Estado.haciendo_ferro.getPriority() if (estadoOld.getPriority() < PedidoLinea.Estado.procesando_pedido.getPriority()
|| estadoOld.getPriority() >= PedidoLinea.Estado.enviado.getPriority()) { || estadoOld.getPriority() >= PedidoLinea.Estado.enviado.getPriority()) {
return Map.of( return Map.of(
"success", false, "success", false,
@ -341,13 +354,26 @@ public class PedidoService {
Map<String, Object> result = skApiClient.checkPedidoEstado(refExterna, locale); Map<String, Object> result = skApiClient.checkPedidoEstado(refExterna, locale);
if (result == null || result.get("estado") == null) { if (result == null) {
return Map.of( return Map.of(
"success", false, "success", false,
"message", messageSource.getMessage("pedido.errors.update-server-error", null, locale)); "message", messageSource.getMessage("pedido.errors.update-server-error", null, locale));
} }
String estadoStr = String.valueOf(result.get("estado")); if (Boolean.TRUE.equals(result.get("notFound"))) {
return Map.of(
"success", true,
"message", String.valueOf(result.getOrDefault("message", "OT no encontrada (404). Se omite.")));
}
Object estadoObj = result.get("estado");
if (estadoObj == null) {
return Map.of(
"success", false,
"message", messageSource.getMessage("pedido.errors.update-server-error", null, locale));
}
String estadoStr = String.valueOf(estadoObj);
PedidoLinea.Estado estadoSk; PedidoLinea.Estado estadoSk;
try { try {
@ -630,7 +656,7 @@ public class PedidoService {
private void saveDireccionesPedidoLinea( private void saveDireccionesPedidoLinea(
Map<String, Object> direcciones, Map<String, Object> direcciones,
Pedido pedido, Pedido pedido,
PedidoLinea linea, Long direccionFacturacionId) { PedidoLinea linea) {
String email = pedido.getCreatedBy().getUserName(); String email = pedido.getCreatedBy().getUserName();
@ -677,7 +703,12 @@ public class PedidoService {
pedidoDireccionRepository.save(direccion); pedidoDireccionRepository.save(direccion);
} }
} }
}
private void saveDireccionFacturacionPedido(Pedido pedido, Long direccionFacturacionId) {
if (direccionFacturacionId != null) { if (direccionFacturacionId != null) {
String email = pedido.getCreatedBy().getUserName();
Direccion dirFact = direccionService.findById(direccionFacturacionId).orElse(null); Direccion dirFact = direccionService.findById(direccionFacturacionId).orElse(null);
if (dirFact != null) { if (dirFact != null) {
HashMap<String, Object> dirFactMap = new HashMap<>(); HashMap<String, Object> dirFactMap = new HashMap<>();
@ -698,7 +729,7 @@ public class PedidoService {
false, false,
dirFactMap, dirFactMap,
pedido, pedido,
linea, null,
false, false,
true); true);
pedidoDireccionRepository.save(direccion); pedidoDireccionRepository.save(direccion);
@ -758,7 +789,7 @@ public class PedidoService {
if (presupuestoService.hasMaquetacion(p)) { if (presupuestoService.hasMaquetacion(p)) {
return Estado.maquetacion; return Estado.maquetacion;
} else { } else {
return Estado.haciendo_ferro; return Estado.procesando_pedido;
} }
} }

View File

@ -12,6 +12,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
@ -29,23 +30,22 @@ import com.imprimelibros.erp.datatables.DataTablesResponse;
import com.imprimelibros.erp.facturacion.service.FacturacionService; import com.imprimelibros.erp.facturacion.service.FacturacionService;
import com.imprimelibros.erp.i18n.TranslationService; import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.paises.PaisesService; import com.imprimelibros.erp.paises.PaisesService;
import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
import com.imprimelibros.erp.users.UserDao; import com.imprimelibros.erp.users.UserDao;
import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Subquery;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
@Controller @Controller
@RequestMapping("/pedidos") @RequestMapping("/pedidos")
public class PedidosController { public class PedidosController {
private final PresupuestoService presupuestoService;
private final PedidoRepository repoPedido; private final PedidoRepository repoPedido;
private final PedidoService pedidoService; private final PedidoService pedidoService;
private final UserDao repoUser; private final UserDao repoUser;
@ -58,7 +58,7 @@ public class PedidosController {
public PedidosController(PedidoRepository repoPedido, PedidoService pedidoService, UserDao repoUser, public PedidosController(PedidoRepository repoPedido, PedidoService pedidoService, UserDao repoUser,
MessageSource messageSource, TranslationService translationService, MessageSource messageSource, TranslationService translationService,
PedidoLineaRepository repoPedidoLinea, PaisesService paisesService, PedidoLineaRepository repoPedidoLinea, PaisesService paisesService,
FacturacionService facturacionService, PresupuestoService presupuestoService) { FacturacionService facturacionService) {
this.repoPedido = repoPedido; this.repoPedido = repoPedido;
this.pedidoService = pedidoService; this.pedidoService = pedidoService;
this.repoUser = repoUser; this.repoUser = repoUser;
@ -67,7 +67,6 @@ public class PedidosController {
this.repoPedidoLinea = repoPedidoLinea; this.repoPedidoLinea = repoPedidoLinea;
this.paisesService = paisesService; this.paisesService = paisesService;
this.facturacionService = facturacionService; this.facturacionService = facturacionService;
this.presupuestoService = presupuestoService;
} }
@GetMapping @GetMapping
@ -111,6 +110,7 @@ public class PedidosController {
"id", "id",
"createdBy.fullName", "createdBy.fullName",
"createdAt", "createdAt",
"titulos",
"total", "total",
"estado"); "estado");
@ -118,8 +118,10 @@ public class PedidosController {
if (!isAdmin) { if (!isAdmin) {
base = base.and((root, query, cb) -> cb.equal(root.get("createdBy").get("id"), currentUserId)); base = base.and((root, query, cb) -> cb.equal(root.get("createdBy").get("id"), currentUserId));
} }
String clientSearch = dt.getColumnSearch("cliente"); String clientSearch = dt.getColumnSearch("cliente");
String estadoSearch = dt.getColumnSearch("estado"); String estadoSearch = dt.getColumnSearch("estado");
String titulosSearch = dt.getColumnSearch("titulos");
// 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification // 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification
if (clientSearch != null) { if (clientSearch != null) {
@ -151,11 +153,45 @@ public class PedidosController {
base = base.and((root, query, cb) -> cb.disjunction()); base = base.and((root, query, cb) -> cb.disjunction());
} }
} }
if (titulosSearch != null && !titulosSearch.isBlank()) {
String like = "%" + titulosSearch.trim().toLowerCase() + "%";
base = base.and((root, query, cb) -> {
Subquery<Long> minLineaIdSq = query.subquery(Long.class);
var plMin = minLineaIdSq.from(PedidoLinea.class);
minLineaIdSq.select(cb.min(plMin.get("id")));
minLineaIdSq.where(cb.equal(plMin.get("pedido"), root));
Subquery<String> firstTitleSq = query.subquery(String.class);
var plFirst = firstTitleSq.from(PedidoLinea.class);
var prFirst = plFirst.join("presupuesto", JoinType.LEFT);
firstTitleSq.select(cb.lower(cb.coalesce(prFirst.get("titulo"), "")));
firstTitleSq.where(
cb.equal(plFirst.get("pedido"), root),
cb.equal(plFirst.get("id"), minLineaIdSq));
return cb.like(firstTitleSq, like);
});
}
Long total = repoPedido.count(base); Long total = repoPedido.count(base);
return DataTable return DataTable
.of(repoPedido, Pedido.class, dt, searchable) .of(repoPedido, Pedido.class, dt, searchable)
.orderable(orderable) .orderable(orderable)
.orderable("titulos", (root, query, cb) -> {
Subquery<Long> minLineaIdSq = query.subquery(Long.class);
var plMin = minLineaIdSq.from(PedidoLinea.class);
minLineaIdSq.select(cb.min(plMin.get("id")));
minLineaIdSq.where(cb.equal(plMin.get("pedido"), root));
Subquery<String> firstTitleSq = query.subquery(String.class);
var plFirst = firstTitleSq.from(PedidoLinea.class);
var prFirst = plFirst.join("presupuesto", JoinType.LEFT);
firstTitleSq.select(cb.lower(cb.coalesce(prFirst.get("titulo"), "")));
firstTitleSq.where(
cb.equal(plFirst.get("pedido"), root),
cb.equal(plFirst.get("id"), minLineaIdSq));
return firstTitleSq;
})
.add("id", Pedido::getId) .add("id", Pedido::getId)
.add("created_at", pedido -> Utils.formatInstant(pedido.getCreatedAt(), locale)) .add("created_at", pedido -> Utils.formatInstant(pedido.getCreatedAt(), locale))
.add("cliente", pedido -> { .add("cliente", pedido -> {
@ -171,6 +207,38 @@ public class PedidosController {
return ""; return "";
} }
}) })
.add("titulos", pedido -> {
List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoIdOrderByIdAsc(pedido.getId());
if (lineas.isEmpty()) {
return "";
}
List<Presupuesto> presupuestos = lineas.stream()
.map(PedidoLinea::getPresupuesto)
.filter(presupuesto -> presupuesto != null)
.collect(Collectors.toList());
if (presupuestos.isEmpty()) {
return "";
}
String primerTitulo = presupuestos.get(0).getTitulo();
if (primerTitulo == null) {
primerTitulo = "";
} else {
primerTitulo = primerTitulo.trim();
}
int extras = presupuestos.size() - 1;
if (extras <= 0) {
return primerTitulo;
}
String suffix = messageSource.getMessage(
"pedido.table.titulos.and-more",
new Object[] { extras },
locale);
return primerTitulo + " " + suffix;
})
.add("estado", pedido -> { .add("estado", pedido -> {
List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoId(pedido.getId()); List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoId(pedido.getId());
if (lineas.isEmpty()) { if (lineas.isEmpty()) {

View File

@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import jakarta.validation.Validator; import jakarta.validation.Validator;
@ -48,12 +49,12 @@ import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion; import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas; import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto; import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.dto.PresupuestoFormDataMapper;
import com.imprimelibros.erp.presupuesto.dto.PresupuestoFormDataMapper.PresupuestoFormDataDto;
import com.imprimelibros.erp.presupuesto.service.PresupuestoService; import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups; import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups;
import com.imprimelibros.erp.users.UserDao; import com.imprimelibros.erp.users.UserDao;
import com.imprimelibros.erp.users.UserDetailsImpl; import com.imprimelibros.erp.users.UserDetailsImpl;
import com.imprimelibros.erp.presupuesto.service.PresupuestoFormDataMapper;
import com.imprimelibros.erp.presupuesto.service.PresupuestoFormDataMapper.PresupuestoFormDataDto;
import com.imprimelibros.erp.common.Utils; import com.imprimelibros.erp.common.Utils;
import com.imprimelibros.erp.common.web.IpUtils; import com.imprimelibros.erp.common.web.IpUtils;
@ -151,7 +152,8 @@ 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<>();
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); Map<String, Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto),
locale);
resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("solapas", datosInterior.get("maxSolapas"));
resultado.put("lomo", datosInterior.get("lomo")); resultado.put("lomo", datosInterior.get("lomo"));
resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale)); resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale));
@ -273,7 +275,8 @@ public class PresupuestoController {
} }
} }
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); Map<String, Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto),
locale);
resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("solapas", datosInterior.get("maxSolapas"));
resultado.put("lomo", datosInterior.get("lomo")); resultado.put("lomo", datosInterior.get("lomo"));
@ -309,7 +312,8 @@ 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
} }
} }
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); Map<String, Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto),
locale);
resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("solapas", datosInterior.get("maxSolapas"));
resultado.put("lomo", datosInterior.get("lomo")); resultado.put("lomo", datosInterior.get("lomo"));
@ -335,7 +339,8 @@ public class PresupuestoController {
} }
Map<String, Object> resultado = new HashMap<>(); Map<String, Object> resultado = new HashMap<>();
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); Map<String, Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto),
locale);
resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("solapas", datosInterior.get("maxSolapas"));
resultado.put("lomo", datosInterior.get("lomo")); resultado.put("lomo", datosInterior.get("lomo"));
@ -513,6 +518,102 @@ public class PresupuestoController {
return ResponseEntity.ok(resumen); return ResponseEntity.ok(resumen);
} }
@PostMapping("/public/prepare-claim")
public ResponseEntity<?> prepareClaim(
@RequestBody Map<String, Object> body,
HttpServletRequest request) {
Long presupuestoId = objectMapper.convertValue(body.get("presupuestoId"), Long.class);
if (presupuestoId == null) {
return ResponseEntity.badRequest().body(Map.of("message", "missing presupuestoId"));
}
Presupuesto p = presupuestoRepository.findById(presupuestoId).orElse(null);
if (p == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(Map.of("message", "presupuesto not found"));
}
if (p.getOrigen() != Presupuesto.Origen.publico) {
return ResponseEntity.badRequest().body(Map.of("message", "presupuesto not public"));
}
request.getSession(true).setAttribute("presupuesto_claim_id", presupuestoId);
return ResponseEntity.ok(Map.of("success", true));
}
@GetMapping("/claim")
@Transactional
public String claimPresupuesto(
HttpServletRequest request,
Authentication authentication,
RedirectAttributes redirectAttributes,
Locale locale) {
Object attr = request.getSession(false) != null
? request.getSession(false).getAttribute("presupuesto_claim_id")
: null;
Long presupuestoId = null;
if (attr instanceof Long) {
presupuestoId = (Long) attr;
} else if (attr != null) {
try {
presupuestoId = Long.valueOf(attr.toString());
} catch (NumberFormatException ignore) {
}
}
if (presupuestoId == null) {
redirectAttributes.addFlashAttribute("errorMessage",
messageSource.getMessage("presupuesto.errores.presupuesto-no-existe", new Object[] { 0 }, locale));
return "redirect:/presupuesto";
}
Presupuesto p = presupuestoRepository.findById(presupuestoId).orElse(null);
if (p == null) {
redirectAttributes.addFlashAttribute("errorMessage",
messageSource.getMessage("presupuesto.errores.presupuesto-no-existe",
new Object[] { presupuestoId }, locale));
return "redirect:/presupuesto";
}
if (p.getUser() != null && authentication != null) {
Long currentUserId = null;
if (authentication.getPrincipal() instanceof UserDetailsImpl udi) {
currentUserId = udi.getId();
} else {
currentUserId = userRepo.findIdByUserNameIgnoreCase(authentication.getName()).orElse(null);
}
if (currentUserId != null && p.getUser().getId().equals(currentUserId)) {
request.getSession().removeAttribute("presupuesto_claim_id");
return "redirect:/presupuesto/edit/" + p.getId();
}
}
if (p.getOrigen() != Presupuesto.Origen.publico) {
redirectAttributes.addFlashAttribute("errorMessage",
messageSource.getMessage("presupuesto.errores.presupuesto-no-existe",
new Object[] { presupuestoId }, locale));
return "redirect:/presupuesto";
}
if (authentication != null) {
if (authentication.getPrincipal() instanceof UserDetailsImpl udi) {
p.setUser(userRepo.getReferenceById(udi.getId()));
} else {
userRepo.findByUserNameIgnoreCase(authentication.getName()).ifPresent(p::setUser);
}
}
p.setOrigen(Presupuesto.Origen.privado);
p.setEstado(Presupuesto.Estado.borrador);
presupuestoRepository.saveAndFlush(p);
request.getSession().removeAttribute("presupuesto_claim_id");
return "redirect:/presupuesto/edit/" + p.getId();
}
// ============================================= // =============================================
// MÉTODOS PARA USUARIOS AUTENTICADOS // MÉTODOS PARA USUARIOS AUTENTICADOS
// ============================================= // =============================================
@ -554,8 +655,7 @@ public class PresupuestoController {
"presupuesto.reimprimir.success.title", "presupuesto.reimprimir.success.title",
"presupuesto.reimprimir.success.text", "presupuesto.reimprimir.success.text",
"presupuesto.reimprimir.error.title", "presupuesto.reimprimir.error.title",
"presupuesto.reimprimir.error.internal" "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);
@ -812,6 +912,14 @@ public class PresupuestoController {
return ResponseEntity.badRequest().body(errores); return ResponseEntity.badRequest().body(errores);
} }
try {
Map<String, Object> lomos = presupuestoService.obtenerLomos(presupuesto);
presupuesto.setLomo(Double.valueOf(lomos.get("lomoInterior").toString()));
presupuesto.setLomoCubierta(Double.valueOf(lomos.get("lomoCubierta").toString()));
} catch (Exception ex) {
log.error("Error al obtener lomos desde SK API", ex);
}
try { try {
Map<String, Object> saveResult = presupuestoService.guardarPresupuesto( Map<String, Object> saveResult = presupuestoService.guardarPresupuesto(
@ -837,6 +945,30 @@ public class PresupuestoController {
} }
} }
@GetMapping("/api/plantilla-cubierta.png")
public ResponseEntity<byte[]> getPlantillaCubierta(
@RequestParam("tipo") String tipoLibro,
@RequestParam("tapa") String tapa,
@RequestParam("ancho") Integer ancho,
@RequestParam("alto") Integer alto,
@RequestParam("lomo") Integer lomo,
@RequestParam("solapas") Integer solapas,
Locale locale) {
Presupuesto.TipoEncuadernacion tipoEncuadernacion = Presupuesto.TipoEncuadernacion.valueOf(tipoLibro);
Presupuesto.TipoCubierta tipoCubierta = Presupuesto.TipoCubierta.valueOf(tapa);
byte[] png = presupuestoService.obtenerPlantillaCubierta(tipoEncuadernacion, tipoCubierta, ancho, alto, lomo,
solapas, locale);
if (png == null)
return ResponseEntity.notFound().build();
return ResponseEntity.ok()
.cacheControl(CacheControl.noCache())
.contentType(MediaType.IMAGE_PNG)
.body(png);
}
@PostMapping("/{id}/comentario") @PostMapping("/{id}/comentario")
@ResponseBody @ResponseBody
public String actualizarComentario(@PathVariable Long id, public String actualizarComentario(@PathVariable Long id,
@ -855,7 +987,6 @@ public class PresupuestoController {
return Map.of("id", entity); return Map.of("id", entity);
} }
@PostMapping("/api/reimprimir/{id}") @PostMapping("/api/reimprimir/{id}")
@ResponseBody @ResponseBody
public Map<String, Object> reimprimirPresupuesto(@PathVariable Long id) { public Map<String, Object> reimprimirPresupuesto(@PathVariable Long id) {

View File

@ -126,6 +126,13 @@ public class PresupuestoFormatter {
}, },
locale); locale);
} }
textoResumen += ms.getMessage(
"presupuesto.resumen-lomos",
new Object[] {
p.getLomo() != null ? Math.round(p.getLomo()) : "N/D",
p.getLomoCubierta() != null ? Math.round(p.getLomoCubierta()) : "N/D"
},
locale);
textoResumen += ms.getMessage("presupuesto.resumen-texto-end", null, locale); textoResumen += ms.getMessage("presupuesto.resumen-texto-end", null, locale);
return textoResumen; return textoResumen;

View File

@ -109,21 +109,6 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
} }
} }
public enum Entrega {
peninsula("presupuesto.entrega.peninsula"),
canarias("presupuesto.entrega.canarias"),
paises_ue("presupuesto.entrega.paises-ue");
private final String messageKey;
Entrega(String messageKey) {
this.messageKey = messageKey;
}
public String getMessageKey() {
return messageKey;
}
}
@Override @Override
public Presupuesto clone() { public Presupuesto clone() {
@ -188,10 +173,6 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
@Column(name = "iva_reducido") @Column(name = "iva_reducido")
private Boolean ivaReducido; private Boolean ivaReducido;
@Column(name = "entrega_tipo")
@Enumerated(EnumType.STRING)
private Entrega entregaTipo;
@Column(name = "iva_importe_4", precision = 12, scale = 2) @Column(name = "iva_importe_4", precision = 12, scale = 2)
private BigDecimal ivaImporte4; private BigDecimal ivaImporte4;
@ -300,6 +281,12 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
@Column(name = "papel_interior_id") @Column(name = "papel_interior_id")
private Integer papelInteriorId; private Integer papelInteriorId;
@Column(name = "lomo")
private Double lomo;
@Column(name = "lomo_cubierta")
private Double lomoCubierta;
@NotNull(message = "{presupuesto.errores.gramaje-interior}", groups = PresupuestoValidationGroups.Interior.class) @NotNull(message = "{presupuesto.errores.gramaje-interior}", groups = PresupuestoValidationGroups.Interior.class)
@Column(name = "gramaje_interior") @Column(name = "gramaje_interior")
private Integer gramajeInterior; private Integer gramajeInterior;
@ -531,14 +518,6 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
this.ivaReducido = ivaReducido; this.ivaReducido = ivaReducido;
} }
public Entrega getEntregaTipo() {
return entregaTipo;
}
public void setEntregaTipo(Entrega entregaTipo) {
this.entregaTipo = entregaTipo;
}
public BigDecimal getIvaImporte4() { public BigDecimal getIvaImporte4() {
return ivaImporte4; return ivaImporte4;
} }
@ -747,6 +726,22 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
this.papelInteriorId = papelInteriorId; this.papelInteriorId = papelInteriorId;
} }
public Double getLomo() {
return lomo;
}
public void setLomo(Double lomo) {
this.lomo = lomo;
}
public Double getLomoCubierta() {
return lomoCubierta;
}
public void setLomoCubierta(Double lomoCubierta) {
this.lomoCubierta = lomoCubierta;
}
public Integer getGramajeInterior() { public Integer getGramajeInterior() {
return gramajeInterior; return gramajeInterior;
} }

View File

@ -1,9 +1,7 @@
package com.imprimelibros.erp.presupuesto.service; package com.imprimelibros.erp.presupuesto.dto;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -20,6 +18,8 @@ public class PresupuestoFormDataMapper {
public Cubierta cubierta = new Cubierta(); public Cubierta cubierta = new Cubierta();
public Servicios servicios = new Servicios(); public Servicios servicios = new Servicios();
public Integer selectedTirada; public Integer selectedTirada;
public Double lomo;
public Double lomoCubierta;
// ===== Datos Generales ===== // ===== Datos Generales =====
public static class DatosGenerales { public static class DatosGenerales {
@ -37,7 +37,6 @@ public class PresupuestoFormDataMapper {
public String paginasColor = ""; public String paginasColor = "";
public String posicionPaginasColor = ""; public String posicionPaginasColor = "";
public String tipoEncuadernacion = "fresado"; // enum name public String tipoEncuadernacion = "fresado"; // enum name
public String entregaTipo = "peninsula"; // enum name
public boolean ivaReducido = true; public boolean ivaReducido = true;
} }
@ -157,7 +156,6 @@ public class PresupuestoFormDataMapper {
vm.datosGenerales.tipoEncuadernacion = enumName(p.getTipoEncuadernacion(), "fresado"); vm.datosGenerales.tipoEncuadernacion = enumName(p.getTipoEncuadernacion(), "fresado");
vm.datosGenerales.entregaTipo = enumName(p.getEntregaTipo(), "peninsula");
vm.datosGenerales.ivaReducido = Boolean.TRUE.equals(p.getIvaReducido()); vm.datosGenerales.ivaReducido = Boolean.TRUE.equals(p.getIvaReducido());
// ===== Interior // ===== Interior
@ -196,6 +194,10 @@ public class PresupuestoFormDataMapper {
// ===== Selected tirada // ===== Selected tirada
vm.selectedTirada = p.getSelectedTirada(); vm.selectedTirada = p.getSelectedTirada();
// ===== Lomos
vm.lomo = p.getLomo();
vm.lomoCubierta = p.getLomoCubierta();
// ===== Servicios desde JSONs // ===== Servicios desde JSONs
vm.servicios.servicios = parse(p.getServiciosJson(), vm.servicios.servicios = parse(p.getServiciosJson(),
new TypeReference<List<PresupuestoFormDataDto.Servicios.DatosServicios>>() { new TypeReference<List<PresupuestoFormDataDto.Servicios.DatosServicios>>() {

View File

@ -6,6 +6,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.Locale; import java.util.Locale;
@ -42,6 +44,15 @@ import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatricesReposito
import com.imprimelibros.erp.presupuesto.marcapaginas.MarcapaginasRepository; import com.imprimelibros.erp.presupuesto.marcapaginas.MarcapaginasRepository;
import com.imprimelibros.erp.users.UserDao; import com.imprimelibros.erp.users.UserDao;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.springframework.core.io.ClassPathResource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.NumberFormat;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import com.imprimelibros.erp.externalApi.skApiClient; import com.imprimelibros.erp.externalApi.skApiClient;
@ -350,7 +361,16 @@ public class PresupuestoService {
body.put("paginasCuadernillo", SK_PAGINAS_CUADERNILLO); body.put("paginasCuadernillo", SK_PAGINAS_CUADERNILLO);
body.put("interior", interior); body.put("interior", interior);
body.put("cubierta", cubierta); body.put("cubierta", cubierta);
if(presupuesto.getTipoCubierta() != Presupuesto.TipoCubierta.tapaBlanda) {
Map<String, Object> guardas = new HashMap<>();
guardas.put("papel", presupuesto.getPapelGuardasId());
guardas.put("gramaje", presupuesto.getGramajeGuardas());
guardas.put("caras", presupuesto.getGuardasImpresas());
body.put("guardas", guardas);
}
else{
body.put("guardas", null); body.put("guardas", null);
}
// Para las reimpresiones // Para las reimpresiones
if (presupuesto.getIsReimpresion() != null && presupuesto.getIsReimpresion()) { if (presupuesto.getIsReimpresion() != null && presupuesto.getIsReimpresion()) {
body.put("reimpresion", 1); body.put("reimpresion", 1);
@ -1162,7 +1182,6 @@ public class PresupuestoService {
// Si la entrega es en peninsula, se mira el valor del iva // Si la entrega es en peninsula, se mira el valor del iva
// Canarias y paises UE no llevan IVA // Canarias y paises UE no llevan IVA
if (presupuesto.getEntregaTipo() == Presupuesto.Entrega.peninsula) {
// Si el iva es reducido, el precio de la tirada y el del prototipo llevan IVA // Si el iva es reducido, el precio de la tirada y el del prototipo llevan IVA
// 4% // 4%
if (presupuesto.getIvaReducido()) { if (presupuesto.getIvaReducido()) {
@ -1177,7 +1196,6 @@ public class PresupuestoService {
BigDecimal.valueOf(100), 2, BigDecimal.valueOf(100), 2,
RoundingMode.HALF_UP); RoundingMode.HALF_UP);
} }
}
baseImponible = baseImponible.add(serviciosTotal); baseImponible = baseImponible.add(serviciosTotal);
BigDecimal totalConIva = baseImponible.add(ivaImporte21).add(ivaImporte4); BigDecimal totalConIva = baseImponible.add(ivaImporte21).add(ivaImporte4);
@ -1299,6 +1317,76 @@ public class PresupuestoService {
} }
public Map<String, Object> obtenerLomos(Presupuesto presupuesto) {
try {
Map<String, Object> response = apiClient.getLomos(this.toSkApiRequest(presupuesto));
return response;
} catch (Exception e) {
System.out.println("Error obteniendo lomos: " + e.getMessage());
return Map.of();
}
}
public byte[] obtenerPlantillaCubierta(
Presupuesto.TipoEncuadernacion tipoLibro,
Presupuesto.TipoCubierta tapa,
Integer ancho,
Integer alto,
Integer lomo,
Integer solapas,
Locale locale) {
try {
String plantillaName = "plantilla";
if (tipoLibro == Presupuesto.TipoEncuadernacion.grapado) {
plantillaName += "-grapado";
}
if (solapas > 0) {
plantillaName += "-solapas";
}
plantillaName += ".svg";
Integer sangrado = 5; // mm,
if (tapa != Presupuesto.TipoCubierta.tapaBlanda && tipoLibro != Presupuesto.TipoEncuadernacion.grapado) {
sangrado = 20;
}
Integer ancho_t = lomo + sangrado * 2 + ancho * 2 + solapas * 2;
Integer alto_t = alto + sangrado * 2;
// 3) Leer SVG template como texto
String basePath = "static/assets/images/imprimelibros/presupuestador/templates-cubierta/";
String svg = readClasspathText(basePath + plantillaName);
// 4) Sustituciones {{...}}
Map<String, String> vars = new HashMap<>();
NumberFormat nf = NumberFormat.getIntegerInstance(locale);
vars.put("ancho", nf.format(ancho)); // mm o lo que representes
vars.put("alto", nf.format(alto));
vars.put("ancho_t", nf.format(ancho_t));
vars.put("alto_t", nf.format(alto_t));
vars.put("lomo_t", nf.format(lomo != null ? lomo : 0));
vars.put("solapa_t", nf.format(solapas != null ? solapas : 0));
vars.put("sangrado", nf.format(sangrado));
vars.put("portada", messageSource.getMessage("presupuesto.plantilla-cubierta.portada", null, locale));
vars.put("contraportada",
messageSource.getMessage("presupuesto.plantilla-cubierta.contraportada", null, locale));
vars.put("lomo", messageSource.getMessage("presupuesto.plantilla-cubierta.lomo", null, locale));
vars.put("solapa", messageSource.getMessage("presupuesto.plantilla-cubierta.solapa", null, locale));
svg = replaceMustache(svg, vars);
// 5) Render SVG -> PNG (Batik)
svg = sanitizeForBatik(svg);
return svgToPng(svg, /* dpi */ 300f);
} catch (Exception e) {
System.err.println("Error obteniendo plantilla de cubierta: " + e.getMessage());
return null;
}
}
/** /**
* PRIVADO (futuro botón "Guardar"): persiste el presupuesto como borrador. * PRIVADO (futuro botón "Guardar"): persiste el presupuesto como borrador.
*/ */
@ -1474,7 +1562,6 @@ public class PresupuestoService {
target.setServiciosTotal(src.getServiciosTotal()); target.setServiciosTotal(src.getServiciosTotal());
target.setBaseImponible(src.getBaseImponible()); target.setBaseImponible(src.getBaseImponible());
target.setIvaReducido(src.getIvaReducido()); target.setIvaReducido(src.getIvaReducido());
target.setEntregaTipo(src.getEntregaTipo());
target.setIvaImporte4(src.getIvaImporte4()); target.setIvaImporte4(src.getIvaImporte4());
target.setIvaImporte21(src.getIvaImporte21()); target.setIvaImporte21(src.getIvaImporte21());
target.setTotalConIva(src.getTotalConIva()); target.setTotalConIva(src.getTotalConIva());
@ -1543,4 +1630,77 @@ public class PresupuestoService {
return ip; return ip;
} }
private static String readClasspathText(String path) throws IOException {
ClassPathResource res = new ClassPathResource(path);
try (InputStream in = res.getInputStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}
private static String replaceMustache(String svg, Map<String, String> vars) {
String out = svg;
for (var entry : vars.entrySet()) {
out = out.replace("{{" + entry.getKey() + "}}", entry.getValue());
}
return out;
}
private static byte[] svgToPng(String svgXml, float dpi) throws Exception {
PNGTranscoder t = new PNGTranscoder();
// Esto SÍ es correcto (convierte unidades a mm según DPI)
t.addTranscodingHint(PNGTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, 25.4f / dpi);
// ❌ NO uses KEY_AOI con null (provoca tu error)
// t.addTranscodingHint(PNGTranscoder.KEY_AOI, null);
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
TranscoderInput input = new TranscoderInput(new StringReader(svgXml));
TranscoderOutput output = new TranscoderOutput(out);
t.transcode(input, output);
return out.toByteArray();
}
}
private static String sanitizeForBatik(String svg) {
String out = svg;
// 1) Batik: context-stroke/context-fill
out = out.replace("context-stroke", "#000");
out = out.replace("context-fill", "none");
// 2) Batik: auto-start-reverse
out = out.replace("auto-start-reverse", "auto");
// 3) Reemplazar markers Triangle*/marker6* -> Arrow2S*
out = replaceMarkerAttr(out, "marker-start", "Arrow2Sstart");
out = replaceMarkerAttr(out, "marker-end", "Arrow2Send");
// 4) Lo MISMO pero cuando viene dentro de style="...marker-start:url(#X);..."
out = replaceMarkerInStyle(out, "marker-start", "Arrow2Sstart");
out = replaceMarkerInStyle(out, "marker-end", "Arrow2Send");
return out;
}
private static String replaceMarkerAttr(String svg, String attr, String newId) {
// Soporta: marker-start="url(#Triangle-5)" o marker-start='url( #Triangle-5 )'
Pattern p = Pattern.compile(
"(" + attr
+ "\\s*=\\s*)([\"'])\\s*url\\(\\s*#(Triangle[^\\s\\)\"']*|marker6[^\\s\\)\"']*)\\s*\\)\\s*\\2",
Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(svg);
return m.replaceAll("$1$2url(#" + newId + ")$2");
}
private static String replaceMarkerInStyle(String svg, String prop, String newId) {
// Soporta dentro de style: marker-start:url(#Triangle-5) (con espacios
// opcionales)
Pattern p = Pattern.compile(
"(" + prop + "\\s*:\\s*)url\\(\\s*#(Triangle[^\\s\\)\"';]*|marker6[^\\s\\)\"';]*)\\s*\\)",
Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(svg);
return m.replaceAll("$1url(#" + newId + ")");
}
} }

View File

@ -11,6 +11,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@ -37,28 +38,28 @@ public class ImpersonationController {
@PostMapping("/impersonate") @PostMapping("/impersonate")
@PreAuthorize("hasRole('ADMIN') or hasRole('SUPERADMIN')") @PreAuthorize("hasRole('ADMIN') or hasRole('SUPERADMIN')")
public String impersonate( public ResponseEntity<Void> impersonate(
@RequestParam("username") String username, @RequestParam("username") String username,
Authentication authentication, Authentication authentication,
HttpServletRequest request) { HttpServletRequest request) {
if (authentication == null) { if (authentication == null) {
return "redirect:/login"; return ResponseEntity.status(401).build();
} }
if (hasRole(authentication, PREVIOUS_ADMIN_ROLE)) { if (hasRole(authentication, PREVIOUS_ADMIN_ROLE)) {
return "redirect:/"; return ResponseEntity.status(409).build();
} }
String normalized = sanitizer.plain(username); String normalized = sanitizer.plain(username);
if (normalized == null || normalized.isBlank()) { if (normalized == null || normalized.isBlank()) {
return "redirect:/users"; return ResponseEntity.badRequest().build();
} }
normalized = normalized.trim().toLowerCase(); normalized = normalized.trim().toLowerCase();
if (authentication.getName() != null if (authentication.getName() != null
&& authentication.getName().equalsIgnoreCase(normalized)) { && authentication.getName().equalsIgnoreCase(normalized)) {
return "redirect:/users"; return ResponseEntity.status(409).build();
} }
UserDetails target; UserDetails target;
@ -68,10 +69,9 @@ public class ImpersonationController {
throw new AccessDeniedException("No autorizado"); throw new AccessDeniedException("No autorizado");
} }
boolean currentIsSuperAdmin = hasRole(authentication, "ROLE_SUPERADMIN");
boolean targetIsSuperAdmin = target.getAuthorities().stream() boolean targetIsSuperAdmin = target.getAuthorities().stream()
.anyMatch(a -> "ROLE_SUPERADMIN".equals(a.getAuthority())); .anyMatch(a -> "ROLE_SUPERADMIN".equals(a.getAuthority()));
if (targetIsSuperAdmin && !currentIsSuperAdmin) { if (targetIsSuperAdmin) {
throw new AccessDeniedException("No autorizado"); throw new AccessDeniedException("No autorizado");
} }
@ -88,7 +88,7 @@ public class ImpersonationController {
newAuth.setDetails(authentication.getDetails()); newAuth.setDetails(authentication.getDetails());
SecurityContextHolder.getContext().setAuthentication(newAuth); SecurityContextHolder.getContext().setAuthentication(newAuth);
return "redirect:/"; return ResponseEntity.noContent().build();
} }
@PostMapping("/impersonate/exit") @PostMapping("/impersonate/exit")

View File

@ -0,0 +1,155 @@
package com.imprimelibros.erp.users;
import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.imprimelibros.erp.config.Sanitizer;
import com.imprimelibros.erp.users.validation.ProfileForm;
@Controller
@RequestMapping("/pages-profile")
@PreAuthorize("isAuthenticated()")
public class ProfileController {
private final UserDao userDao;
private final PasswordEncoder passwordEncoder;
private final MessageSource messageSource;
private final Sanitizer sanitizer;
public ProfileController(UserDao userDao, PasswordEncoder passwordEncoder,
MessageSource messageSource, Sanitizer sanitizer) {
this.userDao = userDao;
this.passwordEncoder = passwordEncoder;
this.messageSource = messageSource;
this.sanitizer = sanitizer;
}
@GetMapping
public String view(
Authentication authentication,
@RequestParam(name = "success", required = false) String success,
Model model,
Locale locale) {
if (authentication == null) {
return "redirect:/login";
}
User user = userDao.findByUserNameIgnoreCase(authentication.getName()).orElse(null);
if (user == null) {
return "redirect:/login";
}
ProfileForm form = new ProfileForm();
form.setId(user.getId());
form.setFullName(user.getFullName());
form.setUserName(user.getUserName());
model.addAttribute("user", form);
model.addAttribute("success", success != null);
return "imprimelibros/users/profile";
}
@PostMapping
public String update(
Authentication authentication,
@Validated @ModelAttribute("user") ProfileForm form,
BindingResult binding,
Model model,
RedirectAttributes redirectAttributes,
Locale locale) {
if (authentication == null) {
return "redirect:/login";
}
User user = userDao.findByUserNameIgnoreCase(authentication.getName()).orElse(null);
if (user == null) {
return "redirect:/login";
}
String normalized = sanitizer.plain(form.getUserName());
if (normalized != null) {
normalized = normalized.trim().toLowerCase();
}
if (normalized == null || normalized.isBlank()) {
binding.rejectValue("userName", "usuarios.error.email",
messageSource.getMessage("usuarios.error.email", null, locale));
} else if (userDao.existsByUserNameIgnoreCaseAndIdNot(normalized, user.getId())) {
binding.rejectValue("userName", "usuarios.error.duplicado",
messageSource.getMessage("usuarios.error.duplicado", null, locale));
}
String cleanName = sanitizer.plain(form.getFullName());
if (cleanName == null || cleanName.isBlank()) {
binding.rejectValue("fullName", "usuarios.error.nombre",
messageSource.getMessage("usuarios.error.nombre", null, locale));
}
boolean wantsPasswordChange = hasText(form.getCurrentPassword())
|| hasText(form.getNewPassword())
|| hasText(form.getConfirmPassword());
if (wantsPasswordChange) {
if (!hasText(form.getCurrentPassword())) {
binding.rejectValue("currentPassword", "usuarios.error.password.actual",
messageSource.getMessage("usuarios.error.password.actual", null, locale));
} else if (!passwordEncoder.matches(form.getCurrentPassword(), user.getPassword())) {
binding.rejectValue("currentPassword", "usuarios.error.password.actual.incorrecta",
messageSource.getMessage("usuarios.error.password.actual.incorrecta", null, locale));
}
if (!hasText(form.getNewPassword())) {
binding.rejectValue("newPassword", "usuarios.error.password.nueva.requerida",
messageSource.getMessage("usuarios.error.password.nueva.requerida", null, locale));
} else if (form.getNewPassword().length() < 6) {
binding.rejectValue("newPassword", "usuarios.error.password.min",
messageSource.getMessage("usuarios.error.password.min", null, locale));
}
if (!hasText(form.getConfirmPassword())) {
binding.rejectValue("confirmPassword", "usuarios.error.confirmPassword.requerida",
messageSource.getMessage("usuarios.error.confirmPassword.requerida", null, locale));
} else if (hasText(form.getNewPassword()) && !form.getNewPassword().equals(form.getConfirmPassword())) {
binding.rejectValue("confirmPassword", "usuarios.error.password-coinciden",
messageSource.getMessage("usuarios.error.password-coinciden", null, locale));
}
}
if (binding.hasErrors()) {
model.addAttribute("success", false);
return "imprimelibros/users/profile";
}
user.setFullName(cleanName.trim());
user.setUserName(normalized);
if (wantsPasswordChange) {
user.setPassword(passwordEncoder.encode(form.getNewPassword()));
}
userDao.save(user);
redirectAttributes.addAttribute("success", "1");
return "redirect:/pages-profile";
}
private static boolean hasText(String value) {
return value != null && !value.isBlank();
}
}

View File

@ -29,6 +29,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import com.imprimelibros.erp.datatables.DataTablesRequest; import com.imprimelibros.erp.datatables.DataTablesRequest;
import com.imprimelibros.erp.datatables.DataTablesParser; import com.imprimelibros.erp.datatables.DataTablesParser;
@ -151,7 +152,7 @@ public class UserController {
.append(user.getId()) .append(user.getId())
.append("\" class=\"link-success btn-edit-user fs-15\"><i class=\"ri-edit-2-line\"></i></a>"); .append("\" class=\"link-success btn-edit-user fs-15\"><i class=\"ri-edit-2-line\"></i></a>");
if (!isSelf && (isSuperAdmin || !targetIsSuperAdmin)) { if (!isSelf && !targetIsSuperAdmin) {
actions.append("<a href=\"javascript:void(0);\" data-username=\"") actions.append("<a href=\"javascript:void(0);\" data-username=\"")
.append(user.getUserName()) .append(user.getUserName())
.append("\" class=\"link-info btn-impersonate-user fs-15\"><i class=\"ri-user-shared-line\"></i></a>"); .append("\" class=\"link-info btn-impersonate-user fs-15\"><i class=\"ri-user-shared-line\"></i></a>");
@ -377,7 +378,7 @@ public class UserController {
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) { @RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(Math.max(0, page - 1), size); Pageable pageable = PageRequest.of(Math.max(0, page - 1), size, Sort.by(Sort.Direction.ASC, "fullName"));
Page<User> users = userService.findByRoleAndSearch(role, q, pageable); Page<User> users = userService.findByRoleAndSearch(role, q, pageable);

View File

@ -0,0 +1,68 @@
package com.imprimelibros.erp.users.validation;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
public class ProfileForm {
private Long id;
@NotBlank(message = "{usuarios.error.nombre}")
private String fullName;
@NotBlank(message = "{usuarios.error.email}")
@Email(message = "{usuarios.error.email.formato}")
private String userName;
private String currentPassword;
private String newPassword;
private String confirmPassword;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getCurrentPassword() {
return currentPassword;
}
public void setCurrentPassword(String currentPassword) {
this.currentPassword = currentPassword;
}
public String getNewPassword() {
return newPassword;
}
public void setNewPassword(String newPassword) {
this.newPassword = newPassword;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
}

View File

@ -33,7 +33,7 @@ logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} -
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n
# Datos de la API de Safekat # Datos de la API de Safekat
safekat.api.url=http://localhost:8000/ safekat.api.url=https://erp-dev.safekat.es/
safekat.api.email=imnavajas@coit.es safekat.api.email=imnavajas@coit.es
safekat.api.password=Safekat2024 safekat.api.password=Safekat2024
@ -41,6 +41,6 @@ safekat.api.password=Safekat2024
redsys.environment=test redsys.environment=test
redsys.url=https://sis-t.redsys.es:25443/sis/realizarPago redsys.url=https://sis-t.redsys.es:25443/sis/realizarPago
redsys.refund.url=https://sis-t.redsys.es:25443/sis/rest/trataPeticionREST redsys.refund.url=https://sis-t.redsys.es:25443/sis/rest/trataPeticionREST
redsys.urls.ok=http://localhost:8080/pagos/redsys/ok redsys.urls.ok=https://imprimelibros.jjimenez.eu/pagos/redsys/ok
redsys.urls.ko=http://localhost:8080/pagos/redsys/ko redsys.urls.ko=https://imprimelibros.jjimenez.eu/pagos/redsys/ko
redsys.urls.notify=https://orological-sacrilegiously-lucille.ngrok-free.dev/pagos/redsys/notify redsys.urls.notify=https://imprimelibros.jjimenez.eu/pagos/redsys/notify

View File

@ -0,0 +1,46 @@
# Profile desarrollo
#
# Logging
#
logging.level.root=INFO
logging.level.org.springframework.security=ERROR
logging.level.org.springframework=ERROR
logging.level.org.springframework.web=ERROR
logging.level.org.thymeleaf=ERROR
logging.level.org.apache.catalina.core=ERROR
# Debug JPA / Hibernate
#logging.level.org.hibernate.SQL=DEBUG
#logging.level.org.hibernate.orm.jdbc.bind=TRACE
#spring.jpa.properties.hibernate.format_sql=true
server.error.include-message=always
server.error.include-stacktrace=on_param
server.error.include-binding-errors=on_param
# Archivo relativo a tu proyecto (asegúrate de que exista el directorio ./logs)
logging.file.name=logs/erp.log
# Rotación tiempo+tamaño (mismo patrón, pero en ./logs)
logging.logback.rollingpolicy.file-name-pattern=logs/erp-%d{yyyy-MM-dd}.%i.log
logging.logback.rollingpolicy.max-file-size=10MB
logging.logback.rollingpolicy.max-history=10
logging.logback.rollingpolicy.total-size-cap=1GB
# Formatos con timestamp
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n
# Datos de la API de Safekat
safekat.api.url=http://localhost:8000/
safekat.api.email=imnavajas@coit.es
safekat.api.password=Safekat2024
# Configuración Redsys
redsys.environment=test
redsys.url=https://sis-t.redsys.es:25443/sis/realizarPago
redsys.refund.url=https://sis-t.redsys.es:25443/sis/rest/trataPeticionREST
redsys.urls.ok=http://localhost:8080/pagos/redsys/ok
redsys.urls.ko=http://localhost:8080/pagos/redsys/ko
redsys.urls.notify=https://orological-sacrilegiously-lucille.ngrok-free.dev/pagos/redsys/notify

View File

@ -1,6 +1,7 @@
spring.application.name=erp spring.application.name=erp
# Active profile # Active profile
spring.profiles.active=dev spring.profiles.active=local
#spring.profiles.active=dev
#spring.profiles.active=test #spring.profiles.active=test
#spring.profiles.active=prod #spring.profiles.active=prod

View File

@ -0,0 +1,20 @@
databaseChangeLog:
- changeSet:
id: 0026-drop-entrega-tipo-from-presupuesto
author: jjo
changes:
- dropColumn:
tableName: presupuesto
columnName: entrega_tipo
rollback:
- addColumn:
tableName: presupuesto
columns:
- column:
name: entrega_tipo
type: ENUM('peninsula', 'canarias', 'paises_ue')
defaultValue: peninsula
afterColumn: base_imponible
constraints:
nullable: false

View File

@ -0,0 +1,18 @@
databaseChangeLog:
- changeSet:
id: 0027-add-lomo-to-presupuesto
author: jjo
changes:
- addColumn:
tableName: presupuesto
columns:
- column:
name: lomo
type: DECIMAL(12, 2)
defaultValueNumeric: 0
afterColumn: papel_interior_id
rollback:
- dropColumn:
tableName: presupuesto
columnName: lomo

View File

@ -0,0 +1,18 @@
databaseChangeLog:
- changeSet:
id: 0028-add-lomo-cubierta-to-presupuesto
author: jjo
changes:
- addColumn:
tableName: presupuesto
columns:
- column:
name: lomo_cubierta
type: DECIMAL(12, 2)
defaultValueNumeric: 0
afterColumn: lomo
rollback:
- dropColumn:
tableName: presupuesto
columnName: lomo_cubierta

View File

@ -0,0 +1,43 @@
databaseChangeLog:
- changeSet:
id: 0029-update-estados-pedidos-lineas
author: jjo
changes:
- modifyDataType:
tableName: pedidos_lineas
columnName: estado
newDataType: >
enum(
'pendiente_pago',
'procesando_pago',
'denegado_pago',
'aprobado',
'procesando_pedido',
'maquetacion',
'haciendo_ferro_digital',
'esperando_aceptacion_ferro_digital',
'haciendo_ferro',
'esperando_aceptacion_ferro',
'produccion',
'terminado',
'enviado',
'cancelado'
)
rollback:
- modifyDataType:
tableName: pedidos_lineas
columnName: estado
newDataType: >
enum(
'pendiente_pago',
'procesando_pago',
'denegado_pago',
'aprobado',
'maquetacion',
'haciendo_ferro',
'esperando_aceptacion_ferro',
'produccion',
'terminado',
'enviado',
'cancelado'
)

View File

@ -1,4 +1,4 @@
databaseChangeLog: databaseChangeLog:
- include: - include:
file: db/changelog/changesets/0001-baseline.yml file: db/changelog/changesets/0001-baseline.yml
- include: - include:
@ -49,3 +49,11 @@ databaseChangeLog:
file: db/changelog/changesets/0024-series-facturacion-seeder.yml file: db/changelog/changesets/0024-series-facturacion-seeder.yml
- include: - include:
file: db/changelog/changesets/0025-create-facturas-direcciones.yml file: db/changelog/changesets/0025-create-facturas-direcciones.yml
- include:
file: db/changelog/changesets/0026-drop-entrega-tipo-from-presupuesto.yml
- include:
file: db/changelog/changesets/0027-add-lomo-to-presupuesto.yml
- include:
file: db/changelog/changesets/0028-add-lomo-cubierta-to-presupuesto.yml
- include:
file: db/changelog/changesets/0029-update-estados-pedidos-lineas.yml

View File

@ -29,6 +29,8 @@ cart.shipping.tipo-envio=Tipo de envío:
cart.shipping.errors.units-error=Por favor, introduzca un número válido entre 1 y {max}. cart.shipping.errors.units-error=Por favor, introduzca un número válido entre 1 y {max}.
cart.shipping.errors.noAddressSelected=Debe seleccionar una dirección de envío para el pedido. cart.shipping.errors.noAddressSelected=Debe seleccionar una dirección de envío para el pedido.
cart.shipping.errors.fillAddressesItems=Debe seleccionar una dirección de envío para cada artículo de la cesta. cart.shipping.errors.fillAddressesItems=Debe seleccionar una dirección de envío para cada artículo de la cesta.
cart.shipping.errors.fillBillingAddressItems=Debe seleccionar una dirección de facturación para poder realizar el pago.
cart.resumen.title=Resumen de la cesta cart.resumen.title=Resumen de la cesta
cart.resumen.base=Base imponible cart.resumen.base=Base imponible

View File

@ -22,10 +22,12 @@ pedido.estado.pendiente_pago=Pendiente de pago
pedido.estado.procesando_pago=Procesando pago pedido.estado.procesando_pago=Procesando pago
pedido.estado.denegado_pago=Pago denegado pedido.estado.denegado_pago=Pago denegado
pedido.estado.aprobado=Aprobado pedido.estado.aprobado=Aprobado
pedido.estado.procesando_pedido=Procesando pedido
pedido.estado.maquetacion=Maquetación pedido.estado.maquetacion=Maquetación
pedido.estado.haciendo_ferro=Haciendo ferro pedido.estado.haciendo_ferro_digital=Haciendo ferro digital
pedido.estado.esperando_aceptacion_ferro=Esperando aceptación de ferro pedido.estado.esperando_aceptacion_ferro_digital=Esperando aceptación de ferro digital
pedido.estado.ferro_cliente=Esperando aprobación de ferro pedido.estado.haciendo_ferro=Haciendo ejemplar de prueba
pedido.estado.esperando_aceptacion_ferro=Esperando aceptación de ejemplar de prueba
pedido.estado.produccion=Producción pedido.estado.produccion=Producción
pedido.estado.terminado=Terminado pedido.estado.terminado=Terminado
pedido.estado.enviado=Enviado pedido.estado.enviado=Enviado
@ -47,10 +49,14 @@ pedido.prueba=Prueba
pedido.table.id=Num. Pedido pedido.table.id=Num. Pedido
pedido.table.cliente=Cliente pedido.table.cliente=Cliente
pedido.table.fecha=Fecha pedido.table.fecha=Fecha
pedido.table.titulos=Títulos
pedido.table.titulos.and-more=y {0} más
pedido.table.importe=Importe pedido.table.importe=Importe
pedido.table.estado=Estado pedido.table.estado=Estado
pedido.table.acciones=Acciones pedido.table.acciones=Acciones
pedido.gasto-anual=Gasto últimos 12 meses
pedido.view.tirada=Tirada pedido.view.tirada=Tirada
pedido.view.view-presupuesto=Ver presupuesto pedido.view.view-presupuesto=Ver presupuesto
pedido.view.aceptar-ferro=Aceptar ferro pedido.view.aceptar-ferro=Aceptar ferro

View File

@ -117,6 +117,10 @@ presupuesto.continuar-cubierta=Diseño cubierta
# Pestaña cubierta # Pestaña cubierta
presupuesto.plantilla-cubierta=Plantilla de cubierta presupuesto.plantilla-cubierta=Plantilla de cubierta
presupuesto.plantilla-cubierta-text=Recuerde que la cubierta es el conjunto formado por la portada, contraportada, lomo y solapas, en caso de que las lleve.<br/><br/><br/>Si tiene dudas de las medidas puede solicitarnos una plantilla cuando haga el pedido. presupuesto.plantilla-cubierta-text=Recuerde que la cubierta es el conjunto formado por la portada, contraportada, lomo y solapas, en caso de que las lleve.<br/><br/><br/>Si tiene dudas de las medidas puede solicitarnos una plantilla cuando haga el pedido.
presupuesto.plantilla-cubierta.portada=Portada
presupuesto.plantilla-cubierta.contraportada=Contraportada
presupuesto.plantilla-cubierta.lomo=Lomo
presupuesto.plantilla-cubierta.solapa=Solapa
presupuesto.tipo-cubierta=Tipo de cubierta presupuesto.tipo-cubierta=Tipo de cubierta
presupuesto.tipo-cubierta-descripcion=Seleccione el tipo de cubierta y sus opciones presupuesto.tipo-cubierta-descripcion=Seleccione el tipo de cubierta y sus opciones
presupuesto.tapa-blanda=Tapa blanda presupuesto.tapa-blanda=Tapa blanda
@ -234,6 +238,7 @@ presupuesto.resumen-texto-acabado-cubierta= <li>Acabado: {0}. </li>
presupuesto.resumen-texto-end=</ul> presupuesto.resumen-texto-end=</ul>
presupuesto.resumen-texto-sobrecubierta=<li>Sobrecubierta impresa en {0} {1} gr. <ul><li>Acabado: {2}</li><li>Solapas: {3} mm.</li></ul></li> presupuesto.resumen-texto-sobrecubierta=<li>Sobrecubierta impresa en {0} {1} gr. <ul><li>Acabado: {2}</li><li>Solapas: {3} mm.</li></ul></li>
presupuesto.resumen-texto-faja=<li>Faja impresa en {0} {1} gr. con un alto de {2} mm. <ul><li>Acabado: {3}</li><li>Solapas: {4} mm.</li></ul></li> presupuesto.resumen-texto-faja=<li>Faja impresa en {0} {1} gr. con un alto de {2} mm. <ul><li>Acabado: {3}</li><li>Solapas: {4} mm.</li></ul></li>
presupuesto.resumen-lomos=<li>Dimensiones de los lomos: <ul><li>Lomo interior: {0} mm</li><li>Lomo total: {1} mm</li></ul></li>
presupuesto.resumen-deposito-legal=Ejemplares para el Depósito Legal presupuesto.resumen-deposito-legal=Ejemplares para el Depósito Legal
presupuesto.volver-extras=Extras del libro presupuesto.volver-extras=Extras del libro
presupuesto.resumen.inicie-sesion=Inicie sesión para continuar presupuesto.resumen.inicie-sesion=Inicie sesión para continuar

View File

@ -1,3 +1,23 @@
usuarios.form.nombre=Full name
usuarios.form.email=Email
usuarios.form.confirmarPassword=Confirm password
usuarios.form.password.actual=Current password
usuarios.form.password.nueva=New password
usuarios.form.password.nota=You can only change the password if you provide the current one.
usuarios.error.nombre=Name is required.
usuarios.error.email=Email is required.
usuarios.error.email.formato=Email is not valid.
usuarios.error.password.min=Password must be at least 6 characters.
usuarios.error.password.actual=Current password is required.
usuarios.error.password.actual.incorrecta=Current password is not correct.
usuarios.error.password.nueva.requerida=New password is required.
usuarios.error.confirmPassword.requerida=Password confirmation is required.
usuarios.error.password-coinciden=Passwords do not match.
usuarios.error.duplicado=There is already a user with that email.
usuarios.impersonate.title=Sign in as user usuarios.impersonate.title=Sign in as user
usuarios.impersonate.text=You are about to sign in as <b>{0}</b>. You can return to your user from the menu. usuarios.impersonate.text=You are about to sign in as <b>{0}</b>. You can return to your user from the menu.
usuarios.impersonate.button=Continue usuarios.impersonate.button=Continue
usuarios.profile.title=Edit profile
usuarios.profile.success=Profile updated successfully.

View File

@ -20,6 +20,9 @@ usuarios.form.nombre=Nombre completo
usuarios.form.email=Correo electrónico usuarios.form.email=Correo electrónico
usuarios.form.password=Contraseña usuarios.form.password=Contraseña
usuarios.form.confirmarPassword=Confirmar contraseña usuarios.form.confirmarPassword=Confirmar contraseña
usuarios.form.password.actual=Contraseña actual
usuarios.form.password.nueva=Nueva contraseña
usuarios.form.password.nota=Solo podrás cambiar la contraseña si indicas la actual.
usuarios.form.rol=Rol usuarios.form.rol=Rol
usuarios.form.estado=Estado usuarios.form.estado=Estado
@ -37,6 +40,9 @@ usuarios.error.email.formato=El correo electrónico no es válido.
usuarios.error.rol=El rol seleccionado no es válido. usuarios.error.rol=El rol seleccionado no es válido.
usuarios.error.password.requerida=La contraseña es obligatoria. usuarios.error.password.requerida=La contraseña es obligatoria.
usuarios.error.password.min=La contraseña debe tener al menos 6 caracteres. usuarios.error.password.min=La contraseña debe tener al menos 6 caracteres.
usuarios.error.password.actual=La contraseña actual es obligatoria.
usuarios.error.password.actual.incorrecta=La contraseña actual no es correcta.
usuarios.error.password.nueva.requerida=La nueva contraseña es obligatoria.
usuarios.error.confirmPassword.requerida=La confirmación de la contraseña es obligatoria. usuarios.error.confirmPassword.requerida=La confirmación de la contraseña es obligatoria.
usuarios.error.password-coinciden=Las contraseñas no coinciden. usuarios.error.password-coinciden=Las contraseñas no coinciden.
usuarios.error.delete-relational-data=No se puede eliminar el usuario porque tiene datos relacionados. usuarios.error.delete-relational-data=No se puede eliminar el usuario porque tiene datos relacionados.
@ -57,3 +63,5 @@ usuarios.delete.ok.text=El usuario ha sido eliminado con éxito.
usuarios.impersonate.title=Entrar como usuario usuarios.impersonate.title=Entrar como usuario
usuarios.impersonate.text=Vas a iniciar sesión como <b>{0}</b>. Podrás volver a tu usuario desde el menú. usuarios.impersonate.text=Vas a iniciar sesión como <b>{0}</b>. Podrás volver a tu usuario desde el menú.
usuarios.impersonate.button=Entrar usuarios.impersonate.button=Entrar
usuarios.profile.title=Editar perfil
usuarios.profile.success=Perfil actualizado correctamente.

View File

@ -0,0 +1,372 @@
:root {
/* ====== Colores (cámbialos a tu gusto) ====== */
--banner-bg-1: #a5a091;
--banner-bg-2: #8292a8;
--banner-panel-bg: #a1b1b2;
--banner-panel-border: rgba(255, 255, 255, .75);
--text-main: #ffffff;
--text-muted: rgba(255, 255, 255, .8);
--accent-1: #e5745b;
/* salmón */
--accent-2: #92b2a7;
/* tu verde corporativo */
--accent-3: #7cc7ff;
/* toque azul claro */
--card-bg: #ffffff;
--card-border: rgba(8, 42, 67, .18);
--card-title: #0a314b;
--card-chip-bg: var(--accent-1);
--card-chip-text: #ffffff;
--shadow: 0 10px 30px rgba(0, 0, 0, .18);
/* ====== Medidas ====== */
--radius-lg: 18px;
--radius-md: 14px;
--radius-sm: 10px;
--pad: 22px;
}
.ib-loyalty-banner {
position: relative;
width: 100%;
overflow: hidden;
border-radius: var(--radius-lg);
box-shadow: var(--shadow);
color: var(--text-main);
/* padding fluido */
padding: clamp(14px, 2.2vw, 22px);
/* Importante: reserva espacio inferior para decoraciones (libro) en desktop */
padding-bottom: clamp(18px, 3.2vw, 46px);
/* Fondo con gradiente + textura sutil */
background:
radial-gradient(1200px 500px at 20% 0%, rgba(124, 199, 255, .20), transparent 60%),
radial-gradient(900px 450px at 90% 20%, rgba(243, 162, 133, .22), transparent 65%),
linear-gradient(135deg, var(--banner-bg-1), var(--banner-bg-2));
}
/* Opcional pero ayuda a que haya “lienzo” para el libro */
@media (min-width: 1101px) {
.ib-loyalty-banner {
min-height: 240px;
}
}
/* ===== Decoraciones generales ===== */
.ib-loyalty-banner .decor {
position: absolute;
inset: 0;
pointer-events: none;
opacity: .95;
}
/* Círculos/“sellos” */
.ib-loyalty-banner .decor::before,
.ib-loyalty-banner .decor::after {
content: "";
position: absolute;
width: 140px;
height: 140px;
border-radius: 50%;
border: 10px solid rgba(243, 162, 133, .65);
box-shadow: inset 0 0 0 10px rgba(255, 255, 255, .08);
}
.ib-loyalty-banner .decor::before {
top: -42px;
right: -50px;
transform: rotate(10deg);
}
.ib-loyalty-banner .decor::after {
bottom: -55px;
left: -55px;
border-color: rgba(243, 162, 133, .55);
}
/* ===== Libros “dibujados” con SVG como background ===== */
.ib-loyalty-banner .book {
position: absolute;
width: 190px;
height: 150px;
opacity: .9;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
background-image: url("/assets/images/open-book.svg");
/* Sombra sin cargarte otros filtros */
filter: drop-shadow(0 10px 18px rgba(0, 0, 0, .25));
}
/* Libro pequeño: lo subimos un poco para que no se quede “bajo” cuando el layout crece */
.ib-loyalty-banner .book.small {
width: 150px;
height: 120px;
left: 22px;
bottom: 18px;
/* antes 10px */
opacity: .85;
z-index: 1;
/* por encima del fondo, por debajo del contenido (contenido z-index 2) */
}
/* ===== Contenido ===== */
.ib-loyalty-inner {
position: relative;
display: grid;
gap: clamp(12px, 2vw, 18px);
/* CLAVE: NO estires ambas columnas a la misma altura */
align-items: start;
z-index: 2;
/* Dos columnas con mínimos reales */
grid-template-columns: minmax(320px, 1.05fr) minmax(320px, .95fr);
}
/* Apila antes para que no se estrangule */
@media (max-width: 1100px) {
.ib-loyalty-inner {
grid-template-columns: 1fr;
}
}
/* Panel principal (logo + textos) */
.ib-loyalty-hero {
border: 2px solid var(--banner-panel-border);
border-radius: var(--radius-lg);
background: var(--banner-panel-bg);
padding: 18px;
position: relative;
}
/*.ib-loyalty-hero::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: -2px;
height: 6px;
background: linear-gradient(90deg, transparent, var(--accent-1), transparent);
opacity: .9;
border-radius: 999px;
}*/
.ib-loyalty-head {
display: flex;
gap: 14px;
align-items: flex-start;
flex-wrap: wrap;
/* responsive */
}
.ib-loyalty-logo {
width: 56px;
height: 56px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
}
.ib-loyalty-logo img {
width: 38px;
height: 38px;
object-fit: contain;
}
.ib-loyalty-title {
margin: 0;
font-size: clamp(1.05rem, 1.2vw, 1.35rem);
font-weight: 800;
letter-spacing: .2px;
}
.ib-loyalty-sub {
margin: 4px 0 0 0;
color: var(--text-muted);
font-weight: 500;
font-size: clamp(.9rem, 1vw, 1rem);
}
/* ===== Rewards ===== */
.ib-rewards {
border-radius: var(--radius-lg);
padding: 14px 14px 10px;
background: rgba(255, 255, 255, .04);
border: 1px solid rgba(255, 255, 255, .12);
}
.ib-rewards h6 {
margin: 4px 6px 12px;
font-size: .95rem;
letter-spacing: .25px;
opacity: .95;
}
.ib-rewards-grid{
display: grid;
gap: 10px;
/* Nunca más de 3 columnas, pero baja si no hay sitio */
grid-template-columns: repeat(3, minmax(160px, 1fr));
}
@media (max-width: 992px){
.ib-rewards-grid{
grid-template-columns: repeat(2, minmax(160px, 1fr));
}
}
@media (max-width: 520px){
.ib-rewards-grid{
grid-template-columns: 1fr;
}
}
.ib-card {
background: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: var(--radius-md);
padding: 10px 10px 9px;
color: var(--card-title);
box-shadow: 0 8px 18px rgba(0, 0, 0, .10);
}
.ib-card .range {
font-size: .82rem;
opacity: .85;
font-weight: 700;
margin-bottom: 6px;
}
.ib-card .percent {
font-size: 1.35rem;
font-weight: 900;
line-height: 1;
margin: 2px 0 8px;
}
.ib-card .chip {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 5px 10px;
border-radius: 999px;
background: var(--card-chip-bg);
color: var(--card-chip-text);
font-weight: 800;
font-size: .78rem;
letter-spacing: .2px;
}
/* Tarjeta “0%” con borde punteado (si la usas) */
.ib-card.is-empty {
background: rgba(255, 255, 255, .9);
border: 2px dashed rgba(10, 49, 75, .35);
}
/* ===== Ajustes extra para móviles ===== */
@media (max-width: 420px) {
.ib-rewards {
padding: 12px;
}
.ib-card {
padding: 12px;
}
}
/* ===== Libro: reduce presencia o desaparece en móvil ===== */
@media (max-width: 1100px) {
.ib-loyalty-banner .book.small {
opacity: .55;
left: 10px;
bottom: 10px;
width: 120px;
height: 95px;
}
}
@media (max-width: 520px) {
.ib-loyalty-banner .book.small {
display: none;
/* fuera en móviles pequeños */
}
}
/* ===== OPCIONAL: hero más compacto en móvil ===== */
@media (max-width: 520px) {
.ib-loyalty-head {
gap: 10px;
}
.ib-loyalty-logo {
width: 48px;
height: 48px;
}
.ib-loyalty-logo img {
width: 32px;
height: 32px;
}
}
.ib-loyalty-left{
display: flex;
flex-direction: column;
gap: 18px;
height: 100%;
}
.ib-loyalty-stat-card{
/* centra en el espacio libre */
margin-top: auto;
margin-bottom: auto;
margin-left: auto;
margin-right: auto;
/* mismo look que el hero */
background: var(--banner-panel-bg);
border: 2px solid var(--banner-panel-border);
border-radius: var(--radius-lg);
padding: 22px;
max-width: 360px; /* evita que se haga enorme */
}
.ib-loyalty-stat-card h6{
letter-spacing: .4px;
margin-bottom: 12px;
}
.ib-loyalty-stat-card h2{
font-weight: 800;
}
.ib-loyalty-stat-card i{
opacity: .85;
}
@media (max-width: 1100px){
.ib-loyalty-stat-card{
margin-top: 12px;
margin-bottom: 0;
max-width: 100%;
}
}

View File

@ -0,0 +1,674 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="189.90369mm"
height="122.48086mm"
viewBox="0 0 189.90369 122.48086"
version="1.1"
id="svg1"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
sodipodi:docname="plantilla-grapado-solapas.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="1.4378367"
inkscape:cx="427.37815"
inkscape:cy="265.32915"
inkscape:window-width="1920"
inkscape:window-height="1009"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<rect
x="148.51912"
y="407.19812"
width="184.41943"
height="125.40522"
id="rect7" />
<rect
x="171.09036"
y="459.02292"
width="159.26704"
height="90.413605"
id="rect3" />
<marker
id="Arrow2Sstart"
inkscape:isstock="true"
inkscape:stockid="Arrow2Sstart"
orient="auto"
refX="0"
refY="0"
style="overflow:visible">
<path
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
id="Arrow2SstartPath"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(0.3,0,0,0.3,-0.69,0)" />
</marker>
<marker
id="Arrow2Send"
inkscape:isstock="true"
inkscape:stockid="Arrow2Send"
orient="auto"
refX="0"
refY="0"
style="overflow:visible">
<path
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
id="Arrow2SendPath"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4" />
</marker>
<marker
style="overflow:visible"
id="marker6-3"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-6"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-1" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-5"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-4" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-4"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-12" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-0"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-0" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-6-1"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-1-7" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-5-7"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-4-7" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-1-9"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-3-0" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-8-0"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-7-6" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-1-9-0"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-3-0-6" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-8-0-6"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-7-6-1" />
</marker>
</defs>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-14.044687,-5.7512817)">
<rect
style="opacity:1;fill:#dee3db;fill-opacity:1;stroke:#8292a8;stroke-width:0.624;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect2"
width="182.32635"
height="115.28469"
x="14.044687"
y="5.7512817" />
<g
id="g2"
transform="translate(2.2085342,-0.96640021)">
<rect
style="opacity:1;fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.69881;stroke-dasharray:none;stroke-opacity:1"
id="rect1"
width="82.014671"
height="94.487244"
x="22.535099"
y="17.283327" />
<rect
style="opacity:1;fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.66258;stroke-dasharray:none;stroke-opacity:1"
id="rect1-8"
width="78.523048"
height="94.523483"
x="104.56826"
y="17.359972" />
<rect
style="fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.4395;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1-8-1-7"
width="18.953001"
height="94.775017"
x="164.55183"
y="17.203754" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1.157;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 23.921577,124.62439 58.700742,-0.18401"
id="path4" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5);marker-end:url(#marker6-3);paint-order:normal"
d="m 107.44694,98.144427 54.56369,0.224688"
id="path5-3"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3);marker-end:url(#marker6-3-9);paint-order:normal"
d="M 43.754233,98.310783 102.27388,98.02943"
id="path5-3-0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-6);marker-end:url(#marker6-3-9-5);paint-order:normal"
d="M 114.29411,109.14665 113.88575,19.631562"
id="path5-3-0-9"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-4);marker-end:url(#marker6-3-9-0);paint-order:normal"
d="m 13.752058,124.20085 178.229792,0.0889"
id="path5-3-0-6"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-6-1);marker-end:url(#marker6-3-9-5-7);paint-order:normal"
d="M 196.99745,120.60716 196.58909,9.1942972"
id="path5-3-0-9-3"
sodipodi:nodetypes="cc" />
</g>
<g
id="g4"
transform="matrix(1.2478873,0,0,1.2478873,-26.713293,-112.63102)">
<text
xml:space="preserve"
id="text3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(4.9613536,4.4099808)"><tspan
x="57.642538"
y="136.9912"
id="tspan6"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan5">{{contraportada}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<g
id="g4-2"
transform="matrix(1.2478873,0,0,1.2478873,41.181684,-112.61247)">
<text
xml:space="preserve"
id="text3-7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(2.1290346,4.4099808)"><tspan
x="64.690151"
y="136.9912"
id="tspan9"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan8">{{portada}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<g
id="g4-2-9"
transform="matrix(0,-1.2478873,1.2478873,0,0.59255852,162.33513)">
<text
xml:space="preserve"
id="text3-7-3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6-8);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(2.5239521,4.4099808)"><tspan
x="65.841054"
y="136.9912"
id="tspan11"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan10">{{solapa}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6-8"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<rect
style="fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.4395;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1-8-1-7-8"
width="18.953001"
height="94.775017"
x="24.558743"
y="16.180035" />
<g
id="g4-2-9-0"
transform="matrix(0,-1.2478873,1.2478873,0,-141.60976,162.27774)">
<text
xml:space="preserve"
id="text3-7-3-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6-8-6);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(2.5239521,4.4099808)"><tspan
x="65.841054"
y="136.9912"
id="tspan13"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan12">{{solapa}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6-8-6"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="-65.329605"
y="113.37225"
id="text13-8-5"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan13-2-3"
style="stroke-width:0"
x="-65.32299"
y="113.37225">{{alto}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="137.0002"
y="101.62363"
id="text13-3"><tspan
sodipodi:role="line"
id="tspan13-6"
style="stroke-width:0"
x="137.00682"
y="101.62363">{{ancho}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="75.285461"
y="101.62363"
id="text13-3-1"><tspan
sodipodi:role="line"
id="tspan13-6-4"
style="stroke-width:0"
x="75.292076"
y="101.62363">{{ancho}} mm</tspan></text>
<g
id="g4-7-0"
transform="matrix(1.2478873,0,0,1.2478873,-134.4255,19.484879)" />
<text
xml:space="preserve"
style="font-size:3.52778px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.07859"
y="126.83306"
id="text7"><tspan
sodipodi:role="line"
id="tspan7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="105.08521"
y="126.83306">{{ancho_t}} mm</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-1-9);marker-end:url(#marker6-3-8-0);paint-order:normal"
d="m 169.57878,97.434632 13.02331,0.06153"
id="path5-3-7-8"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:2.82222px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="176.14073"
y="101.38786"
id="text7-7-3"><tspan
sodipodi:role="line"
id="tspan7-1-4"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.82222px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="176.14735"
y="101.38786">{{solapa_t}} mm</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-1-9-0);marker-end:url(#marker6-3-8-0-6);paint-order:normal"
d="m 27.840231,97.50449 13.023235,0.06153"
id="path5-3-7-8-9"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:2.82222px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="34.403442"
y="101.45772"
id="text7-7-3-6"><tspan
sodipodi:role="line"
id="tspan7-1-4-3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.82222px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="34.410057"
y="101.45772">{{solapa_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-size:3.52778px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.871456"
y="202.53311"
id="text7-8"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="-63.864841"
y="202.53311">{{alto_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.14069"
y="11.526331"
id="text7-1"><tspan
sodipodi:role="line"
id="tspan7-6"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="105.14731"
y="11.526331">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.14069"
y="117.33289"
id="text7-1-1"><tspan
sodipodi:role="line"
id="tspan7-6-8"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="105.1473"
y="117.33289">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.443714"
y="20.223143"
id="text7-1-5"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-6-4"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="-63.437099"
y="20.223143">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.462257"
y="192.31326"
id="text7-1-5-3"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-6-4-4"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="-63.455643"
y="192.31326">{{sangrado}} mm</tspan></text>
<path
style="fill:#ff0000;stroke:#8292a8;stroke-width:0.264999;stroke-dasharray:none;stroke-opacity:1"
d="m 106.42015,36.849792 v 6.808552"
id="path7" />
<path
style="fill:#000000;stroke:#000000;stroke-width:0.865;stroke-dasharray:none;stroke-opacity:1"
d="m 106.97219,35.561688 v 7.728625"
id="path8" />
<path
style="fill:#000000;stroke:#000000;stroke-width:0.865;stroke-dasharray:none;stroke-opacity:1"
d="m 106.81658,77.188818 v 7.728625"
id="path8-9" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,502 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="189.90369mm"
height="122.48086mm"
viewBox="0 0 189.90369 122.48086"
version="1.1"
id="svg1"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
sodipodi:docname="plantilla-grapado.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="1.4378367"
inkscape:cx="427.37815"
inkscape:cy="265.32915"
inkscape:window-width="1920"
inkscape:window-height="1009"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g2" />
<defs
id="defs1">
<rect
x="148.51912"
y="407.19812"
width="184.41943"
height="125.40522"
id="rect7" />
<rect
x="171.09036"
y="459.02292"
width="159.26704"
height="90.413605"
id="rect3" />
<marker
id="Arrow2Sstart"
inkscape:isstock="true"
inkscape:stockid="Arrow2Sstart"
orient="auto"
refX="0"
refY="0"
style="overflow:visible">
<path
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
id="Arrow2SstartPath"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(0.3,0,0,0.3,-0.69,0)" />
</marker>
<marker
id="Arrow2Send"
inkscape:isstock="true"
inkscape:stockid="Arrow2Send"
orient="auto"
refX="0"
refY="0"
style="overflow:visible">
<path
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
id="Arrow2SendPath"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4" />
</marker>
<marker
style="overflow:visible"
id="marker6-3"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-6"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-1" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-5"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-4" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-4"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-12" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-0"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-0" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-6-1"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-1-7" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-5-7"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-4-7" />
</marker>
</defs>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-14.044687,-5.7512817)">
<rect
style="opacity:1;fill:#dee3db;fill-opacity:1;stroke:#8292a8;stroke-width:0.624;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect2"
width="182.32635"
height="115.28469"
x="14.044687"
y="5.7512817" />
<g
id="g2"
transform="translate(2.2085342,-0.96640021)">
<rect
style="opacity:1;fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.69881;stroke-dasharray:none;stroke-opacity:1"
id="rect1"
width="82.014671"
height="94.487244"
x="22.535099"
y="17.283327" />
<rect
style="opacity:1;fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.66258;stroke-dasharray:none;stroke-opacity:1"
id="rect1-8"
width="78.523048"
height="94.523483"
x="104.56826"
y="17.359972" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1.157;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 23.921577,124.62439 58.700742,-0.18401"
id="path4" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5);marker-end:url(#marker6-3);paint-order:normal"
d="m 107.44694,98.144427 72.96518,0.224688"
id="path5-3"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3);marker-end:url(#marker6-3-9);paint-order:normal"
d="M 24.800702,97.758738 102.27388,98.02943"
id="path5-3-0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-6);marker-end:url(#marker6-3-9-5);paint-order:normal"
d="M 114.29411,109.14665 113.88575,19.631562"
id="path5-3-0-9"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-4);marker-end:url(#marker6-3-9-0);paint-order:normal"
d="m 13.752058,124.20085 178.229792,0.0889"
id="path5-3-0-6"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-6-1);marker-end:url(#marker6-3-9-5-7);paint-order:normal"
d="M 196.99745,120.60716 196.58909,9.1942972"
id="path5-3-0-9-3"
sodipodi:nodetypes="cc" />
</g>
<g
id="g4"
transform="matrix(1.2478873,0,0,1.2478873,-26.713293,-112.63102)">
<text
xml:space="preserve"
id="text3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(-2.6328943,4.4099808)"><tspan
x="57.642538"
y="136.9912"
id="tspan6"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan5">{{contraportada}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<g
id="g4-2"
transform="matrix(1.2478873,0,0,1.2478873,50.382429,-112.61247)">
<text
xml:space="preserve"
id="text3-7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(2.1290346,4.4099808)"><tspan
x="64.690151"
y="136.9912"
id="tspan9"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan8">{{portada}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="-65.329605"
y="113.37225"
id="text13-8-5"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan13-2-3"
style="stroke-width:0"
x="-65.32299"
y="113.37225">{{alto}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="146.20094"
y="101.62363"
id="text13-3"><tspan
sodipodi:role="line"
id="tspan13-6"
style="stroke-width:0"
x="146.20757"
y="101.62363">{{ancho}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="65.808701"
y="101.62363"
id="text13-3-1"><tspan
sodipodi:role="line"
id="tspan13-6-4"
style="stroke-width:0"
x="65.815315"
y="101.62363">{{ancho}} mm</tspan></text>
<g
id="g4-7-0"
transform="matrix(1.2478873,0,0,1.2478873,-134.4255,19.484879)" />
<text
xml:space="preserve"
style="font-size:3.52778px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.07859"
y="126.83306"
id="text7"><tspan
sodipodi:role="line"
id="tspan7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="105.08521"
y="126.83306">{{ancho_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-size:3.52778px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.871456"
y="202.53311"
id="text7-8"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="-63.864841"
y="202.53311">{{alto_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.14069"
y="11.526331"
id="text7-1"><tspan
sodipodi:role="line"
id="tspan7-6"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="105.14731"
y="11.526331">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.14069"
y="117.33289"
id="text7-1-1"><tspan
sodipodi:role="line"
id="tspan7-6-8"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="105.1473"
y="117.33289">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.443714"
y="20.223143"
id="text7-1-5"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-6-4"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="-63.437099"
y="20.223143">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.462257"
y="192.31326"
id="text7-1-5-3"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-6-4-4"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="-63.455643"
y="192.31326">{{sangrado}} mm</tspan></text>
<path
style="fill:#ff0000;stroke:#8292a8;stroke-width:0.264999;stroke-dasharray:none;stroke-opacity:1"
d="m 106.42015,36.849792 v 6.808552"
id="path7" />
<path
style="fill:#000000;stroke:#000000;stroke-width:0.865;stroke-dasharray:none;stroke-opacity:1"
d="m 106.97219,35.561688 v 7.728625"
id="path8" />
<path
style="fill:#000000;stroke:#000000;stroke-width:0.865;stroke-dasharray:none;stroke-opacity:1"
d="m 106.81658,77.188818 v 7.728625"
id="path8-9" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,748 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="189.90369mm"
height="122.48086mm"
viewBox="0 0 189.90369 122.48086"
version="1.1"
id="svg1"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
sodipodi:docname="plantilla-solapas.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="1.0167041"
inkscape:cx="370.31423"
inkscape:cy="136.22449"
inkscape:window-width="1920"
inkscape:window-height="1009"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<rect
x="148.51912"
y="407.19812"
width="184.41943"
height="125.40522"
id="rect7" />
<rect
x="171.09036"
y="459.02292"
width="159.26704"
height="90.413605"
id="rect3" />
<marker
id="Arrow2Sstart"
inkscape:isstock="true"
inkscape:stockid="Arrow2Sstart"
orient="auto"
refX="0"
refY="0"
style="overflow:visible">
<path
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
id="Arrow2SstartPath"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(0.3,0,0,0.3,-0.69,0)" />
</marker>
<marker
id="Arrow2Send"
inkscape:isstock="true"
inkscape:stockid="Arrow2Send"
orient="auto"
refX="0"
refY="0"
style="overflow:visible">
<path
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
id="Arrow2SendPath"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4" />
</marker>
<marker
style="overflow:visible"
id="marker6-3"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-1"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-3" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-8"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-7" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-6"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-1" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-5"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-4" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-4"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-12" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-0"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-0" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-6-1"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-1-7" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-5-7"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-4-7" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-1-9"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-3-0" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-8-0"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-7-6" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-1-9-0"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-3-0-6" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-8-0-6"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-7-6-1" />
</marker>
</defs>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-14.044687,-5.7512817)">
<rect
style="opacity:1;fill:#dee3db;fill-opacity:1;stroke:#8292a8;stroke-width:0.624;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect2"
width="182.32635"
height="115.28469"
x="14.044687"
y="5.7512817" />
<g
id="g2"
transform="translate(2.2085342,-0.96640021)">
<rect
style="opacity:1;fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.57858;stroke-dasharray:none;stroke-opacity:1"
id="rect1"
width="70.725983"
height="94.607483"
x="22.474981"
y="17.039194" />
<rect
style="opacity:1;fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.57858;stroke-dasharray:none;stroke-opacity:1"
id="rect1-8"
width="70.725983"
height="94.607483"
x="112.79771"
y="17.057735" />
<rect
style="fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.4395;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1-8-1"
width="18.953001"
height="94.775017"
x="93.596756"
y="16.97538" />
<rect
style="fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.4395;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1-8-1-7"
width="18.953001"
height="94.775017"
x="164.55183"
y="17.019739" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1.157;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 23.921577,124.62439 58.700742,-0.18401"
id="path4" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5);marker-end:url(#marker6-3);paint-order:normal"
d="m 115.91162,98.144427 46.09901,0.224688"
id="path5-3"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3);marker-end:url(#marker6-3-9);paint-order:normal"
d="M 43.754233,98.310783 90.128901,98.02943"
id="path5-3-0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-6);marker-end:url(#marker6-3-9-5);paint-order:normal"
d="M 123.29,109.14665 122.88164,19.631562"
id="path5-3-0-9"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-1);marker-end:url(#marker6-3-8);paint-order:normal"
d="m 96.545605,29.398843 13.023315,0.06153"
id="path5-3-7"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-4);marker-end:url(#marker6-3-9-0);paint-order:normal"
d="m 13.752058,124.20085 178.229792,0.0889"
id="path5-3-0-6"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-6-1);marker-end:url(#marker6-3-9-5-7);paint-order:normal"
d="M 196.99745,120.60716 196.58909,9.1942972"
id="path5-3-0-9-3"
sodipodi:nodetypes="cc" />
</g>
<g
id="g4"
transform="matrix(1.2478873,0,0,1.2478873,-26.713293,-112.63102)">
<text
xml:space="preserve"
id="text3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(0.00878441,4.4099808)"><tspan
x="57.642538"
y="136.9912"
id="tspan9"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan8">{{contraportada}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<g
id="g4-2"
transform="matrix(1.2478873,0,0,1.2478873,45.088586,-112.61247)">
<text
xml:space="preserve"
id="text3-7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(2.1290346,4.4099808)"><tspan
x="64.690151"
y="136.9912"
id="tspan11"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan10">{{portada}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<g
id="g4-2-9"
transform="matrix(0,-1.2478873,1.2478873,0,0.59255852,162.33513)">
<text
xml:space="preserve"
id="text3-7-3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6-8);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(2.5239521,4.4099808)"><tspan
x="65.841054"
y="136.9912"
id="tspan13"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan12">{{solapa}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6-8"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<rect
style="fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.4395;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1-8-1-7-8"
width="18.953001"
height="94.775017"
x="24.558743"
y="15.99602" />
<g
id="g4-2-9-0"
transform="matrix(0,-1.2478873,1.2478873,0,-141.60976,162.27774)">
<text
xml:space="preserve"
id="text3-7-3-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6-8-6);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(2.5239521,4.4099808)"><tspan
x="65.841054"
y="136.9912"
id="tspan15"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan14">{{solapa}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6-8-6"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<g
id="g4-2-1"
transform="matrix(0,-1.2478873,1.2478873,0,-70.725767,159.15213)">
<text
xml:space="preserve"
id="text3-7-8"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6-2);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(0.00878441,4.4099808)"><tspan
x="67.641254"
y="136.9912"
id="tspan17"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan16">{{lomo}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6-2"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="-65.329605"
y="122.36808"
id="text13-8-5"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan13-2-3"
style="stroke-width:0"
x="-65.32299"
y="122.36808">{{alto}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="141.23253"
y="101.62363"
id="text13-3"><tspan
sodipodi:role="line"
id="tspan13-6"
style="stroke-width:0"
x="141.23915"
y="101.62363">{{ancho}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="69.212975"
y="101.62363"
id="text13-3-1"><tspan
sodipodi:role="line"
id="tspan13-6-4"
style="stroke-width:0"
x="69.219589"
y="101.62363">{{ancho}} mm</tspan></text>
<g
id="g4-7-0"
transform="matrix(1.2478873,0,0,1.2478873,-134.4255,19.484879)" />
<text
xml:space="preserve"
style="font-size:3.52778px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.07859"
y="126.83306"
id="text7"><tspan
sodipodi:role="line"
id="tspan7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="105.08521"
y="126.83306">{{ancho_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-size:2.82222px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.31609"
y="26.035662"
id="text7-7"><tspan
sodipodi:role="line"
id="tspan7-1"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.82222px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="105.32271"
y="26.035662">{{lomo_t}} mm</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-1-9);marker-end:url(#marker6-3-8-0);paint-order:normal"
d="m 169.57878,97.434632 13.02331,0.06153"
id="path5-3-7-8"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:2.82222px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="176.14073"
y="101.38786"
id="text7-7-3"><tspan
sodipodi:role="line"
id="tspan7-1-4"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.82222px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="176.14735"
y="101.38786">{{solapa_t}} mm</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-1-9-0);marker-end:url(#marker6-3-8-0-6);paint-order:normal"
d="m 27.840231,97.50449 13.023235,0.06153"
id="path5-3-7-8-9"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:2.82222px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="34.403442"
y="101.45772"
id="text7-7-3-6"><tspan
sodipodi:role="line"
id="tspan7-1-4-3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.82222px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="34.410057"
y="101.45772">{{solapa_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-size:3.52778px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.871456"
y="202.53311"
id="text7-8"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="-63.864841"
y="202.53311">{{alto_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.14069"
y="11.526331"
id="text7-1"><tspan
sodipodi:role="line"
id="tspan7-6"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="105.14731"
y="11.526331">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.14069"
y="117.33289"
id="text7-1-1"><tspan
sodipodi:role="line"
id="tspan7-6-8"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="105.1473"
y="117.33289">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.443714"
y="20.223143"
id="text7-1-5"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-6-4"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="-63.437099"
y="20.223143">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.462257"
y="192.31326"
id="text7-1-5-3"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-6-4-4"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="-63.455643"
y="192.31326">{{sangrado}} mm</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -0,0 +1,576 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="189.90369mm"
height="122.48086mm"
viewBox="0 0 189.90369 122.48086"
version="1.1"
id="svg1"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
sodipodi:docname="plantilla.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="1.4378367"
inkscape:cx="311.23144"
inkscape:cy="214.55844"
inkscape:window-width="1920"
inkscape:window-height="1009"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<rect
x="148.51912"
y="407.19812"
width="184.41943"
height="125.40522"
id="rect7" />
<rect
x="171.09036"
y="459.02292"
width="159.26704"
height="90.413605"
id="rect3" />
<marker
id="Arrow2Sstart"
inkscape:isstock="true"
inkscape:stockid="Arrow2Sstart"
orient="auto"
refX="0"
refY="0"
style="overflow:visible">
<path
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
id="Arrow2SstartPath"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(0.3,0,0,0.3,-0.69,0)" />
</marker>
<marker
id="Arrow2Send"
inkscape:isstock="true"
inkscape:stockid="Arrow2Send"
orient="auto"
refX="0"
refY="0"
style="overflow:visible">
<path
d="M 8.72,4.03 -2.21,0.02 8.72,-4 c -1.75,2.37 -1.74,5.62 0,8.03 z"
id="Arrow2SendPath"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4" />
</marker>
<marker
style="overflow:visible"
id="marker6-3"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-1"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-3" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-8"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-7" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-6"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-1" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-5"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-4" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-4"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-12" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-0"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-0" />
</marker>
<marker
style="overflow:visible"
id="Triangle-5-3-6-1"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135-4-1-1-7" />
</marker>
<marker
style="overflow:visible"
id="marker6-3-9-5-7"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="0.5"
markerHeight="0.5"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid"
markerUnits="userSpaceOnUse">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path6-1-8-4-7" />
</marker>
</defs>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-14.044687,-5.7512817)">
<rect
style="opacity:1;fill:#dee3db;fill-opacity:1;stroke:#8292a8;stroke-width:0.624;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect2"
width="182.32635"
height="115.28469"
x="14.044687"
y="5.7512817" />
<g
id="g2"
transform="translate(2.2085342,-0.96640021)">
<rect
style="opacity:1;fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.57858;stroke-dasharray:none;stroke-opacity:1"
id="rect1"
width="70.725983"
height="94.607483"
x="22.474981"
y="17.039194" />
<rect
style="opacity:1;fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.57858;stroke-dasharray:none;stroke-opacity:1"
id="rect1-8"
width="70.725983"
height="94.607483"
x="112.79771"
y="17.057735" />
<rect
style="fill:#c9d9d4;fill-opacity:1;stroke:#8292a8;stroke-width:1.4395;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1-8-1"
width="18.953001"
height="94.775017"
x="93.596756"
y="16.97538" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1.157;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 23.921577,124.62439 58.700742,-0.18401"
id="path4" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5);marker-end:url(#marker6-3);paint-order:normal"
d="m 115.91162,98.144427 64.68451,-0.235349"
id="path5-3"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3);marker-end:url(#marker6-3-9);paint-order:normal"
d="M 25.444754,98.264779 90.128901,98.02943"
id="path5-3-0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-6);marker-end:url(#marker6-3-9-5);paint-order:normal"
d="M 123.29,109.14665 122.88164,19.631562"
id="path5-3-0-9"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-1);marker-end:url(#marker6-3-8);paint-order:normal"
d="m 96.545605,29.398843 13.023315,0.06153"
id="path5-3-7"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-4);marker-end:url(#marker6-3-9-0);paint-order:normal"
d="m 13.752058,124.20085 178.229792,0.0889"
id="path5-3-0-6"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.820919;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:1.64183, 0.820919;stroke-dashoffset:0.328368;stroke-opacity:1;marker-start:url(#Triangle-5-3-6-1);marker-end:url(#marker6-3-9-5-7);paint-order:normal"
d="M 196.99745,120.60716 196.58909,9.1942972"
id="path5-3-0-9-3"
sodipodi:nodetypes="cc" />
</g>
<g
id="g4"
transform="matrix(1.2478873,0,0,1.2478873,-35.709132,-112.63102)">
<text
xml:space="preserve"
id="text3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(0.00878441,4.4099808)"><tspan
x="57.642538"
y="136.9912"
id="tspan9"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan8">{{contraportada}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<g
id="g4-2"
transform="matrix(1.2478873,0,0,1.2478873,54.613592,-112.61247)">
<text
xml:space="preserve"
id="text3-7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(2.1290346,4.4099808)"><tspan
x="64.690151"
y="136.9912"
id="tspan11"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan10">{{portada}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<g
id="g4-2-1"
transform="matrix(0,-1.2478873,1.2478873,0,-70.725767,159.15213)">
<text
xml:space="preserve"
id="text3-7-8"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.58611px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial, Bold';font-variant-ligatures:no-common-ligatures discretionary-ligatures no-contextual;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;white-space:pre-wrap;shape-inside:url(#rect4-6-2);display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.37291;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
x="27.970261"
y="0"
transform="translate(0.00878441,4.4099808)"><tspan
x="67.641254"
y="136.9912"
id="tspan13"><tspan
style="font-variant-ligatures:normal;text-orientation:upright"
id="tspan12">{{lomo}}</tspan></tspan></text>
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.825551;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect4-6-2"
width="50.751522"
height="10.636283"
x="51.35844"
y="135.72629" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="-65.329605"
y="130.30556"
id="text13-8-5"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan13-2-3"
style="stroke-width:0"
x="-65.32299"
y="130.30556">{{alto}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="150.52528"
y="101.62363"
id="text13-3"><tspan
sodipodi:role="line"
id="tspan13-6"
style="stroke-width:0"
x="150.53191"
y="101.62363">{{ancho}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52777px;line-height:0;font-family:Arial;-inkscape-font-specification:'Arial Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-dashoffset:0.994541;stroke-opacity:1;paint-order:normal"
x="60.058235"
y="101.62363"
id="text13-3-1"><tspan
sodipodi:role="line"
id="tspan13-6-4"
style="stroke-width:0"
x="60.06485"
y="101.62363">{{ancho}} mm</tspan></text>
<g
id="g4-7-0"
transform="matrix(1.2478873,0,0,1.2478873,-134.4255,19.484879)" />
<text
xml:space="preserve"
style="font-size:3.52778px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.07859"
y="126.83306"
id="text7"><tspan
sodipodi:role="line"
id="tspan7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="105.08521"
y="126.83306">{{ancho_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-size:2.82222px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.31609"
y="26.035662"
id="text7-7"><tspan
sodipodi:role="line"
id="tspan7-1"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.82222px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="105.32271"
y="26.035662">{{lomo_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-size:3.52778px;text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.871456"
y="202.53311"
id="text7-8"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:Arial, boldl;-inkscape-font-specification:'Arial, boldl Bold';stroke-width:0.264583"
x="-63.864841"
y="202.53311">{{alto_t}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.14069"
y="11.526331"
id="text7-1"><tspan
sodipodi:role="line"
id="tspan7-6"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="105.1473"
y="11.526331">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="105.14069"
y="117.33289"
id="text7-1-1"><tspan
sodipodi:role="line"
id="tspan7-6-8"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="105.1473"
y="117.33289">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.443714"
y="20.223143"
id="text7-1-5"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-6-4"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="-63.437099"
y="20.223143">{{sangrado}} mm</tspan></text>
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';text-align:center;letter-spacing:0.0132292px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="-63.462257"
y="192.31326"
id="text7-1-5-3"
transform="rotate(-90)"><tspan
sodipodi:role="line"
id="tspan7-6-4-4"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.52778px;font-family:Arial;-inkscape-font-specification:'Arial Italic Condensed';stroke-width:0.264583"
x="-63.455643"
y="192.31326">{{sangrado}} mm</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#ffffff" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 511 511" xml:space="preserve">
<g>
<path d="M487.5,128.106H479v-24.5c0-2.905-1.678-5.549-4.307-6.786C405.088,64.066,325.408,63.6,255.5,95.371
C185.592,63.6,105.912,64.067,36.307,96.82C33.678,98.057,32,100.701,32,103.606v24.5h-8.5c-12.958,0-23.5,10.542-23.5,23.5v264
c0,12.958,10.542,23.5,23.5,23.5h464c12.958,0,23.5-10.542,23.5-23.5v-264C511,138.648,500.458,128.106,487.5,128.106z
M263,239.583c0-0.009,0-0.019,0-0.028V108.416c64.137-28.707,136.861-28.707,201,0v27.161c0,0.01-0.001,0.02-0.001,0.029
s0.001,0.02,0.001,0.029v244.438c-32.237-13.461-66.371-20.193-100.5-20.193c-34.129,0-68.264,6.732-100.5,20.193V239.583z
M215,96.391c11.187,3.204,22.217,7.198,33,12.025v117.177l-12.34-8.227c-2.52-1.68-5.801-1.68-8.32,0L215,225.593V96.391z
M47,135.626c0-0.007,0.001-0.013,0.001-0.02S47,135.594,47,135.587v-27.171c48.563-21.736,102.046-26.999,153-15.82v32.856
c-26.767-5.505-54.078-6.777-81.328-3.75c-4.117,0.457-7.083,4.165-6.626,8.282c0.458,4.116,4.162,7.085,8.282,6.626
c26.708-2.967,53.479-1.562,79.671,4.165v48.686c-15.912-3.265-32.14-5.067-48.377-5.323c-4.145-0.078-7.552,3.239-7.618,7.38
c-0.065,4.142,3.239,7.552,7.38,7.618c16.331,0.258,32.654,2.164,48.614,5.647v16.66c-43.389-8.909-88.39-6.644-130.748,6.665
c-3.952,1.241-6.148,5.451-4.907,9.403c1.007,3.204,3.964,5.254,7.153,5.254c0.745,0,1.502-0.112,2.25-0.347
c40.908-12.852,84.428-14.773,126.252-5.638v2.825c0,2.766,1.522,5.308,3.961,6.612c2.438,1.306,5.398,1.162,7.699-0.372
l19.84-13.227l16.5,11v136.454c-32.237-13.461-66.371-20.193-100.5-20.193c-34.129,0-68.264,6.732-100.5,20.193V135.626z
M224,424.106H23.5c-4.687,0-8.5-3.813-8.5-8.5v-264c0-4.687,3.813-8.5,8.5-8.5H32v248.5v8c0,4.142,3.358,7.5,7.5,7.5H224V424.106z
M57.29,392.106c58.099-22.934,122.32-22.935,180.42,0H57.29z M272,424.106h-33v-17h33V424.106z M453.71,392.106H273.29
C331.389,369.172,395.61,369.172,453.71,392.106z M496,415.606c0,4.687-3.813,8.5-8.5,8.5H287v-17h184.5c4.142,0,7.5-3.358,7.5-7.5
v-8v-248.5h8.5c4.687,0,8.5,3.813,8.5,8.5V415.606z"/>
<path d="M309.96,317.749c-8.302,1.74-16.615,3.911-24.708,6.454c-3.952,1.242-6.148,5.452-4.907,9.403
c1.007,3.204,3.964,5.254,7.153,5.254c0.745,0,1.502-0.112,2.25-0.347c7.628-2.396,15.464-4.443,23.288-6.083
c4.054-0.85,6.652-4.825,5.802-8.879C317.989,319.497,314.011,316.9,309.96,317.749z"/>
<path d="M439.502,338.859c3.189,0,6.147-2.051,7.153-5.254c1.241-3.952-0.956-8.162-4.907-9.403
c-32.073-10.076-65.329-13.842-98.844-11.188c-4.129,0.326-7.211,3.938-6.885,8.068s3.935,7.213,8.068,6.885
c31.59-2.499,62.935,1.048,93.165,10.546C438,338.748,438.757,338.859,439.502,338.859z"/>
<path d="M287.498,306.767c0.745,0,1.502-0.112,2.25-0.347c48.249-15.159,99.256-15.159,147.504,0
c3.952,1.24,8.162-0.956,9.403-4.907c1.241-3.952-0.956-8.162-4.907-9.403c-51.191-16.083-105.306-16.083-156.496,0
c-3.952,1.241-6.149,5.451-4.907,9.403C281.352,304.716,284.309,306.767,287.498,306.767z"/>
<path d="M287.498,274.859c0.745,0,1.502-0.112,2.25-0.347c27.681-8.697,56.409-12.412,85.399-11.037
c4.147,0.192,7.651-2.999,7.847-7.137c0.196-4.138-2.999-7.65-7.137-7.847c-30.753-1.456-61.236,2.483-90.605,11.71
c-3.952,1.242-6.149,5.452-4.907,9.403C281.352,272.81,284.309,274.859,287.498,274.859z"/>
<path d="M441.748,260.202c-10.76-3.38-21.846-6.086-32.952-8.043c-4.08-0.719-7.968,2.006-8.688,6.085
c-0.719,4.079,2.005,7.969,6.085,8.688c10.467,1.844,20.917,4.395,31.058,7.581c0.749,0.235,1.505,0.347,2.25,0.347
c3.189,0,6.147-2.051,7.153-5.254C447.896,265.653,445.7,261.443,441.748,260.202z"/>
<path d="M287.498,242.767c0.745,0,1.502-0.112,2.25-0.347c48.249-15.159,99.256-15.159,147.504,0
c3.952,1.24,8.162-0.956,9.403-4.907c1.241-3.952-0.956-8.162-4.907-9.403c-51.191-16.083-105.306-16.083-156.496,0
c-3.952,1.241-6.149,5.451-4.907,9.403C281.352,240.716,284.309,242.767,287.498,242.767z"/>
<path d="M334.678,185.702c-16.732,1.858-33.362,5.36-49.426,10.407c-3.952,1.241-6.148,5.451-4.907,9.403
c1.007,3.204,3.964,5.254,7.153,5.254c0.745,0,1.502-0.112,2.25-0.347c15.141-4.757,30.815-8.057,46.585-9.809
c4.117-0.457,7.083-4.165,6.626-8.282S338.79,185.244,334.678,185.702z"/>
<path d="M367.386,199.137c23.725,0.375,47.231,4.17,69.866,11.283c0.748,0.234,1.505,0.347,2.25,0.347
c3.189,0,6.146-2.051,7.153-5.254c1.241-3.952-0.956-8.162-4.907-9.403c-24.015-7.545-48.955-11.572-74.125-11.97
c-4.125-0.078-7.552,3.239-7.618,7.38S363.244,199.072,367.386,199.137z"/>
<path d="M390.671,168.704c4.116,0.46,7.825-2.509,8.282-6.626c0.458-4.117-2.509-7.825-6.626-8.282
c-36.252-4.027-72.278-0.526-107.075,10.406c-3.952,1.242-6.148,5.452-4.907,9.403c1.007,3.204,3.964,5.254,7.153,5.254
c0.745,0,1.502-0.112,2.25-0.347C322.545,168.208,356.5,164.909,390.671,168.704z"/>
<path d="M441.748,164.202c-5.418-1.702-10.96-3.246-16.472-4.588c-4.03-0.98-8.082,1.488-9.062,5.512
c-0.98,4.024,1.488,8.082,5.512,9.062c5.196,1.265,10.419,2.72,15.526,4.324c0.748,0.235,1.505,0.347,2.25,0.347
c3.189,0,6.147-2.051,7.153-5.254C447.896,169.653,445.7,165.443,441.748,164.202z"/>
<path d="M287.498,146.767c0.745,0,1.502-0.112,2.25-0.347c5.103-1.604,10.325-3.058,15.521-4.324
c4.024-0.98,6.492-5.037,5.512-9.062s-5.038-6.492-9.062-5.512c-5.513,1.342-11.053,2.886-16.468,4.587
c-3.951,1.242-6.148,5.452-4.907,9.403C281.352,144.716,284.309,146.767,287.498,146.767z"/>
<path d="M336.329,136.611c34.172-3.796,68.126-0.496,100.923,9.809c0.748,0.234,1.505,0.347,2.25,0.347
c3.189,0,6.146-2.051,7.153-5.254c1.241-3.952-0.956-8.162-4.907-9.403c-34.797-10.933-70.824-14.435-107.076-10.406
c-4.117,0.457-7.083,4.165-6.626,8.282C328.504,134.102,332.21,137.07,336.329,136.611z"/>
<path d="M93.96,317.749c-8.302,1.74-16.615,3.911-24.708,6.454c-3.952,1.242-6.148,5.452-4.907,9.403
c1.007,3.204,3.964,5.254,7.153,5.254c0.745,0,1.502-0.112,2.25-0.347c7.628-2.396,15.464-4.443,23.288-6.083
c4.054-0.85,6.652-4.825,5.802-8.879S98.011,316.9,93.96,317.749z"/>
<path d="M223.502,338.859c3.189,0,6.147-2.051,7.153-5.254c1.241-3.952-0.956-8.162-4.907-9.403
c-32.073-10.076-65.331-13.842-98.844-11.188c-4.129,0.326-7.211,3.938-6.885,8.068s3.934,7.213,8.068,6.885
c31.591-2.499,62.935,1.048,93.165,10.546C222,338.748,222.757,338.859,223.502,338.859z"/>
<path d="M71.498,306.767c0.745,0,1.502-0.112,2.25-0.347c48.249-15.159,99.256-15.159,147.504,0
c3.952,1.24,8.162-0.956,9.403-4.907c1.241-3.952-0.956-8.162-4.907-9.403c-51.191-16.083-105.307-16.083-156.496,0
c-3.952,1.241-6.149,5.451-4.907,9.403C65.352,304.716,68.309,306.767,71.498,306.767z"/>
<path d="M71.498,274.859c0.745,0,1.502-0.112,2.25-0.347c27.681-8.697,56.411-12.412,85.399-11.037
c4.158,0.192,7.65-2.999,7.847-7.137c0.196-4.138-2.999-7.65-7.137-7.847c-30.756-1.456-61.236,2.483-90.605,11.71
c-3.952,1.242-6.149,5.452-4.907,9.403C65.352,272.81,68.309,274.859,71.498,274.859z"/>
<path d="M190.194,266.932c10.467,1.844,20.917,4.395,31.058,7.581c0.749,0.235,1.505,0.347,2.25,0.347
c3.189,0,6.147-2.051,7.153-5.254c1.241-3.952-0.956-8.162-4.907-9.403c-10.76-3.38-21.846-6.086-32.952-8.043
c-4.079-0.719-7.969,2.006-8.688,6.085C183.39,262.323,186.114,266.213,190.194,266.932z"/>
<path d="M118.678,185.702c-16.732,1.858-33.362,5.36-49.426,10.407c-3.952,1.241-6.148,5.451-4.907,9.403
c1.007,3.204,3.964,5.254,7.153,5.254c0.745,0,1.502-0.112,2.25-0.347c15.141-4.757,30.815-8.057,46.585-9.809
c4.117-0.457,7.083-4.165,6.626-8.282C126.503,188.212,122.788,185.244,118.678,185.702z"/>
<path d="M64.345,173.605c1.007,3.204,3.964,5.254,7.153,5.254c0.745,0,1.502-0.112,2.25-0.347
c32.797-10.305,66.752-13.604,100.923-9.809c4.116,0.46,7.825-2.509,8.282-6.626c0.458-4.117-2.509-7.825-6.626-8.282
c-36.253-4.027-72.278-0.526-107.075,10.406C65.3,165.444,63.104,169.654,64.345,173.605z"/>
<path d="M71.498,146.767c0.745,0,1.502-0.112,2.25-0.347c5.103-1.604,10.325-3.058,15.521-4.324
c4.024-0.98,6.492-5.037,5.512-9.062s-5.038-6.492-9.062-5.512c-5.513,1.342-11.053,2.886-16.468,4.587
c-3.951,1.242-6.148,5.452-4.907,9.403C65.352,144.716,68.309,146.767,71.498,146.767z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -18,7 +18,7 @@ $(() => {
$('#addBillingAddressBtn').on('click', seleccionarDireccionEnvio); $('#addBillingAddressBtn').on('click', seleccionarDireccionEnvio);
$('#authorization-required').on('change', function () { $(document).on('change', '#authorization-required', function () {
if ($(this).is(':checked')) { if ($(this).is(':checked')) {
if ($('#direccion-div .direccion-card').length > 0) { if ($('#direccion-div .direccion-card').length > 0) {
$('#btn-checkout').prop('disabled', false); $('#btn-checkout').prop('disabled', false);
@ -148,6 +148,24 @@ $(() => {
$('#btn-checkout').prop('disabled', false); $('#btn-checkout').prop('disabled', false);
} }
hideLoader(); hideLoader();
if (direccionId) {
$.ajax({
url: `/checkout/get-summary/${direccionId}/${$('input[name="method"]').val()}`,
type: 'GET',
success: function (response) {
const parent = $('.cart-summary-container').parent();
$('.cart-summary-container').remove();
parent.append(response);
$('#dirFactId').val(direccionId);
$('#dirFactWarning').addClass('d-none');
},
error: function () {
console.error('Error al actualizar el resumen del carrito.');
}
});
}
return true; return true;
} }
hideLoader(); hideLoader();
@ -164,6 +182,19 @@ $(() => {
$card.remove(); $card.remove();
$('#addBillingAddressBtn').removeClass('d-none'); $('#addBillingAddressBtn').removeClass('d-none');
$('#btn-checkout').prop('disabled', true); $('#btn-checkout').prop('disabled', true);
$.ajax({
url: `/checkout/get-summary`,
type: 'GET',
success: function (response) {
const parent = $('.cart-summary-container').parent();
$('.cart-summary-container').remove();
$('#dirFactWarning').removeClass('d-none');
parent.append(response);
},
error: function () {
console.error('Error al actualizar el resumen del carrito.');
}
});
}); });
@ -194,7 +225,7 @@ $(() => {
type: 'POST', // PUT simulado via _method type: 'POST', // PUT simulado via _method
data: $form.serialize(), data: $form.serialize(),
dataType: 'html', dataType: 'html',
success: function (html) { success: async function (html) {
// Si por cualquier motivo llega 200 con fragmento, lo insertamos igual // Si por cualquier motivo llega 200 con fragmento, lo insertamos igual
if (typeof html === 'string' && html.indexOf('id="direccionForm"') !== -1 && html.indexOf('<html') === -1) { if (typeof html === 'string' && html.indexOf('id="direccionForm"') !== -1 && html.indexOf('<html') === -1) {
$('#direccionFormModalBody').html(html); $('#direccionFormModalBody').html(html);
@ -205,7 +236,8 @@ $(() => {
} }
// Éxito real: cerrar y recargar tabla // Éxito real: cerrar y recargar tabla
$('#direccionFormModal').modal('hide'); $('#direccionFormModal').modal('hide');
seleccionarDireccionEnvio(); await seleccionarDireccionEnvio();
}, },
error: function (xhr) { error: function (xhr) {
// Con 422 devolvemos el fragmento con errores aquí // Con 422 devolvemos el fragmento con errores aquí

View File

@ -0,0 +1,34 @@
import {formateaMoneda} from "./utils.js";
$(() => {
// Contador animado
function counter() {
var counter = document.querySelectorAll(".counter-value");
var speed = 250; // The lower the slower
counter &&
Array.from(counter).forEach(function (counter_value) {
function updateCount() {
var target = +counter_value.getAttribute("data-target");
var count = +counter_value.innerText;
var inc = target / speed;
if (inc < 1) {
inc = 1;
}
// Check if target is reached
if (count < target) {
// Add inc to count and output in counter_value
counter_value.innerText = (count + inc).toFixed(0);
// Call function every ms
setTimeout(updateCount, 1);
} else {
counter_value.innerText = formateaMoneda(target);
}
formateaMoneda(counter_value.innerText);
}
updateCount();
});
}
counter();
})

View File

@ -71,7 +71,7 @@ $(() => {
// ----------------------------- // -----------------------------
$table.on('click', '.btn-view-factura', function () { $table.on('click', '.btn-view-factura', function () {
const row = dt.row($(this).closest('tr')).data(); const row = dt.row($(this).closest('tr')).data();
window.location.href = `/facturas/${row.id}`; window.open('/facturas/' + row.id, '_blank', 'noopener,noreferrer');
}); });
// ----------------------------- // -----------------------------

View File

@ -48,6 +48,7 @@ $(() => {
{ data: 'id', name: 'id', orderable: true }, { data: 'id', name: 'id', orderable: true },
{ data: 'cliente', name: 'createdBy.fullName', orderable: true }, { data: 'cliente', name: 'createdBy.fullName', orderable: true },
{ data: 'created_at', name: 'createdAt', orderable: true }, { data: 'created_at', name: 'createdAt', orderable: true },
{ data: 'titulos', name: 'titulos', orderable: true },
{ data: 'total', name: 'total', orderable: true }, { data: 'total', name: 'total', orderable: true },
{ data: 'estado', name: 'estado', orderable: true }, { data: 'estado', name: 'estado', orderable: true },
{ data: 'actions', name: 'actions', orderable: false, searchable: false } { data: 'actions', name: 'actions', orderable: false, searchable: false }

View File

@ -1,8 +1,8 @@
$(() => { $(() => {
$(document).on('click', '.btn-view', function () { $(document).on('click', '.btn-view', function () {
let pedidoId = $(this).data('id'); let pedidoId = $(this).data('id');
let url = `/pedidos/view/${pedidoId}`; if(!pedidoId) return;
window.location.href = url; window.open('/pedidos/view/' + pedidoId, '_blank', 'noopener,noreferrer');
}); });
$(document).on('click', '.btn-pay', async function () { $(document).on('click', '.btn-pay', async function () {

View File

@ -47,6 +47,7 @@ $(() => {
columns: [ columns: [
{ data: 'id', name: 'id', orderable: true }, { data: 'id', name: 'id', orderable: true },
{ data: 'created_at', name: 'createdAt', orderable: true }, { data: 'created_at', name: 'createdAt', orderable: true },
{ data: 'titulos', name: 'titulos', orderable: true },
{ data: 'total', name: 'total', orderable: true }, { data: 'total', name: 'total', orderable: true },
{ data: 'estado', name: 'estado', orderable: true }, { data: 'estado', name: 'estado', orderable: true },
{ data: 'actions', name: 'actions', orderable: false, searchable: false } { data: 'actions', name: 'actions', orderable: false, searchable: false }

View File

@ -34,7 +34,6 @@ export default class PresupuestoWizard {
paginasColor: 0, paginasColor: 0,
posicionPaginasColor: '', posicionPaginasColor: '',
tipoEncuadernacion: 'fresado', tipoEncuadernacion: 'fresado',
entregaTipo: 'peninsula',
ivaReducido: true, ivaReducido: true,
}, },
interior: { interior: {
@ -108,6 +107,8 @@ export default class PresupuestoWizard {
} }
}, },
selectedTirada: 10, selectedTirada: 10,
lomo: 0,
lomoCubierta: 0
} }
// pestaña datos generales // pestaña datos generales
@ -130,7 +131,6 @@ export default class PresupuestoWizard {
this.divPosicionPaginasColor = $('#div-posicion-paginas-color'); this.divPosicionPaginasColor = $('#div-posicion-paginas-color');
this.posicionPaginasColor = $('#posicionPaginasColor'); this.posicionPaginasColor = $('#posicionPaginasColor');
this.paginas = $('#paginas'); this.paginas = $('#paginas');
this.entregaTipo = $('#entregaTipo');
this.ivaReducido = $('#iva-reducido'); this.ivaReducido = $('#iva-reducido');
this.btnIvaReducidoDetail = $('#btn-iva-reducido-detail'); this.btnIvaReducidoDetail = $('#btn-iva-reducido-detail');
this.datos_generales_alert = $('#datos-generales-alert'); this.datos_generales_alert = $('#datos-generales-alert');
@ -318,6 +318,50 @@ export default class PresupuestoWizard {
document.body.removeChild(a); document.body.removeChild(a);
}); });
$(document)
.off('click.login-required', '.btn-login-required')
.on('click.login-required', '.btn-login-required', async (e) => {
e.preventDefault();
const rawId = this.opts.presupuestoId || window.PRESUPUESTO_ID || $('#presupuesto_id').val();
const presupuestoId = rawId ? parseInt(rawId, 10) : null;
if (!presupuestoId || Number.isNaN(presupuestoId)) {
Swal.fire({
icon: 'error',
title: 'No se encontró el presupuesto',
text: 'Vuelve a generar el resumen e inténtalo de nuevo.',
buttonsStyling: false,
customClass: {
confirmButton: 'btn btn-secondary me-2',
cancelButton: 'btn btn-light'
},
});
return;
}
try {
await $.ajax({
url: '/presupuesto/public/prepare-claim',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ presupuestoId })
});
window.location.assign('/presupuesto/claim');
} catch (err) {
Swal.fire({
icon: 'error',
title: 'No se pudo continuar',
text: 'Inténtalo de nuevo en unos segundos.',
buttonsStyling: false,
customClass: {
confirmButton: 'btn btn-secondary me-2',
cancelButton: 'btn btn-light'
},
});
}
});
} }
@ -401,7 +445,9 @@ export default class PresupuestoWizard {
...this.#getDatosGeneralesData(), ...this.#getDatosGeneralesData(),
...this.#getInteriorData(), ...this.#getInteriorData(),
...this.#getCubiertaData(), ...this.#getCubiertaData(),
selectedTirada: this.formData.selectedTirada selectedTirada: this.formData.selectedTirada,
lomo: this.formData.lomo,
lomoCubierta: this.formData.lomoCubierta
}; };
const sobrecubierta = data.sobrecubierta; const sobrecubierta = data.sobrecubierta;
@ -622,7 +668,6 @@ export default class PresupuestoWizard {
paginasColor: this.paginasColor.val(), paginasColor: this.paginasColor.val(),
posicionPaginasColor: this.posicionPaginasColor.val(), posicionPaginasColor: this.posicionPaginasColor.val(),
tipoEncuadernacion: $('.tipo-libro input:checked').val() || 'fresado', tipoEncuadernacion: $('.tipo-libro input:checked').val() || 'fresado',
entregaTipo: this.entregaTipo.val(),
ivaReducido: this.ivaReducido.is(':checked'), ivaReducido: this.ivaReducido.is(':checked'),
}; };
} }
@ -644,7 +689,6 @@ export default class PresupuestoWizard {
paginasColor: data.paginasColor, paginasColor: data.paginasColor,
posicionPaginasColor: data.posicionPaginasColor, posicionPaginasColor: data.posicionPaginasColor,
tipoEncuadernacion: data.tipoEncuadernacion, tipoEncuadernacion: data.tipoEncuadernacion,
entregaTipo: data.entregaTipo,
ivaReducido: data.ivaReducido, ivaReducido: data.ivaReducido,
}; };
} }
@ -674,17 +718,14 @@ export default class PresupuestoWizard {
.prop('checked', true); .prop('checked', true);
this.#updateTipoEncuadernacion(); this.#updateTipoEncuadernacion();
this.formatoPersonalizado.trigger('change');
$('.paginas').trigger('change');
if (this.formatoPersonalizado.is(':checked')) { if (this.formatoPersonalizado.is(':checked')) {
this.ancho.val(this.formData.datosGenerales.ancho); this.ancho.val(this.formData.datosGenerales.ancho);
this.alto.val(this.formData.datosGenerales.alto); this.alto.val(this.formData.datosGenerales.alto);
} else { } else {
const option = this.formato.find('option').filter(() => { const option = this.formato.find('option').filter((index, element) => {
return $(this).data('ancho') == this.formData.datosGenerales.ancho && return $(element).data('ancho') == this.formData.datosGenerales.ancho &&
$(this).data('alto') == this.formData.datosGenerales.alto; $(element).data('alto') == this.formData.datosGenerales.alto;
}); });
if (option.length) { if (option.length) {
@ -692,7 +733,8 @@ export default class PresupuestoWizard {
} }
} }
this.entregaTipo.val(this.formData.datosGenerales.entregaTipo); this.formatoPersonalizado.trigger('change');
$('.paginas').trigger('change');
this.ivaReducido.prop('checked', this.formData.datosGenerales.ivaReducido); this.ivaReducido.prop('checked', this.formData.datosGenerales.ivaReducido);
} }
@ -926,6 +968,7 @@ export default class PresupuestoWizard {
} else { } else {
const maxSolapas = data.solapas ?? 120; const maxSolapas = data.solapas ?? 120;
const lomo = data.lomo ?? 0; 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);
@ -979,6 +1022,7 @@ export default class PresupuestoWizard {
const dataCubierta = this.#getCubiertaData(); const dataCubierta = this.#getCubiertaData();
this.#updateCubiertaData(dataCubierta); this.#updateCubiertaData(dataCubierta);
this.formData.lomo = lomo;
this.#cacheFormData(); this.#cacheFormData();
} }
@ -1099,29 +1143,7 @@ export default class PresupuestoWizard {
******************************/ ******************************/
#initCubierta() { #initCubierta() {
this.btn_plantilla_cubierta.on('click', () => { $('#btn-plantilla-cubierta').on('click', this.#obtener_plantilla_cubierta.bind(this));
Swal.fire({
position: 'top-end',
icon: 'info',
title: window.languageBundle.get('presupuesto.plantilla-cubierta'),
html: `
<div class="text-center p-4">
<img src="/assets/images/imprimelibros/presupuestador/plantilla-cubierta.png" class="img-fluid" alt="" />
</div>
<div class="acitivity-timeline p-4">
${window.languageBundle.get('presupuesto.plantilla-cubierta-text')}
</div>
`,
confirmButtonClass: 'btn btn-primary w-xs mt-2',
showConfirmButton: false,
showCloseButton: true,
buttonsStyling: false,
customClass: {
confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar
cancelButton: 'btn btn-light' // clases para cancelar
},
});
});
this.btn_impresion_cubierta_help.on('click', () => { this.btn_impresion_cubierta_help.on('click', () => {
Swal.fire({ Swal.fire({
@ -1398,7 +1420,10 @@ export default class PresupuestoWizard {
#getCubiertaData() { #getCubiertaData() {
const tipoCubierta = $('.tapa-cubierta input:checked').val() || 'tapaBlanda'; const tipoCubierta = $('.tapa-cubierta input:checked').val() || 'tapaBlanda';
const solapas = $('.solapas-cubierta input:checked').val() == 'sin-solapas' ? 0 : 1 || 0; let solapas = 0;
if (tipoCubierta === 'tapaBlanda') {
solapas = $('.solapas-cubierta input:checked').val() == 'conSolapas' ? 1 : 0 || 0;
}
const tamanioSolapasCubierta = $('#tamanio-solapas-cubierta').val() || '80'; const tamanioSolapasCubierta = $('#tamanio-solapas-cubierta').val() || '80';
const cubiertaCaras = parseInt(this.carasImpresionCubierta.val()) || 2; const cubiertaCaras = parseInt(this.carasImpresionCubierta.val()) || 2;
const papelGuardasId = parseInt($('#papel-guardas option:selected').data('papel-id')) || 3; const papelGuardasId = parseInt($('#papel-guardas option:selected').data('papel-id')) || 3;
@ -1645,6 +1670,11 @@ export default class PresupuestoWizard {
this.divTiradas.append(item.renderCol(this.divTiradas)); this.divTiradas.append(item.renderCol(this.divTiradas));
} }
if (data.lomo_cubierta) {
this.formData.lomoCubierta = data.lomo_cubierta;
this.#cacheFormData();
}
if (this.divTiradas.find('.tirada-card input[type="radio"]:checked').length === 0) { if (this.divTiradas.find('.tirada-card input[type="radio"]:checked').length === 0) {
this.divTiradas.find('.tirada-card input[type="radio"]').first().prop('checked', true).trigger('change'); this.divTiradas.find('.tirada-card input[type="radio"]').first().prop('checked', true).trigger('change');
this.formData.selectedTirada = this.divTiradas.find('.tirada-card input[type="radio"]').first().data('unidades') || data.tiradas[0]; this.formData.selectedTirada = this.divTiradas.find('.tirada-card input[type="radio"]').first().data('unidades') || data.tiradas[0];
@ -1868,6 +1898,7 @@ export default class PresupuestoWizard {
#initResumen() { #initResumen() {
const $row = $('#presupuesto-row'); const $row = $('#presupuesto-row');
$('#btn-plantilla-cubierta2').on('click', this.#obtener_plantilla_cubierta.bind(this));
// 1) Transición al cambiar de pestaña (click o programático) // 1) Transición al cambiar de pestaña (click o programático)
$(document).on('shown.bs.tab', '.custom-nav .nav-link', (e) => { $(document).on('shown.bs.tab', '.custom-nav .nav-link', (e) => {
const targetSelector = $(e.target).data('bs-target'); // ej: "#pills-resumen" const targetSelector = $(e.target).data('bs-target'); // ej: "#pills-resumen"
@ -1953,6 +1984,37 @@ export default class PresupuestoWizard {
/****************************** /******************************
* END RESUMEN * END RESUMEN
******************************/ ******************************/
#obtener_plantilla_cubierta() {
let url = `/presupuesto/api/plantilla-cubierta.png
?tipo=${this.formData.datosGenerales.tipoEncuadernacion}
&tapa=${this.formData.cubierta.tipoCubierta}
&ancho=${this.formData.datosGenerales.ancho}
&alto=${this.formData.datosGenerales.alto}
&lomo=${Math.round(this.formData.lomo) || 0}
&solapas=${this.formData.cubierta.solapasCubierta == 1 ? this.formData.cubierta.tamanioSolapasCubierta : 0}`;
url = url.trim().replace(/\s+/g, '');
Swal.fire({
position: 'top-end',
icon: 'info',
title: window.languageBundle.get('presupuesto.plantilla-cubierta'),
html: `
<div class="text-center p-4">
<img src="${url}" class="img-fluid" alt="" />
</div>
<div class="acitivity-timeline p-4">
${window.languageBundle.get('presupuesto.plantilla-cubierta-text')}
</div>
`,
confirmButtonClass: 'btn btn-primary w-xs mt-2',
showConfirmButton: false,
showCloseButton: true,
buttonsStyling: false,
customClass: {
confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar
cancelButton: 'btn btn-light' // clases para cancelar
},
});
}
} }

View File

@ -72,7 +72,7 @@ import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-ut
e.preventDefault(); e.preventDefault();
const id = $(this).data('id'); const id = $(this).data('id');
if (id) { if (id) {
window.location.href = '/presupuesto/view/' + id; window.open('/presupuesto/view/' + id, '_blank', 'noopener,noreferrer');
} }
}); });
@ -196,7 +196,7 @@ import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-ut
e.preventDefault(); e.preventDefault();
const id = $(this).data('id'); const id = $(this).data('id');
if (id) { if (id) {
window.location.href = '/presupuesto/edit/' + id; window.open('/presupuesto/edit/' + id, '_blank', 'noopener,noreferrer');
} }
}); });

View File

@ -0,0 +1,33 @@
$(() => {
$('#btn-plantilla-cubierta').on('click', () => {
let url = `/presupuesto/api/plantilla-cubierta.png
?tipo=${$('#tipoEncuadernacion').val()}
&tapa=${$('#tipoCubierta').val()}
&ancho=${$('#ancho').val()}
&alto=${$('#alto').val()}
&lomo=${Math.round($('#lomo').val()) || 0}
&solapas=${$('#solapasCubierta').val() == 1 ? $('#tamanioSolapasCubierta').val() : 0}`;
url = url.trim().replace(/\s+/g, '');
Swal.fire({
position: 'top-end',
icon: 'info',
title: window.languageBundle.get('presupuesto.plantilla-cubierta'),
html: `
<div class="text-center p-4">
<img src="${url}" class="img-fluid" alt="" />
</div>
<div class="acitivity-timeline p-4">
${window.languageBundle.get('presupuesto.plantilla-cubierta-text')}
</div>
`,
confirmButtonClass: 'btn btn-primary w-xs mt-2',
showConfirmButton: false,
showCloseButton: true,
buttonsStyling: false,
customClass: {
confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar
cancelButton: 'btn btn-light' // clases para cancelar
},
});
});
});

View File

@ -55,7 +55,11 @@ export async function preguntarTipoPresupuesto() {
url: 'users/api/get-users', // ajusta a tu endpoint url: 'users/api/get-users', // ajusta a tu endpoint
dataType: 'json', dataType: 'json',
delay: 250, delay: 250,
data: (params) => ({ q: params.term }), data: (params) => ({
q: params.term || '',
page: params.page || 1,
size: 10,
}),
processResults: data => ({ processResults: data => ({
results: data.results, results: data.results,
pagination: data.pagination pagination: data.pagination

View File

@ -56,6 +56,8 @@
<h5 class="fs-14 text-truncate mb-1"> <h5 class="fs-14 text-truncate mb-1">
<span th:text="#{cart.item.presupuesto-numero}">Presupuesto #</span> <span th:text="#{cart.item.presupuesto-numero}">Presupuesto #</span>
<span th:text="${item.presupuestoId != null ? item.presupuestoId : ''}">#</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> </h5>
<ul class="list-unstyled text-muted mb-1 ps-0"> <ul class="list-unstyled text-muted mb-1 ps-0">

View File

@ -48,7 +48,7 @@
</div> </div>
<form th:action="@{/pagos/redsys/crear}" method="post"> <form th:action="@{/pagos/redsys/crear}" method="post">
<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" th:value="${method} ?: '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="" /> <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"

View File

@ -50,6 +50,9 @@
<div> <div>
<h5 th:text="#{checkout.billing-address}" class="mb-3">Dirección de envío</h5> <h5 th:text="#{checkout.billing-address}" class="mb-3">Dirección de envío</h5>
<div id="dirFactWarning" class="alert alert-danger alert-shipment" role="alert"
th:text="#{cart.shipping.errors.fillBillingAddressItems}"></div>
<button type="button" class="btn btn-secondary mb-3" id="addBillingAddressBtn" <button type="button" class="btn btn-secondary mb-3" id="addBillingAddressBtn"
th:text="#{cart.shipping.add}">Añadir dirección th:text="#{cart.shipping.add}">Añadir dirección
</button> </button>

View File

@ -0,0 +1,61 @@
<!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">
<!-- CONTENIDO -->
<div class="row justify-content-center">
<div class="col-lg-7">
<div class="card shadow-sm">
<div class="card-body p-4 p-md-5 text-center">
<h1 class="display-4 mb-2" th:text="${status}">404</h1>
<h4 class="mb-3" th:text="${error}">Not Found</h4>
<p class="text-muted mb-4"
th:text="${message != null and !#strings.isEmpty(message)} ? ${message} : 'Ha ocurrido un error inesperado.'">
Ha ocurrido un error inesperado.
</p>
<div class="small text-muted mb-4">
<div><strong>Ruta:</strong> <span th:text="${path}">/logout</span></div>
<div><strong>Fecha:</strong> <span
th:text="${#temporals.format(timestamp, 'dd/MM/yyyy HH:mm:ss')}">--</span></div>
</div>
<div class="d-flex gap-2 justify-content-center">
<a class="btn btn-primary" th:href="@{/}">Volver al inicio</a>
<a class="btn btn-outline-secondary" href="javascript:history.back()">Atrás</a>
</div>
<!-- Opcional: bloque extra sólo para 404 -->
<div class="mt-4" th:if="${is404}">
<div class="alert alert-warning mb-0">
No se ha encontrado el recurso solicitado.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /CONTENIDO -->
</div>
</body>
</html>

View File

@ -0,0 +1,105 @@
<div id="fidelity-banner" th:fragment="home-container-user">
<div class="ib-loyalty-banner">
<!-- Decoraciones -->
<div class="decor"></div>
<div class="book small"></div>
<div class="ib-loyalty-inner">
<!-- ===================== -->
<!-- COLUMNA IZQUIERDA -->
<!-- ===================== -->
<div class="ib-loyalty-left">
<!-- PANEL SUPERIOR: TÍTULO -->
<div class="ib-loyalty-hero">
<div class="ib-loyalty-head">
<div class="ib-loyalty-logo">
<img src="/assets/images/logo-sm.png" alt="Logo" />
</div>
<div>
<h3 class="ib-loyalty-title">Programa de Fidelidad</h3>
<p class="ib-loyalty-sub">
Aumenta tus compras en los últimos 12 meses y obtén descuentos automáticos.
</p>
</div>
</div>
</div>
<!-- PANEL INFERIOR: ESTADÍSTICA (RECUADRO INDEPENDIENTE) -->
<div class="ib-loyalty-stat-card">
<h6 class="text-uppercase fs-13 mb-3">
<span th:text="#{pedido.gasto-anual}">Gasto últimos 12 meses</span>
<i class="ri-arrow-up-circle-line text-success fs-18 float-end align-middle"></i>
</h6>
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<i class="ri-money-euro-circle-line display-6 "></i>
</div>
<div class="flex-grow-1 ms-3">
<h2 class="mb-0">
<h2 class="mb-0"><span class="counter-value" th:attr="data-target=${totalGastado}">0</span></h2>
</h2>
</div>
</div>
</div>
</div>
<!-- ===================== -->
<!-- COLUMNA DERECHA -->
<!-- ===================== -->
<div class="ib-rewards">
<h6>Recompensas</h6>
<div class="ib-rewards-grid">
<div class="ib-card">
<div class="range">Menos de 1.200€</div>
<div class="percent">0%</div>
<span class="chip">Descuento</span>
</div>
<div class="ib-card">
<div class="range">1.200€ 1.999€</div>
<div class="percent">1%</div>
<span class="chip">Descuento</span>
</div>
<div class="ib-card">
<div class="range">2.000€ 2.999€</div>
<div class="percent">2%</div>
<span class="chip">Descuento</span>
</div>
<div class="ib-card">
<div class="range">3.000€ 3.999€</div>
<div class="percent">3%</div>
<span class="chip">Descuento</span>
</div>
<div class="ib-card">
<div class="range">4.000€ 4.999€</div>
<div class="percent">4%</div>
<span class="chip">Descuento</span>
</div>
<div class="ib-card">
<div class="range">Más de 5.000€</div>
<div class="percent">5%</div>
<span class="chip">Descuento</span>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -8,6 +8,8 @@
<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"
th:unless="${#authorization.expression('isAuthenticated()')}" /> th:unless="${#authorization.expression('isAuthenticated()')}" />
<link th:href="@{/assets/css/home.css}" rel="stylesheet"
th:if="${#authorization.expression('isAuthenticated()')}" />
</th:block> </th:block>
</head> </head>
@ -22,7 +24,7 @@
<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">
<div th:insert="~{imprimelibros/home/home-container-user :: home-container-user}"></div>
</div> </div>
</div> </div>
<div th:unless="${#authorization.expression('isAuthenticated()')}"> <div th:unless="${#authorization.expression('isAuthenticated()')}">
@ -41,6 +43,9 @@
<script type="module" <script type="module"
th:src="@{/assets/js/pages/imprimelibros/presupuestador/presupuesto-marcapaginas.js}"></script> th:src="@{/assets/js/pages/imprimelibros/presupuestador/presupuesto-marcapaginas.js}"></script>
</div> </div>
<div th:if="${#authorization.expression('isAuthenticated()')}">
<script type="module" th:src="@{/assets/js/pages/imprimelibros/counter-widget.js}"></script>
</div>
<script th:inline="javascript"> <script th:inline="javascript">
window.languageBundle = /*[[${languageBundle}]]*/ {}; window.languageBundle = /*[[${languageBundle}]]*/ {};
</script> </script>

View File

@ -100,9 +100,6 @@
<a class="dropdown-item" href="/pages-profile"><i <a class="dropdown-item" href="/pages-profile"><i
class="mdi mdi-account-circle text-muted fs-16 align-middle me-1"></i> <span class="mdi mdi-account-circle text-muted fs-16 align-middle me-1"></i> <span
class="align-middle" th:text="#{app.perfil}">Perfil</span></a> class="align-middle" th:text="#{app.perfil}">Perfil</span></a>
<a class="dropdown-item" href="/apps-chat"><i
class="mdi mdi-message-text-outline text-muted fs-16 align-middle me-1"></i>
<span class="align-middle" th:text="#{app.mensajes}">Mensajes</span></a>
<div sec:authorize="hasRole('PREVIOUS_ADMINISTRATOR')"> <div sec:authorize="hasRole('PREVIOUS_ADMINISTRATOR')">
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" <a class="dropdown-item" href="#"

View File

@ -37,6 +37,7 @@
<tr> <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.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.fecha}">Fecha</th>
<th class="text-start" scope="col" th:text="#{pedido.table.titulos}">Títulos</th>
<th class="text-start" scope="col" th:text="#{pedido.table.importe}">Importe</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.estado}">Estado</th>
<th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th> <th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th>
@ -44,6 +45,7 @@
<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="id" /></th>
<th></th> <th></th>
<th><input type="text" class="form-control form-control-sm input-filter" data-col="titulos" /></th>
<th></th> <th></th>
<th> <th>
<select class="form-select form-select-sm input-filter" data-col="estado"> <select class="form-select form-select-sm input-filter" data-col="estado">

View File

@ -38,6 +38,7 @@
<th class="text-start" scope="col" th:text="#{pedido.table.id}">Num. Pedido</th> <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.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.fecha}">Fecha</th>
<th class="text-start" scope="col" th:text="#{pedido.table.titulos}">Títulos</th>
<th class="text-start" scope="col" th:text="#{pedido.table.importe}">Importe</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.estado}">Estado</th>
<th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th> <th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th>
@ -46,6 +47,7 @@
<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="id" /></th>
<th><input type="text" class="form-control form-control-sm input-filter" data-col="createdBy.fullName" /></th> <th><input type="text" class="form-control form-control-sm input-filter" data-col="createdBy.fullName" /></th>
<th></th> <th></th>
<th><input type="text" class="form-control form-control-sm input-filter" data-col="titulos" /></th>
<th></th> <th></th>
<th> <th>
<select class="form-select form-select-sm input-filter" data-col="estado"> <select class="form-select form-select-sm input-filter" data-col="estado">

View File

@ -302,19 +302,6 @@
</label> </label>
</div> </div>
</div> </div>
<div class="row justify-content-center mb-2">
<div class="col-sm-3 justify-content-center">
<label for="entregaTipo" class="form-label mt-2"
th:text="#{presupuesto.entrega}">Entrega</label>
<select class="form-select select2 datos-generales-data" id="entregaTipo" name="entregaTipo">
<option selected value="peninsula" th:text="#{presupuesto.entrega.peninsula}">Península
y
Baleares</option>
<option value="canarias" th:text="#{presupuesto.entrega.canarias}">Canarias</option>
<option value="paises_ue" th:text="#{presupuesto.entrega.paises-ue}">Países UE</option>
</select>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -18,6 +18,17 @@
<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}" <h6 th:if="${presupuesto?.isReimpresion}" th:text="#{presupuesto.reimpresion}"
class="text-uppercase bg-danger text-white text-center">REIMPRESION</h6> class="text-uppercase bg-danger text-white text-center">REIMPRESION</h6>
<div class="row justify-content-center mb-3">
<div class="col-auto">
<button type="button" id="btn-plantilla-cubierta2" class="btn btn-outline-primary btn-border">
<i class="ri-questionnaire-line label-icon align-middle fs-16 me-2"></i>
<span th:text="#{presupuesto.plantilla-cubierta}">
Plantilla de cubierta
</span>
</button>
</div>
</div>
<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>

View File

@ -44,18 +44,40 @@
<input type="hidden" id="presupuesto_id" th:value="${presupuesto.id}" /> <input type="hidden" id="presupuesto_id" th:value="${presupuesto.id}" />
<input type="hidden" id="ancho" th:value="${presupuesto.ancho}" />
<input type="hidden" id="alto" th:value="${presupuesto.alto}" />
<input type="hidden" id="lomo" th:value="${presupuesto.lomo}" />
<input type="hidden" id="tipoEncuadernacion" th:value="${presupuesto.tipoEncuadernacion}" />
<input type="hidden" id="tipoCubierta" th:value="${presupuesto.tipoCubierta}" />
<input type="hidden" id="solapasCubierta" th:value="${presupuesto.solapasCubierta}" />
<input type="hidden" id="tamanioSolapasCubierta" th:value="${presupuesto.tamanioSolapasCubierta}" />
<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 id="titulo" 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"> <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> <h6 th:text="#{presupuesto.reimpresion}"
class="bg-danger py-2 text-center text-white text-uppercase">REIMPRESION</h6>
</div>
<div class="row justify-content-center mb-3">
<div class="col-auto">
<button type="button" id="btn-plantilla-cubierta"
class="btn btn-outline-primary btn-border">
<i class="ri-questionnaire-line label-icon align-middle fs-16 me-2"></i>
<span th:text="#{presupuesto.plantilla-cubierta}">
Plantilla de cubierta
</span>
</button>
</div>
</div> </div>
<div class="card col-12 col-sm-9 mx-auto"> <div class="card col-12 col-sm-9 mx-auto">
@ -234,6 +256,8 @@
<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> <script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestos/duplicate-reprint.js}"></script>
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestos/plantilla-cubierta.js}"></script>
</th:block> </th:block>
</body> </body>

View File

@ -0,0 +1,107 @@
<!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}" />
</head>
<body>
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}" />
<th:block layout:fragment="content">
<div th:if="${#authorization.expression('isAuthenticated()')}">
<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="#{app.perfil}">Perfil</li>
</ol>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0" th:text="#{usuarios.profile.title}">Editar perfil</h5>
</div>
<div class="card-body">
<div th:if="${success}" class="alert alert-success"
th:text="#{usuarios.profile.success}">Perfil actualizado.</div>
<form id="profileForm" novalidate th:action="@{/pages-profile}" th:object="${user}"
method="post">
<div th:if="${#fields.hasGlobalErrors()}" class="alert alert-danger">
<div th:each="e : ${#fields.globalErrors()}" th:text="${e}"></div>
</div>
<div class="mb-3">
<label th:text="#{usuarios.form.nombre}" for="fullName">Nombre</label>
<input type="text" class="form-control" id="fullName" th:field="*{fullName}"
th:classappend="${#fields.hasErrors('fullName')} ? ' is-invalid'" required>
<div class="invalid-feedback" th:if="${#fields.hasErrors('fullName')}"
th:errors="*{fullName}">Error</div>
</div>
<div class="mb-3">
<label th:text="#{usuarios.form.email}" for="userName">Correo electrónico</label>
<input type="email" class="form-control" id="userName" th:field="*{userName}"
th:classappend="${#fields.hasErrors('userName')} ? ' is-invalid'" required>
<div class="invalid-feedback" th:if="${#fields.hasErrors('userName')}"
th:errors="*{userName}">Error</div>
</div>
<hr class="my-4">
<div class="mb-3">
<label th:text="#{usuarios.form.password.actual}" for="currentPassword">Contraseña actual</label>
<input type="password" class="form-control" id="currentPassword"
th:field="*{currentPassword}"
th:classappend="${#fields.hasErrors('currentPassword')} ? ' is-invalid'">
<div class="invalid-feedback" th:if="${#fields.hasErrors('currentPassword')}"
th:errors="*{currentPassword}">Error</div>
</div>
<div class="mb-3">
<label th:text="#{usuarios.form.password.nueva}" for="newPassword">Nueva contraseña</label>
<input type="password" class="form-control" id="newPassword"
th:field="*{newPassword}"
th:classappend="${#fields.hasErrors('newPassword')} ? ' is-invalid'">
<div class="text-muted" th:text="#{usuarios.form.password.nota}">
Solo podrás cambiar la contraseña si indicas la actual.
</div>
<div class="invalid-feedback" th:if="${#fields.hasErrors('newPassword')}"
th:errors="*{newPassword}">Error</div>
</div>
<div class="mb-3">
<label th:text="#{usuarios.form.confirmarPassword}" for="confirmPassword">Confirmar contraseña</label>
<input type="password" class="form-control" id="confirmPassword"
th:field="*{confirmPassword}"
th:classappend="${#fields.hasErrors('confirmPassword')} ? ' is-invalid'">
<div class="invalid-feedback" th:if="${#fields.hasErrors('confirmPassword')}"
th:errors="*{confirmPassword}">Error</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-secondary" th:text="#{app.guardar}">Guardar</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</th:block>
<th:block layout:fragment="modal" />
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
</body>
</html>