Se puede seleccionar como admin el cliente del presupuesto como borrador

This commit is contained in:
2025-12-29 20:05:35 +01:00
parent 982423d766
commit 47866ddead
12 changed files with 6112 additions and 56 deletions

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,12 @@
<liquibase.version>4.29.2</liquibase.version> <liquibase.version>4.29.2</liquibase.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> <artifactId>spring-boot-starter-security</artifactId>

View File

@ -16,6 +16,7 @@ import java.util.Objects;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter; import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto; import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.service.PresupuestoService; import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
import com.imprimelibros.erp.users.UserService;
import com.imprimelibros.erp.cart.dto.CartDireccionRepository; import com.imprimelibros.erp.cart.dto.CartDireccionRepository;
import com.imprimelibros.erp.cart.dto.DireccionCardDTO; import com.imprimelibros.erp.cart.dto.DireccionCardDTO;
import com.imprimelibros.erp.cart.dto.DireccionShipment; import com.imprimelibros.erp.cart.dto.DireccionShipment;
@ -41,11 +42,12 @@ public class CartService {
private final skApiClient skApiClient; private final skApiClient skApiClient;
private final PresupuestoService presupuestoService; private final PresupuestoService presupuestoService;
private final PedidoRepository pedidoRepository; private final PedidoRepository pedidoRepository;
private final UserService userService;
public CartService(CartRepository cartRepo, CartItemRepository itemRepo, public CartService(CartRepository cartRepo, CartItemRepository itemRepo,
CartDireccionRepository cartDireccionRepo, MessageSource messageSource, CartDireccionRepository cartDireccionRepo, MessageSource messageSource,
PresupuestoFormatter presupuestoFormatter, PresupuestoRepository presupuestoRepo, PedidoRepository pedidoRepository, PresupuestoFormatter presupuestoFormatter, PresupuestoRepository presupuestoRepo, PedidoRepository pedidoRepository,
DireccionService direccionService, skApiClient skApiClient,PresupuestoService presupuestoService, EmailService emailService) { DireccionService direccionService, skApiClient skApiClient,PresupuestoService presupuestoService, EmailService emailService, UserService userService) {
this.cartRepo = cartRepo; this.cartRepo = cartRepo;
this.itemRepo = itemRepo; this.itemRepo = itemRepo;
this.cartDireccionRepo = cartDireccionRepo; this.cartDireccionRepo = cartDireccionRepo;
@ -56,6 +58,7 @@ public class CartService {
this.presupuestoService = presupuestoService; this.presupuestoService = presupuestoService;
this.emailService = emailService; this.emailService = emailService;
this.pedidoRepository = pedidoRepository; this.pedidoRepository = pedidoRepository;
this.userService = userService;
} }
public Cart findById(Long cartId) { public Cart findById(Long cartId) {
@ -420,6 +423,13 @@ public class CartService {
cart.setUserId(customerId); cart.setUserId(customerId);
cartRepo.save(cart); cartRepo.save(cart);
// Se mueven los presupuestos de cartitems a ese usuario
List<CartItem> items = itemRepo.findByCartId(cart.getId());
for (CartItem item : items) {
Presupuesto p = item.getPresupuesto();
p.setUser(userService.findById(customerId));
presupuestoRepo.save(p);
}
return true; return true;
} catch (Exception e) { } catch (Exception e) {

View File

@ -86,15 +86,34 @@ public class PedidoService {
} }
// Auditoría mínima // Auditoría mínima
Long userId = cart.getUserId(); /*Long userId = cart.getUserId();
pedido.setCreatedBy(userService.findById(userId)); pedido.setCreatedBy(userService.findById(userId));
pedido.setUpdatedBy(userService.findById(userId));
*/
// Se obtiene el usuario del primer presupuesto del carrito
Long userId = null;
List<CartItem> cartItems = cart.getItems();
if (!cartItems.isEmpty()) {
Presupuesto firstPresupuesto = cartItems.get(0).getPresupuesto();
if (firstPresupuesto != null) {
userId = firstPresupuesto.getUser().getId();
}
}
if(userId == null){
userId = cart.getUserId();
}
pedido.setCreatedBy(userService.findById(userId));
pedido.setUpdatedBy(userService.findById(userId));
pedido.setCreatedAt(Instant.now()); pedido.setCreatedAt(Instant.now());
pedido.setDeleted(false); pedido.setDeleted(false);
pedido.setUpdatedAt(Instant.now()); pedido.setUpdatedAt(Instant.now());
pedido.setUpdatedBy(userService.findById(userId));
// Guardamos el pedido // Guardamos el pedido
Pedido pedidoGuardado = pedidoRepository.save(pedido); Pedido pedidoGuardado = pedidoRepository.save(pedido);
pedidoGuardado.setCreatedBy(userService.findById(userId));
pedidoGuardado.setUpdatedBy(userService.findById(userId));
pedidoRepository.save(pedidoGuardado);
List<CartItem> items = cart.getItems(); List<CartItem> items = cart.getItems();

View File

@ -359,6 +359,7 @@ public class UserController {
@GetMapping(value = "api/get-users", produces = MediaType.APPLICATION_JSON_VALUE) @GetMapping(value = "api/get-users", produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> getUsers( public Map<String, Object> getUsers(
@RequestParam(required = false) String role, // puede venir ausente @RequestParam(required = false) String role, // puede venir ausente
@RequestParam(required = false) Boolean showUsername,
@RequestParam(required = false) String q, @RequestParam(required = false) String q,
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) { @RequestParam(defaultValue = "10") int size) {
@ -373,9 +374,15 @@ public class UserController {
.map(u -> { .map(u -> {
Map<String, Object> m = new HashMap<>(); Map<String, Object> m = new HashMap<>();
m.put("id", u.getId()); m.put("id", u.getId());
m.put("text", (u.getFullName() != null && !u.getFullName().isBlank()) if (showUsername != null && Boolean.TRUE.equals(showUsername)) {
? u.getFullName() m.put("text", (u.getFullName() != null && !u.getFullName().isBlank())
: u.getUserName()); ? u.getFullName() + " (" + u.getUserName() + ")"
: u.getUserName());
} else {
m.put("text", (u.getFullName() != null && !u.getFullName().isBlank())
? u.getFullName()
: u.getUserName());
}
return m; return m;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -385,4 +392,20 @@ public class UserController {
"pagination", Map.of("more", more)); "pagination", Map.of("more", more));
} }
@ResponseBody
@GetMapping(value = "api/get-user/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> getUser(@PathVariable Long id) {
User u = userService.findById(id);
if (u == null) {
return Map.of();
}
Map<String, Object> m = new HashMap<>();
m.put("id", u.getId());
m.put("userName", u.getUserName());
m.put("fullName", u.getFullName());
return m;
}
} }

View File

@ -49,6 +49,7 @@ presupuesto.comentario-administrador=Comentarios
presupuesto.informacion-libro=Información del libro presupuesto.informacion-libro=Información del libro
presupuesto.datos-generales-descripcion=Datos generales del presupuesto presupuesto.datos-generales-descripcion=Datos generales del presupuesto
presupuesto.titulo=Título* presupuesto.titulo=Título*
presupuesto.cliente=Cliente*
presupuesto.autor=Autor presupuesto.autor=Autor
presupuesto.isbn=ISBN presupuesto.isbn=ISBN
presupuesto.tirada=Tirada presupuesto.tirada=Tirada

View File

@ -332,7 +332,7 @@ export default class PresupuestoWizard {
servicios: this.formData.servicios.servicios, servicios: this.formData.servicios.servicios,
datosMaquetacion: this.formData.servicios.datosMaquetacion, datosMaquetacion: this.formData.servicios.datosMaquetacion,
datosMarcapaginas: this.formData.servicios.datosMarcapaginas, datosMarcapaginas: this.formData.servicios.datosMarcapaginas,
cliente_id: $('#cliente_id').val() || null, cliente_id: $('#user_id').val() || null,
}; };
try { try {
@ -1696,7 +1696,7 @@ export default class PresupuestoWizard {
const body = { const body = {
presupuesto: this.#getPresupuestoData(), presupuesto: this.#getPresupuestoData(),
save: this.opts.canSave, save: this.opts.mode == 'public' ? true : this.opts.canSave,
mode: this.opts.mode, mode: this.opts.mode,
servicios: servicios, servicios: servicios,
datosMaquetacion: this.formData.servicios.datosMaquetacion, datosMaquetacion: this.formData.servicios.datosMaquetacion,

View File

@ -0,0 +1,43 @@
$(() => {
// Inicializar select2 para el campo de cliente en el formulario de presupuesto
if ($('#user_id').length) {
$('#user_id').select2({
allowClear: false,
width: '100%',
ajax: {
url: '/users/api/get-users',
dataType: 'json',
data: function (params) {
return {
q: params.term, // término de búsqueda
page: params.page || 1,
size: 10,
showUsername: true
};
},
delay: 250,
processResults: function (data) {
return {
results: data.results || [],
pagination: data.pagination || { more: false }
};
},
cache: true
},
minimumInputLength: 0
});
// Si hay un valor inicial, cargar y establecer el usuario seleccionado
const initialUserId = $('#user_id').val();
if (initialUserId) {
$.ajax({
url: `/users/api/get-user/${initialUserId}`,
dataType: 'json'
}).then(function (data) {
const option = new Option(`${data.fullName} (${data.userName})`, data.id, true, true);
$('#user_id').append(option).trigger('change');
});
}
}
});

View File

@ -46,7 +46,7 @@
url: '/presupuesto/datatable/clientes', url: '/presupuesto/datatable/clientes',
method: 'GET', method: 'GET',
}, },
order: [[0, 'asc']], order: [[0, 'desc']],
columns: [ columns: [
{ data: 'id', name: 'id', orderable: true }, { data: 'id', name: 'id', orderable: true },
{ data: 'titulo', name: 'titulo', orderable: true }, { data: 'titulo', name: 'titulo', orderable: true },

View File

@ -48,7 +48,7 @@ import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-ut
url: '/presupuesto/datatable/anonimos', url: '/presupuesto/datatable/anonimos',
method: 'GET', method: 'GET',
}, },
order: [[0, 'asc']], order: [[0, 'desc']],
columns: [ columns: [
{ data: 'id', name: 'id', orderable: true }, { data: 'id', name: 'id', orderable: true },
{ data: 'titulo', name: 'titulo', orderable: true }, { data: 'titulo', name: 'titulo', orderable: true },
@ -174,7 +174,7 @@ import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-ut
url: '/presupuesto/datatable/clientes', url: '/presupuesto/datatable/clientes',
method: 'GET', method: 'GET',
}, },
order: [[0, 'asc']], order: [[0, 'desc']],
columns: [ columns: [
{ data: 'id', name: 'id', orderable: true }, { data: 'id', name: 'id', orderable: true },
{ data: 'user', name: 'user.fullName', orderable: true }, { data: 'user', name: 'user.fullName', orderable: true },

View File

@ -18,6 +18,28 @@
</div> </div>
<div class="px-2"> <div class="px-2">
<th:block th:if="${presupuesto?.user != null}">
<div sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')" class="row">
<div class="col-sm-12">
<div class="mb-3">
<label for="user_id" class="form-label" th:text="#{presupuesto.cliente}">
>Cliente*</label>
<select class="form-select select2 datos-generales-data" id="user_id">
<option
th:value="${presupuesto?.user.id} ?: ''"
th:text="${presupuesto.user != null ? presupuesto.user.fullName + ' (' + presupuesto.user.userName + ')' : ''}"
selected>
</option>
</select>
</div>
</div>
</div>
<div sec:authorize="isAuthenticated() and !hasAnyRole('SUPERADMIN','ADMIN')">
<input type="hidden" class="datos-generales-data" id="user_id"
th:value="${presupuesto?.user.id} ?: ''">
</div>
</th:block>
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="mb-3"> <div class="mb-3">

View File

@ -89,6 +89,8 @@
th:src="@{/assets/libs/quill/quill.min.js}"></script> th:src="@{/assets/libs/quill/quill.min.js}"></script>
<script sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')" <script sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')"
th:src="@{/assets/js/pages/imprimelibros/presupuestador/text-editor.js}"></script> th:src="@{/assets/js/pages/imprimelibros/presupuestador/text-editor.js}"></script>
<script sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')"
th:src="@{/assets/js/pages/imprimelibros/presupuestos/admin-utils.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>
</th:block> </th:block>