trabajando en el delete

This commit is contained in:
2025-10-10 13:28:03 +02:00
parent 6c4b63daa6
commit d4d83fe118
11 changed files with 221 additions and 55 deletions

View File

@ -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; }
}

View File

@ -320,8 +320,6 @@ public class MargenPresupuestoController {
return repo.findById(id).map(u -> {
try {
u.setDeleted(true);
u.setDeletedAt(LocalDateTime.now());

View File

@ -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<String> keys = List.of();
List<String> 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<String, String> 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)));
}
}

View File

@ -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",
"<div class=\"hstack gap-3 flex-wrap\">" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
@ -182,6 +183,14 @@ public class PresupuestoDatatableService {
+ "\" 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;
}