All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (pull_request) Successful in 29m17s
316 lines
11 KiB
Plaintext
316 lines
11 KiB
Plaintext
@using System
|
|
@using System.Globalization
|
|
@using System.Text.Json
|
|
@using System.Collections.Generic
|
|
@model Domain.Dtos.Stock.ExpeditionDto
|
|
|
|
@{
|
|
Layout = null;
|
|
|
|
var ci = CultureInfo.GetCultureInfo("es-AR");
|
|
CultureInfo.CurrentCulture = ci;
|
|
CultureInfo.CurrentUICulture = ci;
|
|
|
|
// Parseo seguro del snapshot (claves EXACTAS del JSON: Professional, Institution, Patient, SurgeryDate)
|
|
SurgerySnapshot snap;
|
|
if (string.IsNullOrWhiteSpace(Model.ExtrainfoJson))
|
|
{
|
|
snap = new SurgerySnapshot();
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
snap = JsonSerializer.Deserialize<SurgerySnapshot>(Model.ExtrainfoJson) ?? new SurgerySnapshot();
|
|
}
|
|
catch
|
|
{
|
|
snap = new SurgerySnapshot();
|
|
}
|
|
}
|
|
var reprintText = Model.Printcount > 0 ? (" — Reimpresión " + Model.Printcount) : "";
|
|
}
|
|
|
|
@functions {
|
|
// Mapear 1:1 con tu JSON de ExtrainfoJson MODELO ORTOPEDIA
|
|
public class SurgerySnapshot
|
|
{
|
|
public string? Professional { get; set; } // "PECHERVSKY PABLO GUSTAVO"
|
|
public string? Institution { get; set; } // "CORPORACION MEDICA LABORAL S.A."
|
|
public string? Patient { get; set; } // "ALEXIS LASTRA"
|
|
public DateTime? SurgeryDate { get; set; } // "2025-06-10T03:00:00"
|
|
}
|
|
|
|
public static string FQty(decimal q) => q.ToString("G29", CultureInfo.InvariantCulture);
|
|
|
|
// DateTime?
|
|
public static string FDate(DateTime? d) => d.HasValue ? d.Value.ToString("dd/MM/yyyy") : string.Empty;
|
|
|
|
// DateOnly? (por si tus Items usan DateOnly? en Expiration)
|
|
public static string FDate(DateOnly? d) => d.HasValue ? d.Value.ToString("dd/MM/yyyy") : string.Empty;
|
|
}
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>Nota de Entrega @Model.Expeditionnumber</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<style>
|
|
@@page { size: A4; margin: 10mm 9mm 10mm 9mm; }
|
|
|
|
html, body { font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: #000; }
|
|
|
|
.sheet { width: 100%; }
|
|
|
|
.header-legal { display: grid; grid-template-columns: 1.6fr 1fr; gap: 8px; align-items: start; }
|
|
|
|
.company { line-height: 1.15; }
|
|
|
|
.doc-title { text-align: right; }
|
|
.doc-title h1 { font-size: 20px; margin: 0 0 2px 0; letter-spacing: .5px; }
|
|
.doc-title .num { font-weight: bold; }
|
|
.doc-title .date { margin-top: 2px; }
|
|
|
|
.status-line {
|
|
display: flex;
|
|
justify-content: space-between; /* izquierda / derecha en la misma línea */
|
|
align-items: baseline;
|
|
margin: 6px 0 8px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.status-line .left {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.status-line .right {
|
|
font-weight: 400;
|
|
text-align: right;
|
|
}
|
|
|
|
.hr { border-bottom: 1px solid #000; margin: 6px 0; }
|
|
|
|
.snapshot { page-break-inside: avoid; margin-top: 6px; }
|
|
.snapshot table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
|
.snapshot td { padding: 2px 4px; vertical-align: top; }
|
|
.snapshot .lbl { width: 16%; font-weight: 600; }
|
|
.snapshot .val { width: 34%; word-break: break-word; }
|
|
|
|
.items { margin-top: 8px; }
|
|
.items table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
|
.items thead th { border-bottom: 1px solid #000; padding: 4px 4px; text-align: center; }
|
|
.items tbody td { padding: 3px 4px; vertical-align: top; }
|
|
|
|
.col-reg { width: 4%; text-align: center; }
|
|
.col-code { width: 18%; text-align: center; }
|
|
.col-desc { width: 36%; text-align: left; }
|
|
.col-batch { width: 12%; text-align: center; }
|
|
.col-exp { width: 10%; text-align: center; }
|
|
.col-serial { width: 12%; word-break: break-word; text-align: center; }
|
|
.col-qty { width: 8%; text-align: right; text-align: center; }
|
|
|
|
.totals { margin-top: 6px; display: flex; justify-content: space-between; }
|
|
.observ { margin-top: 8px; min-height: 36px; border: 1px dashed #999; padding: 6px; }
|
|
|
|
.footer { margin-top: 10px; font-size: 11px; text-align: center; border-top: 1px solid #000; padding-top: 6px; }
|
|
.muted { color: #111; }
|
|
/* ===== MODO COMPACT ===== */
|
|
.compact {
|
|
font-size: 11px;
|
|
}
|
|
/* texto general un toque más chico */
|
|
|
|
.compact .status-line {
|
|
margin: 4px 0 6px;
|
|
}
|
|
|
|
.compact .doc-title h1 {
|
|
font-size: 18px;
|
|
margin: 0 0 0;
|
|
}
|
|
|
|
.compact .snapshot td {
|
|
padding: 1px 3px;
|
|
}
|
|
/* bloque clínico más apretado */
|
|
|
|
.compact .items table {
|
|
font-size: 10px;
|
|
}
|
|
/* texto de filas más chico */
|
|
.compact .items thead th {
|
|
padding: 2px 3px;
|
|
line-height: 1.05;
|
|
background: #eaeaea;
|
|
-webkit-print-color-adjust: exact;
|
|
print-color-adjust: exact;
|
|
}
|
|
|
|
.compact .items tbody td {
|
|
padding: 2px 3px; /* ↓ padding vertical */
|
|
line-height: 1.05; /* ↓ altura de línea */
|
|
}
|
|
|
|
/* Bordes más finos para no sumar alto visual (algunos engines aceptan 0.6px) */
|
|
.compact .items thead th,
|
|
.compact .items tbody td {
|
|
border: 0.6px solid #000;
|
|
}
|
|
|
|
/* Tabla con bordes tipo grilla */
|
|
.items table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
table-layout: fixed;
|
|
}
|
|
|
|
.items thead th, .items tbody td {
|
|
border: 1px solid #000;
|
|
}
|
|
|
|
/* Repetir encabezado y pie en cada página */
|
|
.items thead {
|
|
display: table-header-group;
|
|
}
|
|
|
|
.items tfoot {
|
|
display: table-footer-group;
|
|
}
|
|
|
|
/* Fila “marco” en el pie (línea inferior por página) */
|
|
.items tfoot .page-frame-row td {
|
|
border: 1px solid #000; /* dibuja línea inferior y laterales */
|
|
height: 0;
|
|
padding: 0;
|
|
line-height: 0; /* que no coma espacio visual */
|
|
}
|
|
/* Encabezados con fondo gris */
|
|
.items thead th {
|
|
background: #eaeaea; /* gris suave */
|
|
color: #000;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.avoid-break { page-break-inside: avoid; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="sheet compact">
|
|
|
|
<!-- Encabezado / identidad -->
|
|
<div class="header-legal">
|
|
<div class="company">
|
|
@if (!string.IsNullOrWhiteSpace(Model.LogoBase64))
|
|
{
|
|
<img src="data:image/png;base64,@Model.LogoBase64" alt="Logo" style="height:48px; margin-bottom:0;" />
|
|
}
|
|
<div style="font-size: 10px; margin-top: 0;">
|
|
Segurola 1885<br />
|
|
C.P. (C1407AOK) - Capital Federal<br/>
|
|
Email: ventas@biodec.net<br />
|
|
Urgencias: 15-2155-9380 * 15-5909-4987 * 15-5909-4892
|
|
</div><br />
|
|
<div class="muted">Mercadería en préstamo</div><br />
|
|
</div>
|
|
<div class="doc-title">
|
|
<h1>Nota de Expedición </h1>
|
|
<div class="num">EXPEDICIÓN: @Model.Expeditionnumber</div>
|
|
<div class="date">Fecha: @Model.Issuedate.ToString("dd/MM/yyyy")</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Estado / auditoría -->
|
|
<div class="status-line">
|
|
<div class="left">
|
|
Estado:
|
|
@((string.IsNullOrWhiteSpace(Model.StatusLabel) ? "DESCONOCIDO" : Model.StatusLabel) + reprintText)
|
|
</div>
|
|
<div class="right">Origen: @(Model.LocationName ?? "-")</div>
|
|
</div>
|
|
<div class="hr"></div>
|
|
|
|
<!-- Institución / Profesional / Paciente / Fecha de cirugía (desde ExtrainfoJson) -->
|
|
<section class="snapshot">
|
|
<table>
|
|
<tr>
|
|
<td class="lbl">Institución:</td>
|
|
<td class="val">@snap.Institution</td>
|
|
<td class="lbl">Profesional:</td>
|
|
<td class="val">@snap.Professional</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="lbl">Paciente:</td>
|
|
<td class="val">@snap.Patient</td>
|
|
<td class="lbl">Fecha CX:</td>
|
|
<td class="val">@FDate(snap.SurgeryDate)</td>
|
|
</tr>
|
|
</table>
|
|
</section>
|
|
<div class="hr"></div>
|
|
<!-- Detalle de ítems -->
|
|
<section class="items">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th class="col-reg">#</th>
|
|
<th class="col-code">Código</th>
|
|
<th class="col-desc">Descripción</th>
|
|
<th class="col-batch">Lote</th>
|
|
<th class="col-exp">Venc.</th>
|
|
<th class="col-serial">Serie</th>
|
|
<th class="col-qty">Cant.</th>
|
|
</tr>
|
|
</thead>
|
|
<tfoot>
|
|
<tr class="page-frame-row">
|
|
<td colspan="7"></td>
|
|
</tr>
|
|
</tfoot>
|
|
<tbody>
|
|
@if (Model.Items != null && Model.Items.Count > 0)
|
|
{
|
|
int repeat = 1; // ← poné 1 para desactivar el stress-test
|
|
|
|
for (int r = 0; r < repeat; r++)
|
|
{
|
|
foreach (var it in Model.Items)
|
|
{
|
|
<tr>
|
|
<td class="col-reg">@r</td>
|
|
<td class="col-code">@it.FactoryCode</td>
|
|
<td class="col-desc">@it.ProductName</td>
|
|
<td class="col-batch">@it.Batch</td>
|
|
<td class="col-exp">@FDate(it.Expiration)</td>
|
|
<td class="col-serial">@it.Serial</td>
|
|
<td class="col-qty">@FQty(it.Quantity)</td>
|
|
</tr>
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
<tr><td colspan="7" style="text-align:center; padding:6px;">Sin ítems</td></tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<!-- Totales y observaciones -->
|
|
<div class="totals">
|
|
<div><strong>Renglones:</strong> @Model.TotalItems</div>
|
|
<div><strong>Unidades:</strong> @FQty(Model.TotalQuantity)</div>
|
|
</div>
|
|
|
|
<div class="observ">
|
|
<strong>Observaciones:</strong> @(Model.Observations ?? string.Empty)
|
|
</div>
|
|
|
|
<!-- Pie -->
|
|
<div class="footer">
|
|
La mercadería viaja por cuenta y riesgo del comprador.
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|