mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-24 09:40:21 +00:00
trabajando en el wizard del presupuesto
This commit is contained in:
@ -5,6 +5,7 @@ import org.springframework.ui.Model;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -16,15 +17,17 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
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.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.imprimelibros.erp.configurationERP.VariableService;
|
||||||
import com.imprimelibros.erp.datatables.*;
|
import com.imprimelibros.erp.datatables.*;
|
||||||
import com.imprimelibros.erp.externalApi.skApiClient;
|
import com.imprimelibros.erp.externalApi.skApiClient;
|
||||||
import com.imprimelibros.erp.i18n.TranslationService;
|
import com.imprimelibros.erp.i18n.TranslationService;
|
||||||
@ -41,6 +44,8 @@ import jakarta.validation.Valid;
|
|||||||
@RequestMapping("/presupuesto")
|
@RequestMapping("/presupuesto")
|
||||||
public class PresupuestoController {
|
public class PresupuestoController {
|
||||||
|
|
||||||
|
private final PresupuestoRepository presupuestoRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected PresupuestoService presupuestoService;
|
protected PresupuestoService presupuestoService;
|
||||||
|
|
||||||
@ -53,12 +58,16 @@ public class PresupuestoController {
|
|||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
private final TranslationService translationService;
|
private final TranslationService translationService;
|
||||||
private final PresupuestoDatatableService dtService;
|
private final PresupuestoDatatableService dtService;
|
||||||
|
private final VariableService variableService;
|
||||||
|
|
||||||
public PresupuestoController(ObjectMapper objectMapper, TranslationService translationService,
|
public PresupuestoController(ObjectMapper objectMapper, TranslationService translationService,
|
||||||
PresupuestoDatatableService dtService) {
|
PresupuestoDatatableService dtService, PresupuestoRepository presupuestoRepository,
|
||||||
|
VariableService variableService) {
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
this.translationService = translationService;
|
this.translationService = translationService;
|
||||||
this.dtService = dtService;
|
this.dtService = dtService;
|
||||||
|
this.presupuestoRepository = presupuestoRepository;
|
||||||
|
this.variableService = variableService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/public/validar/datos-generales")
|
@PostMapping("/public/validar/datos-generales")
|
||||||
@ -421,7 +430,7 @@ public class PresupuestoController {
|
|||||||
// MÉTODOS PARA USUARIOS AUTENTICADOS
|
// MÉTODOS PARA USUARIOS AUTENTICADOS
|
||||||
// =============================================
|
// =============================================
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public String getPresupuestoView(Model model, Authentication authentication, Locale locale) {
|
public String getPresupuestoList(Model model, Authentication authentication, Locale locale) {
|
||||||
|
|
||||||
List<String> keys = List.of();
|
List<String> keys = List.of();
|
||||||
|
|
||||||
@ -431,9 +440,55 @@ public class PresupuestoController {
|
|||||||
return "imprimelibros/presupuestos/presupuesto-list";
|
return "imprimelibros/presupuestos/presupuesto-list";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = { "/edit/{id}", "/view/{id}" })
|
||||||
|
public String getPresupuestoEditForm(
|
||||||
|
@PathVariable(name = "id", required = true) Long id,
|
||||||
|
RedirectAttributes redirectAttributes,
|
||||||
|
Model model,
|
||||||
|
Authentication authentication,
|
||||||
|
Locale locale) {
|
||||||
|
|
||||||
|
List<String> keys = List.of(
|
||||||
|
"presupuesto.plantilla-cubierta",
|
||||||
|
"presupuesto.plantilla-cubierta-text",
|
||||||
|
"presupuesto.impresion-cubierta",
|
||||||
|
"presupuesto.impresion-cubierta-help");
|
||||||
|
|
||||||
|
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||||
|
model.addAttribute("languageBundle", translations);
|
||||||
|
model.addAttribute("pod", variableService.getValorEntero("POD"));
|
||||||
|
model.addAttribute("ancho_alto_min", variableService.getValorEntero("ancho_alto_min"));
|
||||||
|
model.addAttribute("ancho_alto_max", variableService.getValorEntero("ancho_alto_max"));
|
||||||
|
|
||||||
|
// Buscar el presupuesto
|
||||||
|
Optional<Presupuesto> presupuestoOpt = presupuestoRepository.findById(id);
|
||||||
|
boolean isUser = authentication.getAuthorities().stream()
|
||||||
|
.anyMatch(a -> a.getAuthority().equals("ROLE_USER"));
|
||||||
|
|
||||||
|
if (isUser) {
|
||||||
|
// Si es usuario, solo puede ver sus propios presupuestos
|
||||||
|
String username = authentication.getName();
|
||||||
|
if (!presupuestoOpt.get().getUser().getUserName().equals(username)) {
|
||||||
|
presupuestoOpt = Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presupuestoOpt.isEmpty()) {
|
||||||
|
// Añadir mensaje flash para mostrar alerta
|
||||||
|
redirectAttributes.addFlashAttribute("errorMessage",
|
||||||
|
messageSource.getMessage("presupuesto.errores.presupuesto-no-existe", new Object[] { id }, locale));
|
||||||
|
// Redirigir a la vista de lista
|
||||||
|
return "redirect:/presupuesto";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si existe, lo añadimos al modelo
|
||||||
|
model.addAttribute("presupuesto", presupuestoOpt.get());
|
||||||
|
return "imprimelibros/presupuestos/presupuesto-form";
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/datatable/anonimos", produces = "application/json")
|
@GetMapping(value = "/datatable/anonimos", produces = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public DataTablesResponse<Map<String,Object>> datatableAnonimos(
|
public DataTablesResponse<Map<String, Object>> datatableAnonimos(
|
||||||
HttpServletRequest request, Authentication auth, Locale locale) {
|
HttpServletRequest request, Authentication auth, Locale locale) {
|
||||||
|
|
||||||
DataTablesRequest dt = DataTablesParser.from(request);
|
DataTablesRequest dt = DataTablesParser.from(request);
|
||||||
|
|||||||
@ -177,7 +177,7 @@ public class PresupuestoDatatableService {
|
|||||||
m.put("actions",
|
m.put("actions",
|
||||||
"<div class=\"hstack gap-3 flex-wrap\">" +
|
"<div class=\"hstack gap-3 flex-wrap\">" +
|
||||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||||
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-edit-2-line\"></i></a>" +
|
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
|
||||||
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
|
||||||
+ "\" class=\"link-danger btn-delete-anonimo fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>"
|
+ "\" class=\"link-danger btn-delete-anonimo fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>"
|
||||||
+
|
+
|
||||||
|
|||||||
@ -16,4 +16,6 @@ app.logout=Cerrar sesión
|
|||||||
app.sidebar.inicio=Inicio
|
app.sidebar.inicio=Inicio
|
||||||
app.sidebar.presupuestos=Presupuestos
|
app.sidebar.presupuestos=Presupuestos
|
||||||
app.sidebar.configuracion=Configuración
|
app.sidebar.configuracion=Configuración
|
||||||
app.sidebar.usuarios=Usuarios
|
app.sidebar.usuarios=Usuarios
|
||||||
|
|
||||||
|
app.errors.403=No tienes permiso para acceder a esta página.
|
||||||
@ -1,4 +1,5 @@
|
|||||||
presupuesto.title=Presupuestos
|
presupuesto.title=Presupuestos
|
||||||
|
presupuesto.editar.title=Editar presupuesto
|
||||||
presupuesto.datos-generales=Datos Generales
|
presupuesto.datos-generales=Datos Generales
|
||||||
presupuesto.interior=Interior
|
presupuesto.interior=Interior
|
||||||
presupuesto.cubierta=Cubierta
|
presupuesto.cubierta=Cubierta
|
||||||
@ -287,5 +288,7 @@ presupuesto.errores.papel-cubierta=Seleccione el tipo de papel para la cubierta
|
|||||||
presupuesto.errores.gramaje-cubierta=Seleccione el gramaje del papel para la cubierta
|
presupuesto.errores.gramaje-cubierta=Seleccione el gramaje del papel para la cubierta
|
||||||
presupuesto.errores.acabado-cubierta=Seleccione el acabado de la cubierta
|
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-maquetacion=No se pudo calcular el presupuesto de maquetación.
|
||||||
presupuesto.errores.presupuesto-marcapaginas=No se pudo calcular el presupuesto de marcapáginas.
|
presupuesto.errores.presupuesto-marcapaginas=No se pudo calcular el presupuesto de marcapáginas.
|
||||||
@ -1780,15 +1780,15 @@ File: Main Css File
|
|||||||
}
|
}
|
||||||
[data-layout=semibox] .main-content {
|
[data-layout=semibox] .main-content {
|
||||||
margin-left: calc(250px + 25px);
|
margin-left: calc(250px + 25px);
|
||||||
padding: 0 6%;
|
padding: 0 2%;
|
||||||
}
|
}
|
||||||
[data-layout=semibox] .footer {
|
[data-layout=semibox] .footer {
|
||||||
left: calc(250px + 6% + 1.5rem + 25px);
|
left: calc(250px + 2% + 1.5rem + 25px);
|
||||||
right: calc(6% + 1.5rem);
|
right: calc(2% + 1.5rem);
|
||||||
}
|
}
|
||||||
[data-layout=semibox] #page-topbar {
|
[data-layout=semibox] #page-topbar {
|
||||||
left: calc(250px + 6% + 1.5rem + 25px);
|
left: calc(250px + 2% + 1.5rem + 25px);
|
||||||
right: calc(6% + 1.5rem);
|
right: calc(2% + 1.5rem);
|
||||||
top: 25px;
|
top: 25px;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
-webkit-transition: all 0.5s ease;
|
-webkit-transition: all 0.5s ease;
|
||||||
@ -1801,10 +1801,10 @@ File: Main Css File
|
|||||||
margin-left: calc(180px + 25px);
|
margin-left: calc(180px + 25px);
|
||||||
}
|
}
|
||||||
[data-layout=semibox][data-sidebar-size=md] #page-topbar {
|
[data-layout=semibox][data-sidebar-size=md] #page-topbar {
|
||||||
left: calc(180px + 6% + 1.5rem + 25px);
|
left: calc(180px + 2% + 1.5rem + 25px);
|
||||||
}
|
}
|
||||||
[data-layout=semibox][data-sidebar-size=md] .footer {
|
[data-layout=semibox][data-sidebar-size=md] .footer {
|
||||||
left: calc(180px + 6% + 1.5rem + 25px);
|
left: calc(180px + 2% + 1.5rem + 25px);
|
||||||
}
|
}
|
||||||
[data-layout=semibox][data-sidebar-size=sm] .main-content {
|
[data-layout=semibox][data-sidebar-size=sm] .main-content {
|
||||||
margin-left: calc(70px + 25px);
|
margin-left: calc(70px + 25px);
|
||||||
@ -1813,19 +1813,19 @@ File: Main Css File
|
|||||||
top: 25px;
|
top: 25px;
|
||||||
}
|
}
|
||||||
[data-layout=semibox][data-sidebar-size=sm] #page-topbar {
|
[data-layout=semibox][data-sidebar-size=sm] #page-topbar {
|
||||||
left: calc(70px + 6% + 1.5rem + 25px);
|
left: calc(70px + 2% + 1.5rem + 25px);
|
||||||
}
|
}
|
||||||
[data-layout=semibox][data-sidebar-size=sm] .footer {
|
[data-layout=semibox][data-sidebar-size=sm] .footer {
|
||||||
left: calc(70px + 6% + 1.5rem + 25px);
|
left: calc(70px + 2% + 1.5rem + 25px);
|
||||||
}
|
}
|
||||||
[data-layout=semibox][data-sidebar-size=sm-hover] .main-content {
|
[data-layout=semibox][data-sidebar-size=sm-hover] .main-content {
|
||||||
margin-left: calc(70px + 25px);
|
margin-left: calc(70px + 25px);
|
||||||
}
|
}
|
||||||
[data-layout=semibox][data-sidebar-size=sm-hover] #page-topbar {
|
[data-layout=semibox][data-sidebar-size=sm-hover] #page-topbar {
|
||||||
left: calc(70px + 6% + 1.5rem + 25px);
|
left: calc(70px + 2% + 1.5rem + 25px);
|
||||||
}
|
}
|
||||||
[data-layout=semibox][data-sidebar-size=sm-hover] .footer {
|
[data-layout=semibox][data-sidebar-size=sm-hover] .footer {
|
||||||
left: calc(70px + 6% + 1.5rem + 25px);
|
left: calc(70px + 2% + 1.5rem + 25px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[data-layout=semibox] .mx-n4 {
|
[data-layout=semibox] .mx-n4 {
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
if (!lang || lang === getCurrentLang()) return;
|
if (!lang || lang === getCurrentLang()) return;
|
||||||
|
|
||||||
// Guarda la preferencia (opcional)
|
// Guarda la preferencia (opcional)
|
||||||
try { localStorage.setItem("language", lang); } catch {}
|
try { localStorage.setItem("language", lang); } catch { }
|
||||||
|
|
||||||
// Redirige con ?lang=... para que Spring cambie el Locale y renderice en ese idioma
|
// Redirige con ?lang=... para que Spring cambie el Locale y renderice en ese idioma
|
||||||
const url = new URL(location.href);
|
const url = new URL(location.href);
|
||||||
@ -48,6 +48,22 @@
|
|||||||
url.searchParams.set("lang", saved);
|
url.searchParams.set("lang", saved);
|
||||||
location.replace(url); // alinea y no deja historial extra
|
location.replace(url); // alinea y no deja historial extra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initsAlert();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initsAlert() {
|
||||||
|
var alerts = document.querySelectorAll('.alert.alert-dismissible');
|
||||||
|
alerts.forEach(function (el) {
|
||||||
|
// Solo si está visible
|
||||||
|
if (el.classList.contains('show')) {
|
||||||
|
setTimeout(function () {
|
||||||
|
// Usa la API de Bootstrap para cerrar con transición
|
||||||
|
var bsAlert = bootstrap.Alert.getOrCreateInstance(el);
|
||||||
|
bsAlert.close();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", initLanguage);
|
document.addEventListener("DOMContentLoaded", initLanguage);
|
||||||
|
|||||||
@ -66,6 +66,13 @@
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#presupuestos-anonimos-datatable').on('click', '.btn-edit-anonimo', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const id = $(this).data('id');
|
||||||
|
if (id) {
|
||||||
|
window.location.href = '/presupuesto/view/' + id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{imprimelibros/layout}">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<th:block layout:fragment="pagetitle" />
|
||||||
|
<th:block th:replace="~{imprimelibros/partials/head-css :: head-css}" />
|
||||||
|
<th:block layout:fragment="pagecss">
|
||||||
|
<link th:href="@{/assets/libs/datatables/dataTables.bootstrap5.min.css}" rel="stylesheet" />
|
||||||
|
</th:block>
|
||||||
|
<th:block layout:fragment="pagecss">
|
||||||
|
<link th:href="@{/assets/css/presupuestador.css}" rel="stylesheet" />
|
||||||
|
</th:block>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
|
||||||
|
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}"
|
||||||
|
th:unless="${#authorization.expression('isAuthenticated()') and (#authorization.expression('hasRole('SUPERADMIN', 'ADMIN')'))}" />
|
||||||
|
|
||||||
|
<th:block layout:fragment="content">
|
||||||
|
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="/presupuesto" th:text="#{presupuesto.title}"></a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page" th:text="#{presupuesto.editar.title}">
|
||||||
|
Editar presupuesto
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
|
||||||
|
<div th:insert="~{imprimelibros/presupuestos/presupuestador :: presupuestador}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||||
|
<th:block layout:fragment="pagejs">
|
||||||
|
<script th:inline="javascript">
|
||||||
|
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- JS de Buttons y dependencias -->
|
||||||
|
<script type="module" th:src="@{/assets/js/pages/imprimelibros/presupuestador/presupuestador.js}"></script>
|
||||||
|
</th:block>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -21,16 +21,24 @@
|
|||||||
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||||
|
|
||||||
|
|
||||||
<nav aria-label="breadcrumb">
|
<div class="container-fluid">
|
||||||
<ol class="breadcrumb">
|
<nav aria-label="breadcrumb">
|
||||||
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
<ol class="breadcrumb">
|
||||||
<li class="breadcrumb-item active" aria-current="page" th:text="#{presupuesto.title}">Presupuestos
|
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||||
</li>
|
<li class="breadcrumb-item active" aria-current="page" th:text="#{presupuesto.title}">
|
||||||
</ol>
|
Presupuestos
|
||||||
</nav>
|
</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
|
||||||
|
<div th:if="${errorMessage}" class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<span th:text="${errorMessage}"></span>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn btn-secondary mb-3" id="addPresupuestoButton">
|
<button type="button" class="btn btn-secondary mb-3" id="addPresupuestoButton">
|
||||||
<i class="ri-add-line align-bottom me-1"></i> <span th:text="#{presupuesto.add}">Añadir
|
<i class="ri-add-line align-bottom me-1"></i> <span th:text="#{presupuesto.add}">Añadir
|
||||||
presupuesto</span>
|
presupuesto</span>
|
||||||
@ -68,7 +76,7 @@
|
|||||||
<div
|
<div
|
||||||
th:insert="~{imprimelibros/presupuestos/presupuesto-list-items/tabla-anonimos :: tabla-anonimos}">
|
th:insert="~{imprimelibros/presupuestos/presupuesto-list-items/tabla-anonimos :: tabla-anonimos}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user