mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 00:48:49 +00:00
227 lines
8.8 KiB
Java
227 lines
8.8 KiB
Java
package com.imprimelibros.erp.facturacion.controller;
|
|
|
|
import com.imprimelibros.erp.datatables.DataTable;
|
|
import com.imprimelibros.erp.datatables.DataTablesParser;
|
|
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
|
import com.imprimelibros.erp.datatables.DataTablesResponse;
|
|
import com.imprimelibros.erp.facturacion.SerieFactura;
|
|
import com.imprimelibros.erp.facturacion.TipoSerieFactura;
|
|
import com.imprimelibros.erp.facturacion.repo.SerieFacturaRepository;
|
|
import com.imprimelibros.erp.i18n.TranslationService;
|
|
|
|
import jakarta.persistence.EntityNotFoundException;
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
|
|
import org.springframework.context.MessageSource;
|
|
import org.springframework.data.jpa.domain.Specification;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.security.access.prepost.PreAuthorize;
|
|
import org.springframework.stereotype.Controller;
|
|
import org.springframework.ui.Model;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.time.Instant;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
|
|
@Controller
|
|
@RequestMapping("/configuracion/series-facturacion")
|
|
@PreAuthorize("hasRole('SUPERADMIN')")
|
|
public class SeriesFacturacionController {
|
|
|
|
private final SerieFacturaRepository repo;
|
|
private final TranslationService translationService;
|
|
private final MessageSource messageSource;
|
|
|
|
public SeriesFacturacionController(SerieFacturaRepository repo, TranslationService translationService,
|
|
MessageSource messageSource) {
|
|
this.repo = repo;
|
|
this.translationService = translationService;
|
|
this.messageSource = messageSource;
|
|
}
|
|
|
|
// -----------------------------
|
|
// VISTA
|
|
// -----------------------------
|
|
@GetMapping
|
|
public String listView(Model model, Locale locale) {
|
|
|
|
List<String> keys = List.of(
|
|
"series-facturacion.modal.title.add",
|
|
"series-facturacion.modal.title.edit",
|
|
"app.guardar",
|
|
"app.cancelar",
|
|
"app.eliminar",
|
|
"series-facturacion.delete.title",
|
|
"series-facturacion.delete.text",
|
|
"series-facturacion.delete.ok.title",
|
|
"series-facturacion.delete.ok.text");
|
|
|
|
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
|
model.addAttribute("languageBundle", translations);
|
|
|
|
return "imprimelibros/configuracion/series-facturas/series-facturas-list";
|
|
}
|
|
|
|
// -----------------------------
|
|
// API: DataTables (server-side)
|
|
// -----------------------------
|
|
@GetMapping("/api/datatables")
|
|
@ResponseBody
|
|
public DataTablesResponse<Map<String, Object>> datatables(HttpServletRequest request, Locale locale) {
|
|
|
|
DataTablesRequest dt = DataTablesParser.from(request);
|
|
|
|
Specification<SerieFactura> notDeleted = (root, q, cb) -> cb.isNull(root.get("deletedAt"));
|
|
long total = repo.count(notDeleted);
|
|
|
|
return DataTable
|
|
.of(repo, SerieFactura.class, dt, List.of("nombreSerie", "prefijo"))
|
|
.where(notDeleted)
|
|
.orderable(List.of("id", "nombreSerie", "prefijo", "tipo", "numeroActual"))
|
|
.onlyAddedColumns()
|
|
.add("id", SerieFactura::getId)
|
|
.add("nombre_serie", SerieFactura::getNombreSerie)
|
|
.add("prefijo", SerieFactura::getPrefijo)
|
|
.add("tipo", s -> s.getTipo() != null ? s.getTipo().name() : null)
|
|
.add("tipo_label", s -> {
|
|
if (s.getTipo() == null)
|
|
return null;
|
|
return messageSource.getMessage(
|
|
"series-facturacion.tipo." + s.getTipo().name(),
|
|
null,
|
|
s.getTipo().name(),
|
|
locale);
|
|
})
|
|
.add("numero_actual", SerieFactura::getNumeroActual)
|
|
.add("actions", s -> """
|
|
<div class="hstack gap-3 flex-wrap">
|
|
<button type="button"
|
|
class="btn p-0 link-success btn-edit-serie fs-15"
|
|
data-id="%d">
|
|
<i class="ri-edit-2-line"></i>
|
|
</button>
|
|
<button type="button"
|
|
class="btn p-0 link-danger btn-delete-serie fs-15"
|
|
data-id="%d">
|
|
<i class="ri-delete-bin-5-line"></i>
|
|
</button>
|
|
</div>
|
|
""".formatted(s.getId(), s.getId()))
|
|
.toJson(total);
|
|
}
|
|
|
|
// -----------------------------
|
|
// API: CREATE
|
|
// -----------------------------
|
|
@PostMapping(value = "/api", consumes = "application/json")
|
|
@ResponseBody
|
|
public Map<String, Object> create(@RequestBody SerieFacturaPayload payload) {
|
|
validate(payload);
|
|
|
|
SerieFactura s = new SerieFactura();
|
|
s.setNombreSerie(payload.nombre_serie.trim());
|
|
s.setPrefijo(payload.prefijo.trim());
|
|
s.setTipo(TipoSerieFactura.facturacion); // fijo
|
|
s.setNumeroActual(payload.numero_actual);
|
|
|
|
repo.save(s);
|
|
return Map.of("ok", true, "id", s.getId());
|
|
}
|
|
|
|
// -----------------------------
|
|
// API: UPDATE
|
|
// -----------------------------
|
|
@PutMapping(value = "/api/{id}", consumes = "application/json")
|
|
@ResponseBody
|
|
public Map<String, Object> update(@PathVariable Long id, @RequestBody SerieFacturaPayload payload) {
|
|
validate(payload);
|
|
|
|
SerieFactura s = repo.findById(id)
|
|
.orElseThrow(() -> new EntityNotFoundException("Serie no encontrada: " + id));
|
|
|
|
if (s.getDeletedAt() != null) {
|
|
throw new IllegalStateException("No se puede editar una serie eliminada.");
|
|
}
|
|
|
|
s.setNombreSerie(payload.nombre_serie.trim());
|
|
s.setPrefijo(payload.prefijo.trim());
|
|
s.setTipo(TipoSerieFactura.facturacion);
|
|
s.setNumeroActual(payload.numero_actual);
|
|
|
|
repo.save(s);
|
|
return Map.of("ok", true);
|
|
}
|
|
|
|
// -----------------------------
|
|
// API: DELETE (soft)
|
|
// -----------------------------
|
|
@DeleteMapping("/api/{id}")
|
|
@ResponseBody
|
|
public ResponseEntity<?> delete(@PathVariable Long id) {
|
|
SerieFactura s = repo.findById(id)
|
|
.orElseThrow(() -> new EntityNotFoundException("Serie no encontrada: " + id));
|
|
|
|
if (s.getDeletedAt() == null) {
|
|
s.setDeletedAt(Instant.now());
|
|
s.setDeletedBy(null); // luego lo conectamos al usuario actual
|
|
repo.save(s);
|
|
}
|
|
|
|
return ResponseEntity.ok(Map.of("ok", true));
|
|
}
|
|
|
|
// -----------------------------
|
|
// API: GET for select2
|
|
// -----------------------------
|
|
@GetMapping("/api/get-series")
|
|
@ResponseBody
|
|
public Map<String, Object> getSeriesForSelect(
|
|
@RequestParam(value = "q", required = false) String q1,
|
|
@RequestParam(value = "term", required = false) String q2,
|
|
Locale locale) {
|
|
String query = (q1 != null && !q1.isBlank()) ? q1
|
|
: (q2 != null && !q2.isBlank()) ? q2
|
|
: "";
|
|
List<Map<String, Object>> results = repo.searchForSelectSeriesFacturacion(query).stream()
|
|
.map(s -> {
|
|
Map<String, Object> m = new HashMap<>();
|
|
m.put("id", s.getId());
|
|
m.put("text", s.getNombreSerie());
|
|
return m;
|
|
})
|
|
.toList();
|
|
|
|
return Map.of("results", results);
|
|
}
|
|
|
|
// -----------------------------
|
|
// Payload + validación
|
|
// -----------------------------
|
|
public static class SerieFacturaPayload {
|
|
public String nombre_serie;
|
|
public String prefijo;
|
|
public String tipo; // lo manda UI, pero en backend lo fijamos
|
|
public Long numero_actual;
|
|
}
|
|
|
|
private void validate(SerieFacturaPayload p) {
|
|
if (p == null)
|
|
throw new IllegalArgumentException("Body requerido.");
|
|
if (p.nombre_serie == null || p.nombre_serie.trim().isBlank()) {
|
|
throw new IllegalArgumentException("nombre_serie es obligatorio.");
|
|
}
|
|
if (p.prefijo == null || p.prefijo.trim().isBlank()) {
|
|
throw new IllegalArgumentException("prefijo es obligatorio.");
|
|
}
|
|
if (p.prefijo.trim().length() > 10) {
|
|
throw new IllegalArgumentException("prefijo máximo 10 caracteres.");
|
|
}
|
|
if (p.numero_actual == null || p.numero_actual < 1) {
|
|
throw new IllegalArgumentException("numero_actual debe ser >= 1.");
|
|
}
|
|
}
|
|
}
|