phronCare/Core/Services/DeliveryNoteService.cs

296 lines
13 KiB
C#
Raw Normal View History

using Domain.Dtos.Sales;
using Domain.Entities;
using Domain.Generics;
using Models.Interfaces;
using System.Reflection;
using System.Text.Json;
using Transversal.Services;
namespace Core.Services
{
public class DeliveryNoteService(IPhSDeliveryNoteRepository deliveryNoteRepository) : IDeliveryNoteDom
{
private readonly IPhSDeliveryNoteRepository _deliveryNoteRepository = deliveryNoteRepository;
public Task<PagedResult<DeliveryNoteSummaryDto>> SearchAsync(
int? customerId,
string? customerText,
string? deliveryNoteNumber,
int? quoteId,
string? quoteNumber,
DateTime? issueDateFrom,
DateTime? issueDateTo,
string? status,
int page = 1,
int pageSize = 50)
{
page = page <= 0 ? 1 : page;
pageSize = pageSize <= 0 ? 50 : pageSize;
return _deliveryNoteRepository.SearchAsync(
customerId,
string.IsNullOrWhiteSpace(customerText) ? null : customerText.Trim(),
string.IsNullOrWhiteSpace(deliveryNoteNumber) ? null : deliveryNoteNumber.Trim(),
quoteId,
string.IsNullOrWhiteSpace(quoteNumber) ? null : quoteNumber.Trim(),
issueDateFrom,
issueDateTo,
string.IsNullOrWhiteSpace(status) ? null : status.Trim(),
page,
pageSize);
}
public Task<DeliveryNoteDto?> GetDtoByIdAsync(int id)
{
if (id <= 0)
throw new ArgumentOutOfRangeException(nameof(id), "El identificador del remito es inválido.");
return _deliveryNoteRepository.GetDtoByIdAsync(id);
}
public async Task<byte[]> ExportFilteredToExcelAsync(DeliveryNoteSearchParams searchParams)
{
ArgumentNullException.ThrowIfNull(searchParams);
try
{
var searchResult = await _deliveryNoteRepository.SearchAsync(
searchParams.CustomerId,
string.IsNullOrWhiteSpace(searchParams.CustomerText) ? null : searchParams.CustomerText.Trim(),
string.IsNullOrWhiteSpace(searchParams.DeliveryNoteNumber) ? null : searchParams.DeliveryNoteNumber.Trim(),
searchParams.QuoteId,
string.IsNullOrWhiteSpace(searchParams.QuoteNumber) ? null : searchParams.QuoteNumber.Trim(),
searchParams.IssueDateFrom,
searchParams.IssueDateTo,
string.IsNullOrWhiteSpace(searchParams.Status) ? null : searchParams.Status.Trim(),
searchParams.Page <= 0 ? 1 : searchParams.Page,
searchParams.PageSize <= 0 ? 50 : searchParams.PageSize);
if (searchResult?.Items is null || !searchResult.Items.Any())
throw new Exception("No se encontraron remitos para exportar.");
var items = searchResult.Items.ToList();
var exportRows = new List<DeliveryNoteExcelRow>(items.Count);
foreach (var deliveryNote in items)
{
var dto = await _deliveryNoteRepository.GetDtoByIdAsync(deliveryNote.Id);
var snapshot = DeliveryNoteSnapshotInfo.FromJson(dto?.ExtraInfoJson);
exportRows.Add(new DeliveryNoteExcelRow
{
DeliveryNoteNumber = deliveryNote.DeliveryNoteNumber,
IssueDate = deliveryNote.IssueDate.ToString("dd/MM/yyyy"),
QuoteNumber = deliveryNote.QuoteNumber,
CustomerName = deliveryNote.CustomerName,
Status = deliveryNote.Status,
ProfessionalName = snapshot.ProfessionalName,
InstitutionName = snapshot.InstitutionName,
PatientName = snapshot.PatientName,
SurgeryDate = snapshot.SurgeryDate,
Observations = deliveryNote.Observations,
PrintCount = deliveryNote.PrintCount,
CreatedAt = deliveryNote.CreatedAt.ToString("dd/MM/yyyy HH:mm")
});
}
var stream = new XLSXExportBase();
return stream.ExportExcel(exportRows);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
throw new Exception($"{methodName} Message: {ex.Message}", ex);
}
}
public Task<DeliveryNoteDto?> GetDtoByDeliveryNoteNumberAsync(string deliveryNoteNumber)
{
if (string.IsNullOrWhiteSpace(deliveryNoteNumber))
throw new ArgumentException("El número de remito es obligatorio.", nameof(deliveryNoteNumber));
return _deliveryNoteRepository.GetDtoByDeliveryNoteNumberAsync(deliveryNoteNumber.Trim());
}
public Task<IEnumerable<DeliveryNoteDto>> GetDtosByQuoteIdAsync(int quoteId)
{
if (quoteId <= 0)
throw new ArgumentOutOfRangeException(nameof(quoteId), "El identificador del presupuesto es inválido.");
return _deliveryNoteRepository.GetDtosByQuoteIdAsync(quoteId);
}
public async Task<DeliveryNoteCreateResponse> CreateAndIssueDeliveryNoteAsync(DeliveryNoteCreateRequest request)
{
ArgumentNullException.ThrowIfNull(request);
if (string.IsNullOrWhiteSpace(request.DeliveryNoteNumber))
throw new ArgumentException("El número de remito es obligatorio.", nameof(request.DeliveryNoteNumber));
if (request.CustomerId <= 0)
throw new ArgumentException("Debe seleccionar un cliente.", nameof(request.CustomerId));
if (request.IssueDate == default)
throw new ArgumentException("La fecha de emisión es obligatoria.", nameof(request.IssueDate));
if (request.Items is null || request.Items.Count == 0)
throw new InvalidOperationException("Debe incluir al menos un ítem.");
if (request.Items.Any(i => i.Quantity <= 0))
throw new InvalidOperationException("Todas las cantidades deben ser mayores a cero.");
if (request.Items.Any(i => string.IsNullOrWhiteSpace(i.Description)))
throw new InvalidOperationException("Todos los ítems deben incluir descripción.");
var deliveryNoteNumber = request.DeliveryNoteNumber.Trim();
if (await _deliveryNoteRepository.ExistsByDeliveryNoteNumberAsync(deliveryNoteNumber))
throw new InvalidOperationException($"Ya existe un remito con el número '{deliveryNoteNumber}'.");
var now = DateTime.Now;
var entity = new EDeliveryNote
{
Deliverynotenumber = deliveryNoteNumber,
QuoteId = request.QuoteId,
Issuedate = request.IssueDate,
CustomerId = request.CustomerId,
Status = "Emitido",
Observations = string.IsNullOrWhiteSpace(request.Observations) ? null : request.Observations.Trim(),
ExtrainfoJson = string.IsNullOrWhiteSpace(request.ExtraInfoJson) ? null : request.ExtraInfoJson.Trim(),
Printcount = 0,
Createdat = now,
PhSDeliveryNoteDetails = request.Items
.Select((item, index) => new EDeliveryNoteDetail
{
LineNumber = index + 1,
OriginType = item.OriginType,
OriginId = item.OriginId,
QuoteDetailId = item.QuoteDetailId,
Description = item.Description.Trim(),
Quantity = item.Quantity,
Notes = string.IsNullOrWhiteSpace(item.Notes) ? string.Empty : item.Notes.Trim(),
Createdat = now
})
.ToList()
};
var created = await _deliveryNoteRepository.CreateAsync(entity);
return new DeliveryNoteCreateResponse
{
Id = created.Id,
DeliveryNoteNumber = created.Deliverynotenumber
};
}
private sealed class DeliveryNoteExcelRow
{
public string DeliveryNoteNumber { get; set; } = string.Empty;
public string IssueDate { get; set; } = string.Empty;
public string? QuoteNumber { get; set; }
public string? CustomerName { get; set; }
public string? Status { get; set; }
public string? ProfessionalName { get; set; }
public string? InstitutionName { get; set; }
public string? PatientName { get; set; }
public string? SurgeryDate { get; set; }
public string? Observations { get; set; }
public int PrintCount { get; set; }
public string CreatedAt { get; set; } = string.Empty;
}
private sealed class DeliveryNoteSnapshotInfo
{
public string? ProfessionalName { get; private set; }
public string? InstitutionName { get; private set; }
public string? PatientName { get; private set; }
public string? SurgeryDate { get; private set; }
public static DeliveryNoteSnapshotInfo FromJson(string? extraInfoJson)
{
var snapshot = new DeliveryNoteSnapshotInfo();
if (string.IsNullOrWhiteSpace(extraInfoJson))
return snapshot;
try
{
using var document = JsonDocument.Parse(extraInfoJson);
var root = document.RootElement;
snapshot.ProfessionalName = ReadString(root, "professional", "professionalName", "doctor", "doctorName", "medico", "medicoNombre");
snapshot.InstitutionName = ReadString(root, "institution", "institutionName", "hospital", "hospitalName", "institucion", "institucionNombre");
snapshot.PatientName = ReadString(root, "patient", "patientName", "paciente", "pacienteNombre");
snapshot.SurgeryDate = ReadDate(root, "surgeryDate", "estimatedDate", "fechaCirugia", "surgery_date", "estimated_date");
}
catch
{
return snapshot;
}
return snapshot;
}
private static string? ReadString(JsonElement root, params string[] propertyNames)
{
foreach (var propertyName in propertyNames)
{
if (!TryGetPropertyIgnoreCase(root, propertyName, out var value))
continue;
if (value.ValueKind == JsonValueKind.String)
return value.GetString();
if (value.ValueKind != JsonValueKind.Null && value.ValueKind != JsonValueKind.Undefined)
return value.ToString();
}
return null;
}
private static string? ReadDate(JsonElement root, params string[] propertyNames)
{
foreach (var propertyName in propertyNames)
{
if (!TryGetPropertyIgnoreCase(root, propertyName, out var value))
continue;
if (value.ValueKind == JsonValueKind.String)
{
var raw = value.GetString();
if (string.IsNullOrWhiteSpace(raw))
return null;
if (DateTime.TryParse(raw, out var parsedDate))
return parsedDate.ToString("dd/MM/yyyy");
return raw;
}
if (value.ValueKind != JsonValueKind.Null && value.ValueKind != JsonValueKind.Undefined)
return value.ToString();
}
return null;
}
private static bool TryGetPropertyIgnoreCase(JsonElement element, string propertyName, out JsonElement value)
{
foreach (var property in element.EnumerateObject())
{
if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase))
{
value = property.Value;
return true;
}
}
value = default;
return false;
}
}
}
}