mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-12 16:38:48 +00:00
falta ordenar por paginas y revisar la busqueda de los selects
This commit is contained in:
@ -583,7 +583,9 @@ public class PresupuestoController {
|
||||
"presupuesto.plantilla-cubierta",
|
||||
"presupuesto.plantilla-cubierta-text",
|
||||
"presupuesto.impresion-cubierta",
|
||||
"presupuesto.impresion-cubierta-help");
|
||||
"presupuesto.impresion-cubierta-help",
|
||||
"presupuesto.exito.guardado",
|
||||
"presupuesto.add.error.save.title");
|
||||
|
||||
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||
model.addAttribute("languageBundle", translations);
|
||||
@ -592,10 +594,12 @@ public class PresupuestoController {
|
||||
model.addAttribute("ancho_alto_max", variableService.getValorEntero("ancho_alto_max"));
|
||||
|
||||
model.addAttribute("appMode", "add");
|
||||
|
||||
if (!mode.equals("public")) {
|
||||
model.addAttribute("cliente_id", clienteId);
|
||||
}
|
||||
|
||||
model.addAttribute("mode", mode);
|
||||
return "imprimelibros/presupuestos/presupuesto-form";
|
||||
}
|
||||
|
||||
@ -617,15 +621,24 @@ public class PresupuestoController {
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping(value = "/datatable/anonimos", produces = "application/json")
|
||||
@GetMapping(value = "/datatable/{tipo}", produces = "application/json")
|
||||
@ResponseBody
|
||||
public DataTablesResponse<Map<String, Object>> datatableAnonimos(
|
||||
HttpServletRequest request, Authentication auth, Locale locale) {
|
||||
public DataTablesResponse<Map<String, Object>> datatable(
|
||||
HttpServletRequest request, Authentication auth, Locale locale,
|
||||
@PathVariable("tipo") String tipo) {
|
||||
|
||||
DataTablesRequest dt = DataTablesParser.from(request);
|
||||
return dtService.datatableAnonimos(dt, locale);
|
||||
|
||||
if ("anonimos".equals(tipo)) {
|
||||
return dtService.datatablePublicos(dt, locale);
|
||||
} else if ("clientes".equals(tipo)) {
|
||||
return dtService.datatablePrivados(dt, locale);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Tipo de datatable no válido");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Transactional
|
||||
public ResponseEntity<?> delete(@PathVariable Long id, Authentication auth, Locale locale) {
|
||||
@ -709,13 +722,26 @@ public class PresupuestoController {
|
||||
}
|
||||
|
||||
try {
|
||||
var resumen = presupuestoService.getTextosResumen(presupuesto, serviciosList, locale);
|
||||
|
||||
Long cliente_id = objectMapper.convertValue(body.get("cliente_id"), Long.class);
|
||||
if(id == null && cliente_id != null && !mode.equals("public")) {
|
||||
|
||||
presupuesto.setUser(userRepo.findById(cliente_id).orElse(null));
|
||||
presupuesto.setOrigen(Presupuesto.Origen.privado);
|
||||
}
|
||||
if (mode.equals("public")) {
|
||||
var resumen = presupuestoService.getTextosResumen(presupuesto, serviciosList, locale);
|
||||
presupuesto = presupuestoService.generateTotalizadores(presupuesto, serviciosList, resumen, locale);
|
||||
presupuesto.setOrigen(Presupuesto.Origen.publico);
|
||||
String sessionId = request.getSession(true).getId();
|
||||
String ip = request.getRemoteAddr();
|
||||
|
||||
presupuesto = presupuestoService.getDatosLocalizacion(presupuesto, sessionId, ip);
|
||||
if (id != null) {
|
||||
presupuesto.setId(id); // para que actualice, no cree uno nuevo
|
||||
}
|
||||
}
|
||||
presupuesto = presupuestoService.generateTotalizadores(presupuesto, serviciosList, resumen, locale);
|
||||
|
||||
Map<String, Object> saveResult = presupuestoService.guardarPresupuesto(presupuesto);
|
||||
return ResponseEntity.ok(Map.of("id", saveResult.get("presupuesto_id"),
|
||||
"message", messageSource.getMessage("presupuesto.exito.guardado", null, locale)));
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.imprimelibros.erp.presupuesto;
|
||||
|
||||
import jakarta.persistence.criteria.Expression;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.domain.*;
|
||||
@ -29,33 +30,38 @@ public class PresupuestoDatatableService {
|
||||
|
||||
/* ---------- API pública ---------- */
|
||||
|
||||
public DataTablesResponse<Map<String, Object>> datatableAnonimos(DataTablesRequest dt, Locale locale) {
|
||||
public DataTablesResponse<Map<String, Object>> datatablePublicos(DataTablesRequest dt, Locale locale) {
|
||||
String term = extractSearch(dt);
|
||||
Pageable pageable = pageableFrom(dt);
|
||||
|
||||
EnumMatches matches = buildEnumMatches(term, locale);
|
||||
|
||||
Specification<Presupuesto> spec = baseSpec(term, matches, dt);
|
||||
|
||||
spec = spec.and((root, query, cb) -> cb.equal(root.get("origen"), "publico"));
|
||||
|
||||
Page<Presupuesto> page = repo.findAll(spec, pageable);
|
||||
|
||||
var rows = page.getContent().stream()
|
||||
.map(p -> mapAnonimoRow(p, locale)) // 👈 mapper específico “anonimos”
|
||||
.map(p -> mapPresupuestoPublico(p, locale))
|
||||
.toList();
|
||||
|
||||
return new DataTablesResponse<>(dt.draw, repo.count(), page.getTotalElements(), rows);
|
||||
}
|
||||
|
||||
public DataTablesResponse<Map<String, Object>> datatableNoAnonimos(DataTablesRequest dt, Locale locale) {
|
||||
public DataTablesResponse<Map<String, Object>> datatablePrivados(DataTablesRequest dt, Locale locale) {
|
||||
String term = extractSearch(dt);
|
||||
Pageable pageable = pageableFrom(dt);
|
||||
|
||||
EnumMatches matches = buildEnumMatches(term, locale);
|
||||
|
||||
Specification<Presupuesto> spec = baseSpec(term, matches, dt);
|
||||
spec = spec.and((root, query, cb) -> cb.equal(root.get("origen"), "privado"));
|
||||
|
||||
Page<Presupuesto> page = repo.findAll(spec, pageable);
|
||||
|
||||
var rows = page.getContent().stream()
|
||||
.map(p -> mapNoAnonimoRow(p, locale)) // 👈 otro mapper con más/otros campos
|
||||
.map(p -> mapPresupuestoPrivado(p, locale)) // 👈 otro mapper con más/otros campos
|
||||
.toList();
|
||||
|
||||
return new DataTablesResponse<>(dt.draw, repo.count(), page.getTotalElements(), rows);
|
||||
@ -67,6 +73,24 @@ public class PresupuestoDatatableService {
|
||||
return (dt.search != null && dt.search.value != null) ? dt.search.value.trim().toLowerCase() : "";
|
||||
}
|
||||
|
||||
private Map<String, String> extractColumnSearches(DataTablesRequest dt) {
|
||||
Map<String, String> byColumn = new HashMap<>();
|
||||
if (dt.columns == null)
|
||||
return byColumn;
|
||||
|
||||
for (var col : dt.columns) {
|
||||
// Importante: en el front usa columns[i][name] con el nombre del campo JPA
|
||||
String field = col.name;
|
||||
String value = (col.search != null && col.search.value != null)
|
||||
? col.search.value.trim()
|
||||
: "";
|
||||
if (field != null && !field.isBlank() && value != null && !value.isBlank()) {
|
||||
byColumn.put(field, value.toLowerCase());
|
||||
}
|
||||
}
|
||||
return byColumn;
|
||||
}
|
||||
|
||||
private Pageable pageableFrom(DataTablesRequest dt) {
|
||||
int page = dt.length > 0 ? dt.start / dt.length : 0;
|
||||
List<Sort.Order> orders = new ArrayList<>();
|
||||
@ -115,10 +139,16 @@ public class PresupuestoDatatableService {
|
||||
private Specification<Presupuesto> baseSpec(String term, EnumMatches m, DataTablesRequest dt) {
|
||||
return (root, query, cb) -> {
|
||||
List<Predicate> ors = new ArrayList<>();
|
||||
List<Predicate> ands = new ArrayList<>(); // filtros por columna (AND)
|
||||
|
||||
Expression<Integer> totalPag = cb.sum(
|
||||
cb.coalesce(root.get("paginasColor"), cb.literal(0)),
|
||||
cb.coalesce(root.get("paginasNegro"), cb.literal(0)));
|
||||
|
||||
if (!term.isBlank()) {
|
||||
String like = "%" + term + "%";
|
||||
ors.add(cb.like(cb.lower(root.get("titulo")), like));
|
||||
ors.add(cb.like(cb.lower(root.join("user").get("fullName")), like));
|
||||
ors.add(cb.like(cb.lower(root.get("ciudad")), like));
|
||||
ors.add(cb.like(cb.lower(root.get("region")), like));
|
||||
ors.add(cb.like(cb.lower(root.get("pais")), like));
|
||||
@ -132,6 +162,56 @@ public class PresupuestoDatatableService {
|
||||
if (!m.est.isEmpty())
|
||||
ors.add(root.get("estado").in(m.est));
|
||||
|
||||
Map<String, String> byCol = extractColumnSearches(dt);
|
||||
for (var entry : byCol.entrySet()) {
|
||||
String field = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (value.isBlank() || field.isBlank()
|
||||
|| field.contains("tipoEncuadernacion")
|
||||
|| field.contains("tipoCubierta")
|
||||
|| field.contains("tipoImpresion")
|
||||
|| field.contains("estado")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- CASO ESPECIAL: filtro por nombre del usuario ---
|
||||
if ("user".equals(field)) {
|
||||
var userJoin = root.join("user");
|
||||
var expr = cb.lower(userJoin.get("fullName"));
|
||||
ands.add(cb.like(expr, "%" + value.toLowerCase() + "%"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- CASO ESPECIAL: filtro por total de páginas ---
|
||||
if ("paginas".equals(field)) {
|
||||
try {
|
||||
int paginas = Integer.parseInt(value);
|
||||
ands.add(cb.equal(totalPag, paginas));
|
||||
} catch (NumberFormatException nfe) {
|
||||
var asString = cb.function("CONCAT", String.class, cb.literal(""), totalPag);
|
||||
var safe = cb.function("COALESCE", String.class, asString, cb.literal(""));
|
||||
var strExpr = cb.lower(safe);
|
||||
ands.add(cb.like(strExpr, "%" + value.toLowerCase() + "%"));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- RESTO DE CAMPOS: acceso genérico ---
|
||||
var path = root.get(field);
|
||||
Class<?> type = path.getJavaType();
|
||||
|
||||
Expression<String> strExpr;
|
||||
if (String.class.isAssignableFrom(type)) {
|
||||
strExpr = cb.lower(path.as(String.class));
|
||||
} else {
|
||||
var asString = cb.function("CONCAT", String.class, cb.literal(""), path);
|
||||
var safe = cb.function("COALESCE", String.class, asString, cb.literal(""));
|
||||
strExpr = cb.lower(safe);
|
||||
}
|
||||
|
||||
ands.add(cb.like(strExpr, "%" + value.toLowerCase() + "%"));
|
||||
}
|
||||
|
||||
// ORDER BY especial si en columns[i][name] viene 'paginas' o 'estado'
|
||||
if (query != null && !query.getOrderList().isEmpty()) {
|
||||
var jpaOrders = new ArrayList<jakarta.persistence.criteria.Order>();
|
||||
@ -139,26 +219,42 @@ public class PresupuestoDatatableService {
|
||||
String prop = ob.getExpression().toString();
|
||||
boolean asc = ob.isAscending();
|
||||
if ("paginas".equals(prop)) {
|
||||
var totalPag = cb.sum(cb.coalesce(root.get("paginasColor"), 0),
|
||||
cb.coalesce(root.get("paginasNegro"), 0));
|
||||
jpaOrders.add(asc ? cb.asc(totalPag) : cb.desc(totalPag));
|
||||
var totalPagOrder = cb.sum(
|
||||
cb.coalesce(root.get("paginasColor"), cb.literal(0)),
|
||||
cb.coalesce(root.get("paginasNegro"), cb.literal(0)));
|
||||
jpaOrders.add(asc ? cb.asc(totalPagOrder) : cb.desc(totalPagOrder));
|
||||
} else if ("estado".equals(prop)) {
|
||||
var estadoStr = cb.function("str", String.class, root.get("estado"));
|
||||
jpaOrders.add(asc ? cb.asc(estadoStr) : cb.desc(estadoStr));
|
||||
} else {
|
||||
jpaOrders.add(asc ? cb.asc(root.get(prop)) : cb.desc(root.get(prop)));
|
||||
try {
|
||||
jpaOrders.add(asc ? cb.asc(root.get(prop)) : cb.desc(root.get(prop)));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// El campo no existe (como 'paginas'), lo ignoramos
|
||||
// Opcional: puedes loggear si quieres
|
||||
// log.warn("Campo no encontrado para ORDER BY: {}", prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
query.orderBy(jpaOrders);
|
||||
}
|
||||
|
||||
return ors.isEmpty() ? cb.conjunction() : cb.or(ors.toArray(new Predicate[0]));
|
||||
// === Compose final WHERE ===
|
||||
Predicate where = ors.isEmpty()
|
||||
? cb.conjunction()
|
||||
: cb.or(ors.toArray(new Predicate[0]));
|
||||
|
||||
if (!ands.isEmpty()) {
|
||||
where = cb.and(where, cb.and(ands.toArray(new Predicate[0])));
|
||||
}
|
||||
|
||||
return where;
|
||||
};
|
||||
}
|
||||
|
||||
/* ---------- Mappers de filas (puedes tener tantos como vistas) ---------- */
|
||||
|
||||
private Map<String, Object> mapAnonimoRow(Presupuesto p, Locale locale) {
|
||||
private Map<String, Object> mapPresupuestoPublico(Presupuesto p, Locale locale) {
|
||||
int paginas = n(p.getPaginasColor()) + n(p.getPaginasNegro());
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
m.put("id", p.getId());
|
||||
@ -174,30 +270,55 @@ public class PresupuestoDatatableService {
|
||||
m.put("region", p.getRegion());
|
||||
m.put("ciudad", p.getCiudad());
|
||||
m.put("updatedAt", formatDate(p.getUpdatedAt(), locale));
|
||||
if(p.getEstado().equals(Presupuesto.Estado.borrador)){
|
||||
m.put("actions",
|
||||
"<div class=\"hstack gap-3 flex-wrap\">" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-danger btn-delete-anonimo fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>"
|
||||
+
|
||||
"</div>");
|
||||
}
|
||||
else{
|
||||
if (p.getEstado().equals(Presupuesto.Estado.borrador)) {
|
||||
m.put("actions",
|
||||
"<div class=\"hstack gap-3 flex-wrap\">" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
|
||||
"</div>");
|
||||
"<div class=\"hstack gap-3 flex-wrap\">" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-danger btn-delete-anonimo fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>"
|
||||
+
|
||||
"</div>");
|
||||
} else {
|
||||
m.put("actions",
|
||||
"<div class=\"hstack gap-3 flex-wrap\">" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
|
||||
"</div>");
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
private Map<String, Object> mapNoAnonimoRow(Presupuesto p, Locale locale) {
|
||||
Map<String, Object> m = mapAnonimoRow(p, locale); // base común
|
||||
// añade/remueve campos específicos de “no anónimos”
|
||||
// m.put("cliente", p.getCliente().getNombre()); // ejemplo
|
||||
private Map<String, Object> mapPresupuestoPrivado(Presupuesto p, Locale locale) {
|
||||
int paginas = n(p.getPaginasColor()) + n(p.getPaginasNegro());
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
m.put("id", p.getId());
|
||||
m.put("titulo", p.getTitulo());
|
||||
m.put("user", p.getUser().getFullName());
|
||||
m.put("tipoEncuadernacion", msg(p.getTipoEncuadernacion().getMessageKey(), locale));
|
||||
m.put("tipoCubierta", msg(p.getTipoCubierta().getMessageKey(), locale));
|
||||
m.put("tipoImpresion", msg(p.getTipoImpresion().getMessageKey(), locale));
|
||||
m.put("tirada", p.getSelectedTirada());
|
||||
m.put("paginas", paginas);
|
||||
m.put("estado", msg(p.getEstado().getMessageKey(), locale));
|
||||
m.put("totalConIva", formatCurrency(p.getTotalConIva(), locale));
|
||||
m.put("updatedAt", formatDate(p.getUpdatedAt(), locale));
|
||||
if (p.getEstado().equals(Presupuesto.Estado.borrador)) {
|
||||
m.put("actions",
|
||||
"<div class=\"hstack gap-3 flex-wrap\">" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-success btn-edit-privado fs-15\"><i class=\"ri-pencil-line\"></i></a>" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-danger btn-delete-privado fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>"
|
||||
+
|
||||
"</div>");
|
||||
} else {
|
||||
m.put("actions",
|
||||
"<div class=\"hstack gap-3 flex-wrap\">" +
|
||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||
+ "\" class=\"link-success btn-edit-privado fs-15\"><i class=\"ri-pencil-line\"></i></a>" +
|
||||
"</div>");
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
@ -822,23 +822,9 @@ public class PresupuestoService {
|
||||
// 3) Enriquecer el Presupuesto a persistir
|
||||
presupuesto.setEstado(Presupuesto.Estado.borrador);
|
||||
if (mode.equals("public")) {
|
||||
presupuesto.setOrigen(Presupuesto.Origen.publico);
|
||||
presupuesto.setSessionId(sessionId);
|
||||
// IP: guarda hash y trunc (si tienes campos). Si no, guarda tal cual en
|
||||
// ip_trunc/ip_hash según tu modelo.
|
||||
String ipTrunc = anonymizeIp(ip);
|
||||
presupuesto.setIpTrunc(ipTrunc);
|
||||
presupuesto.setIpHash(Integer.toHexString(ip.hashCode()));
|
||||
|
||||
// ubicación (si tienes un servicio GeoIP disponible; si no, omite estas tres
|
||||
// líneas)
|
||||
try {
|
||||
GeoIpService.GeoData geo = geoIpService.lookup(ip).orElse(null);
|
||||
presupuesto.setPais(geo.getPais());
|
||||
presupuesto.setRegion(geo.getRegion());
|
||||
presupuesto.setCiudad(geo.getCiudad());
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
presupuesto = getDatosLocalizacion(presupuesto, sessionId, ip);
|
||||
|
||||
} else
|
||||
presupuesto.setOrigen(Presupuesto.Origen.privado);
|
||||
|
||||
@ -878,6 +864,28 @@ public class PresupuestoService {
|
||||
return resumen;
|
||||
}
|
||||
|
||||
public Presupuesto getDatosLocalizacion(Presupuesto presupuesto, String sessionId, String ip) {
|
||||
|
||||
presupuesto.setOrigen(Presupuesto.Origen.publico);
|
||||
presupuesto.setSessionId(sessionId);
|
||||
// IP: guarda hash y trunc (si tienes campos). Si no, guarda tal cual en
|
||||
// ip_trunc/ip_hash según tu modelo.
|
||||
String ipTrunc = anonymizeIp(ip);
|
||||
presupuesto.setIpTrunc(ipTrunc);
|
||||
presupuesto.setIpHash(Integer.toHexString(ip.hashCode()));
|
||||
|
||||
// ubicación (si tienes un servicio GeoIP disponible; si no, omite estas tres
|
||||
// líneas)
|
||||
try {
|
||||
GeoIpService.GeoData geo = geoIpService.lookup(ip).orElse(null);
|
||||
presupuesto.setPais(geo.getPais());
|
||||
presupuesto.setRegion(geo.getRegion());
|
||||
presupuesto.setCiudad(geo.getCiudad());
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return presupuesto;
|
||||
}
|
||||
|
||||
public Presupuesto generateTotalizadores(
|
||||
Presupuesto presupuesto,
|
||||
List<Map<String, Object>> servicios,
|
||||
|
||||
@ -288,6 +288,7 @@ presupuesto.add.cancel=Cancelar
|
||||
presupuesto.add.select-client=Seleccione cliente
|
||||
presupuesto.add.error.options=Debe seleccionar una opción
|
||||
presupuesto.add.error.options-client=Debe seleccionar un cliente
|
||||
presupuesto.add.error.save.title=Error al guardar
|
||||
presupuesto.error.save-internal-error=No se puede guardar: error interno.
|
||||
presupuesto.exito.guardado=Presupuesto guardado con éxito.
|
||||
presupuesto.exito.guardado-actualizado=Presupuesto actualizado con éxito.
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import PresupuestoWizard from './wizard.js';
|
||||
|
||||
const app = new PresupuestoWizard({
|
||||
mode: 'private',
|
||||
readonly: false,
|
||||
canSave: true,
|
||||
useSessionCache: false,
|
||||
});
|
||||
app.init();
|
||||
@ -29,8 +29,8 @@ export default class PresupuestoWizard {
|
||||
ancho: 148,
|
||||
alto: 218,
|
||||
formatoPersonalizado: false,
|
||||
paginasNegro: 0,
|
||||
paginasColor: 32,
|
||||
paginasNegro: 32,
|
||||
paginasColor: 0,
|
||||
posicionPaginasColor: '',
|
||||
tipoEncuadernacion: 'fresado',
|
||||
},
|
||||
@ -245,7 +245,7 @@ export default class PresupuestoWizard {
|
||||
});
|
||||
|
||||
if (this.opts.canSave) {
|
||||
$('#btn-guardar').on('click', async () => {
|
||||
$('.guardar-presupuesto').on('click', async () => {
|
||||
|
||||
const alert = $('#form-errors');
|
||||
const servicios = [];
|
||||
@ -263,6 +263,7 @@ export default class PresupuestoWizard {
|
||||
mode: this.opts.mode,
|
||||
presupuesto: this.#getPresupuestoData(),
|
||||
servicios: servicios,
|
||||
cliente_id: $('#cliente_id').val() || null,
|
||||
};
|
||||
try {
|
||||
alert.addClass('d-none').find('ul').empty();
|
||||
@ -274,7 +275,7 @@ export default class PresupuestoWizard {
|
||||
}).then((data) => {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: window.languageBundle?.get('common.guardado') || 'Guardado',
|
||||
title: window.languageBundle?.get('presupuesto.exito.guardado') || 'Guardado',
|
||||
timer: 1800,
|
||||
buttonsStyling: false,
|
||||
customClass: {
|
||||
@ -307,7 +308,7 @@ export default class PresupuestoWizard {
|
||||
} catch (e) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error al guardar',
|
||||
title: window.languageBundle?.get('presupuesto.add.error.save.title') || 'Error',
|
||||
text: e?.message || '',
|
||||
buttonsStyling: false,
|
||||
customClass: {
|
||||
|
||||
@ -138,9 +138,9 @@ import { preguntarTipoPresupuesto } from './presupuesto-utils.js';
|
||||
if (!res) return;
|
||||
|
||||
if (res.tipo === 'anonimo') {
|
||||
console.log('Crear presupuesto ANÓNIMO');
|
||||
window.location.href = '/presupuesto/add/public';
|
||||
} else {
|
||||
console.log('Crear presupuesto de CLIENTE:', res.clienteId, res.clienteText);
|
||||
window.location.href = '/presupuesto/add/private/' + res.clienteId;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
th:if="${appMode == 'add' or appMode == 'edit'}"
|
||||
class="order-3 order-md-2 mx-md-auto d-flex">
|
||||
<button id="btn-guardar" type="button"
|
||||
class="btn btn-success d-flex align-items-center">
|
||||
class="btn btn-success d-flex align-items-center guardar-presupuesto">
|
||||
<i class="ri-save-3-line me-2"></i>
|
||||
<span th:text="#{presupuesto.guardar}">Guardar</span>
|
||||
</button>
|
||||
|
||||
@ -132,7 +132,7 @@
|
||||
<label for="paginas-negro" class="form-label" th:text="#{presupuesto.paginas-negro}">Páginas
|
||||
Negro</label>
|
||||
<input type="number" step="2" class="form-control paginas datos-generales-data"
|
||||
id="paginas-negro" name="paginas-negro" value="0">
|
||||
id="paginas-negro" name="paginas-negro" value="32">
|
||||
<div class="form-text" th:text="#{presupuesto.siempre-pares}">
|
||||
Siempre deben ser pares</div>
|
||||
</div>
|
||||
@ -145,7 +145,7 @@
|
||||
<label for="paginas-color" class="form-label" th:text="#{presupuesto.paginas-color}">Páginas
|
||||
Color</label>
|
||||
<input type="number" step="2" class="form-control paginas datos-generales-data"
|
||||
id="paginas-color" name="paginas-color" value="32">
|
||||
id="paginas-color" name="paginas-color" value="0">
|
||||
<div class="form-text" th:text="#{presupuesto.siempre-pares}">
|
||||
Siempre deben ser pares</div>
|
||||
</div>
|
||||
@ -253,8 +253,6 @@
|
||||
|
||||
<div class="d-flex align-items-center justify-content-center gap-3 mt-3">
|
||||
|
||||
<div th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode})}"></div>
|
||||
|
||||
<button type="button" id="next-datos-generales" class="btn btn-secondary d-flex align-items-center ms-auto order-2 order-md-3">
|
||||
<span th:text="#{presupuesto.continuar-interior}">Continuar a diseño interior</span>
|
||||
<i class="ri-arrow-right-circle-line fs-16 ms-2"></i>
|
||||
|
||||
@ -28,9 +28,12 @@
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||
<li class="breadcrumb-item"><a href="/presupuesto" th:text="#{presupuesto.title}"></a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page" th:text="#{presupuesto.editar.title}">
|
||||
Editar presupuesto
|
||||
</li>
|
||||
<li class="breadcrumb-item active" aria-current="page" th:if="${appMode == 'add'}" th:text="#{presupuesto.add}">
|
||||
Nuevo presupuesto
|
||||
</li>
|
||||
<li class="breadcrumb-item active" aria-current="page" th:text="#{presupuesto.editar.title}" th:if="${appMode == 'edit'}">
|
||||
Editar presupuesto
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
@ -59,7 +62,12 @@
|
||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-publicos.js}"></script>
|
||||
</div>
|
||||
<div th:if="${appMode} == 'add'">
|
||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-publicos-add.js}"></script>
|
||||
<div th:if="${mode} == 'public'">
|
||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-publicos-add.js}"></script>
|
||||
</div>
|
||||
<div th:if="${mode} != 'public'">
|
||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/wizard-privado.js}"></script>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
</body>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div th:fragment="tabla-cliente">
|
||||
<table id="presupuesto-clientes-datatable" class="table table-striped table-nowrap responsive w-100">
|
||||
<table id="presupuestos-clientes-datatable" class="table table-striped table-nowrap responsive w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.id}">ID</th>
|
||||
@ -16,7 +16,52 @@
|
||||
<th scope="col" th:text="#{presupuesto.tabla.acciones}">Acciones</th>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="id" /></th>
|
||||
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="titulo" /></th>
|
||||
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="user" />
|
||||
</th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm presupuesto-select-filter" data-col="tipoEncuadernacion">
|
||||
<option value="" th:text="#{margenes-presupuesto.todos}">Todos</option>
|
||||
<option value="fresado" th:text="#{presupuesto.fresado}">Fresado</option>
|
||||
<option value="cosido" th:text="#{presupuesto.cosido}">Cosido</option>
|
||||
<option value="espiral" th:text="#{presupuesto.espiral}">Espiral</option>
|
||||
<option value="wireo" th:text="#{presupuesto.wireo}">Wireo</option>
|
||||
<option value="grapado" th:text="#{presupuesto.grapado}">Grapado</option>
|
||||
</select>
|
||||
</th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm presupuesto-select-filter" data-col="tipoCubierta">
|
||||
<option value="" th:text="#{margenes-presupuesto.todos}">Todos</option>
|
||||
<option value="tapaBlanda" th:text="#{presupuesto.tapa-blanda}">Tapa blanda</option>
|
||||
<option value="tapaDura" th:text="#{presupuesto.tapa-dura}">Tapa dura</option>
|
||||
<option value="tapaDuraLomoRedondo" th:text="#{presupuesto.tapa-dura-lomo-redondo}">Tapa dura
|
||||
lomo redondo</option>
|
||||
</select>
|
||||
</th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm presupuesto-select-filter" data-col="tipoImpresion">
|
||||
<option value="" th:text="#{margenes-presupuesto.todos}">Todos</option>
|
||||
<option value="negro" th:text="#{presupuesto.blanco-negro}">B/N</option>
|
||||
<option value="negrohq" th:text="#{presupuesto.blanco-negro-premium}">B/N HQ</option>
|
||||
<option value="color" th:text="#{presupuesto.color}">Color</option>
|
||||
<option value="colorhq" th:text="#{presupuesto.color-premium}">Color HQ</option>
|
||||
</select>
|
||||
</th>
|
||||
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="selectedTirada" /></th>
|
||||
<th><input type="text" class="form-control form-control-sm presupuesto-filter" data-col="paginas" />
|
||||
</th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm presupuesto-select-filter" data-col="estado">
|
||||
<option value="" th:text="#{margenes-presupuesto.todos}">Todos</option>
|
||||
<option value="borrador" th:text="#{presupuesto.estado.borrador}">Borrador</option>
|
||||
<option value="aceptado" th:text="#{presupuesto.estado.aceptado}">Aceptado</option>
|
||||
<option value="modificado" th:text="#{presupuesto.estado.modificado}">Modificado</option>
|
||||
</select>
|
||||
</th>
|
||||
<th></th> <!-- Total con IVA (sin filtro) -->
|
||||
<th></th> <!-- Actualizado el (sin filtro) -->
|
||||
<th></th> <!-- Acciones (sin filtro) -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
Reference in New Issue
Block a user