Add StockItemModal v1
Some checks failed
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Failing after 15m47s
Some checks failed
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Failing after 15m47s
This commit is contained in:
parent
626f57aab3
commit
1c4c241266
@ -12,5 +12,7 @@ namespace Core.Interfaces
|
||||
Task<IEnumerable<ELookUpItem>> BussinessUnitsListAsync(string filter, int limit = 10);
|
||||
Task<IEnumerable<EProductLookupItem>> ProductsListAsync(string filter, int limit = 10);
|
||||
Task<IEnumerable<ELookUpItem>> PaymentTermsListAsync(string filter, int limit = 10);
|
||||
Task<IEnumerable<ELookUpItem>> ApprovedQuotesListAsync(string filter, int limit = 10);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,12 +7,8 @@ namespace Models.Interfaces
|
||||
public interface IQuoteDom
|
||||
{
|
||||
#region Presupuestos
|
||||
//Task<PagedResult<EQuoteHeader>> GetAllQuotesAsync(int page = 1, int pageSize = 50);
|
||||
//Task<EQuoteHeader?> GetQuoteByIdAsync(int id);
|
||||
//Task<IEnumerable<EQuoteHeader>> GetQuotesByCustomerAsync(int customerId);
|
||||
Task<PagedResult<QuoteDto>> SearchAsync(int? customerId, string? customerText, string? quoteNumber, int? professionalId, string? professionalText, int? institutionId, string? institutionText, int? patientId, string? patientText, DateTime? issueDateFrom, DateTime? issueDateTo, string? status, int page = 1, int pageSize = 50);
|
||||
//Task UpdateQuoteAsync(EQuoteHeader quote);
|
||||
//Task DeleteQuoteAsync(int id);
|
||||
Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber);
|
||||
#endregion
|
||||
#region Exportación
|
||||
//Task<byte[]> ExportFilteredQuotesToExcelAsync(QuoteSearchParams searchParams);
|
||||
|
||||
12
Core/Interfaces/Stock/ILSStockScanDom.cs
Normal file
12
Core/Interfaces/Stock/ILSStockScanDom.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Domain.Dtos.Stock;
|
||||
using Domain.Generics;
|
||||
|
||||
namespace Core.Interfaces.Stock
|
||||
{
|
||||
public interface ILSStockScanDom
|
||||
{
|
||||
Task<PagedResult<StockItemScanResultDto>> SearchAsync(StockItemSearchParams p);
|
||||
Task<PagedResult<StockItemScanResultDto>> SearchParsedAsync(StockItemParsedSearchParams p, CancellationToken ct = default);
|
||||
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,8 @@ namespace Core.Services
|
||||
_repository.ProductsListAsync(filter);
|
||||
public Task<IEnumerable<ELookUpItem>> PaymentTermsListAsync(string filter, int limit = 10)
|
||||
=> _repository.PaymentTermsListAsync(filter, limit);
|
||||
|
||||
public Task<IEnumerable<ELookUpItem>> ApprovedQuotesListAsync(string filter, int limit = 10) =>
|
||||
_repository.ApprovedQuotesListAsync(filter, limit);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +37,11 @@ namespace Core.Services
|
||||
{
|
||||
return await _quoteRepository.GetDtoByIdAsync(id);
|
||||
}
|
||||
|
||||
//public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
|
||||
//{
|
||||
// return await _quoteRepository.GetDtoByIdAsync(quoteNumber);
|
||||
//}
|
||||
#endregion
|
||||
|
||||
#region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
|
||||
@ -74,7 +79,6 @@ namespace Core.Services
|
||||
return await _quoteRepository.AuthorizeQuoteAsync(quoteId, approvedDetails);
|
||||
}
|
||||
|
||||
|
||||
#region Validaciones QuoteCreate
|
||||
private void ValidateQuote(EQuoteHeader quote)
|
||||
{
|
||||
@ -125,6 +129,10 @@ namespace Core.Services
|
||||
throw new ArgumentException("El total del presupuesto no puede ser negativo.");
|
||||
}
|
||||
|
||||
public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
|
||||
{
|
||||
return await _quoteRepository.GetDtoByQuoteNumberAsync(quoteNumber);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -68,6 +68,7 @@ namespace Core.Services
|
||||
p.Id,
|
||||
p.FactoryCode,
|
||||
p.ExternalCode,
|
||||
p.RegulatoryCode,
|
||||
p.Name,
|
||||
p.Descripcion,
|
||||
Tipo = p.ProductType == 1 ? "Implantable" :
|
||||
|
||||
45
Core/Services/Stock/LSStockScanService.cs
Normal file
45
Core/Services/Stock/LSStockScanService.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using Core.Interfaces.Stock;
|
||||
using Domain.Dtos.Stock;
|
||||
using Domain.Generics;
|
||||
using Models.Interfaces;
|
||||
|
||||
namespace Core.Services.Stock
|
||||
{
|
||||
public class LSStockScanService : ILSStockScanDom
|
||||
{
|
||||
#region Declaraciones y Constructor
|
||||
private readonly IPhLSMStockItemRepository _repo;
|
||||
// Constructor injection for repository dependency
|
||||
public LSStockScanService(IPhLSMStockItemRepository repo) => _repo = repo;
|
||||
#endregion
|
||||
#region Metodos
|
||||
public Task<PagedResult<StockItemScanResultDto>> SearchAsync(StockItemSearchParams p)
|
||||
=> _repo.SearchStockItemsAsync(
|
||||
p.CodeOrText,
|
||||
p.Batch,
|
||||
p.LocationId,
|
||||
p.ProductType,
|
||||
p.TraceabilityType,
|
||||
p.PlusProcess,
|
||||
p.Page,
|
||||
p.PageSize);
|
||||
public Task<PagedResult<StockItemScanResultDto>> SearchParsedAsync(
|
||||
StockItemParsedSearchParams p,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var page = p.Page <= 0 ? 1 : p.Page;
|
||||
var take = p.PageSize <= 0 ? 20 : p.PageSize;
|
||||
|
||||
return _repo.SearchStockItemsParsedAsync(
|
||||
gtin: string.IsNullOrWhiteSpace(p.Gtin) ? null : p.Gtin!.Trim(),
|
||||
batch: string.IsNullOrWhiteSpace(p.Batch) ? null : p.Batch!.Trim(),
|
||||
expiration: p.Expiration,
|
||||
serial: string.IsNullOrWhiteSpace(p.Serial) ? null : p.Serial!.Trim(),
|
||||
locationId: p.LocationId,
|
||||
page: page,
|
||||
take: take
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
@ -10,7 +10,12 @@
|
||||
/// <summary>
|
||||
/// Número de presupuesto (ej. "Q-00000001").
|
||||
/// </summary>
|
||||
public string Quotenumber { get; set; } = String.Empty;
|
||||
public string Quotenumber { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Relación con Tickets
|
||||
/// </summary>
|
||||
public Guid? TicketId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de emisión del presupuesto.
|
||||
@ -25,22 +30,22 @@
|
||||
/// <summary>
|
||||
/// Nombre completo del cliente asociado.
|
||||
/// </summary>
|
||||
public string CustomerName { get; set; } = String.Empty;
|
||||
public string CustomerName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Nombre completo del médico responsable.
|
||||
/// </summary>
|
||||
public string ProfessionalName { get; set; } = String.Empty;
|
||||
public string ProfessionalName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Nombre de la institución u hospital.
|
||||
/// </summary>
|
||||
public string InstitutionName { get; set; } = String.Empty;
|
||||
public string InstitutionName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Nombre completo del paciente.
|
||||
/// </summary>
|
||||
public string PatientName { get; set; } = String.Empty;
|
||||
public string PatientName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Días de validez del presupuesto (desde la fecha de emisión).
|
||||
@ -50,18 +55,18 @@
|
||||
/// <summary>
|
||||
/// Descripción de la condición de pago (ej. "Contado", "30 días").
|
||||
/// </summary>
|
||||
public string? PaymentTermDescription { get; set; } = String.Empty;
|
||||
public string? PaymentTermDescription { get; set; } = string.Empty;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Nombre de la unidad de negocio.
|
||||
/// </summary>
|
||||
public string BusinessUnitName { get; set; } = String.Empty;
|
||||
public string BusinessUnitName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Moneda del presupuesto (ej. "ARS", "USD").
|
||||
/// </summary>
|
||||
public string Currency { get; set; } = String.Empty;
|
||||
public string Currency { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Importe total final (incluye impuestos y ajustes).
|
||||
@ -71,16 +76,16 @@
|
||||
/// <summary>
|
||||
/// Estado actual del presupuesto ("Pendiente", "Aprobado", etc.).
|
||||
/// </summary>
|
||||
public string Status { get; set; } = String.Empty;
|
||||
public string Status { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Nombre del vendedor o ejecutivo de ventas.
|
||||
/// </summary>
|
||||
public string SalespersonName { get; set; } = String.Empty;
|
||||
public string SalespersonName { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Nombre del vendedor o ejecutivo de ventas.
|
||||
/// </summary>
|
||||
public string Observations { get; set; } = String.Empty;
|
||||
public string Observations { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Detalle de los ítems o productos cotizados.
|
||||
@ -105,6 +110,6 @@
|
||||
/// <summary>
|
||||
/// Logo de la compañia.
|
||||
/// </summary>
|
||||
public string LogoBase64 { get; set; } = "";
|
||||
public string LogoBase64 { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
12
Domain/Dtos/Stock/Gs1ScanResult.cs
Normal file
12
Domain/Dtos/Stock/Gs1ScanResult.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Domain.Dtos.Stock
|
||||
{
|
||||
public class Gs1ScanResult
|
||||
{
|
||||
public string? Gtin { get; set; }
|
||||
public string? Lot { get; set; }
|
||||
public DateTime? ExpirationDate { get; set; }
|
||||
public string? Serial { get; set; }
|
||||
public string? Raw { get; set; }
|
||||
public string? Variant { get; set; }
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,7 @@
|
||||
public string UnitCode { get; set; } = string.Empty;
|
||||
public bool PlusProcess { get; set; }
|
||||
public string ExternalCode { get; set; } = string.Empty;
|
||||
public string RegulatoryCode { get; set; } = string.Empty;
|
||||
public string? ErrorMessage { get; set; }
|
||||
public bool HasError => !string.IsNullOrWhiteSpace(ErrorMessage);
|
||||
}
|
||||
|
||||
32
Domain/Dtos/Stock/ProductSetItemDto.cs
Normal file
32
Domain/Dtos/Stock/ProductSetItemDto.cs
Normal file
@ -0,0 +1,32 @@
|
||||
namespace Domain.Dtos.Stock
|
||||
{
|
||||
/// <summary>
|
||||
/// Representa un producto que forma parte de un set quirúrgico predefinido.
|
||||
/// Este DTO se utiliza en la UI para sugerir qué productos incluir al seleccionar un set.
|
||||
/// </summary>
|
||||
public class ProductSetItemDto
|
||||
{
|
||||
/// <summary>
|
||||
/// ID del producto asociado al set.
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre corto o técnico del producto (para mostrar en UI).
|
||||
/// </summary>
|
||||
public string ProductName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad sugerida a utilizar para este producto dentro del set.
|
||||
/// Se usa como valor por defecto al cargar el selector de stock.
|
||||
/// Puede incluir fracciones (ej: 0.5, 1.25), por eso es decimal.
|
||||
/// </summary>
|
||||
public decimal DefaultQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indica si este producto es obligatorio dentro del set quirúrgico.
|
||||
/// Si es true, se espera que el usuario no lo omita al cargar los ítems.
|
||||
/// </summary>
|
||||
public bool Mandatory { get; set; }
|
||||
}
|
||||
}
|
||||
80
Domain/Dtos/Stock/StockItemScanResultDto.cs
Normal file
80
Domain/Dtos/Stock/StockItemScanResultDto.cs
Normal file
@ -0,0 +1,80 @@
|
||||
namespace Domain.Dtos.Stock
|
||||
{
|
||||
/// <summary>
|
||||
/// Representa un ítem de stock disponible encontrado mediante búsqueda o escaneo.
|
||||
/// Este DTO se utiliza como resultado en la UI para que el usuario elija qué ítem tomar.
|
||||
/// </summary>
|
||||
public class StockItemScanResultDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador único del ítem en la tabla PhLSM_StockItem.
|
||||
/// </summary>
|
||||
public int StockItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identificador del producto asociado.
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Código de fábrica del producto.
|
||||
/// </summary>
|
||||
public string FactoryCode { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Código externo o alternativo (si existe).
|
||||
/// </summary>
|
||||
public string? ExternalCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre formal o técnico del producto.
|
||||
/// </summary>
|
||||
public string ProductName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Descripción comercial o de uso.
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identificador de la ubicación física (depósito, sala, etc.).
|
||||
/// </summary>
|
||||
public int? LocationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre descriptivo de la ubicación.
|
||||
/// </summary>
|
||||
public string? LocationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número de lote (si aplica).
|
||||
/// </summary>
|
||||
public string? Batch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número de serie de la unidad individual, según etiqueta de trazabilidad del fabricante.
|
||||
/// </summary>
|
||||
public string? Serial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de vencimiento (si aplica).
|
||||
/// </summary>
|
||||
public DateOnly? Expiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tipo de trazabilidad del producto:
|
||||
/// 1 = No aplica, 2 = Por cantidad, 3 = Por lote y vencimiento.
|
||||
/// </summary>
|
||||
public int TraceabilityType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad disponible actualmente.
|
||||
/// </summary>
|
||||
public decimal AvailableQty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indica si requiere proceso adicional (por ejemplo, esterilización).
|
||||
/// </summary>
|
||||
public bool PlusProcess { get; set; }
|
||||
}
|
||||
}
|
||||
51
Domain/Dtos/Stock/StockItemSelectionDto.cs
Normal file
51
Domain/Dtos/Stock/StockItemSelectionDto.cs
Normal file
@ -0,0 +1,51 @@
|
||||
namespace Domain.Dtos.Stock
|
||||
{
|
||||
/// <summary>
|
||||
/// Representa un ítem seleccionado desde el stock real para operaciones como
|
||||
/// expediciones, consumo quirúrgico, ventas directas o devoluciones.
|
||||
/// Este DTO se usa únicamente en la capa de UI y se transforma a entidades de persistencia al guardar.
|
||||
/// </summary>
|
||||
public class StockItemSelectionDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador del ítem de stock seleccionado (PhLSM_StockItem.id)
|
||||
/// </summary>
|
||||
public int StockItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identificador del producto asociado
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre o descripción corta del producto (solo para visualización en UI)
|
||||
/// </summary>
|
||||
public string ProductName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Lote del stock seleccionado
|
||||
/// </summary>
|
||||
public string Batch { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Número de serie de la unidad individual, según etiqueta de trazabilidad del fabricante.
|
||||
/// </summary>
|
||||
public string? Serial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de vencimiento (si aplica)
|
||||
/// </summary>
|
||||
public DateTime? Expiration { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad que el usuario desea usar (puede ser decimal si es por peso o volumen)
|
||||
/// </summary>
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identificador del depósito o ubicación de donde se toma el ítem
|
||||
/// </summary>
|
||||
public int LocationId { get; set; }
|
||||
}
|
||||
}
|
||||
69
Domain/Entities/ELSExpeditionDetail.cs
Normal file
69
Domain/Entities/ELSExpeditionDetail.cs
Normal file
@ -0,0 +1,69 @@
|
||||
namespace Domain.Entities
|
||||
{
|
||||
public class ELSExpeditionDetail
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador interno del ítem de expedición
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Referencia a la cabecera de expedición (PhLSM_ExpeditionHeaders)
|
||||
/// </summary>
|
||||
public int ExpeditionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Producto médico a despachar
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad solicitada del producto
|
||||
/// </summary>
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ubicación específica desde donde se despacha este ítem
|
||||
/// </summary>
|
||||
public int LocationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número de lote (si aplica trazabilidad)
|
||||
/// </summary>
|
||||
public string? Batch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número de serie de la unidad individual, según etiqueta de trazabilidad del fabricante.
|
||||
/// </summary>
|
||||
public string? Serial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de vencimiento del producto (si aplica trazabilidad)
|
||||
/// </summary>
|
||||
public DateOnly? Expiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Descripción libre del ítem (uso interno o impresión)
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Precio estimado unitario del producto (sin efecto contable)
|
||||
/// </summary>
|
||||
public decimal? EstimatedUnitprice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Moneda del precio estimado (ej: ARS, USD)
|
||||
/// </summary>
|
||||
public string? EstimatedCurrency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tipo de cambio aplicado al precio estimado
|
||||
/// </summary>
|
||||
public decimal? EstimatedExchangerate { get; set; }
|
||||
|
||||
//public virtual PhLsmExpeditionHeader Expedition { get; set; } = null!;
|
||||
|
||||
//public virtual PhLsmProduct Product { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
82
Domain/Entities/ELSExpeditionHeader.cs
Normal file
82
Domain/Entities/ELSExpeditionHeader.cs
Normal file
@ -0,0 +1,82 @@
|
||||
namespace Domain.Entities
|
||||
{
|
||||
public class ELSExpeditionHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador interno de la expedición
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Referencia al ticket quirúrgico (si aplica)
|
||||
/// </summary>
|
||||
public Guid? TicketId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número de expedición (formato EX-00000001)
|
||||
/// </summary>
|
||||
public string Expeditionnumber { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Ubicación (depósito) desde donde se despacha
|
||||
/// </summary>
|
||||
public int LocationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de emisión de la expedición
|
||||
/// </summary>
|
||||
public DateTime Issuedate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Estado de la expedición (1=Borrador, 2=Confirmada, etc.)
|
||||
/// </summary>
|
||||
public int Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre del destinatario visible en la impresión
|
||||
/// </summary>
|
||||
public string? RecipientName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número o referencia externa asociada
|
||||
/// </summary>
|
||||
public string? ReferenceNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tipo de origen externo (ej: surgery, demo, préstamo)
|
||||
/// </summary>
|
||||
public string? OriginType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID externo relacionado a otro módulo (ej: ticket, orden)
|
||||
/// </summary>
|
||||
public string? ExternalReference { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Observaciones generales de la expedición
|
||||
/// </summary>
|
||||
public string? Observations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Información adicional en formato JSON (ej: paciente, médico, etc.)
|
||||
/// </summary>
|
||||
public string? ExtrainfoJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad de veces que se imprimió la nota de expedición
|
||||
/// </summary>
|
||||
public int Printcount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de creación del registro
|
||||
/// </summary>
|
||||
public DateTime Createdat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de última modificación del registro
|
||||
/// </summary>
|
||||
public DateTime? Modifiedat { get; set; }
|
||||
|
||||
public virtual ICollection<ELSExpeditionDetail> PhLsmExpeditionDetails { get; set; } = new List<ELSExpeditionDetail>();
|
||||
}
|
||||
}
|
||||
@ -42,6 +42,11 @@
|
||||
/// </summary>
|
||||
public string? ExternalCode { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Código regulatorio (PM) o registro sanitario oficial del producto. Registro otorgado por ANMAT u ente regulador.
|
||||
/// </summary>
|
||||
public string? RegulatoryCode { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// División o familia técnica del producto (ej: columna, trauma, descartables, etc.)
|
||||
/// </summary>
|
||||
|
||||
43
Domain/Entities/ELSProductSet..cs
Normal file
43
Domain/Entities/ELSProductSet..cs
Normal file
@ -0,0 +1,43 @@
|
||||
namespace Domain.Entities
|
||||
{
|
||||
public class ELSProductSet
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador único del set de productos
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Código del set (ej: CMF1.2, TRAUMA2.0)
|
||||
/// </summary>
|
||||
public string Code { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Nombre comercial o técnico del set
|
||||
/// </summary>
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Descripción extendida del set de productos
|
||||
/// </summary>
|
||||
public string? Descripcion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indica si el set requiere un proceso adicional (ej: esterilización)
|
||||
/// </summary>
|
||||
public bool PlusProcess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de creación del registro
|
||||
/// </summary>
|
||||
public DateTime Createdat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de última modificación del registro
|
||||
/// </summary>
|
||||
public DateTime? Modifiedat { get; set; }
|
||||
|
||||
public virtual ICollection<ELSProductSetItem> PhLsmProductSetItems { get; set; } = new List<ELSProductSetItem>();
|
||||
}
|
||||
|
||||
}
|
||||
34
Domain/Entities/ELSProductSetItem..cs
Normal file
34
Domain/Entities/ELSProductSetItem..cs
Normal file
@ -0,0 +1,34 @@
|
||||
namespace Domain.Entities
|
||||
{
|
||||
public class ELSProductSetItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador único del ítem dentro del set
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID del set al que pertenece este ítem
|
||||
/// </summary>
|
||||
public int ProductsetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Producto incluido en el set
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad estándar del producto en el set
|
||||
/// </summary>
|
||||
public decimal DefaultQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indica si este ítem es obligatorio en la composición del set
|
||||
/// </summary>
|
||||
public bool Mandatory { get; set; }
|
||||
|
||||
//public virtual PhLsmProduct Product { get; set; } = null!;
|
||||
|
||||
//public virtual PhLsmProductSet Productset { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
77
Domain/Entities/ELSStockEntry.cs
Normal file
77
Domain/Entities/ELSStockEntry.cs
Normal file
@ -0,0 +1,77 @@
|
||||
namespace Domain.Entities
|
||||
{
|
||||
public class ELSStockEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador único del ingreso de stock
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Producto ingresado al stock
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad ingresada del producto
|
||||
/// </summary>
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Precio unitario usado para valorizar el ingreso
|
||||
/// </summary>
|
||||
public decimal Unitprice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Moneda utilizada en la valorización (ARS, USD, EUR)
|
||||
/// </summary>
|
||||
public string Currency { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Tasa de conversión de la moneda a ARS
|
||||
/// </summary>
|
||||
public decimal Exchangerate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha del ingreso de stock
|
||||
/// </summary>
|
||||
public DateTime Entrydate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Referencia visible del movimiento (ej: factura, orden de compra)
|
||||
/// </summary>
|
||||
public string? Reference { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tipo de origen del ingreso (purchase, return, manual, etc.)
|
||||
/// </summary>
|
||||
public string? Sourcetype { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID de la entidad que generó el ingreso (ej: orden de compra)
|
||||
/// </summary>
|
||||
public int? SourceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ubicación física donde se depositó el producto
|
||||
/// </summary>
|
||||
public int LocationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lote del producto ingresado (si aplica trazabilidad)
|
||||
/// </summary>
|
||||
public string? Batch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de vencimiento del producto ingresado (si aplica)
|
||||
/// </summary>
|
||||
public DateOnly? Expiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de creación del registro
|
||||
/// </summary>
|
||||
public DateTime Createdat { get; set; }
|
||||
|
||||
//public virtual PhLsmProduct Product { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
69
Domain/Entities/ELSStockItem.cs
Normal file
69
Domain/Entities/ELSStockItem.cs
Normal file
@ -0,0 +1,69 @@
|
||||
namespace Domain.Entities
|
||||
{
|
||||
public class ELSStockItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador único del ítem de stock físico
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Producto vinculado al ítem de stock
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ubicación física del stock (depósito, valija, etc.)
|
||||
/// </summary>
|
||||
public int LocationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad actual disponible en esta unidad de stock
|
||||
/// </summary>
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad comprometida o reservada para expediciones futuras
|
||||
/// </summary>
|
||||
public decimal ReservedQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Código de lote (si aplica)
|
||||
/// </summary>
|
||||
public string? Batch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número de serie de la unidad individual, según etiqueta de trazabilidad del fabricante.
|
||||
/// </summary>
|
||||
public string? Serial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de vencimiento (si aplica)
|
||||
/// </summary>
|
||||
public DateOnly? Expiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Estado del ítem (1=Disponible, 2=Reservado, 3=Vencido, etc.)
|
||||
/// </summary>
|
||||
public int Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Comentario libre u observación sobre este ítem de stock
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de creación del registro
|
||||
/// </summary>
|
||||
public DateTime Createdat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de última modificación del registro
|
||||
/// </summary>
|
||||
public DateTime? Modifiedat { get; set; }
|
||||
|
||||
//public virtual PhLsmStockLocation Location { get; set; } = null!;
|
||||
|
||||
//public virtual PhLsmProduct Product { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
22
Domain/Entities/ELSStockLocation.cs
Normal file
22
Domain/Entities/ELSStockLocation.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace Domain.Entities
|
||||
{
|
||||
public class ELSStockLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador único de la ubicación de stock
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre visible de la ubicación (ej: Depósito Central, Cuarentena, Caja A1)
|
||||
/// </summary>
|
||||
public string Nombre { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Descripción o comentario adicional sobre la ubicación
|
||||
/// </summary>
|
||||
public string? Descripcion { get; set; }
|
||||
|
||||
//public virtual ICollection<PhLsmStockItem> PhLsmStockItems { get; set; } = new List<PhLsmStockItem>();
|
||||
}
|
||||
}
|
||||
82
Domain/Entities/ELSStockOut.cs
Normal file
82
Domain/Entities/ELSStockOut.cs
Normal file
@ -0,0 +1,82 @@
|
||||
namespace Domain.Entities
|
||||
{
|
||||
public class ELSStockOut
|
||||
{
|
||||
/// <summary>
|
||||
/// Identificador único del egreso de stock
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Producto retirado del stock
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cantidad retirada del producto
|
||||
/// </summary>
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Precio unitario usado para valorizar el egreso
|
||||
/// </summary>
|
||||
public decimal Unitprice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Moneda utilizada en la valorización (ARS, USD, EUR)
|
||||
/// </summary>
|
||||
public string Currency { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Tasa de conversión de la moneda a ARS
|
||||
/// </summary>
|
||||
public decimal Exchangerate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha del egreso de stock
|
||||
/// </summary>
|
||||
public DateTime Outdate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Referencia visible del movimiento (NE, devolución, cirugía)
|
||||
/// </summary>
|
||||
public string? Reference { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tipo de origen del egreso (surgery, expiration, manual, etc.)
|
||||
/// </summary>
|
||||
public string? Sourcetype { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID de la entidad que generó el egreso (ej: nota de expedición)
|
||||
/// </summary>
|
||||
public int? SourceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identificador del caso quirúrgico asociado al egreso
|
||||
/// </summary>
|
||||
public Guid? TicketId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ubicación física desde donde se retiró el producto
|
||||
/// </summary>
|
||||
public int? LocationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lote del producto egresado (si aplica trazabilidad)
|
||||
/// </summary>
|
||||
public string? Batch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de vencimiento del producto egresado (si aplica)
|
||||
/// </summary>
|
||||
public DateOnly? Expiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de creación del registro
|
||||
/// </summary>
|
||||
public DateTime Createdat { get; set; }
|
||||
|
||||
//public virtual PhLsmProduct Product { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
13
Domain/Generics/StockItemParsedSearchParams.cs
Normal file
13
Domain/Generics/StockItemParsedSearchParams.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Domain.Generics
|
||||
{
|
||||
public class StockItemParsedSearchParams
|
||||
{
|
||||
public string? Gtin { get; set; }
|
||||
public string? Batch { get; set; }
|
||||
public DateOnly? Expiration { get; set; }
|
||||
public string? Serial { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public int Page { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
}
|
||||
}
|
||||
16
Domain/Generics/StockItemSearchParams.cs
Normal file
16
Domain/Generics/StockItemSearchParams.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Domain.Generics
|
||||
{
|
||||
public class StockItemSearchParams
|
||||
{
|
||||
public string? CodeOrText { get; set; }
|
||||
public string? Batch { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public int? ProductType { get; set; }
|
||||
public int? TraceabilityType { get; set; }
|
||||
public bool? PlusProcess { get; set; }
|
||||
|
||||
// paginación
|
||||
public int Page { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
Models/Interfaces/IPhLSMStockItemRepository.cs
Normal file
28
Models/Interfaces/IPhLSMStockItemRepository.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using Domain.Dtos.Stock;
|
||||
using Domain.Generics;
|
||||
|
||||
namespace Models.Interfaces
|
||||
{
|
||||
public interface IPhLSMStockItemRepository
|
||||
{
|
||||
Task<PagedResult<StockItemScanResultDto>> SearchStockItemsAsync(
|
||||
string? codeOrText,
|
||||
string? batch,
|
||||
int? locationId,
|
||||
int? productType,
|
||||
int? traceabilityType,
|
||||
bool? plusProcess,
|
||||
int page,
|
||||
int take
|
||||
);
|
||||
// Búsqueda con datos parseados (GS1/DataMatrix)
|
||||
Task<PagedResult<StockItemScanResultDto>> SearchStockItemsParsedAsync(
|
||||
string? gtin,
|
||||
string? batch,
|
||||
DateOnly? expiration,
|
||||
string? serial, // reservado (si agregás columna Serial en StockItem)
|
||||
int? locationId,
|
||||
int page,
|
||||
int take);
|
||||
}
|
||||
}
|
||||
@ -12,5 +12,6 @@ namespace Models.Interfaces
|
||||
Task<IEnumerable<ELookUpItem>> BussinessUnitsListAsync(string filter, int limit = 10);
|
||||
Task<IEnumerable<EProductLookupItem>> ProductsListAsync(string filter, int limit = 10);
|
||||
Task<IEnumerable<ELookUpItem>> PaymentTermsListAsync(string filter, int limit = 10);
|
||||
Task<IEnumerable<ELookUpItem>> ApprovedQuotesListAsync(string filter, int limit = 10);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using Domain.Entities;
|
||||
using Domain.Dtos;
|
||||
using Domain.Entities;
|
||||
using Domain.Generics;
|
||||
|
||||
namespace Models.Interfaces
|
||||
@ -7,6 +8,7 @@ namespace Models.Interfaces
|
||||
{
|
||||
Task<PagedResult<EQuoteHeader>> GetAllAsync(int page = 1, int pageSize = 50);
|
||||
Task<EQuoteHeader?> GetByIdAsync(int id);
|
||||
Task<EQuoteHeader?> GetByQuoteNumberAsync(string quoteNumber);
|
||||
Task<IEnumerable<EQuoteHeader>> GetByCustomerIdAsync(int customerId);
|
||||
Task UpdateAsync(EQuoteHeader quoteHeader);
|
||||
Task DeleteAsync(int id);
|
||||
|
||||
@ -12,6 +12,7 @@ namespace Models.Interfaces
|
||||
#region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
|
||||
Task<(int Id, string Quotenumber)> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId);
|
||||
Task<QuoteDto?> GetDtoByIdAsync(int id);
|
||||
Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber);
|
||||
Task<bool> AuthorizeQuoteAsync(int quoteId, List<EQuoteDetail> approvedItems);
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -35,6 +35,11 @@ public partial class PhLsmExpeditionDetail
|
||||
/// </summary>
|
||||
public string? Batch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número de serie de la unidad individual, según etiqueta de trazabilidad del fabricante.
|
||||
/// </summary>
|
||||
public string? Serial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de vencimiento del producto (si aplica trazabilidad)
|
||||
/// </summary>
|
||||
|
||||
@ -45,6 +45,11 @@ public partial class PhLsmProduct
|
||||
/// </summary>
|
||||
public string? ExternalCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Código regulatorio (PM) o registro sanitario oficial del producto. Registro otorgado por ANMAT u ente regulador.
|
||||
/// </summary>
|
||||
public string? RegulatoryCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// División o familia técnica del producto (ej: columna, trauma, descartables, etc.)
|
||||
/// </summary>
|
||||
|
||||
@ -35,6 +35,11 @@ public partial class PhLsmStockItem
|
||||
/// </summary>
|
||||
public string? Batch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Número de serie de la unidad individual, según etiqueta de trazabilidad del fabricante.
|
||||
/// </summary>
|
||||
public string? Serial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fecha de vencimiento (si aplica)
|
||||
/// </summary>
|
||||
|
||||
@ -96,8 +96,15 @@ public partial class PhronCareOperationsHubContext : DbContext
|
||||
public virtual DbSet<PhSQuoteTaxis> PhSQuoteTaxes { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
|
||||
=> optionsBuilder.UseSqlServer("data source=srv01.saludlab.com.ar,39458;initial catalog=phronCare_OperationsHub;User ID=sa;Password=HS|s[~xxQzTo/n>9jO;encrypt=False;trustServerCertificate=True;MultipleActiveResultSets=True");
|
||||
#region VERSION DOCKER
|
||||
{
|
||||
if (!optionsBuilder.IsConfigured)
|
||||
{
|
||||
// Dejarlo vacío para usar la configuración externa desde Program.cs o Startup.cs
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
//=> optionsBuilder.UseSqlServer("data source=srv01.saludlab.com.ar,39458;initial catalog=phronCare_OperationsHub;User ID=sa;Password=HS|s[~xxQzTo/n>9jO;encrypt=False;trustServerCertificate=True;MultipleActiveResultSets=True");
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@ -151,6 +158,10 @@ public partial class PhronCareOperationsHubContext : DbContext
|
||||
.HasComment("Cantidad solicitada del producto")
|
||||
.HasColumnType("decimal(18, 4)")
|
||||
.HasColumnName("quantity");
|
||||
entity.Property(e => e.Serial)
|
||||
.HasMaxLength(100)
|
||||
.HasComment("Número de serie de la unidad individual, según etiqueta de trazabilidad del fabricante.")
|
||||
.HasColumnName("serial");
|
||||
|
||||
entity.HasOne(d => d.Expedition).WithMany(p => p.PhLsmExpeditionDetails)
|
||||
.HasForeignKey(d => d.ExpeditionId)
|
||||
@ -267,6 +278,10 @@ public partial class PhronCareOperationsHubContext : DbContext
|
||||
entity.Property(e => e.ProductType)
|
||||
.HasComment("Tipo de producto: 1=Implantable, 2=Instrumental, 3=Inyectable, etc.")
|
||||
.HasColumnName("product_type");
|
||||
entity.Property(e => e.RegulatoryCode)
|
||||
.HasMaxLength(50)
|
||||
.HasComment("Código regulatorio (PM) o registro sanitario oficial del producto. Registro otorgado por ANMAT u ente regulador.")
|
||||
.HasColumnName("regulatory_code");
|
||||
entity.Property(e => e.TraceabilityType)
|
||||
.HasComment("Tipo de trazabilidad: 1=No aplica, 2=Por cantidad, 3=Por lote y vencimiento")
|
||||
.HasColumnName("traceability_type");
|
||||
@ -459,7 +474,21 @@ public partial class PhronCareOperationsHubContext : DbContext
|
||||
|
||||
entity.ToTable("PhLSM_StockItem");
|
||||
|
||||
entity.HasIndex(e => new { e.ProductId, e.LocationId, e.Batch, e.Expiration }, "IX_PhLSM_StockItem_UniqueTraceability").IsUnique();
|
||||
entity.HasIndex(e => new { e.ProductId, e.LocationId, e.Batch }, "UQ_PhLSM_StockItem_ProdLoc_BatchOnly")
|
||||
.IsUnique()
|
||||
.HasFilter("([serial] IS NULL AND [batch] IS NOT NULL AND [expiration] IS NULL)");
|
||||
|
||||
entity.HasIndex(e => new { e.ProductId, e.LocationId, e.Batch, e.Expiration }, "UQ_PhLSM_StockItem_ProdLoc_Batch_Exp")
|
||||
.IsUnique()
|
||||
.HasFilter("([serial] IS NULL AND [batch] IS NOT NULL AND [expiration] IS NOT NULL)");
|
||||
|
||||
entity.HasIndex(e => new { e.ProductId, e.LocationId }, "UQ_PhLSM_StockItem_ProdLoc_None")
|
||||
.IsUnique()
|
||||
.HasFilter("([serial] IS NULL AND [batch] IS NULL AND [expiration] IS NULL)");
|
||||
|
||||
entity.HasIndex(e => new { e.ProductId, e.Serial }, "UQ_PhLSM_StockItem_Product_Serial")
|
||||
.IsUnique()
|
||||
.HasFilter("([serial] IS NOT NULL)");
|
||||
|
||||
entity.Property(e => e.Id)
|
||||
.HasComment("Identificador único del ítem de stock físico")
|
||||
@ -500,6 +529,11 @@ public partial class PhronCareOperationsHubContext : DbContext
|
||||
.HasComment("Cantidad comprometida o reservada para expediciones futuras")
|
||||
.HasColumnType("decimal(18, 4)")
|
||||
.HasColumnName("reserved_quantity");
|
||||
entity.Property(e => e.Serial)
|
||||
.HasMaxLength(100)
|
||||
.IsUnicode(false)
|
||||
.HasComment("Número de serie de la unidad individual, según etiqueta de trazabilidad del fabricante.")
|
||||
.HasColumnName("serial");
|
||||
entity.Property(e => e.Status)
|
||||
.HasComment("Estado del ítem (1=Disponible, 2=Reservado, 3=Vencido, etc.)")
|
||||
.HasColumnName("status");
|
||||
|
||||
@ -92,6 +92,23 @@ namespace Models.Repositories
|
||||
.Take(limit)
|
||||
.ToListAsync();
|
||||
}
|
||||
public async Task<IEnumerable<ELookUpItem>> ApprovedQuotesListAsync(string filter, int limit = 10)
|
||||
{
|
||||
return await (
|
||||
from q in _context.PhSQuoteHeaders
|
||||
join c in _context.PhSCustomers on q.CustomerId equals c.Id
|
||||
where q.Status == "Emitido" &&
|
||||
(q.Quotenumber.Contains(filter) || c.Name.Contains(filter))
|
||||
orderby q.Issuedate descending
|
||||
select new ELookUpItem
|
||||
{
|
||||
Id = q.Id,
|
||||
Nombre = q.Quotenumber + " - " + c.Name
|
||||
}
|
||||
)
|
||||
.Take(limit)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Models.Interfaces;
|
||||
using Models.Helpers;
|
||||
using Models.Models;
|
||||
using Domain.Dtos;
|
||||
using Domain.Entities;
|
||||
using Domain.Generics;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Models.Helpers;
|
||||
using Models.Interfaces;
|
||||
using Models.Models;
|
||||
|
||||
namespace Models.Repositories
|
||||
{
|
||||
public class PhSQuoteHeaderRepository(PhronCareOperationsHubContext context) : IPhSQuoteHeaderRepository
|
||||
{
|
||||
private readonly PhronCareOperationsHubContext _context = context;
|
||||
//private readonly IPhSFormSeriesRepository _formSeriesRepository = formSeriesRepository;
|
||||
#region Metodos
|
||||
public async Task<PagedResult<EQuoteHeader>> GetAllAsync(int page = 1, int pageSize = 50)
|
||||
{
|
||||
@ -42,6 +42,12 @@ namespace Models.Repositories
|
||||
|
||||
return entity != null ? EntityMapper.MapEntity<PhSQuoteHeader, EQuoteHeader>(entity) : null;
|
||||
}
|
||||
public async Task<EQuoteHeader?> GetByQuoteNumberAsync(string quoteNumber)
|
||||
{
|
||||
var entity= await _context.PhSQuoteHeaders
|
||||
.FirstOrDefaultAsync(q => q.Quotenumber == quoteNumber);
|
||||
return entity != null ? EntityMapper.MapEntity<PhSQuoteHeader, EQuoteHeader>(entity) : null;
|
||||
}
|
||||
public async Task<IEnumerable<EQuoteHeader>> GetByCustomerIdAsync(int customerId)
|
||||
{
|
||||
var entities = await _context.PhSQuoteHeaders
|
||||
|
||||
@ -344,7 +344,51 @@ namespace Models.Repositories
|
||||
|
||||
return dto;
|
||||
}
|
||||
public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
|
||||
{
|
||||
var header = await _context.PhSQuoteHeaders
|
||||
.Include(q => q.PhSQuoteRoles)
|
||||
.FirstOrDefaultAsync(q => q.Quotenumber == quoteNumber);
|
||||
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
var customer = await _context.PhSCustomers
|
||||
.FirstOrDefaultAsync(c => c.Id == header.CustomerId);
|
||||
|
||||
return new QuoteDto
|
||||
{
|
||||
Id = header.Id,
|
||||
Quotenumber = header.Quotenumber,
|
||||
EstimatedDate = header.Estimateddate,
|
||||
Observations = header.DispatchInstruction ?? "",
|
||||
CustomerName = customer?.Name ?? "",
|
||||
|
||||
ProfessionalName = header.PhSQuoteRoles
|
||||
.Where(r => r.Entitytype == PhSEntityTypes.Professional)
|
||||
.Select(r => _context.PhSProfessionals
|
||||
.Where(p => p.Id == r.EntityId)
|
||||
.Select(p => p.Fullname)
|
||||
.FirstOrDefault())
|
||||
.FirstOrDefault() ?? "",
|
||||
|
||||
InstitutionName = header.PhSQuoteRoles
|
||||
.Where(r => r.Entitytype == PhSEntityTypes.Institution)
|
||||
.Select(r => _context.PhSInstitutions
|
||||
.Where(i => i.Id == r.EntityId)
|
||||
.Select(i => i.Name)
|
||||
.FirstOrDefault())
|
||||
.FirstOrDefault() ?? "",
|
||||
|
||||
PatientName = header.PhSQuoteRoles
|
||||
.Where(r => r.Entitytype == PhSEntityTypes.Patient)
|
||||
.Select(r => _context.PhSPatients
|
||||
.Where(pt => pt.Id == r.EntityId)
|
||||
.Select(pt => (pt.Firstname + " " + pt.Lastname).Trim())
|
||||
.FirstOrDefault())
|
||||
.FirstOrDefault() ?? ""
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
#region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
|
||||
/// <summary>
|
||||
|
||||
@ -144,6 +144,7 @@ namespace Models.Repositories
|
||||
UnitId = unitId,
|
||||
PlusProcess = item.PlusProcess,
|
||||
ExternalCode = item.ExternalCode,
|
||||
RegulatoryCode = item.RegulatoryCode
|
||||
// otros campos...
|
||||
};
|
||||
|
||||
|
||||
213
Models/Repositories/Stock/PhLSMStockItemRepository.cs
Normal file
213
Models/Repositories/Stock/PhLSMStockItemRepository.cs
Normal file
@ -0,0 +1,213 @@
|
||||
using Domain.Dtos.Stock; // StockItemScanResultDto
|
||||
using Domain.Generics; // PagedResult<T>
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Models.Helpers; // ToPagedResultAsync
|
||||
using Models.Interfaces; // IPhLSMStockItemRepository
|
||||
using Models.Models; // PhronCareOperationsHubContext
|
||||
|
||||
namespace Models.Repositories.Stock
|
||||
{
|
||||
public class PhLSMStockItemRepository(PhronCareOperationsHubContext context) : IPhLSMStockItemRepository
|
||||
{
|
||||
private readonly PhronCareOperationsHubContext _context = context;
|
||||
|
||||
public async Task<PagedResult<StockItemScanResultDto>> SearchStockItemsAsync(
|
||||
string? codeOrText,
|
||||
string? batch,
|
||||
int? locationId,
|
||||
int? productType,
|
||||
int? traceabilityType,
|
||||
bool? plusProcess,
|
||||
int page,
|
||||
int take)
|
||||
{
|
||||
// Base: stock disponible (>0) + joins necesarios
|
||||
var baseQuery =
|
||||
from si in _context.PhLsmStockItems.AsNoTracking()
|
||||
join p in _context.PhLsmProducts.AsNoTracking() on si.ProductId equals p.Id
|
||||
join l in _context.PhLsmStockLocations.AsNoTracking() on si.LocationId equals l.Id into _loc
|
||||
from loc in _loc.DefaultIfEmpty()
|
||||
where si.Quantity > 0
|
||||
select new { si, p, loc };
|
||||
|
||||
// ---- Filtros (case-insensitive donde aplica) ----
|
||||
if (!string.IsNullOrWhiteSpace(codeOrText))
|
||||
{
|
||||
var t = codeOrText.Trim().ToLower();
|
||||
baseQuery = baseQuery.Where(x =>
|
||||
(!string.IsNullOrEmpty(x.p.FactoryCode) && x.p.FactoryCode.ToLower().Contains(t)) ||
|
||||
(!string.IsNullOrEmpty(x.p.ExternalCode) && x.p.ExternalCode.ToLower().Contains(t)) ||
|
||||
(!string.IsNullOrEmpty(x.p.Name) && x.p.Name.ToLower().Contains(t)) ||
|
||||
(!string.IsNullOrEmpty(x.p.Descripcion) && x.p.Descripcion.ToLower().Contains(t)) ||
|
||||
(!string.IsNullOrEmpty(x.si.Batch) && x.si.Batch.ToLower().Contains(t))
|
||||
);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(batch))
|
||||
{
|
||||
var b = batch.Trim().ToLower();
|
||||
baseQuery = baseQuery.Where(x => x.si.Batch != null && x.si.Batch.ToLower().Contains(b));
|
||||
}
|
||||
|
||||
if (locationId.HasValue)
|
||||
baseQuery = baseQuery.Where(x => x.si.LocationId == locationId.Value);
|
||||
|
||||
if (productType.HasValue)
|
||||
baseQuery = baseQuery.Where(x => x.p.ProductType == productType.Value);
|
||||
|
||||
if (traceabilityType.HasValue)
|
||||
baseQuery = baseQuery.Where(x => x.p.TraceabilityType == traceabilityType.Value);
|
||||
|
||||
if (plusProcess.HasValue)
|
||||
baseQuery = baseQuery.Where(x => x.p.PlusProcess == plusProcess.Value);
|
||||
|
||||
// Orden lógico
|
||||
baseQuery = baseQuery
|
||||
.OrderBy(x => x.si.Expiration)
|
||||
.ThenBy(x => x.p.Name);
|
||||
|
||||
// Proyección final a DTO (IQueryable<StockItemScanResultDto>)
|
||||
var dtoQuery = baseQuery.Select(x => new StockItemScanResultDto
|
||||
{
|
||||
StockItemId = x.si.Id,
|
||||
ProductId = x.p.Id,
|
||||
FactoryCode = x.p.FactoryCode ?? string.Empty,
|
||||
ExternalCode = x.p.ExternalCode,
|
||||
ProductName = x.p.Name ?? string.Empty,
|
||||
Description = x.p.Descripcion,
|
||||
LocationId = x.si.LocationId,
|
||||
LocationName = x.loc != null ? x.loc.Descripcion : null,
|
||||
Batch = x.si.Batch,
|
||||
Expiration = x.si.Expiration,
|
||||
TraceabilityType = x.p.TraceabilityType,
|
||||
AvailableQty = x.si.Quantity,
|
||||
PlusProcess = x.p.PlusProcess
|
||||
});
|
||||
|
||||
page = page <= 0 ? 1 : page;
|
||||
take = take <= 0 ? 20 : take;
|
||||
|
||||
// Paginado con tu helper (manteniendo el mismo patrón que ProductRepository)
|
||||
var paged = await dtoQuery.ToPagedResultAsync(page, take);
|
||||
|
||||
return new PagedResult<StockItemScanResultDto>
|
||||
{
|
||||
Items = paged.Items,
|
||||
TotalItems = paged.TotalItems,
|
||||
Page = paged.Page,
|
||||
PageSize = paged.PageSize
|
||||
};
|
||||
}
|
||||
|
||||
// -------- Búsqueda PARSEADA (GS1/DataMatrix) ----------
|
||||
public async Task<PagedResult<StockItemScanResultDto>> SearchStockItemsParsedAsync(
|
||||
string? gtin,
|
||||
string? batch,
|
||||
DateOnly? expiration,
|
||||
string? serial,
|
||||
int? locationId,
|
||||
int page,
|
||||
int take)
|
||||
{
|
||||
// 0) Si no hay NINGÚN dato parseado, no traigas todo
|
||||
if (string.IsNullOrWhiteSpace(gtin) &&
|
||||
string.IsNullOrWhiteSpace(batch) &&
|
||||
!expiration.HasValue &&
|
||||
string.IsNullOrWhiteSpace(serial))
|
||||
{
|
||||
return new PagedResult<StockItemScanResultDto>
|
||||
{ Items = [], TotalItems = 0, Page = page <= 0 ? 1 : page, PageSize = take <= 0 ? 20 : take };
|
||||
}
|
||||
|
||||
// 1) Resolver productos por GTIN/Factory/Regulatory si vino GTIN
|
||||
var productIds = new List<int>();
|
||||
if (!string.IsNullOrWhiteSpace(gtin))
|
||||
{
|
||||
var g = gtin.Trim();
|
||||
productIds = await _context.PhLsmProducts.AsNoTracking()
|
||||
.Where(p => p.ExternalCode == g || p.FactoryCode == g || p.RegulatoryCode == g)
|
||||
.Select(p => p.Id).ToListAsync();
|
||||
|
||||
// Si se pidió GTIN y no matchea ningún producto, devolvé vacío
|
||||
if (productIds.Count == 0)
|
||||
{
|
||||
return new PagedResult<StockItemScanResultDto>
|
||||
{ Items = [], TotalItems = 0, Page = page <= 0 ? 1 : page, PageSize = take <= 0 ? 20 : take };
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Base query (stock disponible)
|
||||
var baseQuery =
|
||||
from si in _context.PhLsmStockItems.AsNoTracking()
|
||||
join p in _context.PhLsmProducts.AsNoTracking() on si.ProductId equals p.Id
|
||||
join l in _context.PhLsmStockLocations.AsNoTracking() on si.LocationId equals l.Id into _loc
|
||||
from loc in _loc.DefaultIfEmpty()
|
||||
where si.Quantity > 0
|
||||
select new { si, p, loc };
|
||||
|
||||
if (productIds.Count > 0)
|
||||
baseQuery = baseQuery.Where(x => productIds.Contains(x.p.Id));
|
||||
|
||||
if (locationId.HasValue)
|
||||
baseQuery = baseQuery.Where(x => x.si.LocationId == locationId.Value);
|
||||
|
||||
// 3) Reglas por tipo de trazabilidad (sin fuzzy)
|
||||
baseQuery = baseQuery.Where(x =>
|
||||
// None: solo producto + ubicación
|
||||
(x.p.TraceabilityType == 1)
|
||||
// BatchOnly: requiere batch exacto
|
||||
|| (x.p.TraceabilityType == 2
|
||||
&& !string.IsNullOrWhiteSpace(batch)
|
||||
&& x.si.Batch == batch!.Trim())
|
||||
// Batch+Exp: requiere batch + expiration exactos
|
||||
|| (x.p.TraceabilityType == 3
|
||||
&& !string.IsNullOrWhiteSpace(batch) && expiration.HasValue
|
||||
&& x.si.Batch == batch!.Trim()
|
||||
&& x.si.Expiration.HasValue && x.si.Expiration.Value == expiration.Value)
|
||||
// SerialUnit: requiere serial exacto (ignora expiration)
|
||||
|| (x.p.TraceabilityType == 4
|
||||
&& !string.IsNullOrWhiteSpace(serial)
|
||||
&& x.si.Serial != null && x.si.Serial == serial!.Trim())
|
||||
// Serial+Exp: requiere serial + expiration exactos
|
||||
|| (x.p.TraceabilityType == 5
|
||||
&& !string.IsNullOrWhiteSpace(serial) && expiration.HasValue
|
||||
&& x.si.Serial != null && x.si.Serial == serial!.Trim()
|
||||
&& x.si.Expiration.HasValue && x.si.Expiration.Value == expiration.Value)
|
||||
);
|
||||
|
||||
// 4) Orden y proyección
|
||||
baseQuery = baseQuery.OrderBy(x => x.si.Expiration).ThenBy(x => x.p.Name);
|
||||
|
||||
var dtoQuery = baseQuery.Select(x => new StockItemScanResultDto
|
||||
{
|
||||
StockItemId = x.si.Id,
|
||||
ProductId = x.p.Id,
|
||||
FactoryCode = x.p.FactoryCode ?? string.Empty,
|
||||
ExternalCode = x.p.ExternalCode,
|
||||
ProductName = x.p.Name ?? string.Empty,
|
||||
Description = x.p.Descripcion,
|
||||
LocationId = x.si.LocationId,
|
||||
LocationName = x.loc != null ? x.loc.Descripcion : null,
|
||||
Batch = x.si.Batch,
|
||||
Expiration = x.si.Expiration, // DateOnly?
|
||||
Serial = x.si.Serial, // si aplica
|
||||
TraceabilityType = x.p.TraceabilityType,
|
||||
AvailableQty = x.si.Quantity,
|
||||
PlusProcess = x.p.PlusProcess
|
||||
});
|
||||
|
||||
page = page <= 0 ? 1 : page;
|
||||
take = take <= 0 ? 20 : take;
|
||||
|
||||
var paged = await dtoQuery.ToPagedResultAsync(page, take);
|
||||
|
||||
return new PagedResult<StockItemScanResultDto>
|
||||
{
|
||||
Items = paged.Items,
|
||||
TotalItems = paged.TotalItems,
|
||||
Page = paged.Page,
|
||||
PageSize = paged.PageSize
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -148,7 +148,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
@ -4098,7 +4098,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
@ -1318,7 +1318,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
181
Transversal/Services/Gs1CodeParser.cs
Normal file
181
Transversal/Services/Gs1CodeParser.cs
Normal file
@ -0,0 +1,181 @@
|
||||
using Domain.Dtos.Stock;
|
||||
|
||||
namespace Transversal.Services
|
||||
{
|
||||
public static class Gs1CodeParser
|
||||
{
|
||||
// GS1 FNC1 (ASCII 29)
|
||||
private const char FNC1 = (char)29;
|
||||
|
||||
/// <summary>
|
||||
/// Parsea un string GS1 (GS1-128/EAN-128/DataMatrix) con AIs en cualquier orden.
|
||||
/// Soporta separadores FNC1 (ASCII 29), '$' y espacios. AIs: (01),(10),(11),(17),(21),(22).
|
||||
/// </summary>
|
||||
public static Gs1ScanResult Parse(string raw)
|
||||
{
|
||||
var result = new Gs1ScanResult
|
||||
{
|
||||
Raw = raw?.Trim() ?? string.Empty
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
return result;
|
||||
|
||||
// 1) Normalizar: $ y espacios -> FNC1; quitar saltos
|
||||
var s = raw.Trim()
|
||||
.Replace("\r", "")
|
||||
.Replace("\n", "")
|
||||
.Replace("$", FNC1.ToString());
|
||||
|
||||
// algunos escáneres meten espacios sueltos entre AIs/valores
|
||||
while (s.Contains(" ")) s = s.Replace(" ", " ");
|
||||
s = s.Replace(" ", string.Empty);
|
||||
|
||||
int i = 0;
|
||||
while (i < s.Length)
|
||||
{
|
||||
if (s[i] == FNC1) { i++; continue; }
|
||||
if (i + 2 > s.Length) break;
|
||||
|
||||
// Intentar leer AI de dos dígitos (los que usamos acá)
|
||||
var ai = s.Substring(i, 2);
|
||||
i += 2;
|
||||
|
||||
switch (ai)
|
||||
{
|
||||
case "01": // GTIN (14 fijos)
|
||||
if (TryTakeFixed(s, ref i, 14, out var gtin))
|
||||
result.Gtin = gtin;
|
||||
break;
|
||||
|
||||
case "17": // Expiración YYMMDD (6 fijos)
|
||||
if (TryTakeFixed(s, ref i, 6, out var yymmdd)
|
||||
&& TryParseYyMmDdToDateTime(yymmdd, out var expDt))
|
||||
{
|
||||
result.ExpirationDate = expDt;
|
||||
}
|
||||
break;
|
||||
|
||||
case "11": // Fabricación YYMMDD (6 fijos) -> opcional guardar
|
||||
if (TryTakeFixed(s, ref i, 6, out var mfgYymmdd)
|
||||
&& TryParseYyMmDdToDateTime(mfgYymmdd, out var mfgDt))
|
||||
{
|
||||
// Si querés guardar: agregá ManufacturingDate en tu DTO
|
||||
// result.ManufacturingDate = mfgDt;
|
||||
}
|
||||
break;
|
||||
|
||||
case "10": // Lote (var-length)
|
||||
result.Lot = TakeVariable(s, ref i);
|
||||
break;
|
||||
|
||||
case "21": // Serie (var-length)
|
||||
result.Serial = TakeVariable(s, ref i);
|
||||
break;
|
||||
|
||||
case "22": // Variante (var-length)
|
||||
result.Variant = TakeVariable(s, ref i);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Heurística: si el char previo a este AI no era FNC1, podría ser que no era AI real.
|
||||
// Retrocede 1 y avanza de a 1 hasta próximo FNC1 o final.
|
||||
i -= 1;
|
||||
SkipUnknownUntilFnc1(s, ref i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Heurística: 17 seguido de 10 sin FNC1 (p.ej. ...17YYMMDD10LOTE)
|
||||
if (result.ExpirationDate.HasValue && string.IsNullOrEmpty(result.Lot))
|
||||
{
|
||||
var idx17 = s.IndexOf("17", StringComparison.Ordinal);
|
||||
if (idx17 >= 0 && idx17 + 8 < s.Length) // "17" + 6 chars de fecha = +8
|
||||
{
|
||||
var after17 = s.Substring(idx17 + 8);
|
||||
var idx10 = after17.IndexOf("10", StringComparison.Ordinal);
|
||||
if (idx10 >= 0)
|
||||
{
|
||||
var start = idx17 + 8 + idx10 + 2;
|
||||
var lot = ReadUntilFnc1OrEnd(s, start);
|
||||
if (!string.IsNullOrEmpty(lot))
|
||||
result.Lot = lot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
private static bool TryTakeFixed(string s, ref int i, int length, out string value)
|
||||
{
|
||||
value = string.Empty;
|
||||
if (i + length > s.Length) return false;
|
||||
value = s.Substring(i, length);
|
||||
i += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extrae un valor de longitud variable (por ejemplo, Batch o Serial) del código GS1.
|
||||
/// Cortamos únicamente cuando encontramos un separador FNC1.
|
||||
/// No se intenta detectar un posible AI dentro del valor, ya que hay casos donde el valor
|
||||
/// legítimamente contiene secuencias como "22" o "17" que podrían confundirse con un AI.
|
||||
/// Ejemplo: un lote "52360227" no debe cortarse en "52360" al detectar "22".
|
||||
/// </summary>
|
||||
private static string TakeVariable(string s, ref int i)
|
||||
{
|
||||
int start = i;
|
||||
while (i < s.Length && s[i] != FNC1) i++;
|
||||
return s.Substring(start, i - start);
|
||||
}
|
||||
|
||||
private static bool LooksLikeNextAi(string s, int index)
|
||||
{
|
||||
if (index + 1 >= s.Length) return false;
|
||||
// AIs que nos interesan acá arrancan con 0–2 y son de 2 dígitos (01,10,11,17,21,22)
|
||||
return char.IsDigit(s[index]) && char.IsDigit(s[index + 1]) &&
|
||||
(s[index] == '0' || s[index] == '1' || s[index] == '2') &&
|
||||
(s.Substring(index, 2) is "01" or "10" or "11" or "17" or "21" or "22");
|
||||
}
|
||||
|
||||
private static void SkipUnknownUntilFnc1(string s, ref int i)
|
||||
{
|
||||
while (i < s.Length && s[i] != FNC1) i++;
|
||||
if (i < s.Length && s[i] == FNC1) i++; // consumir FNC1 si lo hay
|
||||
}
|
||||
|
||||
private static bool TryParseYyMmDdToDateTime(string yymmdd, out DateTime dt)
|
||||
{
|
||||
dt = default;
|
||||
if (yymmdd?.Length != 6) return false;
|
||||
|
||||
int yy = int.Parse(yymmdd.Substring(0, 2));
|
||||
int mm = int.Parse(yymmdd.Substring(2, 2));
|
||||
int dd = int.Parse(yymmdd.Substring(4, 2));
|
||||
|
||||
// GS1 usa 00–99; se suele mapear a 2000–2099. Ajustá si necesitás 1990–1999 para 90–99.
|
||||
int year = 2000 + yy;
|
||||
|
||||
try
|
||||
{
|
||||
dt = new DateTime(year, mm, dd);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadUntilFnc1OrEnd(string s, int start)
|
||||
{
|
||||
int i = start;
|
||||
while (i < s.Length && s[i] != FNC1) i++;
|
||||
return s.Substring(start, i - start);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
@ -37,5 +37,9 @@ namespace phronCare.API.Controllers.Sales
|
||||
[HttpGet("paymentterms")]
|
||||
public Task<IEnumerable<ELookUpItem>> PaymentTerms()
|
||||
=> _lookup.PaymentTermsListAsync(""); // o sin parámetro si lo hacés opcional
|
||||
|
||||
[HttpGet("approvedquotes")]
|
||||
public Task<IEnumerable<ELookUpItem>> ApprovedQuotes([FromQuery] string q)
|
||||
=> _lookup.ApprovedQuotesListAsync(q);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +94,30 @@ namespace phronCare.API.Controllers.Sales
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Devuelve un presupuesto DTO con información básica a partir del número de presupuesto.
|
||||
/// </summary>
|
||||
[HttpGet("summary/{quoteNumber}")]
|
||||
public async Task<ActionResult<QuoteDto>> GetHeaderDtoByQuoteNumber(string quoteNumber)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dto = await _quoteService.GetDtoByQuoteNumberAsync(quoteNumber);
|
||||
if (dto == null)
|
||||
return NotFound($"No se encontró un presupuesto con el número: {quoteNumber}");
|
||||
|
||||
return Ok(dto);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
|
||||
return StatusCode(500, $"{methodName} Message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Genera y devuelve un archivo PDF correspondiente al presupuesto especificado por su ID.
|
||||
/// </summary>
|
||||
[HttpGet("{id}/pdf")]
|
||||
public async Task<IActionResult> GetQuotePdf(int id)
|
||||
{
|
||||
|
||||
47
phronCare.API/Controllers/Stock/LSStockScanController.cs
Normal file
47
phronCare.API/Controllers/Stock/LSStockScanController.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using Core.Interfaces.Stock; // ILSStockScanDom
|
||||
using Domain.Dtos.Stock; // StockItemSearchParams, StockItemScanResultDto
|
||||
using Domain.Generics; // PagedResult<T>
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace API.Controllers.Stock
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class LSStockScanController : ControllerBase
|
||||
{
|
||||
private readonly ILSStockScanDom _service;
|
||||
|
||||
public LSStockScanController(ILSStockScanDom service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Búsqueda paginada de ítems de stock por código/texto, lote y filtros opcionales.
|
||||
/// </summary>
|
||||
[HttpPost("search")]
|
||||
public async Task<ActionResult<PagedResult<StockItemScanResultDto>>> Search([FromBody] StockItemSearchParams searchParams)
|
||||
{
|
||||
var result = await _service.SearchAsync(searchParams);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// Realiza una búsqueda paginada de ítems de stock utilizando datos ya parseados
|
||||
/// (por ejemplo, provenientes de un código GS1 escaneado).
|
||||
/// </summary>
|
||||
/// <param name="searchParams">
|
||||
/// Parámetros de búsqueda ya procesados y listos para filtrar en base de datos,
|
||||
/// incluyendo código de producto, lote, fecha de vencimiento, ubicación, etc.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Lista paginada de ítems de stock que cumplen con los filtros especificados.
|
||||
/// </returns>
|
||||
|
||||
[HttpPost("search-parsed")]
|
||||
public async Task<ActionResult<PagedResult<StockItemScanResultDto>>> SearchParsed([FromBody] StockItemParsedSearchParams searchParams)
|
||||
{
|
||||
var result = await _service.SearchParsedAsync(searchParams);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -271,5 +271,7 @@ static void RepositorysAndServices(WebApplicationBuilder builder)
|
||||
builder.Services.AddScoped<IPhLSMLookUpRepository, PhLSMLookUpRepository>();
|
||||
builder.Services.AddScoped<ILSMLookUpDom, LSMLookUpService>();
|
||||
builder.Services.AddScoped<IPhLSMUnitOfMeasureRepository, PhLSMUnitOfMeasureRepository>();
|
||||
builder.Services.AddScoped<ILSStockScanDom, LSStockScanService>();
|
||||
builder.Services.AddScoped<IPhLSMStockItemRepository, PhLSMStockItemRepository>();
|
||||
|
||||
}
|
||||
Binary file not shown.
@ -758,6 +758,32 @@
|
||||
],
|
||||
"ReturnTypes": []
|
||||
},
|
||||
{
|
||||
"ContainingType": "phronCare.API.Controllers.Sales.LookUpController",
|
||||
"Method": "ApprovedQuotes",
|
||||
"RelativePath": "api/LookUp/approvedquotes",
|
||||
"HttpMethod": "GET",
|
||||
"IsController": true,
|
||||
"Order": 0,
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "q",
|
||||
"Type": "System.String",
|
||||
"IsRequired": false
|
||||
}
|
||||
],
|
||||
"ReturnTypes": [
|
||||
{
|
||||
"Type": "System.Collections.Generic.IEnumerable\u00601[[Domain.Entities.ELookUpItem, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
|
||||
"MediaTypes": [
|
||||
"text/plain",
|
||||
"application/json",
|
||||
"text/json"
|
||||
],
|
||||
"StatusCode": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ContainingType": "phronCare.API.Controllers.Sales.LookUpController",
|
||||
"Method": "BussinessUnits",
|
||||
@ -1200,6 +1226,58 @@
|
||||
],
|
||||
"ReturnTypes": []
|
||||
},
|
||||
{
|
||||
"ContainingType": "API.Controllers.Stock.LSStockScanController",
|
||||
"Method": "Search",
|
||||
"RelativePath": "api/LSStockScan/search",
|
||||
"HttpMethod": "POST",
|
||||
"IsController": true,
|
||||
"Order": 0,
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "searchParams",
|
||||
"Type": "Domain.Generics.StockItemSearchParams",
|
||||
"IsRequired": true
|
||||
}
|
||||
],
|
||||
"ReturnTypes": [
|
||||
{
|
||||
"Type": "Domain.Generics.PagedResult\u00601[[Domain.Dtos.Stock.StockItemScanResultDto, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
|
||||
"MediaTypes": [
|
||||
"text/plain",
|
||||
"application/json",
|
||||
"text/json"
|
||||
],
|
||||
"StatusCode": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ContainingType": "API.Controllers.Stock.LSStockScanController",
|
||||
"Method": "SearchParsed",
|
||||
"RelativePath": "api/LSStockScan/search-parsed",
|
||||
"HttpMethod": "POST",
|
||||
"IsController": true,
|
||||
"Order": 0,
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "searchParams",
|
||||
"Type": "Domain.Generics.StockItemParsedSearchParams",
|
||||
"IsRequired": true
|
||||
}
|
||||
],
|
||||
"ReturnTypes": [
|
||||
{
|
||||
"Type": "Domain.Generics.PagedResult\u00601[[Domain.Dtos.Stock.StockItemScanResultDto, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
|
||||
"MediaTypes": [
|
||||
"text/plain",
|
||||
"application/json",
|
||||
"text/json"
|
||||
],
|
||||
"StatusCode": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ContainingType": "phronCare.API.Controllers.Stock.LSUnitOfMeasureController",
|
||||
"Method": "Create",
|
||||
@ -2196,6 +2274,32 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
|
||||
"Method": "GetHeaderDtoByQuoteNumber",
|
||||
"RelativePath": "api/Quote/summary/{quoteNumber}",
|
||||
"HttpMethod": "GET",
|
||||
"IsController": true,
|
||||
"Order": 0,
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "quoteNumber",
|
||||
"Type": "System.String",
|
||||
"IsRequired": true
|
||||
}
|
||||
],
|
||||
"ReturnTypes": [
|
||||
{
|
||||
"Type": "Domain.Dtos.QuoteDto",
|
||||
"MediaTypes": [
|
||||
"text/plain",
|
||||
"application/json",
|
||||
"text/json"
|
||||
],
|
||||
"StatusCode": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ContainingType": "phronCare.API.Controllers.Sales.TaxConditionController",
|
||||
"Method": "GetAll",
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -163,7 +163,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -229,7 +229,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -311,7 +311,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -479,7 +479,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -559,7 +559,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -639,7 +639,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
@ -9811,7 +9811,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -166,7 +166,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -246,7 +246,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
@ -2503,7 +2503,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@
|
||||
</NavLink>
|
||||
</div>
|
||||
<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/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="stock/expeditions/create" activeClass="bg-secondary text-white fw-semibold">
|
||||
<span class="oi oi-data-transfer-download me-2 text-danger"></span> Expediciones
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
<br/>
|
||||
<div class="col-12" style="text-align:center;">
|
||||
<button type="submit" class="btn btn-primary"><span class="fa fa-vault"></span> Restablecer</button>
|
||||
<button class="btn btn-warning btn-circle" style="fData.Login.Loginght;" @olick="ToggleIsSecret">
|
||||
<button class="btn btn-warning btn-circle" style="fData.Login.Loginght;" @onclick="ToggleIsSecret">
|
||||
<span class="fa fa-circle-left"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,276 @@
|
||||
@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; }
|
||||
}
|
||||
}
|
||||
@ -138,7 +138,7 @@
|
||||
|
||||
private List<string> TableColumns = new()
|
||||
{
|
||||
"Id", "Código Fábrica", "Código Externo", "Nombre", "Descripción", "División", "Unidad", "Tipo", "Trazabilidad", "Esteriliza"
|
||||
"Id", "Código Fábrica", "Código Externo", "Código Regulatorio", "Nombre", "Descripción", "División", "Unidad", "Tipo", "Trazabilidad", "Esteriliza"
|
||||
};
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@ -176,6 +176,7 @@
|
||||
{ "Id", p.Id },
|
||||
{ "Código Fábrica", p.FactoryCode },
|
||||
{ "Código Externo", p.ExternalCode?? string.Empty },
|
||||
{ "Código Regulatorio", p.RegulatoryCode?? string.Empty },
|
||||
{ "Nombre", p.Name?? string.Empty },
|
||||
{ "Descripción", p.Descripcion },
|
||||
{ "División", p.Division?.Name ?? "" },
|
||||
|
||||
@ -24,14 +24,18 @@
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-4">
|
||||
<label>Código Fábrica *</label>
|
||||
<InputText class="form-control" @bind-Value="product.FactoryCode" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-4">
|
||||
<label>Código Externo</label>
|
||||
<InputText class="form-control" @bind-Value="product.ExternalCode" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label>Código Regulatorio</label>
|
||||
<InputText class="form-control" @bind-Value="product.RegulatoryCode" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
|
||||
@ -0,0 +1,221 @@
|
||||
@using Blazored.Modal
|
||||
@using Blazored.Modal.Services
|
||||
@using Domain.Dtos.Stock
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
|
||||
@inject IToastService toastService
|
||||
@inject IStockScanService stockScanService
|
||||
@inject IModalService Modal
|
||||
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="modal-header bg-dark text-white">
|
||||
<h5 class="modal-title">Seleccionar artículos de stock</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="Cancel"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body" style="zoom:0.8;">
|
||||
<div class="mb-3">
|
||||
<label for="scan" class="form-label">Escanear o ingresar código</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-qrcode"></i>
|
||||
</span>
|
||||
|
||||
<!-- Input nativo: @ref es ElementReference; capturamos Enter -->
|
||||
<input class="form-control"
|
||||
id="scan"
|
||||
@bind="InputCode"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="HandleKeyDown"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
@ref="scanInput" />
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-secondary mt-2" @onclick="HandleScan">Buscar</button>
|
||||
</div>
|
||||
|
||||
@if (StockList.Any())
|
||||
{
|
||||
var last = StockList.First();
|
||||
<div class="alert alert-info small">
|
||||
Último escaneado: <strong>@last.ProductName</strong> | Lote <strong>@last.Batch</strong> | Venc: <strong>@last.Expiration?.ToShortDateString()</strong>
|
||||
</div>
|
||||
}
|
||||
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Producto</th>
|
||||
<th>Lote</th>
|
||||
<th>Vencimiento</th>
|
||||
<th>Disponible</th>
|
||||
<th>Cantidad a usar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in StockList)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.ProductName</td>
|
||||
<td>@item.Batch</td>
|
||||
<td>@item.Expiration?.ToShortDateString()</td>
|
||||
<td>@item.Available</td>
|
||||
<td>
|
||||
<InputNumber @bind-Value="item.Selected" class="form-control form-control-sm" min="0" max="@item.Available" />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @onclick="Cancel">Cancelar</button>
|
||||
<button class="btn btn-primary" @onclick="Confirm">Agregar a lista</button>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] BlazoredModalInstance ModalInstance { get; set; } = default!;
|
||||
|
||||
[Parameter] public int? ProductId { get; set; }
|
||||
[Parameter] public int? LocationId { get; set; }
|
||||
[Parameter] public List<ProductSetItemDto>? SetItems { get; set; }
|
||||
|
||||
private string InputCode { get; set; } = string.Empty;
|
||||
private ElementReference scanInput;
|
||||
|
||||
private readonly List<StockItemSelectionDto> SelectedItems = new();
|
||||
private List<StockDisplayRow> StockList = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadMockStock();
|
||||
}
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
await scanInput.FocusAsync();
|
||||
}
|
||||
private async Task OnKeyDown(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Key == "Enter")
|
||||
await HandleScan();
|
||||
}
|
||||
private async Task HandleKeyDown(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Key is "Enter" or "NumpadEnter")
|
||||
{
|
||||
await HandleScan();
|
||||
await scanInput.FocusAsync();
|
||||
}
|
||||
}
|
||||
private async Task HandleScan()
|
||||
{
|
||||
var code = (InputCode ?? string.Empty).Trim(); // limpia CR/LF del lector
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
{
|
||||
toastService.ShowWarning("Ingrese un código válido.");
|
||||
await Refocus();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var matchedItem = await stockScanService.ParseAndMatchAsync(code, LocationId ?? 1);
|
||||
|
||||
if (matchedItem is null)
|
||||
{
|
||||
toastService.ShowWarning("No se encontró el producto en stock.");
|
||||
await Refocus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (StockList.Any(s => s.StockItemId == matchedItem.StockItemId))
|
||||
{
|
||||
toastService.ShowInfo("Este ítem ya está listado.");
|
||||
await Refocus();
|
||||
return;
|
||||
}
|
||||
|
||||
StockList.Insert(0, new StockDisplayRow
|
||||
{
|
||||
StockItemId = matchedItem.StockItemId,
|
||||
ProductId = matchedItem.ProductId,
|
||||
ProductName = matchedItem.ProductName,
|
||||
Batch = matchedItem.Batch,
|
||||
Expiration = matchedItem.Expiration,
|
||||
Available = matchedItem.Quantity,
|
||||
Selected = 1,
|
||||
LocationId = matchedItem.LocationId
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
toastService.ShowError($"Error en escaneo: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
InputCode = string.Empty;
|
||||
await Refocus();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Refocus()
|
||||
{
|
||||
await Task.Yield(); // asegura que el DOM está listo
|
||||
await scanInput.FocusAsync(); // deja el cursor listo para el próximo escaneo
|
||||
}
|
||||
|
||||
private Task Cancel() => ModalInstance.CancelAsync();
|
||||
|
||||
private async Task Confirm()
|
||||
{
|
||||
var selected = StockList
|
||||
.Where(x => x.Selected > 0)
|
||||
.Select(x => new StockItemSelectionDto
|
||||
{
|
||||
StockItemId = x.StockItemId,
|
||||
ProductId = x.ProductId,
|
||||
ProductName = x.ProductName,
|
||||
Batch = x.Batch,
|
||||
Expiration = x.Expiration,
|
||||
Quantity = x.Selected,
|
||||
LocationId = x.LocationId
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (!selected.Any())
|
||||
{
|
||||
toastService.ShowWarning("No se seleccionó ningún producto.");
|
||||
return;
|
||||
}
|
||||
|
||||
await ModalInstance.CloseAsync(ModalResult.Ok(selected));
|
||||
}
|
||||
|
||||
private async Task LoadMockStock()
|
||||
{
|
||||
StockList = new List<StockDisplayRow>
|
||||
{
|
||||
new StockDisplayRow { StockItemId = 101, ProductId = 1, ProductName = "Tornillo 4mm x 20mm", Batch = "LOTE001", Expiration = DateTime.Today.AddMonths(12), Available = 5, Selected = 0, LocationId = LocationId ?? 1 },
|
||||
new StockDisplayRow { StockItemId = 102, ProductId = 1, ProductName = "Tornillo 4mm x 20mm", Batch = "LOTE002", Expiration = DateTime.Today.AddMonths(18), Available = 10, Selected = 0, LocationId = LocationId ?? 1 },
|
||||
new StockDisplayRow { StockItemId = 103, ProductId = 2, ProductName = "Placa LCP 6 orificios", Batch = "PL001-A", Expiration = DateTime.Today.AddYears(2), Available = 3, Selected = 0, LocationId = LocationId ?? 1 }
|
||||
};
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public class StockDisplayRow
|
||||
{
|
||||
public int StockItemId { get; set; }
|
||||
public int ProductId { get; set; }
|
||||
public string ProductName { get; set; } = string.Empty;
|
||||
public string Batch { get; set; } = string.Empty;
|
||||
public DateTime? Expiration { get; set; }
|
||||
public decimal Available { get; set; }
|
||||
public decimal Selected { get; set; }
|
||||
public int LocationId { get; set; }
|
||||
}
|
||||
}
|
||||
123
phronCare.UIBlazor/Pages/Stock/Shared/StockScanModal.razor
Normal file
123
phronCare.UIBlazor/Pages/Stock/Shared/StockScanModal.razor
Normal file
@ -0,0 +1,123 @@
|
||||
@using Blazored.Modal
|
||||
@using Blazored.Modal.Services
|
||||
@using Domain.Dtos.Stock
|
||||
@inject IStockScanService StockScanService
|
||||
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title">Escaneo de producto</h5>
|
||||
<button type="button" class="btn-close" @onclick="Cancel"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="scan" class="form-label">Escanear o ingresar código</label>
|
||||
@* <InputText id="scan"
|
||||
class="form-control"
|
||||
@bind-Value="ScanInput" /> *@
|
||||
<input @bind="ScanInput"
|
||||
@bind:event="oninput"
|
||||
class="form-control form-control-sm"
|
||||
placeholder="Scannear..."
|
||||
style="width: 250px;" />
|
||||
@* <button class="btn btn-secondary mt-2" @onclick="HandleScan">Buscar</button> *@
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(ErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger">@ErrorMessage</div>
|
||||
}
|
||||
|
||||
@if (ScanResults.Any())
|
||||
{
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Producto</th>
|
||||
<th>Lote</th>
|
||||
<th>Vencimiento</th>
|
||||
<th>Disponible</th>
|
||||
<th>Cantidad a usar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in ScanResults)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.ProductName</td>
|
||||
<td>@item.Batch</td>
|
||||
<td>@item.Expiration?.ToShortDateString()</td>
|
||||
<td>@item.Quantity</td>
|
||||
<td>
|
||||
<InputNumber @bind-Value="item.Quantity"
|
||||
class="form-control form-control-sm"
|
||||
min="0" />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @onclick="Cancel">Cancelar</button>
|
||||
<button class="btn btn-primary" @onclick="ConfirmSelection" disabled="@(!ScanResults.Any(r => r.Quantity > 0))">Agregar</button>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] BlazoredModalInstance ModalInstance { get; set; }
|
||||
|
||||
[Parameter] public int? LocationId { get; set; }
|
||||
private string SearchAddress { get; set; } = string.Empty;
|
||||
|
||||
private string ScanInput { get; set; } = string.Empty;
|
||||
private string ErrorMessage { get; set; } = string.Empty;
|
||||
|
||||
private List<StockItemSelectionDto> ScanResults { get; set; } = new();
|
||||
|
||||
private async Task HandleScan()
|
||||
{
|
||||
ErrorMessage = string.Empty;
|
||||
ScanResults.Clear();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ScanInput))
|
||||
{
|
||||
ErrorMessage = "Ingrese un código válido.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (LocationId is null)
|
||||
{
|
||||
ErrorMessage = "Falta el depósito para escanear correctamente.";
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await StockScanService.ParseAndMatchAsync(ScanInput, LocationId.Value);
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
ScanResults.Add(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage = "No se encontró stock coincidente.";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"Error: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void Cancel() => ModalInstance.CancelAsync();
|
||||
|
||||
private void ConfirmSelection()
|
||||
{
|
||||
var selected = ScanResults.Where(r => r.Quantity > 0).ToList();
|
||||
ModalInstance.CloseAsync(ModalResult.Ok(selected));
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Blazored.Modal;
|
||||
using Blazored.Toast;
|
||||
using phronCare.UIBlazor.Services.Stock;
|
||||
using phronCare.UIBlazor.Services.Stock.Expeditions;
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
@ -48,6 +49,7 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
|
||||
builder.Services.AddScoped<ISalesLookupService, SalesLookupService>();
|
||||
builder.Services.AddScoped<IStockLookUpService, StockLookUpService>();
|
||||
builder.Services.AddScoped<IExchangeRateService, ExchangeRateService>();
|
||||
builder.Services.AddScoped<IStockScanService, StockScanService>();
|
||||
|
||||
builder.Services.AddScoped<ExchangeRateService>();
|
||||
builder.Services.AddScoped<QuoteService>();
|
||||
@ -67,6 +69,7 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
|
||||
builder.Services.AddScoped<ProductDivisionService>();
|
||||
builder.Services.AddScoped<LSProductService>();
|
||||
builder.Services.AddScoped<LSUnitOfMeasureService>();
|
||||
builder.Services.AddScoped<ExpeditionService>();
|
||||
|
||||
|
||||
}
|
||||
@ -14,5 +14,6 @@ namespace phronCare.UIBlazor.Services.Lookups
|
||||
Task<IEnumerable<EAdjustmentReason>> GetAdjustmentReasonsAsync();
|
||||
Task<IEnumerable<ETaxType>> GetTaxTypesAsync();
|
||||
Task<IEnumerable<ELookUpItem>> GetPaymentTermsAsync();
|
||||
Task<IEnumerable<ELookUpItem>> SearchApprovedQuotesAsync(string filtro);
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,6 @@ namespace phronCare.UIBlazor.Services.Lookups
|
||||
var items = await _http.GetFromJsonAsync<ELookUpItem[]>(url);
|
||||
return items ?? Array.Empty<ELookUpItem>();
|
||||
}
|
||||
|
||||
public Task<IEnumerable<EProductLookupItem>> SearchProductsAsync(string filtro)
|
||||
=> FetchProductsAsync($"api/lookup/products?q={Uri.EscapeDataString(filtro)}");
|
||||
public async Task<IEnumerable<EAdjustmentReason>> GetAdjustmentReasonsAsync()
|
||||
@ -43,7 +42,6 @@ namespace phronCare.UIBlazor.Services.Lookups
|
||||
var items = await _http.GetFromJsonAsync<EAdjustmentReason[]>("api/adjustmentreason/getall");
|
||||
return items ?? Array.Empty<EAdjustmentReason>();
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<EProductLookupItem>> FetchProductsAsync(string url)
|
||||
{
|
||||
var items = await _http.GetFromJsonAsync<EProductLookupItem[]>(url);
|
||||
@ -59,5 +57,7 @@ namespace phronCare.UIBlazor.Services.Lookups
|
||||
var items = await _http.GetFromJsonAsync<ELookUpItem[]>("api/lookup/paymentterms?q=");
|
||||
return items ?? Array.Empty<ELookUpItem>();
|
||||
}
|
||||
public Task<IEnumerable<ELookUpItem>> SearchApprovedQuotesAsync(string filtro)
|
||||
=> FetchAsync($"api/lookup/approvedquotes?q={Uri.EscapeDataString(filtro)}", filtro);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ namespace Services.Sales.Quotes
|
||||
{
|
||||
public interface IQuoteService
|
||||
{
|
||||
Task<CreateQuoteResult> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId);
|
||||
//Task<CreateQuoteResult> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId);
|
||||
// Aquí podrías agregar otros métodos: GetById, Search, etc.
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,6 +124,9 @@ namespace phronCare.UIBlazor.Services.Sales.Quotes
|
||||
throw new Exception($"ExportPdfAsync: {message}", ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Envía una solicitud al backend para autorizar un presupuesto específico.
|
||||
/// </summary>
|
||||
public async Task<bool> AuthorizeQuoteAsync(QuoteAuthorizationRequest request)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync("/api/quote/authorize", request);
|
||||
@ -136,6 +139,7 @@ namespace phronCare.UIBlazor.Services.Sales.Quotes
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene un presupuesto completo por ID para su visualización y autorización.
|
||||
/// </summary>
|
||||
@ -152,6 +156,7 @@ namespace phronCare.UIBlazor.Services.Sales.Quotes
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CreateQuoteResult
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
using Domain.Dtos;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
||||
{
|
||||
public class ExpeditionService
|
||||
{
|
||||
private readonly IJSRuntime _js;
|
||||
private readonly HttpClient _http;
|
||||
public ExpeditionService(HttpClient http, IJSRuntime js)
|
||||
{
|
||||
_js = js;
|
||||
_http = http;
|
||||
}
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
||||
{
|
||||
public interface IExpeditionService
|
||||
{
|
||||
}
|
||||
}
|
||||
7
phronCare.UIBlazor/Services/Stock/IStockScanService.cs
Normal file
7
phronCare.UIBlazor/Services/Stock/IStockScanService.cs
Normal file
@ -0,0 +1,7 @@
|
||||
using Domain.Dtos.Stock;
|
||||
|
||||
public interface IStockScanService
|
||||
{
|
||||
Task<StockItemSelectionDto?> ParseAndMatchAsync(string rawInput, int locationId);
|
||||
}
|
||||
|
||||
30
phronCare.UIBlazor/Services/Stock/MockStockScanService.cs
Normal file
30
phronCare.UIBlazor/Services/Stock/MockStockScanService.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using Domain.Dtos.Stock;
|
||||
|
||||
public class MockStockScanService : IStockScanService
|
||||
{
|
||||
public async Task<StockItemSelectionDto?> ParseAndMatchAsync(string rawInput, int locationId)
|
||||
{
|
||||
// Simula lógica de parseo de código escaneado
|
||||
await Task.Delay(100); // simula delay
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rawInput))
|
||||
return null;
|
||||
|
||||
// Simulación: si empieza con 0108... devolvemos un producto de prueba
|
||||
if (rawInput.StartsWith("0108"))
|
||||
{
|
||||
return new StockItemSelectionDto
|
||||
{
|
||||
StockItemId = 999,
|
||||
ProductId = 88,
|
||||
ProductName = "Tornillo 6x30 mm",
|
||||
Batch = "LOTE-MOCK",
|
||||
Expiration = DateTime.Today.AddMonths(10),
|
||||
Quantity = 10,
|
||||
LocationId = locationId
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
110
phronCare.UIBlazor/Services/Stock/StockScanService.cs
Normal file
110
phronCare.UIBlazor/Services/Stock/StockScanService.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using System.Net.Http.Json;
|
||||
using Domain.Dtos.Stock;
|
||||
using Domain.Generics;
|
||||
using Transversal.Services;
|
||||
|
||||
public class StockScanService : IStockScanService
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public StockScanService(HttpClient http)
|
||||
{
|
||||
_http = http;
|
||||
}
|
||||
|
||||
public async Task<StockItemSelectionDto?> ParseAndMatchAsync(string rawInput, int locationId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rawInput))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
//var parsed = Gs1CodeParser.Parse(rawInput);
|
||||
//var raw = rawInput.Trim();
|
||||
|
||||
//// usar raw como GTIN solo si NO hay AIs parseados
|
||||
//bool hasParsedAis = !string.IsNullOrWhiteSpace(parsed.Lot)
|
||||
// || parsed.ExpirationDate.HasValue
|
||||
// || !string.IsNullOrWhiteSpace(parsed.Serial);
|
||||
|
||||
//string? gtinToSend = parsed.Gtin;
|
||||
//if (gtinToSend is null && !hasParsedAis && IsPlainCode(raw))
|
||||
// gtinToSend = raw; // ej: factory_code tipeado ("336005")
|
||||
var parsed = Gs1CodeParser.Parse(rawInput);
|
||||
var raw = rawInput.Trim();
|
||||
|
||||
bool hasParsedAis = !string.IsNullOrWhiteSpace(parsed.Lot)
|
||||
|| parsed.ExpirationDate.HasValue
|
||||
|| !string.IsNullOrWhiteSpace(parsed.Serial)
|
||||
|| !string.IsNullOrWhiteSpace(parsed.Variant); // incluir (22)
|
||||
|
||||
string? gtinToSend = parsed.Gtin ?? parsed.Variant; // (22) como fallback
|
||||
if (gtinToSend is null && !hasParsedAis && IsPlainCode(raw))
|
||||
gtinToSend = raw; // código plano tipeado (factory/regulatory)
|
||||
|
||||
|
||||
// 3. Armar parámetros de búsqueda
|
||||
var sp = new StockItemParsedSearchParams
|
||||
{
|
||||
Gtin = gtinToSend,
|
||||
Batch = string.IsNullOrWhiteSpace(parsed.Lot) ? null : parsed.Lot,
|
||||
Expiration = parsed.ExpirationDate.HasValue
|
||||
? DateOnly.FromDateTime(parsed.ExpirationDate.Value)
|
||||
: null,
|
||||
Serial = string.IsNullOrWhiteSpace(parsed.Serial) ? null : parsed.Serial,
|
||||
LocationId = locationId,
|
||||
Page = 1,
|
||||
PageSize = 10
|
||||
};
|
||||
|
||||
// 4. Log para depuración (quitar en producción)
|
||||
Console.WriteLine($"[ParseAndMatchAsync] Gtin={sp.Gtin}, Batch={sp.Batch}, Exp={sp.Expiration}, Serial={sp.Serial}, Loc={sp.LocationId}");
|
||||
|
||||
// 5. Llamar a la API
|
||||
var resp = await _http.PostAsJsonAsync("/api/lsstockscan/search-parsed", sp);
|
||||
if (!resp.IsSuccessStatusCode)
|
||||
{
|
||||
var err = await resp.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"[ParseAndMatchAsync] API devolvió error {resp.StatusCode}: {err}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 6. Leer resultado
|
||||
var pr = await resp.Content.ReadFromJsonAsync<PagedResult<StockItemScanResultDto>>();
|
||||
var first = pr?.Items?.FirstOrDefault();
|
||||
if (first == null)
|
||||
{
|
||||
Console.WriteLine("[ParseAndMatchAsync] No se encontró ningún ítem que coincida.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 7. Mapear a DTO de selección
|
||||
return new StockItemSelectionDto
|
||||
{
|
||||
StockItemId = first.StockItemId,
|
||||
ProductId = first.ProductId,
|
||||
ProductName = first.ProductName,
|
||||
Batch = first.Batch ?? string.Empty,
|
||||
Expiration = first.Expiration?.ToDateTime(TimeOnly.MinValue),
|
||||
Quantity = first.AvailableQty,
|
||||
LocationId = first.LocationId ?? 0
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[ParseAndMatchAsync] Error inesperado: {ex}");
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
private static bool IsPlainCode(string s)
|
||||
{
|
||||
// sin FNC1 ($), sin espacios y sin prefijos AI típicos
|
||||
if (s.Contains('$') || s.Contains((char)29) || s.Contains(' ')) return false;
|
||||
// evita raws que empiezan como AIs "01","10","17","21","22"
|
||||
var prefix = s.Length >= 2 ? s[..2] : s;
|
||||
if (prefix is "01" or "10" or "11" or "17" or "21" or "22") return false;
|
||||
// permite letras/dígitos y algunos separadores comunes
|
||||
return s.All(c => char.IsLetterOrDigit(c) || c is '-' or '/' or '_');
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -152,13 +152,13 @@
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"suppressParent": "All",
|
||||
"target": "Package",
|
||||
"version": "[8.0.18, )",
|
||||
"version": "[8.0.19, )",
|
||||
"autoReferenced": true
|
||||
},
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack": {
|
||||
"suppressParent": "All",
|
||||
"target": "Package",
|
||||
"version": "[9.0.7, )",
|
||||
"version": "[9.0.8, )",
|
||||
"autoReferenced": true
|
||||
},
|
||||
"PSC.Blazor.Components.Chartjs": {
|
||||
@ -180,7 +180,7 @@
|
||||
"downloadDependencies": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App.Runtime.Mono.browser-wasm",
|
||||
"version": "[8.0.18, 8.0.18]"
|
||||
"version": "[8.0.19, 8.0.19]"
|
||||
}
|
||||
],
|
||||
"frameworkReferences": {
|
||||
@ -188,7 +188,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
},
|
||||
"runtimes": {
|
||||
@ -273,7 +273,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
@ -33,8 +33,8 @@
|
||||
</ItemGroup>
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)psc.blazor.components.chartjs\8.0.8\buildTransitive\PSC.Blazor.Components.Chartjs.props" Condition="Exists('$(NuGetPackageRoot)psc.blazor.components.chartjs\8.0.8\buildTransitive\PSC.Blazor.Components.Chartjs.props')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.7\build\Microsoft.NET.Sdk.WebAssembly.Pack.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.7\build\Microsoft.NET.Sdk.WebAssembly.Pack.props')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.net.illink.tasks\8.0.18\build\Microsoft.NET.ILLink.Tasks.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.illink.tasks\8.0.18\build\Microsoft.NET.ILLink.Tasks.props')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.8\build\Microsoft.NET.Sdk.WebAssembly.Pack.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.8\build\Microsoft.NET.Sdk.WebAssembly.Pack.props')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.net.illink.tasks\8.0.19\build\Microsoft.NET.ILLink.Tasks.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.illink.tasks\8.0.19\build\Microsoft.NET.ILLink.Tasks.props')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.aspnetcore.components.webassembly\8.0.6\build\net8.0\Microsoft.AspNetCore.Components.WebAssembly.props" Condition="Exists('$(NuGetPackageRoot)microsoft.aspnetcore.components.webassembly\8.0.6\build\net8.0\Microsoft.AspNetCore.Components.WebAssembly.props')" />
|
||||
<Import Project="$(NuGetPackageRoot)blazored.typeahead\4.7.0\buildTransitive\Blazored.Typeahead.props" Condition="Exists('$(NuGetPackageRoot)blazored.typeahead\4.7.0\buildTransitive\Blazored.Typeahead.props')" />
|
||||
<Import Project="$(NuGetPackageRoot)blazored.toast\4.2.1\buildTransitive\Blazored.Toast.props" Condition="Exists('$(NuGetPackageRoot)blazored.toast\4.2.1\buildTransitive\Blazored.Toast.props')" />
|
||||
@ -42,8 +42,8 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<PkgNewtonsoft_Json Condition=" '$(PkgNewtonsoft_Json)' == '' ">C:\Users\maski\.nuget\packages\newtonsoft.json\10.0.3</PkgNewtonsoft_Json>
|
||||
<PkgMicrosoft_NET_Sdk_WebAssembly_Pack Condition=" '$(PkgMicrosoft_NET_Sdk_WebAssembly_Pack)' == '' ">C:\Users\maski\.nuget\packages\microsoft.net.sdk.webassembly.pack\9.0.7</PkgMicrosoft_NET_Sdk_WebAssembly_Pack>
|
||||
<PkgMicrosoft_NET_ILLink_Tasks Condition=" '$(PkgMicrosoft_NET_ILLink_Tasks)' == '' ">C:\Users\maski\.nuget\packages\microsoft.net.illink.tasks\8.0.18</PkgMicrosoft_NET_ILLink_Tasks>
|
||||
<PkgMicrosoft_NET_Sdk_WebAssembly_Pack Condition=" '$(PkgMicrosoft_NET_Sdk_WebAssembly_Pack)' == '' ">C:\Users\maski\.nuget\packages\microsoft.net.sdk.webassembly.pack\9.0.8</PkgMicrosoft_NET_Sdk_WebAssembly_Pack>
|
||||
<PkgMicrosoft_NET_ILLink_Tasks Condition=" '$(PkgMicrosoft_NET_ILLink_Tasks)' == '' ">C:\Users\maski\.nuget\packages\microsoft.net.illink.tasks\8.0.19</PkgMicrosoft_NET_ILLink_Tasks>
|
||||
<PkgMicrosoft_AspNetCore_Components_WebAssembly_DevServer Condition=" '$(PkgMicrosoft_AspNetCore_Components_WebAssembly_DevServer)' == '' ">C:\Users\maski\.nuget\packages\microsoft.aspnetcore.components.webassembly.devserver\8.0.6</PkgMicrosoft_AspNetCore_Components_WebAssembly_DevServer>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@ -4,7 +4,7 @@
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.1\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.1\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.aspnetcore.components.analyzers\8.0.6\buildTransitive\netstandard2.0\Microsoft.AspNetCore.Components.Analyzers.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.aspnetcore.components.analyzers\8.0.6\buildTransitive\netstandard2.0\Microsoft.AspNetCore.Components.Analyzers.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.7\build\Microsoft.NET.Sdk.WebAssembly.Pack.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.7\build\Microsoft.NET.Sdk.WebAssembly.Pack.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.8\build\Microsoft.NET.Sdk.WebAssembly.Pack.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.8\build\Microsoft.NET.Sdk.WebAssembly.Pack.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.binder\8.0.1\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.binder\8.0.1\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.aspnetcore.components.webassembly.devserver\8.0.6\build\Microsoft.AspNetCore.Components.WebAssembly.DevServer.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.aspnetcore.components.webassembly.devserver\8.0.6\build\Microsoft.AspNetCore.Components.WebAssembly.DevServer.targets')" />
|
||||
</ImportGroup>
|
||||
|
||||
@ -628,13 +628,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.NET.ILLink.Tasks/8.0.18": {
|
||||
"Microsoft.NET.ILLink.Tasks/8.0.19": {
|
||||
"type": "package",
|
||||
"build": {
|
||||
"build/Microsoft.NET.ILLink.Tasks.props": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack/9.0.7": {
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack/9.0.8": {
|
||||
"type": "package",
|
||||
"build": {
|
||||
"build/Microsoft.NET.Sdk.WebAssembly.Pack.props": {},
|
||||
@ -2962,13 +2962,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.NET.ILLink.Tasks/8.0.18": {
|
||||
"Microsoft.NET.ILLink.Tasks/8.0.19": {
|
||||
"type": "package",
|
||||
"build": {
|
||||
"build/Microsoft.NET.ILLink.Tasks.props": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack/9.0.7": {
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack/9.0.8": {
|
||||
"type": "package",
|
||||
"build": {
|
||||
"build/Microsoft.NET.Sdk.WebAssembly.Pack.props": {},
|
||||
@ -5752,10 +5752,10 @@
|
||||
"microsoft.net.http.headers.nuspec"
|
||||
]
|
||||
},
|
||||
"Microsoft.NET.ILLink.Tasks/8.0.18": {
|
||||
"sha512": "OiXqr2YIBEV9dsAWEtasK470ALyJ0VxJ9k4MotOxlWV6HeEgrJKYMW4HHj1OCCXvqE0/A25wEKPkpfiBARgDZA==",
|
||||
"Microsoft.NET.ILLink.Tasks/8.0.19": {
|
||||
"sha512": "IhHf+zeZiaE5EXRyxILd4qM+Hj9cxV3sa8MpzZgeEhpvaG3a1VEGF6UCaPFLO44Kua3JkLKluE0SWVamS50PlA==",
|
||||
"type": "package",
|
||||
"path": "microsoft.net.illink.tasks/8.0.18",
|
||||
"path": "microsoft.net.illink.tasks/8.0.19",
|
||||
"hasTools": true,
|
||||
"files": [
|
||||
".nupkg.metadata",
|
||||
@ -5769,7 +5769,7 @@
|
||||
"build/Microsoft.NET.ILLink.Analyzers.props",
|
||||
"build/Microsoft.NET.ILLink.Tasks.props",
|
||||
"build/Microsoft.NET.ILLink.targets",
|
||||
"microsoft.net.illink.tasks.8.0.18.nupkg.sha512",
|
||||
"microsoft.net.illink.tasks.8.0.19.nupkg.sha512",
|
||||
"microsoft.net.illink.tasks.nuspec",
|
||||
"tools/net472/ILLink.Tasks.dll",
|
||||
"tools/net472/ILLink.Tasks.dll.config",
|
||||
@ -5803,10 +5803,10 @@
|
||||
"useSharedDesignerContext.txt"
|
||||
]
|
||||
},
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack/9.0.7": {
|
||||
"sha512": "5ehgbqGUERh0JVhTUPwFizw4hIoAglkFk/WMs45djePp16YHP11Vnmx44rOQ3gLW8/aDYN1j+pKdAtcEp4QOcw==",
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack/9.0.8": {
|
||||
"sha512": "+JJYRyS8YoLMzLquskLfdF8RFp2PhN5v+lGCGDLHsywj3JHfSV/Zqo8+T0Fl0Xvoc9Js8YkrAWTnm7M2/3CniA==",
|
||||
"type": "package",
|
||||
"path": "microsoft.net.sdk.webassembly.pack/9.0.7",
|
||||
"path": "microsoft.net.sdk.webassembly.pack/9.0.8",
|
||||
"hasTools": true,
|
||||
"files": [
|
||||
".nupkg.metadata",
|
||||
@ -5837,7 +5837,7 @@
|
||||
"build/Microsoft.NET.Sdk.WebAssembly.Pack.targets",
|
||||
"build/Wasm.web.config",
|
||||
"build/browser.runtimeconfig.template.json",
|
||||
"microsoft.net.sdk.webassembly.pack.9.0.7.nupkg.sha512",
|
||||
"microsoft.net.sdk.webassembly.pack.9.0.8.nupkg.sha512",
|
||||
"microsoft.net.sdk.webassembly.pack.nuspec",
|
||||
"tools/net472/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks.dll",
|
||||
"tools/net472/Microsoft.NET.WebAssembly.Webcil.dll",
|
||||
@ -10559,8 +10559,8 @@
|
||||
"Microsoft.AspNetCore.Components.Authorization >= 8.0.6",
|
||||
"Microsoft.AspNetCore.Components.WebAssembly >= 8.0.6",
|
||||
"Microsoft.AspNetCore.Components.WebAssembly.DevServer >= 8.0.6",
|
||||
"Microsoft.NET.ILLink.Tasks >= 8.0.18",
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack >= 9.0.7",
|
||||
"Microsoft.NET.ILLink.Tasks >= 8.0.19",
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack >= 9.0.8",
|
||||
"PSC.Blazor.Components.Chartjs >= 8.0.8",
|
||||
"Transversal >= 1.0.0"
|
||||
]
|
||||
@ -10651,13 +10651,13 @@
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"suppressParent": "All",
|
||||
"target": "Package",
|
||||
"version": "[8.0.18, )",
|
||||
"version": "[8.0.19, )",
|
||||
"autoReferenced": true
|
||||
},
|
||||
"Microsoft.NET.Sdk.WebAssembly.Pack": {
|
||||
"suppressParent": "All",
|
||||
"target": "Package",
|
||||
"version": "[9.0.7, )",
|
||||
"version": "[9.0.8, )",
|
||||
"autoReferenced": true
|
||||
},
|
||||
"PSC.Blazor.Components.Chartjs": {
|
||||
@ -10679,7 +10679,7 @@
|
||||
"downloadDependencies": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App.Runtime.Mono.browser-wasm",
|
||||
"version": "[8.0.18, 8.0.18]"
|
||||
"version": "[8.0.19, 8.0.19]"
|
||||
}
|
||||
],
|
||||
"frameworkReferences": {
|
||||
@ -10687,7 +10687,7 @@
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.302/PortableRuntimeIdentifierGraph.json"
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
},
|
||||
"runtimes": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user