@page "/salesdocuments/create" @using Domain.Constants @using Domain.Dtos.Sales @using Domain.Generics @using phronCare.UIBlazor.Services.Sales.SalesDocuments @inject NavigationManager Navigation @inject ISalesDocumentService SalesDocumentService @inject IToastService toastService

Nuevo Sales Document desde remitos

Sales Document representa el documento comercial facturable. No emite comprobante fiscal ni integra ARCA/AFIP.
Remitos emitidos
Seleccionados: @SelectedIds.Count
@if (Candidates.Items.Any()) {
@foreach (var item in Candidates.Items) { }
Remito Fecha Cliente fiscal Presupuesto Items Importe aprobado
@item.DeliveryNoteNumber @item.IssueDate.ToString("dd/MM/yyyy") @item.CustomerName @(item.QuoteNumber ?? item.QuoteId?.ToString() ?? "-") @item.ItemCount @item.ApprovedAmount.ToString("N2")
} else {
Busca remitos emitidos para seleccionar sus items facturables.
}
Items facturables
Lineas: @SelectedItemIds.Count
@if (IsLoadingItems) {
Cargando items...
} else if (ItemCandidates.Any()) {
@foreach (var item in ItemCandidates) { var isSelected = SelectedItemIds.Contains(item.DeliveryNoteDetailId); }
Remito Linea Descripcion Entregado Facturado Pendiente A facturar Precio Importe
@item.DeliveryNoteNumber @item.LineNumber @item.Description @item.DeliveredQuantity.ToString("N2") @item.AlreadyBilledQuantity.ToString("N2") @item.PendingQuantity.ToString("N2") @item.ApprovedUnitPrice.ToString("N2") @GetSelectedAmount(item).ToString("N2")
} else {
Selecciona uno o mas remitos para ver sus items con saldo pendiente.
}
Resumen comercial
@if (SelectedItems.Any()) {
Cliente fiscal:
@SelectedCustomerLabel
Remitos:
@SelectedDeliveryNoteCount
Total:
@SelectedTotal.ToString("N2")
Validacion:
@if (HasSingleFiscalCustomer && HasValidSelectedQuantities) { Seleccion valida } else { Revisar seleccion }
} else {
Selecciona items facturables para ver el resumen.
}
@code { private string? CustomerText; private string? DeliveryNoteNumber; private int? QuoteId; private DateTime? IssueDateFrom; private DateTime? IssueDateTo; private string? Observations; private bool IsLoading; private bool IsLoadingItems; private bool IsSaving; private PagedResult Candidates = new(); private List ItemCandidates = new(); private readonly HashSet SelectedIds = new(); private readonly Dictionary SelectedMap = new(); private readonly HashSet SelectedItemIds = new(); private List SelectedCandidates => SelectedMap.Values.OrderBy(x => x.IssueDate).ThenBy(x => x.Id).ToList(); private List SelectedItems => ItemCandidates .Where(x => SelectedItemIds.Contains(x.DeliveryNoteDetailId)) .OrderBy(x => x.DeliveryNoteIssueDate) .ThenBy(x => x.DeliveryNoteId) .ThenBy(x => x.LineNumber) .ToList(); private bool HasSingleFiscalCustomer => SelectedItems.Select(x => x.CustomerId).Distinct().Count() <= 1; private bool HasValidSelectedQuantities => SelectedItems.All(x => x.SelectedQuantity > 0 && x.SelectedQuantity <= x.PendingQuantity); private bool CanCreate => SelectedItems.Any() && HasSingleFiscalCustomer && HasValidSelectedQuantities && SelectedTotal > 0; private decimal SelectedTotal => SelectedItems.Sum(GetSelectedAmount); private int SelectedDeliveryNoteCount => SelectedItems.Select(x => x.DeliveryNoteId).Distinct().Count(); private string SelectedCustomerLabel => SelectedItems.FirstOrDefault()?.CustomerName ?? "-"; private async Task SearchCandidatesAsync() { try { IsLoading = true; Candidates = await SalesDocumentService.SearchDeliveryNoteCandidatesAsync( null, CustomerText, DeliveryNoteNumber, QuoteId, IssueDateFrom, IssueDateTo, 1, 50); SelectedIds.Clear(); SelectedMap.Clear(); ItemCandidates.Clear(); SelectedItemIds.Clear(); } catch (Exception ex) { toastService.ShowError(ex.Message); } finally { IsLoading = false; } } private async Task ToggleSelectionAsync(SalesDocumentDeliveryNoteCandidateDto item, ChangeEventArgs args) { var selected = args.Value is bool value && value; if (selected) { SelectedIds.Add(item.Id); SelectedMap[item.Id] = item; } else { SelectedIds.Remove(item.Id); SelectedMap.Remove(item.Id); } await LoadItemCandidatesAsync(); } private async Task LoadItemCandidatesAsync() { try { IsLoadingItems = true; ItemCandidates.Clear(); SelectedItemIds.Clear(); if (SelectedIds.Count == 0) return; ItemCandidates = await SalesDocumentService.GetDeliveryNoteItemCandidatesAsync(SelectedIds); foreach (var item in ItemCandidates) SelectedItemIds.Add(item.DeliveryNoteDetailId); } catch (Exception ex) { toastService.ShowError(ex.Message); } finally { IsLoadingItems = false; } } private void ToggleItemSelection(SalesDocumentDeliveryNoteItemCandidateDto item, ChangeEventArgs args) { var selected = args.Value is bool value && value; if (selected) SelectedItemIds.Add(item.DeliveryNoteDetailId); else SelectedItemIds.Remove(item.DeliveryNoteDetailId); } private void ClearFilters() { CustomerText = null; DeliveryNoteNumber = null; QuoteId = null; IssueDateFrom = null; IssueDateTo = null; Candidates = new PagedResult(); SelectedIds.Clear(); SelectedMap.Clear(); ItemCandidates.Clear(); SelectedItemIds.Clear(); } private async Task CreateAsync() { if (!CanCreate) { toastService.ShowError("Debe seleccionar items pendientes de un unico cliente fiscal y cantidades validas."); return; } try { IsSaving = true; var created = await SalesDocumentService.CreateFromDeliveryNoteItemsAsync(new SalesDocumentCreateFromDeliveryNoteItemsRequest { Items = SelectedItems.Select(x => new SalesDocumentDeliveryNoteItemSelectionDto { DeliveryNoteDetailId = x.DeliveryNoteDetailId, SelectedQuantity = x.SelectedQuantity }).ToList(), DocumentType = (int)SalesDocumentType.Invoice, IssueDate = DateTime.Today, Currency = "ARS", ExchangeRate = 1, Observations = Observations }); toastService.ShowSuccess("Sales Document creado correctamente desde items de remitos."); Navigation.NavigateTo($"/salesdocuments/{created.Id}"); } catch (Exception ex) { toastService.ShowError(ex.Message); } finally { IsSaving = false; } } private static decimal GetSelectedAmount(SalesDocumentDeliveryNoteItemCandidateDto item) { return item.ApprovedUnitPrice * item.SelectedQuantity; } private void BackToList() => Navigation.NavigateTo("/salesdocuments"); }