feat(MED-161): update sync private message
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { getSupabaseServerAdminClient } from "@/packages/supabase/src/clients/server-admin-client";
|
||||
import type { AnalysisResponseElement } from "../types/analysis-response-element";
|
||||
import { AnalysisOrderStatus } from '@/packages/shared/src/types/medipost-analysis';
|
||||
|
||||
export async function getExistingAnalysisResponseElements({
|
||||
analysisResponseId,
|
||||
@@ -15,3 +16,40 @@ export async function getExistingAnalysisResponseElements({
|
||||
|
||||
return data as AnalysisResponseElement[];
|
||||
}
|
||||
|
||||
export async function upsertAnalysisResponse({
|
||||
analysisOrderId,
|
||||
orderNumber,
|
||||
orderStatus,
|
||||
userId,
|
||||
}: {
|
||||
analysisOrderId: number;
|
||||
orderNumber: string;
|
||||
orderStatus: typeof AnalysisOrderStatus[keyof typeof AnalysisOrderStatus];
|
||||
userId: string;
|
||||
}) {
|
||||
const { data: analysisResponse } = await getSupabaseServerAdminClient()
|
||||
.schema('medreport')
|
||||
.from('analysis_responses')
|
||||
.upsert(
|
||||
{
|
||||
analysis_order_id: analysisOrderId,
|
||||
order_number: orderNumber,
|
||||
order_status: orderStatus,
|
||||
user_id: userId,
|
||||
},
|
||||
{ onConflict: 'order_number', ignoreDuplicates: false },
|
||||
)
|
||||
.select('id')
|
||||
.throwOnError();
|
||||
|
||||
|
||||
const analysisResponseId = analysisResponse?.[0]?.id;
|
||||
if (!analysisResponseId) {
|
||||
throw new Error(
|
||||
`Failed to insert or update analysis order response (order id: ${analysisOrderId}, order number: ${orderNumber})`,
|
||||
);
|
||||
}
|
||||
|
||||
return { analysisResponseId };
|
||||
}
|
||||
|
||||
@@ -10,10 +10,12 @@ import {
|
||||
import { AnalysisOrderStatus } from '@/packages/shared/src/types/medipost-analysis';
|
||||
import type {
|
||||
ResponseUuringuGrupp,
|
||||
MedipostOrderResponse
|
||||
MedipostOrderResponse,
|
||||
ResponseUuring,
|
||||
} from '@/packages/shared/src/types/medipost-analysis';
|
||||
import { toArray } from '@/lib/utils';
|
||||
import type { AnalysisOrder } from '~/lib/types/analysis-order';
|
||||
import type { AnalysisResponseElement } from '~/lib/types/analysis-response-element';
|
||||
|
||||
import { Tables } from '@kit/supabase/database';
|
||||
import { getSupabaseServerAdminClient } from '@/packages/supabase/src/clients/server-admin-client';
|
||||
@@ -27,7 +29,7 @@ import { composeOrderXML, OrderedAnalysisElement } from './medipostXML.service';
|
||||
import { getAccountAdmin } from '../account.service';
|
||||
import { logMedipostDispatch } from '../audit.service';
|
||||
import { MedipostValidationError } from './MedipostValidationError';
|
||||
import { getExistingAnalysisResponseElements } from '../analysis-order.service';
|
||||
import { getExistingAnalysisResponseElements, upsertAnalysisResponse } from '../analysis-order.service';
|
||||
|
||||
const BASE_URL = process.env.MEDIPOST_URL!;
|
||||
const USER = process.env.MEDIPOST_USER!;
|
||||
@@ -56,68 +58,89 @@ export async function getLatestPrivateMessageListItem({
|
||||
return getLatestMessage({ messages: data?.messages, excludedMessageIds });
|
||||
}
|
||||
|
||||
export async function syncPrivateMessage({
|
||||
messageResponse,
|
||||
order,
|
||||
}: {
|
||||
messageResponse: NonNullable<MedipostOrderResponse['Saadetis']['Vastus']>;
|
||||
order: Tables<{ schema: 'medreport' }, 'analysis_orders'>;
|
||||
}) {
|
||||
const supabase = getSupabaseServerAdminClient()
|
||||
|
||||
const { data: analysisOrder, error: analysisOrderError } = await supabase
|
||||
.schema('medreport')
|
||||
.from('analysis_orders')
|
||||
.select('user_id')
|
||||
.eq('id', order.id);
|
||||
|
||||
if (analysisOrderError || !analysisOrder?.[0]?.user_id) {
|
||||
throw new Error(
|
||||
`Could not find analysis order with id ${messageResponse.ValisTellimuseId}`,
|
||||
);
|
||||
const logger = (analysisOrder: AnalysisOrder, externalId: string, analysisResponseId: string) => (message: string, error?: PostgrestError | null) => {
|
||||
const messageFormatted = `[${analysisOrder.id}] [${externalId}] [${analysisResponseId}] ${message}`;
|
||||
if (error) {
|
||||
console.info(messageFormatted, error);
|
||||
} else {
|
||||
console.info(messageFormatted);
|
||||
}
|
||||
};
|
||||
|
||||
const { data: analysisResponse, error } = await supabase
|
||||
.schema('medreport')
|
||||
.from('analysis_responses')
|
||||
.upsert(
|
||||
{
|
||||
analysis_order_id: order.id,
|
||||
order_number: messageResponse.TellimuseNumber,
|
||||
order_status: AnalysisOrderStatus[messageResponse.TellimuseOlek],
|
||||
user_id: analysisOrder[0].user_id,
|
||||
function canCreateAnalysisResponseElement({
|
||||
existingElements,
|
||||
groupUuring: {
|
||||
UuringuElement: {
|
||||
UuringOlek: status,
|
||||
UuringId: analysisElementOriginalId,
|
||||
},
|
||||
{ onConflict: 'order_number', ignoreDuplicates: false },
|
||||
)
|
||||
.select('id');
|
||||
|
||||
if (error || !analysisResponse?.[0]?.id) {
|
||||
throw new Error(
|
||||
`Failed to insert or update analysis order response (external id: ${messageResponse?.TellimuseNumber})`,
|
||||
);
|
||||
},
|
||||
responseValue,
|
||||
log,
|
||||
}: {
|
||||
existingElements: AnalysisResponseElement[];
|
||||
groupUuring: ResponseUuring;
|
||||
responseValue: number | null;
|
||||
log: ReturnType<typeof logger>;
|
||||
}) {
|
||||
const existingAnalysisResponseElement = existingElements.find(({ analysis_element_original_id }) => analysis_element_original_id === analysisElementOriginalId);
|
||||
if (!existingAnalysisResponseElement) {
|
||||
return true;
|
||||
}
|
||||
const analysisGroups = toArray(messageResponse.UuringuGrupp);
|
||||
console.info(`Order has results for ${analysisGroups.length} analysis groups`);
|
||||
|
||||
const responses: Omit<
|
||||
Tables<{ schema: 'medreport' }, 'analysis_response_elements'>,
|
||||
'id' | 'created_at' | 'updated_at'
|
||||
>[] = [];
|
||||
if (Number(existingAnalysisResponseElement.status) > status) {
|
||||
log(`Analysis response element id=${analysisElementOriginalId} already exists for order in higher status ${existingAnalysisResponseElement.status} than ${status}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const analysisResponseId = analysisResponse[0]!.id;
|
||||
if (existingAnalysisResponseElement.response_value && !responseValue) {
|
||||
log(`Analysis response element id=${analysisElementOriginalId} already exists for order with response value ${existingAnalysisResponseElement.response_value} but new response has no value`);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const analysisGroup of analysisGroups) {
|
||||
const groupItems = toArray(
|
||||
analysisGroup.Uuring as ResponseUuringuGrupp['Uuring'],
|
||||
);
|
||||
console.info(`Order has results in group ${analysisGroup.UuringuGruppNimi} for ${groupItems.length} analysis elements`);
|
||||
for (const item of groupItems) {
|
||||
const element = item.UuringuElement;
|
||||
const elementAnalysisResponses = toArray(element.UuringuVastus);
|
||||
return true;
|
||||
}
|
||||
|
||||
responses.push(
|
||||
...elementAnalysisResponses.map((response) => ({
|
||||
analysis_element_original_id: element.UuringId,
|
||||
|
||||
async function getAnalysisResponseElementsForGroup({
|
||||
analysisResponseId,
|
||||
analysisGroup,
|
||||
log,
|
||||
}: {
|
||||
analysisResponseId: number;
|
||||
analysisGroup: ResponseUuringuGrupp;
|
||||
log: ReturnType<typeof logger>;
|
||||
}) {
|
||||
const groupUuringItems = toArray(analysisGroup.Uuring as ResponseUuringuGrupp['Uuring']);
|
||||
log(`Order has results in group '${analysisGroup.UuringuGruppNimi}' for ${groupUuringItems.length} analysis elements`);
|
||||
|
||||
const existingElements = await getExistingAnalysisResponseElements({ analysisResponseId });
|
||||
|
||||
const results: Omit<AnalysisResponseElement, 'created_at' | 'updated_at' | 'id'>[] = [];
|
||||
|
||||
for (const groupUuring of groupUuringItems) {
|
||||
const groupUuringElement = groupUuring.UuringuElement;
|
||||
const elementAnalysisResponses = toArray(groupUuringElement.UuringuVastus);
|
||||
|
||||
const status = groupUuringElement.UuringOlek;
|
||||
log(`Group uuring '${analysisGroup.UuringuGruppNimi}' has status ${status}`);
|
||||
|
||||
for (const response of elementAnalysisResponses) {
|
||||
const analysisElementOriginalId = groupUuringElement.UuringId;
|
||||
const responseValue = (() => {
|
||||
const valueAsNumber = Number(response.VastuseVaartus);
|
||||
if (isNaN(valueAsNumber)) {
|
||||
return null;
|
||||
}
|
||||
return valueAsNumber;
|
||||
})();
|
||||
|
||||
if (!canCreateAnalysisResponseElement({ existingElements, groupUuring, responseValue, log })) {
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push({
|
||||
analysis_element_original_id: analysisElementOriginalId,
|
||||
analysis_response_id: analysisResponseId,
|
||||
norm_lower: response.NormAlum?.['#text'] ?? null,
|
||||
norm_lower_included:
|
||||
@@ -127,47 +150,80 @@ export async function syncPrivateMessage({
|
||||
norm_upper_included:
|
||||
response.NormYlem?.['@_kaasaarvatud'].toLowerCase() === 'jah',
|
||||
response_time: response.VastuseAeg ?? null,
|
||||
response_value: response.VastuseVaartus,
|
||||
unit: element.Mootyhik ?? null,
|
||||
original_response_element: element,
|
||||
analysis_name: element.UuringNimi || element.KNimetus,
|
||||
comment: element.UuringuKommentaar ?? '',
|
||||
})),
|
||||
);
|
||||
response_value: responseValue,
|
||||
unit: groupUuringElement.Mootyhik ?? null,
|
||||
original_response_element: groupUuringElement,
|
||||
analysis_name: groupUuringElement.UuringNimi || groupUuringElement.KNimetus,
|
||||
comment: groupUuringElement.UuringuKommentaar ?? null,
|
||||
status: status.toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { error: deleteError } = await supabase
|
||||
return results;
|
||||
}
|
||||
|
||||
export async function syncPrivateMessage({
|
||||
messageResponse,
|
||||
order,
|
||||
}: {
|
||||
messageResponse: NonNullable<MedipostOrderResponse['Saadetis']['Vastus']>;
|
||||
order: Tables<{ schema: 'medreport' }, 'analysis_orders'>;
|
||||
}) {
|
||||
const supabase = getSupabaseServerAdminClient();
|
||||
|
||||
const externalId = messageResponse.ValisTellimuseId;
|
||||
const orderNumber = messageResponse.TellimuseNumber;
|
||||
const orderStatus = AnalysisOrderStatus[messageResponse.TellimuseOlek];
|
||||
|
||||
const log = logger(order, externalId, orderNumber);
|
||||
|
||||
const { data: analysisOrder } = await supabase
|
||||
.schema('medreport')
|
||||
.from('analysis_orders')
|
||||
.select('id, user_id')
|
||||
.eq('id', order.id)
|
||||
.single()
|
||||
.throwOnError();
|
||||
|
||||
const { analysisResponseId } = await upsertAnalysisResponse({
|
||||
analysisOrderId: order.id,
|
||||
orderNumber,
|
||||
orderStatus,
|
||||
userId: analysisOrder.user_id,
|
||||
});
|
||||
|
||||
const analysisGroups = toArray(messageResponse.UuringuGrupp);
|
||||
|
||||
log(`Order has results for ${analysisGroups.length} analysis groups`);
|
||||
|
||||
for (const analysisGroup of analysisGroups) {
|
||||
log(`[${analysisGroups.indexOf(analysisGroup) + 1}/${analysisGroups.length}] Syncing analysis group '${analysisGroup.UuringuGruppNimi}'`);
|
||||
|
||||
const elements = await getAnalysisResponseElementsForGroup({
|
||||
analysisResponseId,
|
||||
analysisGroup,
|
||||
log,
|
||||
});
|
||||
|
||||
for (const element of elements) {
|
||||
const { error } = await supabase
|
||||
.schema('medreport')
|
||||
.from('analysis_response_elements')
|
||||
.delete()
|
||||
.eq('analysis_response_id', analysisResponseId);
|
||||
|
||||
if (deleteError) {
|
||||
throw new Error(
|
||||
`Failed to clean up response elements for response id ${analysisResponseId}`,
|
||||
);
|
||||
.insert(element);
|
||||
if (error) {
|
||||
log(`Failed to insert order response elements for response id ${analysisResponseId} (order id: ${analysisOrder.id})`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { error: elementInsertError } = await supabase
|
||||
.schema('medreport')
|
||||
.from('analysis_response_elements')
|
||||
.insert(responses);
|
||||
|
||||
if (elementInsertError) {
|
||||
throw new Error(
|
||||
`Failed to insert order response elements for response id ${analysisResponseId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const existingAnalysisResponseElements = await getExistingAnalysisResponseElements({ analysisResponseId });
|
||||
const allOrderResponseElements = await getExistingAnalysisResponseElements({ analysisResponseId });
|
||||
const expectedOrderResponseElements = order.analysis_element_ids?.length ?? 0;
|
||||
if (existingAnalysisResponseElements.length !== expectedOrderResponseElements) {
|
||||
if (allOrderResponseElements.length !== expectedOrderResponseElements) {
|
||||
return { isPartial: true };
|
||||
}
|
||||
|
||||
const statusFromResponse = AnalysisOrderStatus[messageResponse.TellimuseOlek];
|
||||
return { isCompleted: statusFromResponse === 'COMPLETED' };
|
||||
return { isCompleted: orderStatus === 'COMPLETED' };
|
||||
}
|
||||
|
||||
export async function readPrivateMessageResponse({
|
||||
|
||||
Reference in New Issue
Block a user