using System.Net.Http.Json; using System.Text.Json; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; /// /// Drives lab-result batches with potassium observations through the HTTP pipeline. /// Uses DataSeeder usernames (intake1, entry1, verifier1, approver1). /// public static class CorrectionPipelineHelper { /// /// Creates a LAB_RESULTS batch with K and Na draft observations, drives it through /// verify → approve, and returns the promoted batch ID and live patient ID. /// public static async Task<(Guid BatchId, Guid PatientId)> PromoteLabBatchAsync( ApiFixture fixture, decimal potassiumValue, decimal sodiumValue = 140m) { var batchId = await CreateLabBatchThroughVerificationAsync(fixture, potassiumValue, sodiumValue); var client = await AuthHelper.LoginAsync(fixture, "approver1"); client.DefaultRequestHeaders.Add("Idempotency-Key", Guid.NewGuid().ToString()); var approveResponse = await client.PostAsJsonAsync( $"/api/v1/digitization-batches/{batchId}/approve", new { enableRetroactiveAlerts = false }); approveResponse.EnsureSuccessStatusCode(); using var scope = fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var batch = await db.DigitizationBatches.AsNoTracking().FirstAsync(b => b.Id == batchId); return (batchId, batch.PatientId!.Value); } /// /// Uploads a correction batch linked to a promoted batch, drives entry → verify → approve. /// public static async Task PromoteCorrectionBatchAsync( ApiFixture fixture, Guid supersedesBatchId, Guid patientId, decimal potassiumValue, decimal sodiumValue = 140m) { var client = await AuthHelper.LoginAsync(fixture, "intake1"); var fileContent = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes("%PDF-correction")); fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf"); var formData = new MultipartFormDataContent { { fileContent, "file", $"correction-{Guid.NewGuid()}.pdf" }, { new StringContent("LAB_RESULTS"), "batchType" }, { new StringContent("BACKFILL"), "track" }, { new StringContent(patientId.ToString()), "patientId" }, { new StringContent(supersedesBatchId.ToString()), "supersedesBatchId" } }; var uploadResponse = await client.PostAsync("/api/v1/digitization-batches", formData); uploadResponse.EnsureSuccessStatusCode(); var uploadBody = await uploadResponse.Content.ReadFromJsonAsync(); var correctionBatchId = uploadBody.GetProperty("data").GetProperty("id").GetGuid(); // Data entry — correction re-enters all observations client = await AuthHelper.LoginAsync(fixture, "entry2"); await client.PostAsJsonAsync( $"/api/v1/digitization-batches/{correctionBatchId}/draft/observations", new { observationCode = "K", value = potassiumValue, unit = "mmol/L", recordedAt = DateTimeOffset.UtcNow }); await client.PostAsJsonAsync( $"/api/v1/digitization-batches/{correctionBatchId}/draft/observations", new { observationCode = "Na", value = sodiumValue, unit = "mmol/L", recordedAt = DateTimeOffset.UtcNow }); var submitResponse = await client.PostAsync( $"/api/v1/digitization-batches/{correctionBatchId}/submit-for-verification", null); submitResponse.EnsureSuccessStatusCode(); // Verify and approve client = await AuthHelper.LoginAsync(fixture, "verifier2"); var verifyResponse = await client.PostAsJsonAsync( $"/api/v1/digitization-batches/{correctionBatchId}/verify", new { fieldChecks = new[] { new { fieldName = "observation.K", status = "ok", note = (string?)null } }, passed = true }); verifyResponse.EnsureSuccessStatusCode(); client = await AuthHelper.LoginAsync(fixture, "approver2"); client.DefaultRequestHeaders.Add("Idempotency-Key", Guid.NewGuid().ToString()); var approveResponse = await client.PostAsJsonAsync( $"/api/v1/digitization-batches/{correctionBatchId}/approve", new { enableRetroactiveAlerts = false }); approveResponse.EnsureSuccessStatusCode(); return correctionBatchId; } private static async Task CreateLabBatchThroughVerificationAsync( ApiFixture fixture, decimal potassiumValue, decimal sodiumValue) { var client = await AuthHelper.LoginAsync(fixture, "intake1"); var fileContent = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes("%PDF-lab")); fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf"); var formData = new MultipartFormDataContent { { fileContent, "file", $"lab-{Guid.NewGuid()}.pdf" }, { new StringContent("LAB_RESULTS"), "batchType" }, { new StringContent("BACKFILL"), "track" } }; var uploadResponse = await client.PostAsync("/api/v1/digitization-batches", formData); uploadResponse.EnsureSuccessStatusCode(); var uploadBody = await uploadResponse.Content.ReadFromJsonAsync(); var batchId = uploadBody.GetProperty("data").GetProperty("id").GetGuid(); client = await AuthHelper.LoginAsync(fixture, "entry1"); await client.PutAsJsonAsync( $"/api/v1/digitization-batches/{batchId}/draft/patient", new { fullName = "Correction Test Patient", dateOfBirth = "1980-01-15", sex = "Female" }); await client.PutAsJsonAsync( $"/api/v1/digitization-batches/{batchId}/draft/encounter", new { admissionDate = "2024-06-01T08:00:00Z", department = Department.InternalMedicine.ToDbString(), roomBed = "4A-12", admissionReason = "Electrolyte panel", status = "active" }); await client.PostAsJsonAsync( $"/api/v1/digitization-batches/{batchId}/draft/observations", new { observationCode = "K", value = potassiumValue, unit = "mmol/L", recordedAt = DateTimeOffset.UtcNow }); await client.PostAsJsonAsync( $"/api/v1/digitization-batches/{batchId}/draft/observations", new { observationCode = "Na", value = sodiumValue, unit = "mmol/L", recordedAt = DateTimeOffset.UtcNow }); await client.PostAsync( $"/api/v1/digitization-batches/{batchId}/submit-for-verification", null); client = await AuthHelper.LoginAsync(fixture, "verifier1"); await client.PostAsJsonAsync( $"/api/v1/digitization-batches/{batchId}/verify", new { fieldChecks = new[] { new { fieldName = "observation.K", status = "ok", note = (string?)null } }, passed = true }); return batchId; } }