@@ -127,10 +123,18 @@ const Analysis = ({
{isCancelled || !results || hasNestedElements ? null : (
<>
- {!(hasIsNegative || hasIsWithinNorm) && (
+ {!hasTextualResponse && (
<>
{normRangeText}
diff --git a/app/home/(user)/(dashboard)/analysis-results/test/test-responses.ts b/app/home/(user)/(dashboard)/analysis-results/test/test-responses.ts
index e4da467..14ef88e 100644
--- a/app/home/(user)/(dashboard)/analysis-results/test/test-responses.ts
+++ b/app/home/(user)/(dashboard)/analysis-results/test/test-responses.ts
@@ -139,7 +139,7 @@ const big1: AnalysisTestResponse = {
"unit": null,
"normLower": null,
"normUpper": 2,
- "normStatus": 0,
+ "normStatus": 2,
"responseTime": "2024-02-29T10:13:01+00:00",
"responseValue": null,
"responseValueIsNegative": null,
@@ -150,6 +150,26 @@ const big1: AnalysisTestResponse = {
"analysisElementOriginalId": "59156-0"
}
},
+ {
+ "analysisIdOriginal": "59156-0",
+ "isWaitingForResults": false,
+ "analysisName": "Glükoos",
+ "results": {
+ "nestedElements": [],
+ "unit": null,
+ "normLower": null,
+ "normUpper": 2,
+ "normStatus": 0,
+ "responseTime": "2024-02-29T10:13:01+00:00",
+ "responseValue": null,
+ "responseValueIsNegative": null,
+ "responseValueIsWithinNorm": true,
+ "normLowerIncluded": false,
+ "normUpperIncluded": false,
+ "status": "4",
+ "analysisElementOriginalId": "59156-0"
+ }
+ },
{
"analysisIdOriginal": "13955-0",
"isWaitingForResults": false,
diff --git a/lib/services/analysis-order.service.ts b/lib/services/analysis-order.service.ts
index cd57b9a..5fa5ed5 100644
--- a/lib/services/analysis-order.service.ts
+++ b/lib/services/analysis-order.service.ts
@@ -18,16 +18,32 @@ export async function getExistingAnalysisResponseElements({
return data as AnalysisResponseElement[];
}
-export async function createAnalysisResponseElement({
+export async function upsertAnalysisResponseElement({
element,
}: {
element: Omit
;
}) {
- await getSupabaseServerAdminClient()
+ const { data } = await getSupabaseServerAdminClient()
.schema('medreport')
.from('analysis_response_elements')
- .insert(element)
+ .upsert(
+ element,
+ {
+ onConflict: 'analysis_response_id,analysis_element_original_id',
+ ignoreDuplicates: false
+ }
+ )
+ .select('id')
.throwOnError();
+
+ const analysisResponseElementId = data?.[0]?.id;
+ if (!analysisResponseElementId) {
+ throw new Error(
+ `Failed to insert or update analysis response element (response id: ${element.analysis_response_id}, element id: ${element.analysis_element_original_id})`
+ );
+ }
+
+ return { analysisResponseElementId };
}
export async function upsertAnalysisResponse({
diff --git a/lib/services/medipost/medipostMessageBase.service.ts b/lib/services/medipost/medipostMessageBase.service.ts
index ef452ec..d3194e8 100644
--- a/lib/services/medipost/medipostMessageBase.service.ts
+++ b/lib/services/medipost/medipostMessageBase.service.ts
@@ -27,7 +27,7 @@ export async function getLatestMessage({
);
}
-export async function createMedipostActionLog({
+export async function upsertMedipostActionLog({
action,
xml,
hasAnalysisResults = false,
@@ -40,8 +40,7 @@ export async function createMedipostActionLog({
action:
| 'send_order_to_medipost'
| 'sync_analysis_results_from_medipost'
- | 'send_fake_analysis_results_to_medipost'
- | 'send_analysis_results_to_medipost';
+ | 'send_fake_analysis_results_to_medipost';
xml: string;
hasAnalysisResults?: boolean;
medusaOrderId?: string | null;
@@ -50,19 +49,34 @@ export async function createMedipostActionLog({
medipostExternalOrderId?: string | null;
medipostPrivateMessageId?: string | null;
}) {
- await getSupabaseServerAdminClient()
+ const { data } = await getSupabaseServerAdminClient()
.schema('medreport')
.from('medipost_actions')
- .insert({
- action,
- xml,
- has_analysis_results: hasAnalysisResults,
- medusa_order_id: medusaOrderId,
- response_xml: responseXml,
- has_error: hasError,
- medipost_external_order_id: medipostExternalOrderId,
- medipost_private_message_id: medipostPrivateMessageId,
- })
+ .upsert(
+ {
+ action,
+ xml,
+ has_analysis_results: hasAnalysisResults,
+ medusa_order_id: medusaOrderId,
+ response_xml: responseXml,
+ has_error: hasError,
+ medipost_external_order_id: medipostExternalOrderId,
+ medipost_private_message_id: medipostPrivateMessageId,
+ },
+ {
+ onConflict: 'medipost_private_message_id',
+ ignoreDuplicates: false
+ }
+ )
.select('id')
.throwOnError();
+
+ const medipostActionId = data?.[0]?.id;
+ if (!medipostActionId) {
+ throw new Error(
+ `Failed to insert or update medipost action (private message id: ${medipostPrivateMessageId})`
+ );
+ }
+
+ return { medipostActionId };
}
diff --git a/lib/services/medipost/medipostPrivateMessage.service.ts b/lib/services/medipost/medipostPrivateMessage.service.ts
index f6356e3..fba8b76 100644
--- a/lib/services/medipost/medipostPrivateMessage.service.ts
+++ b/lib/services/medipost/medipostPrivateMessage.service.ts
@@ -21,7 +21,7 @@ import { Tables } from '@kit/supabase/database';
import { getSupabaseServerAdminClient } from '@/packages/supabase/src/clients/server-admin-client';
import { getAnalysisElementsAdmin } from '../analysis-element.service';
import { getAnalyses } from '../analyses.service';
-import { createMedipostActionLog, getLatestMessage } from './medipostMessageBase.service';
+import { upsertMedipostActionLog, getLatestMessage } from './medipostMessageBase.service';
import { validateMedipostResponse } from './medipostValidate.service';
import { getAnalysisOrder, updateAnalysisOrderStatus } from '../order.service';
import { parseXML } from '../util/xml.service';
@@ -29,7 +29,7 @@ import { composeOrderXML, OrderedAnalysisElement } from './medipostXML.service';
import { getAccountAdmin } from '../account.service';
import { logMedipostDispatch } from '../audit.service';
import { MedipostValidationError } from './MedipostValidationError';
-import { createAnalysisResponseElement, getExistingAnalysisResponseElements, upsertAnalysisResponse } from '../analysis-order.service';
+import { upsertAnalysisResponseElement, getExistingAnalysisResponseElements, upsertAnalysisResponse } from '../analysis-order.service';
const BASE_URL = process.env.MEDIPOST_URL!;
const USER = process.env.MEDIPOST_USER!;
@@ -242,7 +242,7 @@ export async function syncPrivateMessage({
for (const element of newElements) {
try {
- await createAnalysisResponseElement({
+ await upsertAnalysisResponseElement({
element: {
...element,
analysis_response_id: analysisResponseId,
@@ -305,7 +305,7 @@ export async function readPrivateMessageResponse({
const hasInvalidOrderId = isNaN(analysisOrderId);
if (hasInvalidOrderId || !messageResponse || !patientPersonalCode) {
- await createMedipostActionLog({
+ await upsertMedipostActionLog({
action: 'sync_analysis_results_from_medipost',
xml: privateMessageXml,
hasAnalysisResults: false,
@@ -342,7 +342,7 @@ export async function readPrivateMessageResponse({
const status = await syncPrivateMessage({ messageResponse, order: analysisOrder });
- await createMedipostActionLog({
+ await upsertMedipostActionLog({
action: 'sync_analysis_results_from_medipost',
xml: privateMessageXml,
hasAnalysisResults: true,
@@ -475,7 +475,7 @@ export async function sendOrderToMedipost({
isMedipostError,
errorMessage: e.response,
});
- await createMedipostActionLog({
+ await upsertMedipostActionLog({
action: 'send_order_to_medipost',
xml: orderXml,
hasAnalysisResults: false,
@@ -489,7 +489,7 @@ export async function sendOrderToMedipost({
isSuccess: false,
isMedipostError,
});
- await createMedipostActionLog({
+ await upsertMedipostActionLog({
action: 'send_order_to_medipost',
xml: orderXml,
hasAnalysisResults: false,
@@ -505,7 +505,7 @@ export async function sendOrderToMedipost({
isSuccess: true,
isMedipostError: false,
});
- await createMedipostActionLog({
+ await upsertMedipostActionLog({
action: 'send_order_to_medipost',
xml: orderXml,
hasAnalysisResults: false,
diff --git a/supabase/migrations/20250918143530_analysis_response_element_unique_by_order.sql b/supabase/migrations/20250918143530_analysis_response_element_unique_by_order.sql
new file mode 100644
index 0000000..082cab4
--- /dev/null
+++ b/supabase/migrations/20250918143530_analysis_response_element_unique_by_order.sql
@@ -0,0 +1,18 @@
+-- Add unique constraint on analysis_response_elements by analysis_response_id + analysis_element_original_id
+CREATE UNIQUE INDEX analysis_response_elements_unique_by_response_and_element
+ON "medreport"."analysis_response_elements"
+USING btree (analysis_response_id, analysis_element_original_id);
+
+ALTER TABLE "medreport"."analysis_response_elements"
+ADD CONSTRAINT "analysis_response_elements_unique_by_response_and_element"
+UNIQUE USING INDEX "analysis_response_elements_unique_by_response_and_element";
+
+-- Add updated_at column to medipost_actions table
+ALTER TABLE "medreport"."medipost_actions" ADD COLUMN "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT now();
+
+-- Add unique constraint on medipost_actions by medipost_private_message_id
+-- Using partial index to allow multiple NULL values but enforce uniqueness for non-NULL values
+CREATE UNIQUE INDEX medipost_actions_unique_by_private_message_id
+ON "medreport"."medipost_actions"
+USING btree (medipost_private_message_id)
+WHERE medipost_private_message_id IS NOT NULL;