leandro 6419ac8843
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (pull_request) Successful in 34m41s
feat(expeditions): permitir transición Emitida → EnTransito desde la consulta
closes #7
2026-03-14 21:23:06 -03:00

221 lines
8.1 KiB
C#

using Domain.Dtos;
using Domain.Dtos.Stock;
using Domain.Entities;
using Domain.Generics;
using Microsoft.JSInterop;
using System.Net.Http.Json;
using System.Reflection;
using System.Text;
using System.Text.Json;
namespace phronCare.UIBlazor.Services.Stock.Expeditions
{
public class ExpeditionService: IExpeditionService
{
private readonly IJSRuntime _js; //Todavia no se utiliza pero eventualmente para exportaciones seguramente./
private readonly HttpClient _http;
public ExpeditionService(HttpClient http, IJSRuntime js)
{
_js = js;
_http = http;
}
/// <summary>
/// Búsqueda de expediciones con filtros y paginación.
/// Filtros típicos: número, estado, fechas, ubicación (opcional).
/// </summary>
/// <summary>Buscar expediciones (simétrico a QuoteService.SearchAsync)</summary>
public async Task<PagedResult<ExpeditionDto>> SearchAsync(
string? expeditionNumber = null,
string? status = null,
DateTime? issueDateFrom = null,
DateTime? issueDateTo = null,
int? locationId = null,
int page = 1,
int pageSize = 10)
{
var query = new List<string>();
void Add(string key, string? val)
{
if (!string.IsNullOrWhiteSpace(val))
query.Add($"{key}={Uri.EscapeDataString(val)}");
}
Add("expeditionNumber", expeditionNumber);
Add("status", status);
Add("issueDateFrom", issueDateFrom?.ToString("o"));
Add("issueDateTo", issueDateTo?.ToString("o"));
Add("locationId", locationId?.ToString());
Add("page", page.ToString());
Add("pageSize", pageSize.ToString());
// FIX: controller es singular => /api/expedition/search
var url = "/api/expedition/search";
if (query.Any()) url += "?" + string.Join("&", query);
var result = await _http.GetFromJsonAsync<PagedResult<ExpeditionDto>>(url);
return result!;
}
/// <summary>
/// Obtiene un presupuesto por QuoteNumber.
/// </summary>
public async Task<QuoteDto?> GetQuoteByNumberAsync(string quoteNumber)
{
try
{
var result = await _http.GetFromJsonAsync<QuoteDto?>($"api/quote/summary/{quoteNumber}");
return result;
}
catch (Exception ex)
{
Console.WriteLine($"Error al obtener QuoteDto por QuoteNumber: {ex.Message}");
return null;
}
}
/// <summary>
/// Envía el header + details de la expedición junto con el formSeriesId
/// y recibe el número de expedición generado o un mensaje de error.
/// </summary>
public async Task<CreateExpeditionResult> CreateAndIssueAsync(
ELSExpeditionHeader header,
IEnumerable<ELSExpeditionDetail> details,
int formSeriesId)
{
// 1) Poner los ítems dentro del header
header.PhLsmExpeditionDetails = details?.ToList() ?? new List<ELSExpeditionDetail>();
var request = new CreateFullExpeditionRequest
{
Expedition = header,
FormSeriesId = formSeriesId
};
var response = await _http.PostAsJsonAsync("/api/expedition/createfull", request);
if (!response.IsSuccessStatusCode)
{
var serverMessage = await response.Content.ReadAsStringAsync();
return new CreateExpeditionResult
{
Success = false,
ErrorMessage = serverMessage
};
}
var result = await response.Content.ReadFromJsonAsync<CreateExpeditionResult>();
return result ?? new CreateExpeditionResult { Success = false, ErrorMessage = "Respuesta vacía del servidor." };
}
/// <summary>
/// Obtiene la expedición completa por ID para visualización (drawer/pantalla de detalle).
/// </summary>
public async Task<ExpeditionDto?> GetDtoByIdAsync(int id)
{
try
{
var dto = await _http.GetFromJsonAsync<ExpeditionDto>($"/api/expedition/{id}");
return dto;
}
catch (Exception ex)
{
Console.WriteLine($"Error al obtener ExpeditionDto por ID: {ex.Message}");
return null;
}
}
/// <summary>
/// Descarga el PDF de la expedición en el navegador usando saveAsFile (base64).
/// </summary>
public async Task ExportPdfAsync(int expeditionId, string expeditionNumber)
{
try
{
var response = await _http.GetAsync($"/api/expedition/{expeditionId}/pdf");
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync();
throw new Exception($"Error al generar PDF: {error}");
}
var bytes = await response.Content.ReadAsByteArrayAsync();
var base64 = Convert.ToBase64String(bytes);
var fileName = $"{expeditionNumber}.pdf";
await _js.InvokeVoidAsync("saveAsFile", fileName, base64);
}
catch (Exception ex)
{
var message = ex.Message ?? "No message";
throw new Exception($"ExportPdfAsync: {message}", ex);
}
}
public async Task ExportFilteredAsync(LSProductSearchParams searchParams)
{
try
{
var content = new StringContent(JsonSerializer.Serialize(searchParams), Encoding.UTF8, "application/json");
var response = await _http.PostAsync("api/Expedition/exportfiltered", content);
//response.EnsureSuccessStatusCode();
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync();
throw new Exception(errorContent);
}
var bytes = await response.Content.ReadAsByteArrayAsync();
var base64 = Convert.ToBase64String(bytes);
var timestamp = DateTime.Now.ToString("yyyyMMddHHmm");
var fileName = $"{timestamp}_expeditions.xlsx";
await _js.InvokeVoidAsync("saveAsFile", fileName, base64);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
var message = ex.Message ?? "No message provided";
throw new Exception($"{message}", ex);
}
}
public async Task MarkInTransitAsync(int expeditionId)
{
if (expeditionId <= 0)
throw new ArgumentException("El identificador de la expedición no es válido.");
var response = await _http.PostAsync($"api/expedition/{expeditionId}/mark-in-transit", null);
if (!response.IsSuccessStatusCode)
{
var message = await response.Content.ReadAsStringAsync();
if (string.IsNullOrWhiteSpace(message))
message = "No se pudo pasar la expedición a 'En tránsito'.";
throw new Exception(message);
}
}
}
/// <summary>
/// Contrato de request.
/// </summary>
public class CreateFullExpeditionRequest
{
public ELSExpeditionHeader Expedition { get; set; } = default!;
public int FormSeriesId { get; set; }
}
/// <summary>
/// Resultado del create/issue.
/// </summary>
public class CreateExpeditionResult
{
public bool Success { get; set; }
public int Id { get; set; }
public string ExpeditionNumber { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
}
}