Some checks failed
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Failing after 15m47s
277 lines
9.8 KiB
Plaintext
277 lines
9.8 KiB
Plaintext
@page "/stock/expeditions/create"
|
|
@using Blazored.Typeahead
|
|
@using Domain.Dtos.Stock
|
|
@using Domain.Entities
|
|
@using Services.Lookups
|
|
@using Services.Stock.Expeditions
|
|
@using phronCare.UIBlazor.Pages.Stock.Shared
|
|
|
|
@* @using static phronCare.UIBlazor.Pages.Stock.Shared.StockItemSelectorModal
|
|
*@
|
|
@inject NavigationManager Navigation
|
|
@inject ExpeditionService expeditionService
|
|
@* @inject Lookup lookUpService *@
|
|
@inject ISalesLookupService lookUpService
|
|
@inject IToastService toastService
|
|
@inject IModalService Modal
|
|
|
|
|
|
|
|
<EditForm Model="Model" OnValidSubmit="HandleValidSubmit">
|
|
<DataAnnotationsValidator />
|
|
<ValidationSummary />
|
|
|
|
<div class="card mt-4" style="zoom:80%">
|
|
<div class="card-header d-flex justify-content-center align-items-center">
|
|
<h3 class="card-title m-0">Nueva Expedición</h3>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Presupuesto aprobado</label>
|
|
<BlazoredTypeahead id="quotes" TItem="ELookUpItem" TValue="ELookUpItem"
|
|
SearchMethod="SearchQuotes"
|
|
Value="SelectedQuote"
|
|
ValueChanged="OnQuoteSelected"
|
|
ValueExpression="@(() => SelectedQuote)"
|
|
MaximumSuggestions="10"
|
|
Placeholder="Buscar presupuesto aprobado..."
|
|
TextProperty="Nombre">
|
|
<ResultTemplate Context="item">
|
|
@item.Nombre
|
|
</ResultTemplate>
|
|
<SelectedTemplate Context="item">
|
|
@item.Nombre
|
|
</SelectedTemplate>
|
|
</BlazoredTypeahead>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Ticket ID</label>
|
|
<InputText class="form-control"
|
|
@bind-Value="ticketIdString"
|
|
@bind-Value:event="oninput" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Profesional</label>
|
|
<InputText class="form-control" @bind-Value="ExtraInfo.Professional" />
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Institución</label>
|
|
<InputText class="form-control" @bind-Value="ExtraInfo.Institution" />
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Paciente</label>
|
|
<InputText class="form-control" @bind-Value="ExtraInfo.Patient" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Fecha de Cirugía</label>
|
|
<InputDate class="form-control" @bind-Value="ExtraInfo.SurgeryDate" />
|
|
</div>
|
|
<div class="col-md-8">
|
|
<label class="form-label">Observaciones</label>
|
|
<InputTextArea class="form-control" @bind-Value="Model.Observations" />
|
|
</div>
|
|
</div>
|
|
|
|
@if (!string.IsNullOrWhiteSpace(DispatchInstruction))
|
|
{
|
|
<div class="alert alert-info">
|
|
<strong>Instrucciones desde presupuesto:</strong><br />
|
|
@DispatchInstruction
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mt-3" style="zoom:90%">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="card-title m-0">Productos a Expedir</h5>
|
|
<div class="btn-group">
|
|
<button class="btn btn-dark btn-sm" title="Agregar producto" @onclick="OpenStockItemSelectorModal">
|
|
<i class="fas fa-box"></i> Producto
|
|
</button>
|
|
<button class="btn btn-dark btn-sm" title="Agregar set">
|
|
<i class="fas fa-th-large"></i> Set-Box
|
|
</button>
|
|
<button class="btn btn-dark btn-sm" title="Escanear producto">
|
|
<i class="fas fa-barcode"></i> Scanner
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body p-2">
|
|
@if (Details.Any())
|
|
{
|
|
<table class="table table-sm table-bordered">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Producto</th>
|
|
<th>Cant.</th>
|
|
<th>Lote</th>
|
|
<th>Serial</th>
|
|
<th>Vencimiento</th>
|
|
<th>Ubicación</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var item in Details)
|
|
{
|
|
<tr>
|
|
<td>@item.ProductId</td>
|
|
<td>@item.Quantity</td>
|
|
<td>@item.Batch</td>
|
|
<td>@item.Serial</td>
|
|
<td>@item.Expiration?.ToString("yyyy-MM-dd")</td>
|
|
<td>@item.LocationId</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-danger" @onclick="() => RemoveItem(item)">✕</button>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
}
|
|
else
|
|
{
|
|
<p class="text-muted">No hay productos agregados.</p>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3 d-flex justify-content-end">
|
|
<button type="submit" class="btn btn-primary">Guardar Expedición</button>
|
|
</div>
|
|
</EditForm>
|
|
|
|
@code {
|
|
private ELSExpeditionHeader Model = new();
|
|
private ExtraInfoModel ExtraInfo = new();
|
|
private string DispatchInstruction = string.Empty;
|
|
private ELookUpItem? SelectedQuote;
|
|
private List<ELSExpeditionDetail> Details = new();
|
|
|
|
private List<ProductSetItemDto> ProductSetItems = new();
|
|
|
|
|
|
private string ticketIdString
|
|
{
|
|
get => Model.TicketId?.ToString() ?? string.Empty;
|
|
set => Model.TicketId = Guid.TryParse(value, out var guid) ? guid : null;
|
|
}
|
|
|
|
private async Task<IEnumerable<ELookUpItem>> SearchQuotes(string filter)
|
|
{
|
|
return await lookUpService.SearchApprovedQuotesAsync(filter);
|
|
}
|
|
|
|
private async Task OnQuoteSelected(ELookUpItem? selected)
|
|
{
|
|
if (selected is null || string.IsNullOrWhiteSpace(selected.Nombre))
|
|
{
|
|
SelectedQuote = null;
|
|
ExtraInfo = new(); // Limpiar datos cargados
|
|
DispatchInstruction = "";
|
|
return;
|
|
}
|
|
|
|
SelectedQuote = selected;
|
|
|
|
var quoteNumber = selected.Nombre.Split(" - ")[0];
|
|
var quote = await expeditionService.GetQuoteByNumberAsync(quoteNumber);
|
|
|
|
if (quote is null)
|
|
{
|
|
toastService.ShowError("No se pudo cargar el presupuesto.");
|
|
return;
|
|
}
|
|
|
|
ExtraInfo.Professional = quote.ProfessionalName;
|
|
ExtraInfo.Institution = quote.InstitutionName;
|
|
ExtraInfo.Patient = quote.PatientName;
|
|
ExtraInfo.SurgeryDate = quote.EstimatedDate;
|
|
DispatchInstruction = quote.Observations ?? "";
|
|
}
|
|
|
|
private void AddProduct()
|
|
{
|
|
// TODO: abrir modal de producto individual
|
|
}
|
|
|
|
private void AddSet()
|
|
{
|
|
// TODO: abrir modal de set
|
|
}
|
|
|
|
private void ScanProduct()
|
|
{
|
|
// TODO: activar input de escáner
|
|
}
|
|
|
|
private void RemoveItem(ELSExpeditionDetail item)
|
|
{
|
|
Details.Remove(item);
|
|
}
|
|
|
|
private async Task HandleValidSubmit()
|
|
{
|
|
// TODO: Lógica de guardado de la expedición completa
|
|
}
|
|
private async Task OpenStockItemSelectorModal()
|
|
{
|
|
|
|
var parameters = new ModalParameters();
|
|
parameters.Add(nameof(StockItemSelectorModal.SetItems), ProductSetItems); // o null
|
|
//parameters.Add(nameof(StockItemSelectorModal.LocationId), SelectedLocationId);
|
|
|
|
var options = new ModalOptions()
|
|
{
|
|
Size = ModalSize.Large,
|
|
HideHeader = true
|
|
};
|
|
var modal = Modal.Show<StockItemSelectorModal>("", parameters, options);
|
|
|
|
var result = await modal.Result;
|
|
|
|
if (!result.Cancelled && result.Data is List<StockItemSelectionDto> selectedItems)
|
|
{
|
|
foreach (var s in selectedItems)
|
|
{
|
|
var detail = new ELSExpeditionDetail
|
|
{
|
|
ProductId = s.ProductId,
|
|
Quantity = s.Quantity, // si es Serial*, probablemente 1
|
|
Batch = s.Batch,
|
|
Expiration = s.Expiration.HasValue
|
|
? DateOnly.FromDateTime(s.Expiration.Value)
|
|
: (DateOnly?)null,
|
|
Serial = s.Serial, // si es Serial*, probablemente null
|
|
LocationId = s.LocationId // si tu detalle lo maneja
|
|
};
|
|
|
|
Details.Add(detail);
|
|
}
|
|
|
|
StateHasChanged();
|
|
toastService.ShowSuccess($"{selectedItems.Count} item(s) agregados a la expedición.");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
private class ExtraInfoModel
|
|
{
|
|
public string? Professional { get; set; }
|
|
public string? Institution { get; set; }
|
|
public string? Patient { get; set; }
|
|
public DateTime? SurgeryDate { get; set; }
|
|
}
|
|
}
|