289 lines
8.7 KiB
TypeScript
289 lines
8.7 KiB
TypeScript
'use server';
|
|
|
|
import { MedipostAction } from '@/lib/types/medipost';
|
|
import { createUserAnalysesApi } from '@/packages/features/user-analyses/src/server/api';
|
|
import type {
|
|
ResponseUuringuGrupp,
|
|
UuringElement,
|
|
} from '@/packages/shared/src/types/medipost-analysis';
|
|
import { getSupabaseServerAdminClient } from '@/packages/supabase/src/clients/server-admin-client';
|
|
import axios from 'axios';
|
|
|
|
import { toArray } from '@kit/shared/utils';
|
|
|
|
import type { AnalysisResponseElement } from '~/lib/types/analysis-response-element';
|
|
|
|
import { getAccountAdmin } from '../account.service';
|
|
import { getAnalyses } from '../analyses.service';
|
|
import { getAnalysisElementsAdmin } from '../analysis-element.service';
|
|
import { logMedipostDispatch } from '../audit.service';
|
|
import { getAnalysisOrder } from '../order.service';
|
|
import { MedipostValidationError } from './MedipostValidationError';
|
|
import {
|
|
upsertMedipostActionLog,
|
|
} from './medipostMessageBase.service';
|
|
import { validateMedipostResponse } from './medipostValidate.service';
|
|
import { OrderedAnalysisElement, composeOrderXML } from './medipostXML.service';
|
|
import type { Logger } from './types';
|
|
|
|
const BASE_URL = process.env.MEDIPOST_URL!;
|
|
const USER = process.env.MEDIPOST_USER!;
|
|
const PASSWORD = process.env.MEDIPOST_PASSWORD!;
|
|
const RECIPIENT = process.env.MEDIPOST_RECIPIENT!;
|
|
|
|
export async function canCreateAnalysisResponseElement({
|
|
existingElements,
|
|
groupUuring: {
|
|
UuringuElement: { UuringOlek: status, UuringId: analysisElementOriginalId },
|
|
},
|
|
responseValue,
|
|
log,
|
|
}: {
|
|
existingElements: Pick<
|
|
AnalysisResponseElement,
|
|
'analysis_element_original_id' | 'status' | 'response_value'
|
|
>[];
|
|
groupUuring: {
|
|
UuringuElement: Pick<UuringElement, 'UuringOlek' | 'UuringId'>;
|
|
};
|
|
responseValue: number | null;
|
|
log: Logger;
|
|
}) {
|
|
const existingAnalysisResponseElement = existingElements.find(
|
|
({ analysis_element_original_id }) =>
|
|
analysis_element_original_id === analysisElementOriginalId,
|
|
);
|
|
if (!existingAnalysisResponseElement) {
|
|
return true;
|
|
}
|
|
|
|
if (Number(existingAnalysisResponseElement.status) > status) {
|
|
log(
|
|
`Analysis response element id=${analysisElementOriginalId} already exists for order in higher status ${existingAnalysisResponseElement.status} than ${status}`,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (existingAnalysisResponseElement.response_value && !responseValue) {
|
|
log(
|
|
`Analysis response element id=${analysisElementOriginalId} ${existingAnalysisResponseElement.response_value} but new response has no value`,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
export async function getAnalysisResponseElementsForGroup({
|
|
analysisGroup,
|
|
existingElements,
|
|
log,
|
|
}: {
|
|
analysisGroup: Pick<ResponseUuringuGrupp, 'UuringuGruppNimi' | 'Uuring'>;
|
|
existingElements: Pick<
|
|
AnalysisResponseElement,
|
|
'analysis_element_original_id' | 'status' | 'response_value'
|
|
>[];
|
|
log: Logger;
|
|
}) {
|
|
const groupUuringItems = toArray(
|
|
analysisGroup.Uuring as ResponseUuringuGrupp['Uuring'],
|
|
);
|
|
log(
|
|
`Order has results in group '${analysisGroup.UuringuGruppNimi}' for ${groupUuringItems.length} analysis elements`,
|
|
);
|
|
|
|
const results: Omit<
|
|
AnalysisResponseElement,
|
|
'created_at' | 'updated_at' | 'id' | 'analysis_response_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 vastuseVaartus = response.VastuseVaartus;
|
|
const responseValue = (() => {
|
|
const valueAsNumber = Number(vastuseVaartus);
|
|
if (isNaN(valueAsNumber)) {
|
|
return null;
|
|
}
|
|
return valueAsNumber;
|
|
})();
|
|
|
|
if (
|
|
!(await canCreateAnalysisResponseElement({
|
|
existingElements,
|
|
groupUuring,
|
|
responseValue,
|
|
log,
|
|
}))
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const mappedResponse = createUserAnalysesApi(
|
|
getSupabaseServerAdminClient(),
|
|
).mapUuringVastus({ uuringVastus: response });
|
|
|
|
results.push({
|
|
analysis_element_original_id: analysisElementOriginalId,
|
|
norm_lower: mappedResponse.normLower,
|
|
norm_lower_included: mappedResponse.normLowerIncluded,
|
|
norm_status: mappedResponse.normStatus,
|
|
norm_upper: mappedResponse.normUpper,
|
|
norm_upper_included: mappedResponse.normUpperIncluded,
|
|
response_time: mappedResponse.responseTime,
|
|
response_value: mappedResponse.responseValue,
|
|
unit: groupUuringElement.Mootyhik ?? null,
|
|
original_response_element: groupUuringElement,
|
|
analysis_name:
|
|
groupUuringElement.UuringNimi || groupUuringElement.KNimetus,
|
|
comment: groupUuringElement.UuringuKommentaar ?? null,
|
|
status: status.toString(),
|
|
response_value_is_within_norm: mappedResponse.responseValueIsWithinNorm,
|
|
response_value_is_negative: mappedResponse.responseValueIsNegative,
|
|
});
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
export async function sendPrivateMessage(messageXml: string) {
|
|
const body = new FormData();
|
|
body.append('Action', MedipostAction.SendPrivateMessage);
|
|
body.append('User', USER);
|
|
body.append('Password', PASSWORD);
|
|
body.append('Receiver', RECIPIENT);
|
|
body.append('MessageType', 'Tellimus');
|
|
body.append(
|
|
'Message',
|
|
new Blob([messageXml], {
|
|
type: 'text/xml; charset=UTF-8',
|
|
}),
|
|
);
|
|
|
|
const { data } = await axios.post(BASE_URL, body);
|
|
|
|
await validateMedipostResponse(data);
|
|
}
|
|
|
|
export async function sendOrderToMedipost({
|
|
medusaOrderId,
|
|
orderedAnalysisElements,
|
|
}: {
|
|
medusaOrderId: string;
|
|
orderedAnalysisElements: OrderedAnalysisElement[];
|
|
}) {
|
|
const medreportOrder = await getAnalysisOrder({ medusaOrderId });
|
|
const account = await getAccountAdmin({
|
|
primaryOwnerUserId: medreportOrder.user_id,
|
|
});
|
|
|
|
const orderedAnalysesIds = orderedAnalysisElements
|
|
.map(({ analysisId }) => analysisId)
|
|
.filter(Boolean) as number[];
|
|
const orderedAnalysisElementsIds = orderedAnalysisElements
|
|
.map(({ analysisElementId }) => analysisElementId)
|
|
.filter(Boolean) as number[];
|
|
|
|
const analyses = await getAnalyses({ ids: orderedAnalysesIds });
|
|
if (analyses.length !== orderedAnalysesIds.length) {
|
|
throw new Error(
|
|
`Got ${analyses.length} analyses, expected ${orderedAnalysesIds.length}`,
|
|
);
|
|
}
|
|
const analysisElements = await getAnalysisElementsAdmin({
|
|
ids: orderedAnalysisElementsIds,
|
|
});
|
|
if (analysisElements.length !== orderedAnalysisElementsIds.length) {
|
|
throw new Error(
|
|
`Got ${analysisElements.length} analysis elements, expected ${orderedAnalysisElementsIds.length}`,
|
|
);
|
|
}
|
|
|
|
const orderXml = await composeOrderXML({
|
|
analyses,
|
|
analysisElements,
|
|
person: {
|
|
idCode: account.personal_code!,
|
|
firstName: account.name ?? '',
|
|
lastName: account.last_name ?? '',
|
|
phone: account.phone ?? '',
|
|
},
|
|
orderId: medreportOrder.id,
|
|
comment: '',
|
|
});
|
|
|
|
try {
|
|
await sendPrivateMessage(orderXml);
|
|
} catch (e) {
|
|
const isMedipostError = e instanceof MedipostValidationError;
|
|
if (isMedipostError) {
|
|
await logMedipostDispatch({
|
|
medusaOrderId,
|
|
isSuccess: false,
|
|
isMedipostError,
|
|
errorMessage: e.response,
|
|
});
|
|
console.error(
|
|
`Failed to send order to Medipost, medusaOrderId=${medusaOrderId}, error=${e.response}`,
|
|
);
|
|
await upsertMedipostActionLog({
|
|
action: 'send_order_to_medipost',
|
|
xml: orderXml,
|
|
hasAnalysisResults: false,
|
|
medusaOrderId,
|
|
responseXml: e.response,
|
|
hasError: true,
|
|
});
|
|
} else {
|
|
console.error(
|
|
`Failed to send order to Medipost, medusaOrderId=${medusaOrderId}, error=${e}`,
|
|
);
|
|
await logMedipostDispatch({
|
|
medusaOrderId,
|
|
isSuccess: false,
|
|
isMedipostError,
|
|
});
|
|
await upsertMedipostActionLog({
|
|
action: 'send_order_to_medipost',
|
|
xml: orderXml,
|
|
hasAnalysisResults: false,
|
|
medusaOrderId,
|
|
hasError: true,
|
|
});
|
|
}
|
|
|
|
throw e;
|
|
}
|
|
console.info(
|
|
`Successfully sent order to Medipost, medusaOrderId=${medusaOrderId}`,
|
|
);
|
|
await logMedipostDispatch({
|
|
medusaOrderId,
|
|
isSuccess: true,
|
|
isMedipostError: false,
|
|
});
|
|
await upsertMedipostActionLog({
|
|
action: 'send_order_to_medipost',
|
|
xml: orderXml,
|
|
hasAnalysisResults: false,
|
|
medusaOrderId,
|
|
});
|
|
await createUserAnalysesApi(
|
|
getSupabaseServerAdminClient(),
|
|
).updateAnalysisOrderStatus({
|
|
medusaOrderId,
|
|
orderStatus: 'PROCESSING',
|
|
});
|
|
}
|