feat(deliverynote): add excel export with clinical snapshot parsing
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (pull_request) Successful in 6m55s
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (pull_request) Successful in 6m55s
- Implement exportfiltered endpoint - Generate Excel using XLSXExportBase (EPPlus) - Map Delivery Note summary fields - Parse ExtraInfoJson into business columns (Professional, Institution, Patient, SurgeryDate) - Format dates for Excel - Keep export at header level (no items) Closes #46
This commit is contained in:
parent
833cc1660f
commit
8f81614922
@ -22,6 +22,11 @@ public interface IDeliveryNoteDom
|
||||
int page = 1,
|
||||
int pageSize = 50);
|
||||
|
||||
/// <summary>
|
||||
/// Exporta a Excel los Delivery Notes filtrados.
|
||||
/// </summary>
|
||||
Task<byte[]> ExportFilteredToExcelAsync(DeliveryNoteSearchParams searchParams);
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene un Delivery Note por su identificador único.
|
||||
/// </summary>
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
using Core.Interfaces;
|
||||
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
|
||||
{
|
||||
@ -46,6 +48,62 @@ namespace Core.Services
|
||||
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))
|
||||
@ -125,5 +183,113 @@ namespace Core.Services
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ public partial class PhSDeliveryNoteDetail
|
||||
|
||||
public int? QuoteDetailId { get; set; }
|
||||
|
||||
public string Description { get; set; } = null!;
|
||||
public string? Description { get; set; }
|
||||
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
|
||||
@ -1104,9 +1104,7 @@ public partial class PhronCareOperationsHubContext : DbContext
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("createdat");
|
||||
entity.Property(e => e.DeliverynoteId).HasColumnName("deliverynote_id");
|
||||
entity.Property(e => e.Description)
|
||||
.HasMaxLength(500)
|
||||
.HasColumnName("description");
|
||||
entity.Property(e => e.Description).HasColumnName("description");
|
||||
entity.Property(e => e.LineNumber).HasColumnName("line_number");
|
||||
entity.Property(e => e.Modifiedat)
|
||||
.HasColumnType("datetime")
|
||||
|
||||
@ -176,7 +176,7 @@ namespace Models.Repositories
|
||||
OriginType = source.OriginType,
|
||||
OriginId = source.OriginId,
|
||||
QuoteDetailId = source.QuoteDetailId,
|
||||
Description = source.Description,
|
||||
Description = source.Description??string.Empty,
|
||||
Quantity = source.Quantity,
|
||||
Notes = source.Notes,
|
||||
Createdat = source.Createdat,
|
||||
|
||||
@ -150,5 +150,31 @@ namespace phronCare.API.Controllers.Sales
|
||||
return StatusCode(500, $"{methodName} Message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("exportfiltered")]
|
||||
public async Task<IActionResult> ExportFiltered([FromBody] DeliveryNoteSearchParams searchParams)
|
||||
{
|
||||
try
|
||||
{
|
||||
var file = await _deliveryNoteService.ExportFilteredToExcelAsync(searchParams);
|
||||
return File(
|
||||
file,
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"Remitos.xlsx");
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
|
||||
return BadRequest($"{methodName} Message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user