using System.Globalization; using System.Text.Json; using Dapper; using Microsoft.Data.SqlClient; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); var cs = app.Configuration.GetConnectionString("Fum")!; var auditPath = Path.Combine(app.Environment.ContentRootPath, "App_Data", "auditoria.log"); Directory.CreateDirectory(Path.GetDirectoryName(auditPath)!); app.UseDefaultFiles(); app.UseStaticFiles(); SqlConnection Db() => new(cs); // ---------- Listado de procesos ---------- app.MapGet("/api/procesos", async () => { using var db = Db(); var rows = await db.QueryAsync(@" select f.id, f.derivada_de, f.camara, c.nombre as camaraNombre, f.tipofumigacion, rtrim(t.nombreoficial) as tipoNombre, f.estadofumigacion, (select min(fecha) from fum_historial h where h.id_fumigacion = isnull(f.derivada_de, f.id)) as inicio, (select max(fecha) from fum_historial h where h.id_fumigacion = isnull(f.derivada_de, f.id)) as fin, rtrim(d.producto1) as producto, rtrim(d.inspector) as inspector, rtrim(d.operador) as operador from fum_fumigacion f left join fum_camara c on c.id = f.camara left join fum_tipofumigacion t on t.id = f.tipofumigacion left join fum_fumigaciondatos d on d.fumigacion = f.id order by f.id desc"); return rows.Select(r => new { r.id, r.derivada_de, r.camara, r.camaraNombre, r.tipofumigacion, r.tipoNombre, r.estadofumigacion, inicio = Iso((long?)r.inicio), fin = Iso((long?)r.fin), r.producto, r.inspector, r.operador }); }); // ---------- Catálogos ---------- app.MapGet("/api/catalogos", async () => { using var db = Db(); var camaras = await db.QueryAsync("select id, rtrim(nombre) as nombre from fum_camara order by id"); var tipos = await db.QueryAsync("select id, rtrim(nombreoficial) as nombre, tipodosis, duracion from fum_tipofumigacion order by nombreoficial, duracion"); return new { camaras, tipos, eventos = Informe.CatalogoEventos() }; }); // ---------- Informe completo (equivale a pdf.php) ---------- app.MapGet("/api/procesos/{id:long}", async (long id) => { using var db = Db(); var m = await Informe.Construir(db, id); return m is null ? Results.NotFound() : Results.Json(m); }); // ---------- PDF (réplica del informe_fumigacion.php): ?descargar=1 fuerza descarga ---------- app.MapGet("/api/procesos/{id:long}/pdf", async (long id, int? descargar) => { using var db = Db(); var m = await Informe.Construir(db, id); if (m is null) return Results.NotFound(); var bytes = PdfInforme.Generar(JsonSerializer.SerializeToElement(m)); return descargar == 1 ? Results.File(bytes, "application/pdf", $"informe_fumigacion_{id}.pdf") : Results.File(bytes, "application/pdf"); }); // ---------- Edición: datos del proceso ---------- var camposDatosTxt = new[] { "certificado1_ini", "certificado1_fin", "certificado2_ini", "certificado2_fin", "producto1", "procedencia1", "origen1", "destino1", "operador", "inspector" }; var camposDatosInt = new[] { "pallets1", "cajas1", "tamb", "tppa1", "tppa2", "tppa3", "tppa4", "tppa5", "duracion" }; app.MapPut("/api/procesos/{id:long}/datos", async (long id, Dictionary body) => { using var db = Db(); if (await db.ExecuteScalarAsync("select count(*) from fum_fumigacion where id=@id", new { id }) == 0) return Results.NotFound(); if (await db.ExecuteScalarAsync("select count(*) from fum_fumigaciondatos where fumigacion=@id", new { id }) == 0) await db.ExecuteAsync("insert into fum_fumigaciondatos (fumigacion) values (@id)", new { id }); foreach (var (campo, val) in body) { if (campo is "tipofumigacion" or "camara") { var nuevo = val.GetInt32(); var viejo = await db.ExecuteScalarAsync($"select {campo} from fum_fumigacion where id=@id", new { id }); await db.ExecuteAsync($"update fum_fumigacion set {campo}=@v where id=@id", new { v = nuevo, id }); Audit("fum_fumigacion", id, campo, viejo, nuevo); continue; } object? v; if (camposDatosTxt.Contains(campo)) v = val.ValueKind == JsonValueKind.Null ? "" : val.ToString(); else if (camposDatosInt.Contains(campo)) v = int.TryParse(val.ToString(), out var n) ? n : 0; else return Results.BadRequest($"Campo no permitido: {campo}"); var old = await db.ExecuteScalarAsync($"select {campo} from fum_fumigaciondatos where fumigacion=@id", new { id }); await db.ExecuteAsync($"update fum_fumigaciondatos set {campo}=@v where fumigacion=@id", new { v, id }); Audit("fum_fumigaciondatos", id, campo, old, v); } return Results.Ok(); }); // ---------- Edición: filas de historial ---------- var camposHist = new HashSet( Enumerable.Range(1, 15).Select(i => $"pulpa{i}") .Concat(new[] { "ambiente1", "ambiente2", "evaporador", "dumper", "ventilador", "valvula", "eventid", "rangodosis" }) .Concat(Enumerable.Range(1, 6).Select(i => $"concentracion{i}"))); app.MapPut("/api/historial/{id:long}", async (long id, Dictionary body) => { using var db = Db(); foreach (var (campo, val) in body) { object? v; if (campo == "fecha") v = ToEpoch(val.GetString()!); else if (campo == "pesoinyeccion") v = decimal.Parse(val.ToString().Replace(',', '.'), CultureInfo.InvariantCulture); else if (campo == "equipo") v = val.ToString(); else if (camposHist.Contains(campo)) v = val.GetInt32(); else return Results.BadRequest($"Campo no permitido: {campo}"); var old = await db.ExecuteScalarAsync($"select {campo} from fum_historial where id=@id", new { id }); await db.ExecuteAsync($"update fum_historial set {campo}=@v where id=@id", new { v, id }); Audit("fum_historial", id, campo, old, v); } return Results.Ok(); }); app.MapPost("/api/procesos/{id:long}/historial", async (long id, Dictionary body) => { using var db = Db(); var derivada = await db.ExecuteScalarAsync("select isnull(derivada_de,id) from fum_fumigacion where id=@id", new { id }); if (derivada is null) return Results.NotFound(); var fecha = body.TryGetValue("fecha", out var f) ? ToEpoch(f.GetString()!) : DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var eventid = body.TryGetValue("eventid", out var e) ? e.GetInt32() : 104; var cols = string.Join(",", Enumerable.Range(1, 15).Select(i => $"pulpa{i}")) + ",ambiente1,ambiente2,evaporador"; var vals = string.Join(",", Enumerable.Repeat("555", 18)); var nuevoId = await db.ExecuteScalarAsync($@" insert into fum_historial (id_fumigacion, fecha, eventid, {cols}, dumper, ventilador, valvula, concentracion1, concentracion2, concentracion3, concentracion4, concentracion5, concentracion6, rangodosis, pesoinyeccion, equipo) values (@d, @fecha, @eventid, {vals}, 768, 256, 768, -555, -555, -555, -555, -555, -555, 0, 0, ''); select cast(scope_identity() as bigint)", new { d = derivada, fecha, eventid }); Audit("fum_historial", nuevoId, "INSERT", null, $"proceso {derivada} evento {eventid}"); return Results.Ok(new { id = nuevoId }); }); app.MapDelete("/api/historial/{id:long}", async (long id) => { using var db = Db(); var antes = await db.QuerySingleOrDefaultAsync("select * from fum_historial where id=@id", new { id }); if (antes is null) return Results.NotFound(); await db.ExecuteAsync("delete from fum_historial where id=@id", new { id }); Audit("fum_historial", id, "DELETE", JsonSerializer.Serialize((IDictionary)antes), null); return Results.Ok(); }); app.Run(); // ---------- helpers ---------- static string? Iso(long? epoch) => epoch is null ? null : Informe.ADate(epoch.Value).ToString("yyyy-MM-ddTHH:mm:ss"); static long ToEpoch(string iso) => new DateTimeOffset(DateTime.SpecifyKind(DateTime.Parse(iso, CultureInfo.InvariantCulture), DateTimeKind.Local)).ToUnixTimeSeconds(); void Audit(string tabla, long id, string campo, object? antes, object? despues) { try { File.AppendAllText(auditPath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}\t{tabla}\t{id}\t{campo}\t{antes}\t=>\t{despues}{Environment.NewLine}"); } catch (Exception ex) { Console.Error.WriteLine("AUDITORIA FALLÓ: " + ex.Message); /* nunca debe impedir guardar */ } } record Bal(long id, decimal peso); // ===================================================================== // Port de pdf.php: arma todos los bloques del informe a partir de las tablas. // Fechas en la BD = epoch Unix (segundos). Temperaturas = décimas de °C. // Digitales: 256 = estado "normal" (AB/EN/AB), otro valor = CE/AP/CE. // ===================================================================== static class Informe { static readonly string[] Meses = { "Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic" }; public static readonly string[] Analogos = { "pulpa1","pulpa2","pulpa3","pulpa4","pulpa5","pulpa6","pulpa7","pulpa8","pulpa9","pulpa10", "pulpa11","pulpa12","pulpa13","pulpa14","pulpa15","ambiente1","ambiente2" }; static readonly string[] Inspector = { "tamb", "tppa1", "tppa2", "tppa3", "tppa4", "tppa5" }; static readonly string[] PendI = { "sin evento","inicio proceso","Peso inicial cilindro","Peso final cilindro","lectura normal cada 10 min","Detener ventilador.","Ingresar concentraciones N° 1","Ingresar concentraciones N° 2","Ingresar concentraciones N° 3","Ingresar concentraciones N° 4","Ingresar concentraciones N° 5","Fin proceso de inyeccion","lectura normal inyeccion (ARCAICO)","timeout (fumigacion desierta)","Requiere reinyeccion","Preparar fumiscopio para lectura","Temperatura cercana al valor de anulacion","No hechos.","Tiempo de fumigación finalizado.","Fumigacion Iniciada","inyectado adicional","Permitir Abortar","Fin del proceso de fumigación" }; static readonly string[] PendNr = { "sin evento","No registrado :Inicio Proceso","No registrado :Peso inicial cilindro","No registrado :Peso final cilindro","Posible corte de energia durante este tiempo","Posible corte de energia durante este tiempo","No se ingreso concentraciones N° 1","No se ingreso concentraciones N° 2","No se ingreso concentraciones N° 3","No se ingreso concentraciones N° 4","No se ingreso concentraciones N° 5","No registrado :Fin proceso de inyeccion","Posible corte de energia durante este tiempo","No registrado :timeout.","No registrado :Requiere reinyeccion.","Preparar fumiscopio para lectura","Temperatura cercana al valor de anulacion","No hechos.","Fumigacion no terminada correctamente.","No registrado :fumigacion Iniciada","No registrado :inyectado adicional","Permitir abortar","Fin del Proceso de fumigacion" }; static readonly string[] Efectos = { "sin evento","dumper abierto","ventilador operando","válvula inyección abierta","dumper cerrado","ventilador detenido, encienda ventilador","válvula inyección cerrada.","temperatura bajo rango minimo.","temperatura ambiente baja","temperatura evaporador baja","temperatura pulpa desconectada","temperatura ambiente desconectada","temperatura evaporador desconectada","precaucion :temperatura cercana al rango inferior","cambio de dosis","precaucion :temperatura cercana a valor de anulacion" }; static readonly string[] EfectosE = { "sin evento","apertura de dumper improcedente","ventilador operando","re-inyección improcedente, cierre válvula","dumper cerrado","ventilador detenido, encienda ventilador","válvula inyección cerrada.","temperatura bajo rango minimo.","temperatura ambiente baja","temperatura evaporador baja","temperatura pulpa desconectada","temperatura ambiente desconectada","temperatura evaporador desconectada","precaucion :temperatura cercana al rango inferior","cambio de dosis","precaucion :temperatura cercana a valor de anulacion" }; static readonly string[] EfectosW = Efectos.Select((s, i) => i == 0 ? s : "advertencia :" + s).ToArray(); // Códigos de evento que el sistema original sabe mostrar en la sección 4. public static object CatalogoEventos() { var l = new List(); void Add(int baseCode, string[] arr, string pref = "") { for (int i = 1; i < arr.Length; i++) { var t = pref + arr[i]; l.Add(new { code = baseCode + i, texto = char.ToUpper(t[0]) + t[1..] }); } } Add(100, PendI); Add(200, PendNr); Add(1000, EfectosW); Add(2000, EfectosE); Add(3000, Efectos); return l; } public static DateTime ADate(long epoch) => DateTimeOffset.FromUnixTimeSeconds(epoch).LocalDateTime; static string FechaCorta(long e) { var d = ADate(e); return $"{d:dd}-{Meses[d.Month - 1]}-{d:yy}"; } static string FechaLarga(long e) => FechaCorta(e) + ADate(e).ToString(" HH:mm:ss"); static string Hora(long e) => ADate(e).ToString("HH:mm:ss"); static string Iso(long e) => ADate(e).ToString("yyyy-MM-ddTHH:mm:ss"); static long L(IDictionary r, string k) => r.TryGetValue(k, out var v) && v is not null ? Convert.ToInt64(v) : 0; static int I(IDictionary r, string k) => (int)L(r, k); static decimal D(IDictionary r, string k) => r.TryGetValue(k, out var v) && v is not null ? Convert.ToDecimal(v) : 0m; static string S(IDictionary r, string k) => r.TryGetValue(k, out var v) && v is not null ? v.ToString()!.Trim() : ""; static IDictionary Fila(object o) => ((IDictionary)o).ToDictionary(k => k.Key, k => k.Value is DBNull ? null : k.Value, StringComparer.OrdinalIgnoreCase); static int[] Rangos(int tipoDosis) => tipoDosis switch { 2 => new[] { 0, 64, 64 }, 3 => new[] { 0, 48, 48 }, 4 => new[] { 0, 56, 56, 48, 48 }, 5 => new[] { 0, 192, 192, 160, 160, 72, 72, 56, 56, 40, 40 }, 6 => new[] { 0, 56, 56 }, 7 => new[] { 0, 64, 64 }, 1 => new[] { 0, 32, 32 }, _ => new[] { 0, 64, 64, 48, 48, 40, 40, 32, 32, 24, 24 }, }; static decimal Dec1(decimal v) => Math.Round(v, 1, MidpointRounding.AwayFromZero); static string Gr(decimal v) => v.ToString("0.#", CultureInfo.InvariantCulture); public static async Task Construir(SqlConnection db, long id) { var fumO = await db.QuerySingleOrDefaultAsync("select * from fum_fumigacion where id=@id", new { id }); if (fumO is null) return null; var fum = Fila(fumO); long derivada = fum["derivada_de"] is null ? id : L(fum, "derivada_de"); if (derivada == 0) derivada = id; var planta = Fila(await db.QuerySingleAsync("select * from fum_planta where id=1")); var camO = await db.QuerySingleOrDefaultAsync("select * from fum_camara where id=@c", new { c = I(fum, "camara") }); var cam = camO is null ? new Dictionary() : Fila(camO); var tipoO = await db.QuerySingleOrDefaultAsync("select * from fum_tipofumigacion where id=@t", new { t = I(fum, "tipofumigacion") }); var tipo = tipoO is null ? new Dictionary() : Fila(tipoO); var datosO = await db.QuerySingleOrDefaultAsync("select * from fum_fumigaciondatos where fumigacion=@id", new { id }); var datos = datosO is null ? new Dictionary() : Fila(datosO); var hist = (await db.QueryAsync("select * from fum_historial where id_fumigacion=@d order by fecha, id", new { d = derivada })).Select(Fila).ToList(); decimal m3 = D(cam, "metroscuadrados"); var rangos = Rangos(I(fum, "tipodosis")); long finicial = hist.Count > 0 ? L(hist[0], "fecha") : 0; long ffinal = hist.Count > 0 ? L(hist[^1], "fecha") : 0; // ---- temperaturas ingresadas por inspector ---- var insp = new List(); decimal manualMin = 555; string manualNombre = ""; int tempInspMin = 555; foreach (var c in Inspector) { int t = I(datos, c); bool nr = t == 555 || t == 0; insp.Add(new { campo = c, valor = nr ? (int?)null : t }); if (!nr) { if (t < tempInspMin) tempInspMin = t; if (t < manualMin) { manualMin = t; manualNombre = c[1..]; } } } manualMin /= 10; // ---- Con.ini / Con.fin ---- var h3003 = hist.Where(h => I(h, "eventid") == 3003).ToList(); object? conIni = null, conFin = null; if (h3003.Count > 0) { int rd = I(h3003[0], "rangodosis"); int tmin = 555; foreach (var c in Analogos) { var t = I(h3003[0], c); if (t > -500 && t < tmin) tmin = t; } if (tempInspMin < tmin) tmin = tempInspMin; int dosisIni = rd < rangos.Length ? rangos[rd] : 0; long inifecha = L(h3003[0], "fecha"); conIni = new { dosis = dosisIni, tmin = tmin / 10m, fecha = FechaLarga(inifecha), kg = Math.Ceiling(dosisIni * m3 / 100) / 10 }; var cambio = h3003.Where(h => I(h, "rangodosis") != rd).ToList(); var fin = cambio.Count > 0 ? cambio : h3003.Where(h => L(h, "fecha") >= inifecha).ToList(); var f0 = fin[0]; foreach (var c in Analogos) { var t = I(f0, c); if (t > -500 && t < tmin) tmin = t; } if (tempInspMin < tmin && cambio.Count == 0) tmin = tempInspMin; int rf = I(f0, "rangodosis"); int dosisFin = rf < rangos.Length ? rangos[rf] : 0; conFin = new { dosis = dosisFin, tmin = tmin / 10m, fecha = FechaLarga(L(f0, "fecha")), kg = Math.Ceiling(dosisFin * m3 / 100) / 10 }; } // ---- inyección (pesos de eventos 102/103, fechas de eventos 3003/3006) ---- long fechaReiny = hist.Where(h => I(h, "eventid") == 120).Select(h => L(h, "fecha")).FirstOrDefault(); var pesoIni = new Bal?[4]; var pesoFin = new Bal?[4]; var fIni = new long[4]; var fFin = new long[4]; int ci = 0, cf = 0; foreach (var h in hist.Where(h => I(h, "eventid") is 102 or 103)) { long f = L(h, "fecha"); var reg = new Bal(L(h, "id"), D(h, "pesoinyeccion")); if (I(h, "eventid") == 102) { if (fechaReiny < f && fechaReiny != 0 && ci == 1) ci++; if (ci < 4) pesoIni[ci] = reg; ci++; } else { if (fechaReiny < f && fechaReiny != 0 && cf == 1) cf++; if (cf < 4) pesoFin[cf] = reg; cf++; } } ci = 0; cf = 0; foreach (var h in hist.Where(h => I(h, "eventid") is 3003 or 3006)) { long f = L(h, "fecha"); if (I(h, "eventid") == 3003) { if (fechaReiny < f && fechaReiny != 0 && ci == 1) ci++; if (ci < 4) fIni[ci] = f; ci++; } else { if (fechaReiny < f && fechaReiny != 0 && cf == 1) cf++; if (cf < 4) fFin[cf] = f; cf++; } } if (fFin[1] == 0) fFin[1] = fFin[0]; if (fFin[3] == 0) fFin[3] = fFin[2]; static decimal P(Bal?[] a, int i) => a[i]?.peso ?? 0; string? FL(long e) => e == 0 ? null : FechaLarga(e); decimal totIny = Dec1(P(pesoIni, 0) - P(pesoFin, 0) + P(pesoIni, 1) - P(pesoFin, 1)); decimal totRe = Dec1(P(pesoIni, 2) - P(pesoFin, 2) + P(pesoIni, 3) - P(pesoFin, 3)); var inyeccion = new { pesoIni, pesoFin, inicioIny = FL(fIni[0]), finIny = FL(fFin[1]), inicioRe = FL(fIni[2]), finRe = FL(fFin[3]), total = totIny, totalRe = totRe, granTotal = totIny + totRe }; // ---- tabla 2: temperaturas y eventos ---- var lecturas = new List(); decimal minT = 5555; string minF = "", minH = "", minS = ""; foreach (var h in hist.Where(h => I(h, "eventid") is 104 or 22 or 122 or 101 || I(h, "eventid") >= 1000)) { var temps = new List(); for (int k = 0; k < Analogos.Length; k++) { int t = I(h, Analogos[k]); temps.Add(t); if (t != 555 && t != -555 && t / 10m < minT) { minT = t / 10m; minF = FechaCorta(L(h, "fecha")); minH = Hora(L(h, "fecha")); minS = Analogos[k]; } } lecturas.Add(new { id = L(h, "id"), fecha = Iso(L(h, "fecha")), f = FechaCorta(L(h, "fecha")), h = Hora(L(h, "fecha")), eventid = I(h, "eventid"), temps, valvula = I(h, "valvula") != 256 ? "CE" : "AB", ventilador = I(h, "ventilador") != 256 ? "AP" : "EN", dumper = I(h, "dumper") != 256 ? "CE" : "AB" }); } string tminTexto = minT <= manualMin && minT != 5555 ? $"{Gr(minT)} (°C) el : {minF} {minH} en el sensor {minS}" : $"{Gr(manualMin)} (°C) el : {FechaLarga(finicial)} en el ingreso por insp. {manualNombre}"; // ---- tabla 3: concentraciones ---- int nMed = I(cam, "mediciones_concentraciones"); if (nMed < 1 || nMed > 6) nMed = 5; var nombresConc = new[] { "Pta.", "Med.", "Fdo.", "Int.Pta.", "Int.Fdo.", "Extra" }.Take(nMed).ToArray(); var conc = new List(); foreach (var h in hist.Where(h => I(h, "eventid") is >= 106 and <= 110).OrderBy(h => I(h, "eventid"))) { var partes = S(h, "equipo").Split('|'); conc.Add(new { id = L(h, "id"), fecha = Iso(L(h, "fecha")), f = FechaCorta(L(h, "fecha")), h = Hora(L(h, "fecha")), valores = Enumerable.Range(1, nMed).Select(i => I(h, "concentracion" + i)).ToList(), equipo = partes[0], responsable = partes.Length > 1 && partes[1] == "2" ? "Operador" : "Inspector" }); } // ---- sección 4: alarmas / eventos (port del switch de pdf.php) ---- var alarmas = new List(); int balon = 0; foreach (var h in hist) { int ev = I(h, "eventid"); long f = L(h, "fecha"); decimal peso = D(h, "pesoinyeccion"); string? msg = null; int e = ev; if (e is >= 100 and <= 199) e -= 100; if (ev is >= 100 and <= 199 || (e is >= 0 and <= 99 && e != 4)) { msg = e < PendI.Length ? PendI[e] : ""; if (e is >= 6 and <= 10) { var med = Enumerable.Range(1, 6).Select(i => I(h, "concentracion" + i)).ToArray(); int maxD = 0; foreach (var a in med) foreach (var b in med) maxD = Math.Max(maxD, Math.Abs(a - b)); if (maxD > 999) msg += $" (concentracion difiere en mas de {maxD} g/m3)"; } if (e == 2) { balon++; msg += $" {balon} : {Gr(peso)} Kg."; if (fechaReiny != 0 && f > fechaReiny) msg = "Re-inyeccion " + msg; } if (e == 3) { msg += $" {balon} : {Gr(peso)} Kg."; if (fechaReiny != 0 && f > fechaReiny) msg = "Re-inyeccion " + msg; } if (e == 20) msg += $" {Gr(-peso)} Kg."; } else if (ev is >= 200 and <= 299) msg = ev - 200 < PendNr.Length ? PendNr[ev - 200] : ""; else if (ev is >= 1000 and <= 1999) msg = ev - 1000 < EfectosW.Length ? EfectosW[ev - 1000] : ""; else if (ev is >= 2000 and <= 2999) msg = ev - 2000 < EfectosE.Length ? EfectosE[ev - 2000] : ""; else if (ev is >= 3000 and <= 3999) msg = ev - 3000 < Efectos.Length ? Efectos[ev - 3000] : ""; if (msg is null) continue; if (msg.Length > 0) msg = char.ToUpper(msg[0]) + msg[1..]; alarmas.Add(new { id = L(h, "id"), f = FechaCorta(f), h = Hora(f), fecha = Iso(f), eventid = ev, peso, msg }); } return new { id, idCorto = id % 100000, derivada, titulo1 = S(planta, "tituloreporte1"), titulo2 = S(planta, "tituloreporte2"), camaraId = I(fum, "camara"), camara = S(cam, "nombre"), m3, tipoId = I(fum, "tipofumigacion"), tipo = S(tipo, "nombreoficial"), inicio = finicial == 0 ? "" : FechaLarga(finicial), informe = ffinal == 0 ? "" : FechaLarga(ffinal), conIni, conFin, inyeccion, insp, datos = new { producto1 = S(datos, "producto1"), pallets1 = I(datos, "pallets1"), cajas1 = I(datos, "cajas1"), procedencia1 = S(datos, "procedencia1"), origen1 = S(datos, "origen1"), destino1 = S(datos, "destino1"), certificado1_ini = S(datos, "certificado1_ini"), certificado1_fin = S(datos, "certificado1_fin"), certificado2_ini = S(datos, "certificado2_ini"), certificado2_fin = S(datos, "certificado2_fin"), inspector = S(datos, "inspector"), operador = S(datos, "operador"), duracion = I(datos, "duracion") }, columnasTemp = new[] { "P1","P2","P3","P4","P5","P6","P7","P8","P9","P10","P11","P12","P13","P14","P15","A1","A2" }, lecturas, tminTexto, nombresConc, conc, alarmas }; } }