diff --git a/src/main/java/com/imprimelibros/erp/common/jpa/AbstractAuditedEntity.java b/src/main/java/com/imprimelibros/erp/common/jpa/AbstractAuditedEntity.java index d2227d3..a344f4e 100644 --- a/src/main/java/com/imprimelibros/erp/common/jpa/AbstractAuditedEntity.java +++ b/src/main/java/com/imprimelibros/erp/common/jpa/AbstractAuditedEntity.java @@ -46,6 +46,11 @@ public abstract class AbstractAuditedEntity { @Column(name = "deleted_at") private Instant deletedAt; + @LastModifiedBy + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "deleted_by") + private User deletedBy; + // Getters/Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } @@ -67,4 +72,7 @@ public abstract class AbstractAuditedEntity { public Instant getDeletedAt() { return deletedAt; } public void setDeletedAt(Instant deletedAt) { this.deletedAt = deletedAt; } + + public User getDeletedBy() { return deletedBy; } + public void setDeletedBy(User deletedBy) { this.deletedBy = deletedBy; } } diff --git a/src/main/java/com/imprimelibros/erp/configuracion/margenes_presupuestos/MargenPresupuestoController.java b/src/main/java/com/imprimelibros/erp/configuracion/margenes_presupuestos/MargenPresupuestoController.java index a1a14dc..7982295 100644 --- a/src/main/java/com/imprimelibros/erp/configuracion/margenes_presupuestos/MargenPresupuestoController.java +++ b/src/main/java/com/imprimelibros/erp/configuracion/margenes_presupuestos/MargenPresupuestoController.java @@ -320,8 +320,6 @@ public class MargenPresupuestoController { return repo.findById(id).map(u -> { try { - - u.setDeleted(true); u.setDeletedAt(LocalDateTime.now()); diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java index 76c90b9..281ba69 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java @@ -1,7 +1,11 @@ package com.imprimelibros.erp.presupuesto; import org.springframework.stereotype.Controller; +import org.springframework.transaction.annotation.Transactional; import org.springframework.ui.Model; + +import java.time.Instant; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -21,12 +25,15 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.web.server.ResponseStatusException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.core.type.TypeReference; @@ -40,6 +47,9 @@ import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas; import com.imprimelibros.erp.presupuesto.dto.Presupuesto; import com.imprimelibros.erp.presupuesto.service.PresupuestoService; import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups; +import com.imprimelibros.erp.users.UserDao; +import com.imprimelibros.erp.users.User; +import com.imprimelibros.erp.users.UserDetailsImpl; import com.imprimelibros.erp.presupuesto.service.PresupuestoFormDataMapper; import com.imprimelibros.erp.presupuesto.service.PresupuestoFormDataMapper.PresupuestoFormDataDto; @@ -66,16 +76,18 @@ public class PresupuestoController { private final PresupuestoDatatableService dtService; private final VariableService variableService; private final PresupuestoFormDataMapper formDataMapper; + private final UserDao userRepo; public PresupuestoController(ObjectMapper objectMapper, TranslationService translationService, PresupuestoDatatableService dtService, PresupuestoRepository presupuestoRepository, - VariableService variableService, PresupuestoFormDataMapper formDataMapper) { + VariableService variableService, PresupuestoFormDataMapper formDataMapper, UserDao userRepo) { this.objectMapper = objectMapper; this.translationService = translationService; this.dtService = dtService; this.presupuestoRepository = presupuestoRepository; this.variableService = variableService; this.formDataMapper = formDataMapper; + this.userRepo = userRepo; } @PostMapping("/public/validar/datos-generales") @@ -122,7 +134,6 @@ public class PresupuestoController { errores.put(error.getField(), msg); }); - // errores globales (@ConsistentTiradas...) result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage())); @@ -151,7 +162,6 @@ public class PresupuestoController { errores.put(error.getField(), msg); }); - // errores globales (@ConsistentTiradas...) result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage())); @@ -187,7 +197,6 @@ public class PresupuestoController { errores.put(error.getField(), msg); }); - // errores globales (@ConsistentTiradas...) result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage())); @@ -219,7 +228,6 @@ public class PresupuestoController { errores.put(error.getField(), msg); }); - if (!errores.isEmpty()) { return ResponseEntity.badRequest().body(errores); } @@ -271,7 +279,6 @@ public class PresupuestoController { errores.put(error.getField(), msg); }); - if (!errores.isEmpty()) { return ResponseEntity.badRequest().body(errores); } @@ -305,7 +312,6 @@ public class PresupuestoController { errores.put(error.getField(), msg); }); - if (!errores.isEmpty()) { return ResponseEntity.badRequest().body(errores); } @@ -484,7 +490,15 @@ public class PresupuestoController { @GetMapping public String getPresupuestoList(Model model, Authentication authentication, Locale locale) { - List keys = List.of(); + List keys = List.of( + "presupuesto.delete.title", + "presupuesto.delete.text", + "presupuesto.eliminar", + "presupuesto.delete.button", + "app.yes", + "app.cancelar", + "presupuesto.delete.ok.title", + "presupuesto.delete.ok.text"); Map translations = translationService.getTranslations(locale, keys); model.addAttribute("languageBundle", translations); @@ -570,4 +584,55 @@ public class PresupuestoController { return dtService.datatableAnonimos(dt, locale); } + @DeleteMapping("/{id}") + @Transactional + public ResponseEntity delete(@PathVariable Long id, Authentication auth, Locale locale) { + + Presupuesto p = presupuestoRepository.findById(id) + .orElseThrow(() -> new ResponseStatusException( + HttpStatus.NOT_FOUND, + messageSource.getMessage("presupuesto.error.not-found", null, locale))); + + boolean isUser = auth != null && auth.getAuthorities().stream() + .anyMatch(a -> a.getAuthority().equals("ROLE_USER")); + + // compara por IDs (no uses equals entre tipos distintos) + Long ownerId = p.getUser() != null ? p.getUser().getId() : null; + + User currentUser = null; + Long currentUserId = null; + if (auth != null && auth.getPrincipal() instanceof UserDetailsImpl udi) { + currentUserId = udi.getId(); + currentUser = userRepo.findById(currentUserId).orElse(null); + } else if (auth != null) { + currentUserId = userRepo.findIdByUserNameIgnoreCase(auth.getName()).orElse(null); // fallback + currentUser = userRepo.findById(currentUserId).orElse(null); + } + + boolean isOwner = ownerId != null && ownerId.equals(currentUserId); + + if (isUser && !isOwner) { + throw new ResponseStatusException( + HttpStatus.FORBIDDEN, + messageSource.getMessage("presupuesto.error.delete-permission-denied", null, locale)); + } + + if (p.getEstado() != null && !p.getEstado().equals(Presupuesto.Estado.borrador)) { + throw new ResponseStatusException( + HttpStatus.FORBIDDEN, + messageSource.getMessage("presupuesto.error.delete-not-draft", null, locale)); + } + + // SOFT DELETE (no uses deleteById) + p.setDeleted(true); + p.setDeletedAt(Instant.now()); + p.setDeletedBy(currentUser); + + presupuestoRepository.save(p); + return ResponseEntity.ok(Map.of("message", + messageSource.getMessage("presupuesto.exito.eliminado", null, locale))); + + + } + } diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoDatatableService.java b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoDatatableService.java index de4774a..ea2205c 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoDatatableService.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoDatatableService.java @@ -174,6 +174,7 @@ 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", "
" + "" + "
"); + } + else{ + m.put("actions", + "
" + + "" + + "
"); + } return m; } diff --git a/src/main/resources/i18n/presupuesto_es.properties b/src/main/resources/i18n/presupuesto_es.properties index c691497..3c3bd35 100644 --- a/src/main/resources/i18n/presupuesto_es.properties +++ b/src/main/resources/i18n/presupuesto_es.properties @@ -1,5 +1,6 @@ presupuesto.title=Presupuestos presupuesto.editar.title=Editar presupuesto +presupuesto.eliminar=Eliminar presupuesto presupuesto.datos-generales=Datos Generales presupuesto.interior=Interior presupuesto.cubierta=Cubierta @@ -265,6 +266,18 @@ presupuesto.marcapaginas.acabado.plastificado-mate-2c=Plastificado mate 2/C presupuesto.marcapaginas.precio-unidad=Precio por unidad presupuesto.marcapaginas.precio-total=Precio total +# Mensajes de eliminación de presupuesto +presupuesto.delete.title=Eliminar presupuesto +presupuesto.delete.button=Si, ELIMINAR +presupuesto.delete.text=¿Está seguro de que desea eliminar este presupuesto?
Esta acción no se puede deshacer. +presupuesto.delete.ok.title=Presupuesto eliminado +presupuesto.delete.ok.text=El presupuesto ha sido eliminado con éxito. +presupuesto.exito.eliminado=Presupuesto eliminado con éxito. +presupuesto.error.delete-internal-error=No se puede eliminar: error interno. +presupuesto.error.delete-permission-denied=No se puede eliminar: permiso denegado. +presupuesto.error.delete-not-found=No se puede eliminar: presupuesto no encontrado. +presupuesto.error.delete-not-draft=Solo se pueden eliminar presupuestos en estado Borrador. + # Errores presupuesto.errores-title=Corrija los siguientes errores: presupuesto.errores.titulo=El título es obligatorio @@ -291,4 +304,6 @@ presupuesto.errores.acabado-cubierta=Seleccione el acabado de la cubierta presupuesto.errores.presupuesto-no-existe=El presupuesto con ID {0} no existe. presupuesto.errores.presupuesto-maquetacion=No se pudo calcular el presupuesto de maquetación. -presupuesto.errores.presupuesto-marcapaginas=No se pudo calcular el presupuesto de marcapáginas. \ No newline at end of file +presupuesto.errores.presupuesto-marcapaginas=No se pudo calcular el presupuesto de marcapáginas. + +presupuesto.info.presupuestos-anonimos-view=Estos presupuestos son anónimos y no están asociados a ningún cliente. No se guardarán los cambios (sólo consulta). \ No newline at end of file diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/imagen-selector.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/imagen-selector.js index 11bfff5..ba56c38 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/imagen-selector.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/imagen-selector.js @@ -1,20 +1,24 @@ +$('.imagen-container-group').on('click', '.image-container', function (e) { + e.preventDefault(); // <- evita que el label dispare el cambio nativo (2º change) + e.stopPropagation(); -$('.imagen-container-group').on('click', '.image-container', function () { const clicked = $(this); const group = clicked.closest('.imagen-container-group'); - // Limpiar selección anterior + // Si ya está seleccionado, no hagas nada + const $radio = clicked.find('input[type="radio"]'); + if ($radio.prop('checked')) return; + + // Limpiar selección anterior (solo clases/animación) group.find('.image-container').removeClass('selected') .find('.image-presupuesto').removeClass('zoom-anim'); - // Marcar nuevo seleccionado + // Marcar nuevo seleccionado (clases/animación) clicked.addClass('selected'); - - // Aplicar animación de zoom const img = clicked.find('.image-presupuesto'); - void img[0].offsetWidth; // Forzar reflow + void img[0].offsetWidth; img.addClass('zoom-anim'); - - clicked.find('input[type="radio"]').prop('checked', true).trigger('change'); + // Marca el radio y dispara UN único change + $radio.prop('checked', true).trigger('change'); }); diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js index 8bcbb99..9655687 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js @@ -173,6 +173,9 @@ export default class PresupuestoWizard { this.summaryTableSobrecubierta = $('#summary-sobrecubierta'); this.summaryTableFaja = $('#summary-faja'); this.summaryTableExtras = $('#summary-servicios-extras'); + + // variable para evitar disparar eventos al cargar datos + this._hydrating = false; } async init() { @@ -181,9 +184,10 @@ export default class PresupuestoWizard { const mode = root?.dataset.mode || 'public'; const presupuestoId = root?.dataset.id || null; - this.opts = { mode, presupuestoId }; - - const stored = sessionStorage.getItem("formData"); + let stored = null; + if(this.opts.useSessionCache) { + stored = sessionStorage.getItem("formData"); + } this.#initDatosGenerales(); @@ -261,18 +265,11 @@ export default class PresupuestoWizard { }); } - // Limpiar el sessionStorage al salir de la página - window.addEventListener('beforeunload', () => { - sessionStorage.removeItem('formData'); - }); } #cacheFormData() { if (!this.opts.useSessionCache) return; - const key = this.opts.mode === 'edit' && this.opts.presupuestoId - ? `formData:edit:${this.opts.presupuestoId}` - : `formData:${this.opts.mode}`; - sessionStorage.setItem(key, JSON.stringify(this.formData)); + sessionStorage.setItem('formData', JSON.stringify(this.formData)); } #changeTab(idContenidoTab) { @@ -382,8 +379,9 @@ export default class PresupuestoWizard { // Eventos para el resumen $(document).on('change', 'input[name="tipoEncuadernacion"]', (e) => { + if ($(e.target).is(':checked')) { - // Actualizar el resumen + Summary.updateEncuadernacion(); } }); @@ -474,7 +472,7 @@ export default class PresupuestoWizard { paginasNegro: this.paginasNegro.val(), paginasColor: this.paginasColor.val(), posicionPaginasColor: this.posicionPaginasColor.val(), - tipoEncuadernacion: ($('.tipo-libro.selected').length > 0) ? $('.tipo-libro.selected').attr('id') : 'fresado', + tipoEncuadernacion: $('.tipo-libro input:checked').val() || 'fresado', }; } @@ -519,7 +517,6 @@ export default class PresupuestoWizard { this.alto.val(this.formData.datosGenerales.alto); } - $('.tipo-libro').removeClass('selected'); $('input[name="tipoEncuadernacion"][value="' + this.formData.datosGenerales.tipoEncuadernacion + '"]') .prop('checked', true); this.#updateTipoEncuadernacion(); @@ -565,7 +562,7 @@ export default class PresupuestoWizard { #updateTipoEncuadernacion() { const paginas = parseInt($('#paginas').val()); - const selectedTipo = $('.tipo-libro.selected').attr('id'); + const selectedTipo = $('.tipo-libro input:checked').val(); $('.tipo-libro').removeClass('selected'); if (paginas < 32) { @@ -591,19 +588,17 @@ export default class PresupuestoWizard { $('.tipo-libro#grapado').removeClass('d-none'); } - if (selectedTipo && $('.tipo-libro#' + selectedTipo).length > 0 && !$('.tipo-libro#' + selectedTipo).hasClass('d-none')) { - $('.tipo-libro#' + selectedTipo).addClass('selected'); - } - else { + if (!(selectedTipo && $('.tipo-libro#' + selectedTipo).length > 0 && !$('.tipo-libro#' + selectedTipo).hasClass('d-none'))) { + let firstVisible = $('.tipo-libro').not('.d-none').first(); if (firstVisible.length) { - firstVisible.addClass('selected'); + firstVisible.trigger('click'); } } - if ($('.tipo-libro.selected').length > 0) { - this.formData.datosGenerales.tipoEncuadernacion = $('.tipo-libro.selected').attr('id'); + if ($('.tipo-libro input:checked').length > 0) { + this.formData.datosGenerales.tipoEncuadernacion = $('.tipo-libro input:checked').val(); Summary.updateEncuadernacion(); } else { @@ -629,6 +624,8 @@ export default class PresupuestoWizard { $(document).on('change', 'input[name="tipoImpresion"]', (e) => { + if (!$(e.target).is(':checked')) + return; const data = this.#getPresupuestoData(); Summary.updateTipoImpresion(); @@ -681,6 +678,10 @@ export default class PresupuestoWizard { $(document).on('change', 'input[name="papelInterior"]', (e) => { + + if (!$(e.target).is(':checked')) + return; + const data = this.#getPresupuestoData(); Summary.updatePapelInterior(); @@ -951,6 +952,12 @@ export default class PresupuestoWizard { $(document).on('change', 'input[name="tipoCubierta"]', (e) => { + if (!$(e.target).is(':checked')) + return; + + if(this._hydrating) + return; + $('.tapa-dura-options').eq(0).removeClass('animate-fadeInUpBounce'); $('.tapa-blanda-options').eq(0).removeClass('animate-fadeInUpBounce'); @@ -971,6 +978,9 @@ export default class PresupuestoWizard { $(document).on('change', 'input[name="solapasCubierta"]', (e) => { + if (!$(e.target).is(':checked')) + return; + if (e.currentTarget.closest('.image-container').id === 'sin-solapas') { this.divSolapasCubierta.addClass('d-none'); } @@ -986,7 +996,7 @@ export default class PresupuestoWizard { }); - $(document).on('click', '.papel-cubierta', (e) => { + $(document).on('change', 'input[name="papel-cubierta"]', (e) => { const data = this.#getPresupuestoData(); @@ -1026,6 +1036,9 @@ export default class PresupuestoWizard { $(document).on('change', '.datos-cubierta', (e) => { + if(this._hydrating) + return; + const dataToStore = this.#getCubiertaData(); this.#updateCubiertaData(dataToStore); this.#cacheFormData(); @@ -1165,13 +1178,13 @@ export default class PresupuestoWizard { if (item.extraData["sk-id"] == this.formData.cubierta.papelCubiertaId) { item.setSelected(true); } + item.group='papel-cubierta'; this.divPapelCubierta.append(item.render()); } - if (this.divPapelCubierta.find('.image-container.selected').length === 0) { - this.divPapelCubierta.find('.image-container').first().addClass('selected'); - this.formData.cubierta.papelCubiertaId = - this.divPapelCubierta.find('.image-container').first().data('sk-id') || 3; + if (this.divPapelCubierta.find('input[name="papel-cubierta"]:checked').length === 0) { + + this.divPapelCubierta.find('input[name="papel-cubierta"]').first().prop('checked', true).trigger('change'); } this.#addGramajesCubierta(data.opciones_gramaje_cubierta); @@ -1192,13 +1205,13 @@ export default class PresupuestoWizard { #getCubiertaData() { - const tipoCubierta = $('.tapa-cubierta.selected').attr('id') || 'tapaBlanda'; - const solapas = $('.solapas-cubierta.selected').id == 'sin-solapas' ? 0 : 1 || 0; + const tipoCubierta = $('.tapa-cubierta input:checked').val() || 'tapaBlanda'; + const solapas = $('.solapas-cubierta input:checked').val() == 'sin-solapas' ? 0 : 1 || 0; const tamanioSolapasCubierta = $('#tamanio-solapas-cubierta').val() || '80'; const cubiertaCaras = parseInt(this.carasImpresionCubierta.val()) || 2; const papelGuardasId = parseInt($('#papel-guardas option:selected').data('papel-id')) || 3; const gramajeGuardas = parseInt($('#papel-guardas option:selected').data('gramaje')) || 170; - const guardasImpresas = parseInt(this.guardasImpresas) || 0; + const guardasImpresas = parseInt(this.guardasImpresas.val()) || 0; const cabezada = this.cabezada.val() || 'WHI'; const papelCubiertaId = $('#div-papel-cubierta .image-container input:checked').parent().data('sk-id') || this.formData.cubierta.papelCubiertaId || 3; const gramajeCubierta = $('input[name="gramaje-cubierta"]:checked').data('gramaje') || this.formData.cubierta.gramajeCubierta || 170; @@ -1279,7 +1292,7 @@ export default class PresupuestoWizard { for (let i = 0; i < gramajes.length; i++) { const gramaje = gramajes[i]; - this.#addGramaje(this.divGramajeCubierta, 'gramaje-cubierta datos-cubierta', gramaje, 'gramaje-cubierta'); + this.#addGramaje(this.divGramajeCubierta, 'gramaje-cubierta', gramaje, 'gramaje-cubierta'); // Seleccionar el gramaje por defecto if (this.formData.cubierta.gramajeCubierta === '' && i === 0) { @@ -1298,9 +1311,11 @@ export default class PresupuestoWizard { #loadCubiertaData() { + this._hydrating = true; + $('input[name="tipoCubierta"][value="' + this.formData.cubierta.tipoCubierta + '"]') .prop('checked', true); - + if (this.formData.cubierta.tipoCubierta === 'tapaBlanda') { $('.tapa-blanda-options').removeClass('d-none'); $('.tapa-dura-options').addClass('d-none'); @@ -1315,15 +1330,16 @@ export default class PresupuestoWizard { this.cabezada.val(this.formData.cubierta.cabezada); } + this._hydrating = false; + $('input[name="tipoCubierta"][value="' + this.formData.cubierta.tipoCubierta + '"]').trigger('change'); if (this.formData.cubierta.solapasCubierta === 0) { - $('.solapas-cubierta#sin-solapas').addClass('selected'); + $('.solapas-cubierta#sin-solapas input').prop('checked', true); this.divSolapasCubierta.addClass('d-none'); } else { - $('.solapas-cubierta').removeClass('selected'); - $(`.solapas-cubierta#con-solapas`).addClass('selected'); + $('.solapas-cubierta#con-solapas input').prop('checked', true); this.divSolapasCubierta.removeClass('d-none'); this.carasImpresionCubierta.val(this.formData.cubierta.cubiertaCaras); this.tamanioSolapasCubierta.val(this.formData.cubierta.tamanioSolapasCubierta); diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/list.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/list.js index c45ea46..5ca5ffe 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/list.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/list.js @@ -67,6 +67,7 @@ }); $('#presupuestos-anonimos-datatable').on('click', '.btn-edit-anonimo', function (e) { + e.preventDefault(); const id = $(this).data('id'); if (id) { @@ -74,5 +75,48 @@ } }); + $('#presupuestos-anonimos-datatable').on('click', '.btn-delete-anonimo', function (e) { + + e.preventDefault(); + const id = $(this).data('id'); + + Swal.fire({ + title: window.languageBundle.get(['presupuesto.delete.title']) || 'Eliminar presupuesto', + html: window.languageBundle.get(['presupuesto.delete.text']) || 'Esta acción no se puede deshacer.', + icon: 'warning', + showCancelButton: true, + buttonsStyling: false, + customClass: { + confirmButton: 'btn btn-danger w-xs mt-2', + cancelButton: 'btn btn-light w-xs mt-2' + }, + confirmButtonText: window.languageBundle.get(['presupuesto.delete.button']) || 'Eliminar', + cancelButtonText: window.languageBundle.get(['app.cancelar']) || 'Cancelar', + }).then((result) => { + if (!result.isConfirmed) return; + + $.ajax({ + url: '/presupuesto/' + id, + type: 'DELETE', + success: function () { + Swal.fire({ + icon: 'success', title: window.languageBundle.get(['presupuesto.delete.ok.title']) || 'Eliminado', + text: window.languageBundle.get(['presupuesto.delete.ok.text']) || 'El presupuesto ha sido eliminado con éxito.', + showConfirmButton: true, + customClass: { + confirmButton: 'btn btn-secondary w-xs mt-2', + }, + }); + $('#presupuestos-anonimos-datatable').DataTable().ajax.reload(null, false); + }, + error: function (xhr) { + // usa el mensaje del backend; fallback genérico por si no llega JSON + const msg = (xhr.responseJSON && xhr.responseJSON.message) + || 'Error al eliminar el presupuesto.'; + Swal.fire({ icon: 'error', title: 'No se pudo eliminar', text: msg }); + } + }); + }); + }); })(); diff --git a/src/main/resources/templates/imprimelibros/partials/sidebar.html b/src/main/resources/templates/imprimelibros/partials/sidebar.html index 50bc809..3dc8e52 100644 --- a/src/main/resources/templates/imprimelibros/partials/sidebar.html +++ b/src/main/resources/templates/imprimelibros/partials/sidebar.html @@ -60,7 +60,7 @@
diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador.html index ac80c98..1886b29 100644 --- a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador.html +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador.html @@ -17,6 +17,7 @@
+