Merge pull request 'Correcion pendiente, colores de background, textos, menues y login redirect' (#80) from feature/web/078-backoffice-ui-fix-sales-colors into master
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 4m27s

Reviewed-on: #80
This commit is contained in:
Leandro Hernan Rojas 2026-07-24 19:04:12 +00:00
commit 3d9d9f2ed9
8 changed files with 151 additions and 65 deletions

View File

@ -21,7 +21,7 @@
<div class="@($"{NavMenuCssClass} nav-scrollable {(navMenuService.Minimized ? "nav-minimized" : string.Empty)}")" @onclick="ToggleNavMenu"> <div class="@($"{NavMenuCssClass} nav-scrollable {(navMenuService.Minimized ? "nav-minimized" : string.Empty)}")" @onclick="ToggleNavMenu">
<nav class="flex-column" aria-label="Navegación principal"> <nav class="flex-column" aria-label="Navegación principal">
<div class="nav-item px-2 py-0"> <div class="nav-item px-2 py-0">
<NavLink class="nav-link text-white d-flex align-items-center gap-2 py-0 px-2" href="/" Match="NavLinkMatch.All" activeClass="bg-secondary text-white"> <NavLink class="nav-link text-white d-flex align-items-center gap-2 py-0 px-2" href="/" Match="NavLinkMatch.All" activeClass="active text-white fw-semibold">
<span class="oi oi-home" aria-hidden="true"></span> <span class="oi oi-home" aria-hidden="true"></span>
@if (!navMenuService.Minimized) @if (!navMenuService.Minimized)
{ {
@ -32,7 +32,7 @@
<AuthorizeView Roles="Admin"> <AuthorizeView Roles="Admin">
<div class="nav-item px-2 py-0"> <div class="nav-item px-2 py-0">
<NavLink class="nav-link text-white d-flex align-items-center gap-2 py-0 px-2" href="roles" activeClass="bg-secondary text-white"> <NavLink class="nav-link text-white d-flex align-items-center gap-2 py-0 px-2" href="roles" activeClass="active text-white fw-semibold">
<span class="oi oi-shield" aria-hidden="true"></span> <span class="oi oi-shield" aria-hidden="true"></span>
@if (!navMenuService.Minimized) @if (!navMenuService.Minimized)
{ {
@ -41,7 +41,7 @@
</NavLink> </NavLink>
</div> </div>
<div class="nav-item px-2 py-0"> <div class="nav-item px-2 py-0">
<NavLink class="nav-link text-white d-flex align-items-center gap-2 py-0 px-2" href="users" activeClass="bg-secondary text-white"> <NavLink class="nav-link text-white d-flex align-items-center gap-2 py-0 px-2" href="users" activeClass="active text-white fw-semibold">
<span class="oi oi-people" aria-hidden="true"></span> <span class="oi oi-people" aria-hidden="true"></span>
@if (!navMenuService.Minimized) @if (!navMenuService.Minimized)
{ {
@ -64,46 +64,46 @@
<ul class="nav flex-column"> <ul class="nav flex-column">
<!-- Expediciones --> <!-- Expediciones -->
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="expeditions" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="expeditions" activeClass="active text-white fw-semibold">
<span class="oi oi-data-transfer-download me-2 text-danger"></span> Expediciones <span class="oi oi-data-transfer-download me-2 text-danger"></span> Expediciones
</NavLink> </NavLink>
</div> </div>
<!-- Productos --> <!-- Productos -->
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/products" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/products" activeClass="active text-white fw-semibold">
<span class="oi oi-tags me-2 text-warning"></span> Productos <span class="oi oi-tags me-2 text-warning"></span> Productos
</NavLink> </NavLink>
</div> </div>
<!-- Divisiones de productos --> <!-- Divisiones de productos -->
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/productdivisions" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/productdivisions" activeClass="active text-white fw-semibold">
<span class="oi oi-grid-three-up me-2 text-info"></span> Divisiones <span class="oi oi-grid-three-up me-2 text-info"></span> Divisiones
</NavLink> </NavLink>
</div> </div>
<!-- Unidades de medida --> <!-- Unidades de medida -->
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/units" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/units" activeClass="active text-white fw-semibold">
<span class="oi oi-resize-both me-2 text-success"></span> Unidades <span class="oi oi-resize-both me-2 text-success"></span> Unidades
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/locations" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/locations" activeClass="active text-white fw-semibold">
<span class="oi oi-map-marker me-2 text-success"></span> Ubicaciones <span class="oi oi-map-marker me-2 text-success"></span> Ubicaciones
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/current" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/current" activeClass="active text-white fw-semibold">
<span class="oi oi-inbox me-2 text-info"></span> Stock actual <span class="oi oi-inbox me-2 text-info"></span> Stock actual
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/movements" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/movements" activeClass="active text-white fw-semibold">
<span class="oi oi-loop-circular me-2 text-primary"></span> Movimientos <span class="oi oi-loop-circular me-2 text-primary"></span> Movimientos
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/consumes" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="stock/consumes" activeClass="active text-white fw-semibold">
<span class="oi oi-check me-2 text-secondary"></span> Consumos <span class="oi oi-check me-2 text-secondary"></span> Consumos
</NavLink> </NavLink>
</div> </div>
@ -124,47 +124,47 @@
{ {
<ul class="nav flex-column"> <ul class="nav flex-column">
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/customers/" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/customers/" activeClass="active text-white fw-semibold">
<i class="bi bi-person-lines-fill me-2 text-primary"></i> Clientes <i class="bi bi-person-lines-fill me-2 text-primary"></i> Clientes
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="quotes/dashboard" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="quotes/dashboard" activeClass="active text-white fw-semibold">
<i class="bi bi-receipt me-2 text-white"></i> Presupuesto <i class="bi bi-receipt me-2 text-white"></i> Presupuesto
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="deliverynotes" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="deliverynotes" activeClass="active text-white fw-semibold">
<i class="bi bi-truck me-2 text-warning"></i> Remitos <i class="bi bi-truck me-2 text-warning"></i> Remitos
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="salesdocuments" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="salesdocuments" activeClass="active text-white fw-semibold">
<i class="bi bi-receipt-cutoff me-2 text-success"></i> Sales Documents <i class="bi bi-receipt-cutoff me-2 text-success"></i> Facturación
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/institutions/" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/institutions/" activeClass="active text-white fw-semibold">
<i class="bi bi-building me-2 text-info"></i> Instituciones <i class="bi bi-building me-2 text-info"></i> Instituciones
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/patients/" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/patients/" activeClass="active text-white fw-semibold">
<i class="bi bi-person-heart me-2 text-danger"></i> Pacientes <i class="bi bi-person-heart me-2 text-danger"></i> Pacientes
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/products/" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/products/" activeClass="active text-white fw-semibold">
<i class="bi bi-box-seam me-2 text-warning"></i> Productos <i class="bi bi-box-seam me-2 text-warning"></i> Productos
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/professionals/" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/professionals/" activeClass="active text-white fw-semibold">
<i class="bi bi-person-badge me-2 text-info"></i> Profesionales <i class="bi bi-person-badge me-2 text-info"></i> Profesionales
</NavLink> </NavLink>
</div> </div>
<div class="nav-item ps-4 py-0 border-start border-2 border-white"> <div class="nav-item ps-4 py-0 border-start border-2 border-white">
<NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/people/" activeClass="bg-secondary text-white fw-semibold"> <NavLink class="nav-link small py-0 px-2 text-start d-flex align-items-center" href="sales/people/" activeClass="active text-white fw-semibold">
<i class="bi bi-people me-2 text-success"></i> Vendedores <i class="bi bi-people me-2 text-success"></i> Vendedores
</NavLink> </NavLink>
</div> </div>
@ -173,7 +173,7 @@
</div> </div>
<div class="nav-item px-2 py-0"> <div class="nav-item px-2 py-0">
<NavLink class="nav-link text-white d-flex align-items-center gap-2 py-0 px-2" href="tickets/ticketform/" activeClass="bg-secondary text-white"> <NavLink class="nav-link text-white d-flex align-items-center gap-2 py-0 px-2" href="tickets/ticketform/" activeClass="active text-white fw-semibold">
<span class="oi oi-list-rich" aria-hidden="true"></span> <span class="oi oi-list-rich" aria-hidden="true"></span>
@if (!navMenuService.Minimized) @if (!navMenuService.Minimized)
{ {

View File

@ -153,7 +153,7 @@
await _iLoginService.Login(tokenResponse); await _iLoginService.Login(tokenResponse);
_navigationManager.NavigateTo("/registration-confirmation"); _navigationManager.NavigateTo("/");
} }
else if (response.StatusCode == HttpStatusCode.BadRequest) else if (response.StatusCode == HttpStatusCode.BadRequest)
{ {

View File

@ -2,19 +2,20 @@
@using Domain.Constants @using Domain.Constants
@using Domain.Dtos.Sales @using Domain.Dtos.Sales
@using Domain.Generics @using Domain.Generics
@using System.Globalization
@using phronCare.UIBlazor.Services.Sales.SalesDocuments @using phronCare.UIBlazor.Services.Sales.SalesDocuments
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject ISalesDocumentService SalesDocumentService @inject ISalesDocumentService SalesDocumentService
@inject IToastService toastService @inject IToastService toastService
<div class="container mt-4" style="zoom:.8;"> <div class="sales-document-create container mt-4" style="zoom:.8;">
<div class="card shadow-sm mb-3"> <div class="card shadow-sm mb-3">
<div class="card-header d-flex justify-content-center align-items-center"> <div class="card-header d-flex justify-content-center align-items-center">
<h3 class="mb-0">Nuevo Sales Document desde remitos</h3> <h3 class="mb-0">Nuevo documento de venta desde remitos</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="alert alert-info mb-3"> <div class="alert alert-info mb-3">
Sales Document representa el documento comercial facturable. No emite comprobante fiscal ni integra ARCA/AFIP. El documento de venta representa el documento comercial facturable. No emite comprobante fiscal ni integra ARCA/AFIP.
</div> </div>
<div class="row g-2 mb-3"> <div class="row g-2 mb-3">
@ -24,7 +25,7 @@
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<label class="form-label">Remito</label> <label class="form-label">Remito</label>
<input class="form-control" @bind="DeliveryNoteNumber" placeholder="Numero de remito..." /> <input class="form-control" @bind="DeliveryNoteNumber" placeholder="Número de remito..." />
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label">Presupuesto ID</label> <label class="form-label">Presupuesto ID</label>
@ -56,13 +57,13 @@
<div class="card shadow-sm mb-3"> <div class="card shadow-sm mb-3">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Remitos emitidos</h5> <h5 class="mb-0">Remitos emitidos</h5>
<span class="badge bg-secondary">Seleccionados: @SelectedIds.Count</span> <span class="badge sales-count-badge">Seleccionados: @SelectedIds.Count</span>
</div> </div>
<div class="card-body p-2"> <div class="card-body p-2">
@if (Candidates.Items.Any()) @if (Candidates.Items.Any())
{ {
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-sm table-hover align-middle mb-0"> <table class="table table-sm table-hover align-middle mb-0 sales-table remittances-table">
<thead class="table-light"> <thead class="table-light">
<tr> <tr>
<th style="width:48px;"></th> <th style="width:48px;"></th>
@ -70,14 +71,14 @@
<th>Fecha</th> <th>Fecha</th>
<th>Cliente fiscal</th> <th>Cliente fiscal</th>
<th>Presupuesto</th> <th>Presupuesto</th>
<th class="text-end">Items</th> <th class="text-end">Ítems</th>
<th class="text-end">Importe aprobado</th> <th class="text-end">Importe aprobado</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var item in Candidates.Items) @foreach (var item in Candidates.Items)
{ {
<tr class="@(SelectedIds.Contains(item.Id) ? "table-primary" : string.Empty)"> <tr class="@(SelectedIds.Contains(item.Id) ? "sales-row-selected" : string.Empty)">
<td class="text-center"> <td class="text-center">
<input type="checkbox" class="form-check-input" checked="@SelectedIds.Contains(item.Id)" @onchange="async args => await ToggleSelectionAsync(item, args)" /> <input type="checkbox" class="form-check-input" checked="@SelectedIds.Contains(item.Id)" @onchange="async args => await ToggleSelectionAsync(item, args)" />
</td> </td>
@ -95,34 +96,34 @@
} }
else else
{ {
<div class="text-center text-muted py-4">Busca remitos emitidos para seleccionar sus items facturables.</div> <div class="text-center text-muted py-4">Buscá remitos emitidos para seleccionar sus ítems facturables.</div>
} }
</div> </div>
</div> </div>
<div class="card shadow-sm mb-3"> <div class="card shadow-sm mb-3">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Items facturables</h5> <h5 class="mb-0">Ítems facturables</h5>
<span class="badge bg-secondary">Lineas: @SelectedItemIds.Count</span> <span class="badge sales-count-badge">Líneas: @SelectedItemIds.Count</span>
</div> </div>
<div class="card-body p-2"> <div class="card-body p-2">
@if (IsLoadingItems) @if (IsLoadingItems)
{ {
<div class="text-center text-muted py-4"> <div class="text-center text-muted py-4">
<span class="spinner-border spinner-border-sm me-2"></span> <span class="spinner-border spinner-border-sm me-2"></span>
Cargando items... Cargando ítems...
</div> </div>
} }
else if (ItemCandidates.Any()) else if (ItemCandidates.Any())
{ {
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-sm table-hover align-middle mb-0"> <table class="table table-sm table-hover align-middle mb-0 sales-table billable-items-table">
<thead class="table-light"> <thead class="table-light">
<tr> <tr>
<th style="width:48px;"></th> <th style="width:48px;"></th>
<th>Remito</th> <th>Remito</th>
<th>Linea</th> <th>Línea</th>
<th>Descripcion</th> <th>Descripción</th>
<th class="text-end">Entregado</th> <th class="text-end">Entregado</th>
<th class="text-end">Facturado</th> <th class="text-end">Facturado</th>
<th class="text-end">Pendiente</th> <th class="text-end">Pendiente</th>
@ -135,25 +136,25 @@
@foreach (var item in ItemCandidates) @foreach (var item in ItemCandidates)
{ {
var isSelected = SelectedItemIds.Contains(item.DeliveryNoteDetailId); var isSelected = SelectedItemIds.Contains(item.DeliveryNoteDetailId);
<tr class="@(isSelected ? "table-primary" : string.Empty)"> <tr class="@(isSelected ? "sales-row-selected" : string.Empty)">
<td class="text-center"> <td class="text-center">
<input type="checkbox" class="form-check-input" checked="@isSelected" @onchange="args => ToggleItemSelection(item, args)" /> <input type="checkbox" class="form-check-input" checked="@isSelected" @onchange="args => ToggleItemSelection(item, args)" />
</td> </td>
<td>@item.DeliveryNoteNumber</td> <td>@item.DeliveryNoteNumber</td>
<td>@item.LineNumber</td> <td>@item.LineNumber</td>
<td>@item.Description</td> <td class="description-cell">
<span class="item-description" title="@item.Description">@item.Description</span>
</td>
<td class="text-end">@item.DeliveredQuantity.ToString("N2")</td> <td class="text-end">@item.DeliveredQuantity.ToString("N2")</td>
<td class="text-end">@item.AlreadyBilledQuantity.ToString("N2")</td> <td class="text-end">@item.AlreadyBilledQuantity.ToString("N2")</td>
<td class="text-end">@item.PendingQuantity.ToString("N2")</td> <td class="text-end">@item.PendingQuantity.ToString("N2")</td>
<td> <td>
<input type="number" <input type="text"
class="form-control form-control-sm text-end" class="form-control form-control-sm text-end"
min="0" inputmode="decimal"
step="0.01"
max="@item.PendingQuantity"
disabled="@(!isSelected)" disabled="@(!isSelected)"
@bind="item.SelectedQuantity" value="@FormatQuantity(item.SelectedQuantity)"
@bind:event="oninput" /> @oninput="args => UpdateSelectedQuantity(item, args.Value?.ToString())" />
</td> </td>
<td class="text-end">@item.ApprovedUnitPrice.ToString("N2")</td> <td class="text-end">@item.ApprovedUnitPrice.ToString("N2")</td>
<td class="text-end">@GetSelectedAmount(item).ToString("N2")</td> <td class="text-end">@GetSelectedAmount(item).ToString("N2")</td>
@ -165,7 +166,7 @@
} }
else else
{ {
<div class="text-center text-muted py-4">Selecciona uno o mas remitos para ver sus items con saldo pendiente.</div> <div class="text-center text-muted py-4">Seleccioná uno o más remitos para ver sus ítems con saldo pendiente.</div>
} }
</div> </div>
</div> </div>
@ -187,14 +188,14 @@
<strong>Total:</strong><br />@SelectedTotal.ToString("N2") <strong>Total:</strong><br />@SelectedTotal.ToString("N2")
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<strong>Validacion:</strong><br /> <strong>Validación:</strong><br />
@if (HasSingleFiscalCustomer && HasValidSelectedQuantities) @if (HasSingleFiscalCustomer && HasValidSelectedQuantities)
{ {
<span class="badge bg-success">Seleccion valida</span> <span class="badge bg-success">Selección válida</span>
} }
else else
{ {
<span class="badge bg-danger">Revisar seleccion</span> <span class="badge bg-danger">Revisar selección</span>
} }
</div> </div>
</div> </div>
@ -205,7 +206,7 @@
} }
else else
{ {
<div class="text-muted">Selecciona items facturables para ver el resumen.</div> <div class="text-muted">Seleccioná ítems facturables para ver el resumen.</div>
} }
</div> </div>
</div> </div>
@ -219,7 +220,7 @@
{ {
<span class="spinner-border spinner-border-sm me-2"></span> <span class="spinner-border spinner-border-sm me-2"></span>
} }
<i class="fas fa-save me-1"></i> Crear Sales Document <i class="fas fa-save me-1"></i> Crear documento de venta
</button> </button>
</div> </div>
</div> </div>
@ -336,6 +337,22 @@
SelectedItemIds.Remove(item.DeliveryNoteDetailId); SelectedItemIds.Remove(item.DeliveryNoteDetailId);
} }
private static string FormatQuantity(decimal value) =>
value.ToString("N2", CultureInfo.GetCultureInfo("es-AR"));
private static void UpdateSelectedQuantity(SalesDocumentDeliveryNoteItemCandidateDto item, string? rawValue)
{
if (string.IsNullOrWhiteSpace(rawValue))
{
item.SelectedQuantity = 0;
return;
}
var normalizedValue = rawValue.Trim().Replace(".", ",");
if (decimal.TryParse(normalizedValue, NumberStyles.Number, CultureInfo.GetCultureInfo("es-AR"), out var quantity))
item.SelectedQuantity = Math.Clamp(quantity, 0, item.PendingQuantity);
}
private void ClearFilters() private void ClearFilters()
{ {
CustomerText = null; CustomerText = null;
@ -354,7 +371,7 @@
{ {
if (!CanCreate) if (!CanCreate)
{ {
toastService.ShowError("Debe seleccionar items pendientes de un unico cliente fiscal y cantidades validas."); toastService.ShowError("Debe seleccionar ítems pendientes de un único cliente fiscal y cantidades válidas.");
return; return;
} }
@ -375,7 +392,7 @@
Observations = Observations Observations = Observations
}); });
toastService.ShowSuccess("Sales Document creado correctamente desde items de remitos."); toastService.ShowSuccess("Documento de venta creado correctamente desde ítems de remitos.");
Navigation.NavigateTo($"/salesdocuments/{created.Id}"); Navigation.NavigateTo($"/salesdocuments/{created.Id}");
} }
catch (Exception ex) catch (Exception ex)

View File

@ -0,0 +1,69 @@
.sales-document-create .card {
box-shadow: 0 8px 24px color-mix(in srgb, var(--ph-canvas) 35%, transparent);
}
.sales-document-create .card-header {
background: linear-gradient(
90deg,
color-mix(in srgb, var(--ph-primary-soft) 72%, var(--ph-surface)),
var(--ph-surface)
);
}
.sales-document-create .sales-count-badge {
color: var(--ph-primary);
background: var(--ph-primary-soft);
border: 1px solid color-mix(in srgb, var(--ph-primary) 32%, var(--ph-border));
}
.sales-document-create .sales-table {
min-width: 900px;
}
.sales-document-create .billable-items-table {
min-width: 1280px;
}
.sales-document-create .sales-table thead th {
color: var(--ph-text);
background: color-mix(in srgb, var(--ph-primary-soft) 52%, var(--ph-surface-subtle));
border-bottom-color: var(--ph-border-strong);
}
.sales-document-create .sales-table tbody tr:nth-child(even):not(.sales-row-selected) > * {
background: color-mix(in srgb, var(--ph-surface-subtle) 72%, transparent);
}
.sales-document-create .sales-table tbody tr.sales-row-selected > * {
color: var(--ph-text) !important;
background: color-mix(in srgb, var(--ph-primary-soft) 78%, var(--ph-surface)) !important;
border-bottom-color: color-mix(in srgb, var(--ph-primary) 28%, var(--ph-border)) !important;
}
.sales-document-create .sales-table tbody tr.sales-row-selected > *:first-child {
box-shadow: inset 4px 0 0 var(--ph-primary);
}
.sales-document-create .sales-table tbody tr:hover > * {
color: var(--ph-text) !important;
}
.sales-document-create .description-cell {
width: 42%;
min-width: 390px;
}
.sales-document-create .item-description {
display: -webkit-box;
overflow: hidden;
color: var(--ph-text);
line-height: 1.45;
text-transform: none;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
.sales-document-create .billable-items-table td:not(.description-cell),
.sales-document-create .billable-items-table th:not(:nth-child(4)) {
white-space: nowrap;
}

View File

@ -8,7 +8,7 @@
<div class="container mt-4" style="zoom:.8;"> <div class="container mt-4" style="zoom:.8;">
<div class="d-flex justify-content-between align-items-center mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<h3 class="mb-0">Sales Document</h3> <h3 class="mb-0">Documento de venta</h3>
<div class="d-flex flex-wrap gap-2"> <div class="d-flex flex-wrap gap-2">
@if (Document?.Status == (int)SalesDocumentStatus.Draft) @if (Document?.Status == (int)SalesDocumentStatus.Draft)
{ {
@ -28,7 +28,7 @@
} }
else if (Document is null) else if (Document is null)
{ {
<div class="alert alert-warning">No se pudo cargar el Sales Document.</div> <div class="alert alert-warning">No se pudo cargar el documento de venta.</div>
} }
else else
{ {

View File

@ -10,7 +10,7 @@
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3"> <div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
<div> <div>
<h3 class="mb-1">Revision administrativa</h3> <h3 class="mb-1">Revision administrativa</h3>
<div class="text-muted">Sales Document Draft Review & Validation</div> <div class="text-muted">Revisión y validación del borrador del documento de venta</div>
</div> </div>
<div class="d-flex flex-wrap gap-2"> <div class="d-flex flex-wrap gap-2">
<button type="button" class="btn btn-secondary rounded-pill" @onclick="BackToList"> <button type="button" class="btn btn-secondary rounded-pill" @onclick="BackToList">
@ -30,7 +30,7 @@
} }
else if (Document is null) else if (Document is null)
{ {
<div class="alert alert-warning">No se pudo cargar el Sales Document.</div> <div class="alert alert-warning">No se pudo cargar el documento de venta.</div>
} }
else else
{ {
@ -361,7 +361,7 @@
IsSaving = true; IsSaving = true;
var preview = await SalesDocumentService.ValidateDraftAsync(Id); var preview = await SalesDocumentService.ValidateDraftAsync(Id);
ApplyPreview(preview); ApplyPreview(preview);
toastService.ShowSuccess("Sales Document validado correctamente."); toastService.ShowSuccess("Documento de venta validado correctamente.");
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -10,7 +10,7 @@
<div class="card shadow-sm mb-3" style="zoom: 0.8;"> <div class="card shadow-sm mb-3" style="zoom: 0.8;">
<div class="card-header py-2"> <div class="card-header py-2">
<div class="d-flex justify-content-center align-items-center"> <div class="d-flex justify-content-center align-items-center">
<h3 class="card-title m-0">Consulta de Sales Documents</h3> <h3 class="card-title m-0">Consulta de documentos de venta</h3>
</div> </div>
</div> </div>
<div class="card-body pt-2 pb-0"> <div class="card-body pt-2 pb-0">

View File

@ -89,7 +89,7 @@ namespace phronCare.UIBlazor.Services.Sales.SalesDocuments
{ {
var serverMessage = await response.Content.ReadAsStringAsync(); var serverMessage = await response.Content.ReadAsStringAsync();
throw new Exception(string.IsNullOrWhiteSpace(serverMessage) throw new Exception(string.IsNullOrWhiteSpace(serverMessage)
? "No se pudo crear el Sales Document desde remitos." ? "No se pudo crear el documento de venta desde remitos."
: serverMessage); : serverMessage);
} }
@ -125,7 +125,7 @@ namespace phronCare.UIBlazor.Services.Sales.SalesDocuments
{ {
var serverMessage = await response.Content.ReadAsStringAsync(); var serverMessage = await response.Content.ReadAsStringAsync();
throw new Exception(string.IsNullOrWhiteSpace(serverMessage) throw new Exception(string.IsNullOrWhiteSpace(serverMessage)
? "No se pudo crear el Sales Document desde items de remitos." ? "No se pudo crear el documento de venta desde ítems de remitos."
: serverMessage); : serverMessage);
} }
@ -153,7 +153,7 @@ namespace phronCare.UIBlazor.Services.Sales.SalesDocuments
{ {
var serverMessage = await response.Content.ReadAsStringAsync(); var serverMessage = await response.Content.ReadAsStringAsync();
throw new Exception(string.IsNullOrWhiteSpace(serverMessage) throw new Exception(string.IsNullOrWhiteSpace(serverMessage)
? "No se pudo guardar la revision del Sales Document." ? "No se pudo guardar la revisión del documento de venta."
: serverMessage); : serverMessage);
} }
@ -169,7 +169,7 @@ namespace phronCare.UIBlazor.Services.Sales.SalesDocuments
{ {
var serverMessage = await response.Content.ReadAsStringAsync(); var serverMessage = await response.Content.ReadAsStringAsync();
throw new Exception(string.IsNullOrWhiteSpace(serverMessage) throw new Exception(string.IsNullOrWhiteSpace(serverMessage)
? "No se pudo validar el Sales Document." ? "No se pudo validar el documento de venta."
: serverMessage); : serverMessage);
} }
@ -187,7 +187,7 @@ namespace phronCare.UIBlazor.Services.Sales.SalesDocuments
{ {
var serverMessage = await response.Content.ReadAsStringAsync(); var serverMessage = await response.Content.ReadAsStringAsync();
throw new Exception(string.IsNullOrWhiteSpace(serverMessage) throw new Exception(string.IsNullOrWhiteSpace(serverMessage)
? "No se pudo crear el Sales Document." ? "No se pudo crear el documento de venta."
: serverMessage); : serverMessage);
} }