cambios de denuncias
This commit is contained in:
@@ -0,0 +1,593 @@
|
||||
@page "/Buscador"
|
||||
@rendermode InteractiveServer
|
||||
@attribute [Authorize]
|
||||
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Headers
|
||||
@using System.Text
|
||||
@using System.Text.Json
|
||||
@using System.Linq
|
||||
@using GestionaDenunciasAN.Models
|
||||
|
||||
@inject HttpClient Http
|
||||
@inject IConfiguration Configuration
|
||||
|
||||
<h3>Buscador de expedientes por tercero</h3>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-body">
|
||||
<!-- NIF -->
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">DNI / NIF</label>
|
||||
<input class="form-control"
|
||||
placeholder="Ej.: 12345678Z"
|
||||
@bind="nifBuscado"
|
||||
@bind:event="oninput" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- Modo de fechas -->
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<label class="form-label d-block">Rango de fechas</label>
|
||||
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
id="modoTodas"
|
||||
name="modoFecha"
|
||||
checked="@IsModo(ModoTodas)"
|
||||
@onchange="@(() => SetModo(ModoTodas))" />
|
||||
<label class="form-check-label" for="modoTodas">
|
||||
Todas las fechas
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
id="modoRango"
|
||||
name="modoFecha"
|
||||
checked="@IsModo(ModoRango)"
|
||||
@onchange="@(() => SetModo(ModoRango))" />
|
||||
<label class="form-check-label" for="modoRango">
|
||||
Entre fechas
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
id="modoUltimos"
|
||||
name="modoFecha"
|
||||
checked="@IsModo(ModoUltimos)"
|
||||
@onchange="@(() => SetModo(ModoUltimos))" />
|
||||
<label class="form-check-label" for="modoUltimos">
|
||||
Últimos X meses
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rango de fechas -->
|
||||
@if (modoFecha == ModoRango)
|
||||
{
|
||||
<div class="row g-2 mt-2">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Desde</label>
|
||||
<input class="form-control"
|
||||
type="date"
|
||||
@bind="fechaDesde" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Hasta</label>
|
||||
<input class="form-control"
|
||||
type="date"
|
||||
@bind="fechaHasta" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (modoFecha == ModoUltimos)
|
||||
{
|
||||
<div class="row g-2 mt-2">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Últimos meses</label>
|
||||
<select class="form-select" @bind="mesesUltimos">
|
||||
<option value="3">3 meses</option>
|
||||
<option value="6">6 meses</option>
|
||||
<option value="9">9 meses</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Botón buscar -->
|
||||
<div class="row g-2 mt-3">
|
||||
<div class="col-md-3">
|
||||
<button class="btn btn-primary"
|
||||
@onclick="BuscarAsync"
|
||||
disabled="@isSearching">
|
||||
@if (isSearching)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="ms-2">Buscando…</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<i class="bi bi-search"></i>
|
||||
<span class="ms-1">Buscar</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger mt-3">@errorMessage</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (haBuscado)
|
||||
{
|
||||
<div class="mt-4">
|
||||
<h5>Resultados para <strong>@nifMostrado</strong></h5>
|
||||
|
||||
@if (expedientes == null || !expedientes.Any())
|
||||
{
|
||||
<div class="alert alert-warning mt-3">
|
||||
No se han encontrado expedientes asociados a este tercero con los filtros seleccionados.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover mt-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Expediente</th>
|
||||
<th>Asunto</th>
|
||||
<th>Fecha creación</th>
|
||||
<th>Estado</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var exp in expedientes)
|
||||
{
|
||||
<tr>
|
||||
<td>@exp.CodigoExpediente</td>
|
||||
<td>@exp.Asunto</td>
|
||||
<td>@(exp.FechaCreacion?.ToLocalTime().ToString("dd/MM/yyyy HH:mm"))</td>
|
||||
<td>@exp.Estado</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-outline-primary"
|
||||
@onclick="() => ToggleDetalle(exp)">
|
||||
@(expedienteSeleccionado == exp ? "Ocultar" : "Abrir expediente")
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@if (expedienteSeleccionado == exp)
|
||||
{
|
||||
<tr class="table-active">
|
||||
<td colspan="5">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-2">Expediente</dt>
|
||||
<dd class="col-sm-10">@exp.CodigoExpediente</dd>
|
||||
|
||||
<dt class="col-sm-2">Asunto</dt>
|
||||
<dd class="col-sm-10">@exp.Asunto</dd>
|
||||
|
||||
<dt class="col-sm-2">Fecha creación</dt>
|
||||
<dd class="col-sm-10">
|
||||
@exp.FechaCreacion?.ToLocalTime().ToString("dd/MM/yyyy HH:mm")
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-2">Estado</dt>
|
||||
<dd class="col-sm-10">@exp.Estado</dd>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(exp.FileUrl))
|
||||
{
|
||||
<dt class="col-sm-2">Enlace Gestiona</dt>
|
||||
<dd class="col-sm-10">
|
||||
<a href="@exp.FileUrl" target="_blank">@exp.FileUrl</a>
|
||||
</dd>
|
||||
}
|
||||
</dl>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
// ============================================================
|
||||
// CONFIG (mismo origen que GestionaService)
|
||||
// ============================================================
|
||||
|
||||
private string ApiBase => Configuration["Gestiona:ApiBase"] ?? "";
|
||||
private string AccessToken => Configuration["Gestiona:AccessToken"] ?? "";
|
||||
|
||||
private string RestBaseUrl =>
|
||||
string.IsNullOrWhiteSpace(ApiBase)
|
||||
? ""
|
||||
: $"{ApiBase.TrimEnd('/')}/rest";
|
||||
|
||||
private readonly JsonSerializerOptions jsonOpts = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// ESTADO UI
|
||||
// ============================================================
|
||||
|
||||
private string nifBuscado = string.Empty;
|
||||
private string nifMostrado = string.Empty;
|
||||
|
||||
private const string ModoTodas = "todas";
|
||||
private const string ModoRango = "rango";
|
||||
private const string ModoUltimos = "ultimos";
|
||||
|
||||
private string modoFecha = ModoTodas;
|
||||
|
||||
private DateTime? fechaDesde;
|
||||
private DateTime? fechaHasta;
|
||||
|
||||
private int mesesUltimos = 3;
|
||||
|
||||
private bool isSearching;
|
||||
private bool haBuscado;
|
||||
private string errorMessage = string.Empty;
|
||||
|
||||
private List<ExpedienteTerceroDto> expedientes = new();
|
||||
|
||||
// expediente cuyo detalle está abierto
|
||||
private ExpedienteTerceroDto? expedienteSeleccionado;
|
||||
|
||||
private bool IsModo(string valor) => string.Equals(modoFecha, valor, StringComparison.Ordinal);
|
||||
private void SetModo(string valor) => modoFecha = valor;
|
||||
|
||||
private void ToggleDetalle(ExpedienteTerceroDto exp)
|
||||
{
|
||||
if (expedienteSeleccionado == exp)
|
||||
expedienteSeleccionado = null;
|
||||
else
|
||||
expedienteSeleccionado = exp;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// MODELOS AUXILIARES (similares a tu servicio)
|
||||
// ============================================================
|
||||
|
||||
private class LinkDto
|
||||
{
|
||||
public string Rel { get; set; } = string.Empty;
|
||||
public string Href { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
private class ThirdDto
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Nif { get; set; } = string.Empty;
|
||||
public List<LinkDto> Links { get; set; } = new();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// BÚSQUEDA PRINCIPAL
|
||||
// ============================================================
|
||||
|
||||
private async Task BuscarAsync()
|
||||
{
|
||||
errorMessage = string.Empty;
|
||||
haBuscado = false;
|
||||
expedienteSeleccionado = null;
|
||||
expedientes.Clear();
|
||||
|
||||
var nif = (nifBuscado ?? string.Empty).Trim().ToUpperInvariant();
|
||||
if (string.IsNullOrWhiteSpace(nif))
|
||||
{
|
||||
errorMessage = "Introduce un NIF para buscar.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(RestBaseUrl) || string.IsNullOrWhiteSpace(AccessToken))
|
||||
{
|
||||
errorMessage = "No está configurada la conexión a Gestiona (ApiBase / AccessToken).";
|
||||
return;
|
||||
}
|
||||
|
||||
DateTimeOffset? desde = null;
|
||||
DateTimeOffset? hasta = null;
|
||||
|
||||
// Calcular rango según el modo
|
||||
if (modoFecha == ModoRango)
|
||||
{
|
||||
if (!fechaDesde.HasValue || !fechaHasta.HasValue)
|
||||
{
|
||||
errorMessage = "Debes indicar las dos fechas (Desde y Hasta).";
|
||||
return;
|
||||
}
|
||||
|
||||
desde = new DateTimeOffset(fechaDesde.Value.Date, TimeSpan.Zero);
|
||||
hasta = new DateTimeOffset(fechaHasta.Value.Date.AddDays(1).AddTicks(-1), TimeSpan.Zero);
|
||||
|
||||
if (desde > hasta)
|
||||
{
|
||||
errorMessage = "La fecha 'Desde' no puede ser mayor que 'Hasta'.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (modoFecha == ModoUltimos)
|
||||
{
|
||||
if (mesesUltimos <= 0)
|
||||
{
|
||||
errorMessage = "El número de meses debe ser mayor que 0.";
|
||||
return;
|
||||
}
|
||||
|
||||
var ahora = DateTimeOffset.UtcNow;
|
||||
desde = ahora.AddMonths(-mesesUltimos);
|
||||
hasta = ahora;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
isSearching = true;
|
||||
|
||||
// 1) Buscar el tercero por NIF
|
||||
var tercero = await BuscarTerceroPorNifAsync(nif);
|
||||
if (string.IsNullOrEmpty(tercero.Id) || string.IsNullOrEmpty(tercero.SelfHref))
|
||||
{
|
||||
errorMessage = "No se ha encontrado ningún tercero con ese NIF.";
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) Listar expedientes usando third_rest_link
|
||||
expedientes = await ObtenerExpedientesPorTerceroFiltradoAsync(
|
||||
tercero.SelfHref,
|
||||
desde,
|
||||
hasta
|
||||
);
|
||||
|
||||
nifMostrado = nif;
|
||||
haBuscado = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = $"Error al buscar expedientes: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
isSearching = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// LLAMADAS A GESTIONA
|
||||
// ============================================================
|
||||
|
||||
private void AddBasicHeaders(HttpRequestMessage req)
|
||||
{
|
||||
req.Headers.TryAddWithoutValidation("X-Gestiona-Access-Token", AccessToken);
|
||||
req.Headers.TryAddWithoutValidation("Accept", "application/vnd.gestiona.files-page+json");
|
||||
}
|
||||
|
||||
private async Task<(string Id, string SelfHref)> BuscarTerceroPorNifAsync(string nif)
|
||||
{
|
||||
var filtro = new
|
||||
{
|
||||
result = new { max_results = 25 },
|
||||
filter = new { nif }
|
||||
};
|
||||
var jsonFiltro = JsonSerializer.Serialize(filtro);
|
||||
var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(jsonFiltro));
|
||||
var url = $"{ApiBase.TrimEnd('/')}/rest/thirds?filter-view={Uri.EscapeDataString(b64)}";
|
||||
|
||||
using var req = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
req.Headers.TryAddWithoutValidation("X-Gestiona-Access-Token", AccessToken);
|
||||
req.Headers.TryAddWithoutValidation("Accept", "application/vnd.gestiona.thirds-page+json");
|
||||
|
||||
using var resp = await Http.SendAsync(req);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
if (!doc.RootElement.TryGetProperty("content", out var content) ||
|
||||
content.ValueKind != JsonValueKind.Array)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var match = content.EnumerateArray().FirstOrDefault(e =>
|
||||
e.TryGetProperty("nif", out var nifProp) &&
|
||||
string.Equals(nifProp.GetString(), nif, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (match.ValueKind == JsonValueKind.Undefined)
|
||||
return default;
|
||||
|
||||
var id = match.GetProperty("id").GetString() ?? string.Empty;
|
||||
|
||||
string selfHref = string.Empty;
|
||||
if (match.TryGetProperty("links", out var linksEl) && linksEl.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var selfLink = linksEl.EnumerateArray().FirstOrDefault(l =>
|
||||
l.TryGetProperty("rel", out var rel) &&
|
||||
string.Equals(rel.GetString(), "self", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (selfLink.ValueKind != JsonValueKind.Undefined &&
|
||||
selfLink.TryGetProperty("href", out var hrefEl))
|
||||
{
|
||||
selfHref = hrefEl.GetString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
return (id, selfHref);
|
||||
}
|
||||
|
||||
private async Task<List<ExpedienteTerceroDto>> ObtenerExpedientesPorTerceroFiltradoAsync(
|
||||
string thirdSelfHref,
|
||||
DateTimeOffset? desde,
|
||||
DateTimeOffset? hasta)
|
||||
{
|
||||
var url = $"{ApiBase.TrimEnd('/')}/rest/files";
|
||||
|
||||
var bodyObj = new
|
||||
{
|
||||
third_rest_link = new
|
||||
{
|
||||
rel = "third",
|
||||
href = thirdSelfHref
|
||||
}
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(bodyObj, jsonOpts);
|
||||
|
||||
using var req = new HttpRequestMessage(HttpMethod.Get, url)
|
||||
{
|
||||
Content = new StringContent(json, Encoding.UTF8, "application/vnd.gestiona.filter.files")
|
||||
};
|
||||
|
||||
AddBasicHeaders(req);
|
||||
|
||||
using var resp = await Http.SendAsync(req);
|
||||
var respBody = await resp.Content.ReadAsStringAsync();
|
||||
|
||||
if (!resp.IsSuccessStatusCode)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"GET /rest/files filtrado: {(int)resp.StatusCode} {resp.ReasonPhrase}\n{respBody}");
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(respBody);
|
||||
|
||||
JsonElement content;
|
||||
if (doc.RootElement.TryGetProperty("content", out var contentEl) &&
|
||||
contentEl.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
content = contentEl;
|
||||
}
|
||||
else if (doc.RootElement.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
content = doc.RootElement;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new List<ExpedienteTerceroDto>();
|
||||
}
|
||||
|
||||
var lista = new List<ExpedienteTerceroDto>();
|
||||
|
||||
foreach (var item in content.EnumerateArray())
|
||||
{
|
||||
// ===== Fecha creación (string o número) =====
|
||||
DateTimeOffset? creation = null;
|
||||
if (item.TryGetProperty("creation_date", out var pCreation))
|
||||
{
|
||||
if (pCreation.ValueKind == JsonValueKind.Number &&
|
||||
pCreation.TryGetInt64(out var tsNum))
|
||||
{
|
||||
creation = DateTimeOffset.FromUnixTimeSeconds(tsNum);
|
||||
}
|
||||
else if (pCreation.ValueKind == JsonValueKind.String &&
|
||||
long.TryParse(pCreation.GetString(), out var tsStr))
|
||||
{
|
||||
creation = DateTimeOffset.FromUnixTimeSeconds(tsStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (desde.HasValue && creation.HasValue && creation.Value < desde.Value)
|
||||
continue;
|
||||
|
||||
if (hasta.HasValue && creation.HasValue && creation.Value > hasta.Value)
|
||||
continue;
|
||||
|
||||
// ===== URLs =====
|
||||
string? selfHref = null;
|
||||
if (item.TryGetProperty("links", out var linksEl) &&
|
||||
linksEl.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var l in linksEl.EnumerateArray())
|
||||
{
|
||||
if (l.TryGetProperty("rel", out var relProp) &&
|
||||
string.Equals(relProp.GetString(), "self", StringComparison.OrdinalIgnoreCase) &&
|
||||
l.TryGetProperty("href", out var hrefProp))
|
||||
{
|
||||
selfHref = hrefProp.GetString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string? ehomeUrl = null;
|
||||
if (item.TryGetProperty("ehome_url", out var pEhome) &&
|
||||
pEhome.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
ehomeUrl = pEhome.GetString();
|
||||
}
|
||||
|
||||
var fileUrl = ehomeUrl ?? selfHref;
|
||||
if (string.IsNullOrWhiteSpace(fileUrl))
|
||||
continue;
|
||||
|
||||
var code = GetJsonString(item, "code");
|
||||
var subject = GetJsonString(item, "subject");
|
||||
var freeTitle = GetJsonString(item, "free_title");
|
||||
var selectableTitle = GetJsonString(item, "selectable_title");
|
||||
var procedureName = GetJsonString(item, "procedure_name");
|
||||
var asunto = FirstNonEmpty(freeTitle, subject, selectableTitle, procedureName) ?? string.Empty;
|
||||
|
||||
// ===== Estado (state / status / full_state) =====
|
||||
string? state = null;
|
||||
if (item.TryGetProperty("state", out var pState) && pState.ValueKind == JsonValueKind.String)
|
||||
state = pState.GetString();
|
||||
else if (item.TryGetProperty("status", out var pStatus) && pStatus.ValueKind == JsonValueKind.String)
|
||||
state = pStatus.GetString();
|
||||
else if (item.TryGetProperty("full_state", out var pFullState) && pFullState.ValueKind == JsonValueKind.String)
|
||||
state = pFullState.GetString();
|
||||
|
||||
var dto = new ExpedienteTerceroDto
|
||||
{
|
||||
FileUrl = fileUrl!,
|
||||
CodigoExpediente = code ?? string.Empty,
|
||||
Asunto = asunto,
|
||||
FechaCreacion = creation,
|
||||
Estado = state ?? string.Empty
|
||||
};
|
||||
|
||||
lista.Add(dto);
|
||||
}
|
||||
|
||||
return lista;
|
||||
}
|
||||
|
||||
private static string? GetJsonString(JsonElement item, string propertyName)
|
||||
{
|
||||
return item.TryGetProperty(propertyName, out var property) &&
|
||||
property.ValueKind == JsonValueKind.String
|
||||
? property.GetString()
|
||||
: null;
|
||||
}
|
||||
|
||||
private static string? FirstNonEmpty(params string?[] values)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user