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>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<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.dto.Presupuesto;
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.DireccionCardDTO;
import com.imprimelibros.erp.cart.dto.DireccionShipment;
@ -41,11 +42,12 @@ public class CartService {
private final skApiClient skApiClient;
private final PresupuestoService presupuestoService;
private final PedidoRepository pedidoRepository;
private final UserService userService;
public CartService(CartRepository cartRepo, CartItemRepository itemRepo,
CartDireccionRepository cartDireccionRepo, MessageSource messageSource,
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.itemRepo = itemRepo;
this.cartDireccionRepo = cartDireccionRepo;
@ -56,6 +58,7 @@ public class CartService {
this.presupuestoService = presupuestoService;
this.emailService = emailService;
this.pedidoRepository = pedidoRepository;
this.userService = userService;
}
public Cart findById(Long cartId) {
@ -420,6 +423,13 @@ public class CartService {
cart.setUserId(customerId);
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;
} catch (Exception e) {

View File

@ -86,15 +86,34 @@ public class PedidoService {
}
// Auditoría mínima
Long userId = cart.getUserId();
/*Long userId = cart.getUserId();
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.setDeleted(false);
pedido.setUpdatedAt(Instant.now());
pedido.setUpdatedBy(userService.findById(userId));
// Guardamos el pedido
Pedido pedidoGuardado = pedidoRepository.save(pedido);
pedidoGuardado.setCreatedBy(userService.findById(userId));
pedidoGuardado.setUpdatedBy(userService.findById(userId));
pedidoRepository.save(pedidoGuardado);
List<CartItem> items = cart.getItems();

View File

@ -359,6 +359,7 @@ public class UserController {
@GetMapping(value = "api/get-users", produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> getUsers(
@RequestParam(required = false) String role, // puede venir ausente
@RequestParam(required = false) Boolean showUsername,
@RequestParam(required = false) String q,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
@ -373,9 +374,15 @@ public class UserController {
.map(u -> {
Map<String, Object> m = new HashMap<>();
m.put("id", u.getId());
m.put("text", (u.getFullName() != null && !u.getFullName().isBlank())
? u.getFullName()
: u.getUserName());
if (showUsername != null && Boolean.TRUE.equals(showUsername)) {
m.put("text", (u.getFullName() != null && !u.getFullName().isBlank())
? u.getFullName() + " (" + u.getUserName() + ")"
: u.getUserName());
} else {
m.put("text", (u.getFullName() != null && !u.getFullName().isBlank())
? u.getFullName()
: u.getUserName());
}
return m;
})
.collect(Collectors.toList());
@ -385,4 +392,20 @@ public class UserController {
"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.datos-generales-descripcion=Datos generales del presupuesto
presupuesto.titulo=Título*
presupuesto.cliente=Cliente*
presupuesto.autor=Autor
presupuesto.isbn=ISBN
presupuesto.tirada=Tirada

View File

@ -332,7 +332,7 @@ export default class PresupuestoWizard {
servicios: this.formData.servicios.servicios,
datosMaquetacion: this.formData.servicios.datosMaquetacion,
datosMarcapaginas: this.formData.servicios.datosMarcapaginas,
cliente_id: $('#cliente_id').val() || null,
cliente_id: $('#user_id').val() || null,
};
try {
@ -1696,7 +1696,7 @@ export default class PresupuestoWizard {
const body = {
presupuesto: this.#getPresupuestoData(),
save: this.opts.canSave,
save: this.opts.mode == 'public' ? true : this.opts.canSave,
mode: this.opts.mode,
servicios: servicios,
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',
method: 'GET',
},
order: [[0, 'asc']],
order: [[0, 'desc']],
columns: [
{ data: 'id', name: 'id', 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',
method: 'GET',
},
order: [[0, 'asc']],
order: [[0, 'desc']],
columns: [
{ data: 'id', name: 'id', orderable: true },
{ data: 'titulo', name: 'titulo', orderable: true },
@ -174,7 +174,7 @@ import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-ut
url: '/presupuesto/datatable/clientes',
method: 'GET',
},
order: [[0, 'asc']],
order: [[0, 'desc']],
columns: [
{ data: 'id', name: 'id', orderable: true },
{ data: 'user', name: 'user.fullName', orderable: true },

View File

@ -18,6 +18,28 @@
</div>
<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="col-sm-12">
<div class="mb-3">

View File

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