feat(sales): precargar ítems aprobados desde presupuesto en delivery note create #40

Merged
leandro merged 1 commits from feature/leandro/39-deliverynote-precarga-items into master 2026-03-24 21:06:18 +00:00
2 changed files with 100 additions and 7 deletions

View File

@ -2,14 +2,19 @@
@using System.ComponentModel.DataAnnotations @using System.ComponentModel.DataAnnotations
@using Blazored.Typeahead @using Blazored.Typeahead
@using Domain.Constants @using Domain.Constants
@using Domain.Dtos
@using Domain.Dtos.Sales @using Domain.Dtos.Sales
@using phronCare.UIBlazor.Services.Lookups @using phronCare.UIBlazor.Services.Lookups
@using phronCare.UIBlazor.Services.Sales.DeliveryNotes @using phronCare.UIBlazor.Services.Sales.DeliveryNotes
@using phronCare.UIBlazor.Services.Sales.Quotes
@using phronCare.UIBlazor.Shared.Modals
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject IDeliveryNoteService DeliveryNoteService @inject IDeliveryNoteService DeliveryNoteService
@inject ISalesLookupService SalesLookupService @inject ISalesLookupService SalesLookupService
@inject IQuoteService QuoteService
@inject IToastService toastService @inject IToastService toastService
@inject IModalService Modal
<EditForm Model="Model" OnValidSubmit="HandleValidSubmit"> <EditForm Model="Model" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator /> <DataAnnotationsValidator />
@ -90,7 +95,7 @@
@if (Items.Any()) @if (Items.Any())
{ {
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-sm table-bordered align-middle mb-0"> <table class="table table-sm table-bordered mb-0 deliverynote-items-table">
<thead class="table-light"> <thead class="table-light">
<tr> <tr>
<th style="width: 60px;" class="text-center">#</th> <th style="width: 60px;" class="text-center">#</th>
@ -105,9 +110,9 @@
@foreach (var item in Items) @foreach (var item in Items)
{ {
<tr> <tr>
<td class="text-center">@item.LineNumber</td> <td class="text-center line-number-cell">@item.LineNumber</td>
<td> <td>
<InputText class="form-control form-control-sm" @bind-Value="item.Description" /> <InputTextArea class="form-control form-control-sm item-description" rows="3" @bind-Value="item.Description" />
</td> </td>
<td> <td>
<InputNumber class="form-control form-control-sm text-end" @bind-Value="item.Quantity" /> <InputNumber class="form-control form-control-sm text-end" @bind-Value="item.Quantity" />
@ -121,9 +126,9 @@
</InputSelect> </InputSelect>
</td> </td>
<td> <td>
<InputText class="form-control form-control-sm" @bind-Value="item.Notes" /> <InputTextArea class="form-control form-control-sm item-notes" rows="3" @bind-Value="item.Notes" />
</td> </td>
<td class="text-center"> <td class="text-center actions-cell">
<button type="button" class="btn btn-link p-0 text-danger" title="Eliminar" @onclick="() => RemoveItem(item)"> <button type="button" class="btn btn-link p-0 text-danger" title="Eliminar" @onclick="() => RemoveItem(item)">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
@ -203,11 +208,66 @@
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task OnQuoteSelected(ELookUpItem? quote) private async Task OnQuoteSelected(ELookUpItem? quote)
{ {
SelectedQuote = quote; SelectedQuote = quote;
Model.QuoteId = quote?.Id; Model.QuoteId = quote?.Id;
return Task.CompletedTask;
if (quote is null)
return;
var quoteDto = await QuoteService.GetDtoByIdAsync(quote.Id);
if (quoteDto is null)
{
toastService.ShowError("No se pudo cargar el presupuesto seleccionado.");
return;
}
var mappedItems = BuildItemsFromApprovedQuote(quoteDto);
if (mappedItems.Count == 0)
{
toastService.ShowWarning("El presupuesto seleccionado no tiene ítems aprobados para precargar.");
return;
}
if (Items.Any())
{
var parameters = new ModalParameters();
parameters.Add(nameof(ConfirmModal.Title), "Reemplazar ítems");
parameters.Add(nameof(ConfirmModal.Message), "Ya hay ítems cargados. ¿Desea reemplazarlos por los ítems aprobados del presupuesto?");
var modal = Modal.Show<ConfirmModal>("Confirmación", parameters);
var result = await modal.Result;
if (result.Cancelled)
return;
}
Items = mappedItems;
ReindexItems();
StateHasChanged();
}
private List<DeliveryNoteItemRow> BuildItemsFromApprovedQuote(QuoteDto quote)
{
return quote.Items
.Where(item => item.Approved)
.Select(item => new
{
Item = item,
Quantity = item.ApprovedQuantity.HasValue && item.ApprovedQuantity.Value > 0
? item.ApprovedQuantity.Value
: item.Quantity
})
.Where(x => x.Quantity > 0)
.Select((x, index) => new DeliveryNoteItemRow
{
LineNumber = index + 1,
OriginType = (byte)DeliveryNoteItemOriginType.QuoteDetail,
QuoteDetailId = x.Item.Id,
Description = x.Item.Description,
Quantity = x.Quantity
})
.ToList();
} }
private string? ValidateBeforeSave() private string? ValidateBeforeSave()

View File

@ -0,0 +1,33 @@
.deliverynote-items-table td {
vertical-align: top;
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.deliverynote-items-table .line-number-cell,
.deliverynote-items-table .actions-cell {
vertical-align: middle;
}
.deliverynote-items-table .form-control,
.deliverynote-items-table .form-select {
min-height: 38px;
}
.deliverynote-items-table .item-description,
.deliverynote-items-table .item-notes {
min-height: calc(1.5em * 3 + 1rem + 2px);
resize: vertical;
}
.deliverynote-items-table textarea.form-control {
line-height: 1.35;
}
.deliverynote-items-table .text-end {
min-width: 90px;
}
.deliverynote-items-table .actions-cell .btn {
margin-top: 0.35rem;
}