747 lines
23 KiB
TypeScript
747 lines
23 KiB
TypeScript
'use server';
|
|
|
|
import {
|
|
SupabaseClient,
|
|
createClient as createCustomClient,
|
|
} from '@supabase/supabase-js';
|
|
|
|
import {
|
|
getAnalysisGroup,
|
|
getClientInstitution,
|
|
getClientPerson,
|
|
getConfidentiality,
|
|
getPais,
|
|
getPatient,
|
|
getProviderInstitution,
|
|
getSpecimen,
|
|
} from '@/lib/templates/medipost-order';
|
|
import { SyncStatus } from '@/lib/types/audit';
|
|
import {
|
|
AnalysisOrderStatus,
|
|
GetMessageListResponse,
|
|
IMedipostResponseXMLBase,
|
|
MaterjalideGrupp,
|
|
MedipostAction,
|
|
MedipostOrderResponse,
|
|
MedipostPublicMessageResponse,
|
|
Message,
|
|
ResponseUuringuGrupp,
|
|
UuringuGrupp,
|
|
} from '@/lib/types/medipost';
|
|
import { toArray } from '@/lib/utils';
|
|
import axios from 'axios';
|
|
import { XMLParser } from 'fast-xml-parser';
|
|
import { uniqBy } from 'lodash';
|
|
|
|
import { Tables } from '@kit/supabase/database';
|
|
import { createAnalysisGroup } from './analysis-group.service';
|
|
import { getSupabaseServerAdminClient } from '@/packages/supabase/src/clients/server-admin-client';
|
|
import { getOrder } from './order.service';
|
|
import { getAnalysisElements, getAnalysisElementsAdmin } from './analysis-element.service';
|
|
import { getAnalyses } from './analyses.service';
|
|
import { getAccountAdmin } from './account.service';
|
|
import { StoreOrder } from '@medusajs/types';
|
|
import { listProducts } from '@lib/data/products';
|
|
import { listRegions } from '@lib/data/regions';
|
|
import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product';
|
|
|
|
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!;
|
|
|
|
const ANALYSIS_ELEMENT_HANDLE_PREFIX = 'analysis-element-';
|
|
const ANALYSIS_PACKAGE_HANDLE_PREFIX = 'analysis-package-';
|
|
|
|
function parseXML(xml: string) {
|
|
const parser = new XMLParser({ ignoreAttributes: false });
|
|
return parser.parse(xml);
|
|
}
|
|
|
|
export async function validateMedipostResponse(response: string, { canHaveEmptyCode = false }: { canHaveEmptyCode?: boolean } = {}) {
|
|
const parsed: IMedipostResponseXMLBase = parseXML(response);
|
|
const code = parsed.ANSWER?.CODE;
|
|
if (canHaveEmptyCode) {
|
|
if (code && code !== 0) {
|
|
console.error("Bad response", response);
|
|
throw new Error(`Medipost response is invalid`);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (typeof code !== 'number' || (code !== 0 && !canHaveEmptyCode)) {
|
|
console.error("Bad response", response);
|
|
throw new Error(`Medipost response is invalid`);
|
|
}
|
|
}
|
|
|
|
export async function getMessages() {
|
|
try {
|
|
const publicMessage = await getLatestPublicMessageListItem();
|
|
|
|
if (!publicMessage) {
|
|
return null;
|
|
}
|
|
|
|
//Teenused tuleb mappida kokku MedReport teenustega. <UuringId> alusel
|
|
return getPublicMessage(publicMessage.messageId);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
export async function getLatestPublicMessageListItem() {
|
|
const { data } = await axios.get<GetMessageListResponse>(BASE_URL, {
|
|
params: {
|
|
Action: MedipostAction.GetPublicMessageList,
|
|
User: USER,
|
|
Password: PASSWORD,
|
|
Sender: 'syndev',
|
|
// LastChecked (date+time) can be used here to get only messages since the last check - add when cron is created
|
|
// MessageType check only for messages of certain type
|
|
},
|
|
});
|
|
|
|
if (data.code && data.code !== 0) {
|
|
throw new Error('Failed to get public message list');
|
|
}
|
|
|
|
return getLatestMessage({ messages: data?.messages });
|
|
}
|
|
|
|
export async function getPublicMessage(messageId: string) {
|
|
const { data } = await axios.get(BASE_URL, {
|
|
params: {
|
|
Action: MedipostAction.GetPublicMessage,
|
|
User: USER,
|
|
Password: PASSWORD,
|
|
MessageId: messageId,
|
|
},
|
|
headers: {
|
|
Accept: 'application/xml',
|
|
},
|
|
});
|
|
await validateMedipostResponse(data);
|
|
return parseXML(data) as MedipostPublicMessageResponse;
|
|
}
|
|
|
|
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 getLatestPrivateMessageListItem({
|
|
excludedMessageIds,
|
|
}: {
|
|
excludedMessageIds: string[];
|
|
}) {
|
|
const { data } = await axios.get<GetMessageListResponse>(BASE_URL, {
|
|
params: {
|
|
Action: MedipostAction.GetPrivateMessageList,
|
|
User: USER,
|
|
Password: PASSWORD,
|
|
},
|
|
});
|
|
|
|
if (data.code && data.code !== 0) {
|
|
throw new Error('Failed to get private message list');
|
|
}
|
|
|
|
return getLatestMessage({ messages: data?.messages, excludedMessageIds });
|
|
}
|
|
|
|
export async function getPrivateMessage(messageId: string) {
|
|
const { data } = await axios.get(BASE_URL, {
|
|
params: {
|
|
Action: MedipostAction.GetPrivateMessage,
|
|
User: USER,
|
|
Password: PASSWORD,
|
|
MessageId: messageId,
|
|
},
|
|
headers: {
|
|
Accept: 'application/xml',
|
|
},
|
|
});
|
|
|
|
await validateMedipostResponse(data, { canHaveEmptyCode: true });
|
|
|
|
return parseXML(data) as MedipostOrderResponse;
|
|
}
|
|
|
|
export async function deletePrivateMessage(messageId: string) {
|
|
const { data } = await axios.get(BASE_URL, {
|
|
params: {
|
|
Action: MedipostAction.DeletePrivateMessage,
|
|
User: USER,
|
|
Password: PASSWORD,
|
|
MessageId: messageId,
|
|
},
|
|
});
|
|
|
|
if (data.code && data.code !== 0) {
|
|
throw new Error(`Failed to delete private message (id: ${messageId})`);
|
|
}
|
|
}
|
|
|
|
export async function readPrivateMessageResponse({
|
|
excludedMessageIds,
|
|
}: {
|
|
excludedMessageIds: string[];
|
|
}) {
|
|
let messageIdErrored: string | null = null;
|
|
let messageIdProcessed: string | null = null;
|
|
try {
|
|
const privateMessage = await getLatestPrivateMessageListItem({ excludedMessageIds });
|
|
if (!privateMessage) {
|
|
throw new Error(`No private message found`);
|
|
}
|
|
|
|
messageIdErrored = privateMessage.messageId;
|
|
if (!messageIdErrored) {
|
|
throw new Error(`No message id found`);
|
|
}
|
|
|
|
const privateMessageContent = await getPrivateMessage(
|
|
privateMessage.messageId,
|
|
);
|
|
const messageResponse = privateMessageContent?.Saadetis?.Vastus;
|
|
|
|
if (!messageResponse) {
|
|
throw new Error(`Invalid data in private message response`);
|
|
}
|
|
|
|
const status = await syncPrivateMessage({ messageResponse });
|
|
|
|
if (status === 'COMPLETED') {
|
|
await deletePrivateMessage(privateMessage.messageId);
|
|
messageIdProcessed = privateMessage.messageId;
|
|
}
|
|
} catch (e) {
|
|
console.warn(`Failed to process private message id=${messageIdErrored}, message=${(e as Error).message}`);
|
|
}
|
|
|
|
return { messageIdErrored, messageIdProcessed };
|
|
}
|
|
|
|
async function saveAnalysisGroup(
|
|
analysisGroup: UuringuGrupp,
|
|
supabase: SupabaseClient,
|
|
) {
|
|
const analysisGroupId = await createAnalysisGroup({
|
|
id: analysisGroup.UuringuGruppId,
|
|
name: analysisGroup.UuringuGruppNimi,
|
|
order: analysisGroup.UuringuGruppJarjekord,
|
|
});
|
|
|
|
const analysisGroupCodes = toArray(analysisGroup.Kood);
|
|
const codes: Partial<Tables<{ schema: 'medreport' }, 'codes'>>[] =
|
|
analysisGroupCodes.map((kood) => ({
|
|
hk_code: kood.HkKood,
|
|
hk_code_multiplier: kood.HkKoodiKordaja,
|
|
coefficient: kood.Koefitsient,
|
|
price: kood.Hind,
|
|
analysis_group_id: analysisGroupId,
|
|
}));
|
|
|
|
const analysisGroupItems = toArray(analysisGroup.Uuring);
|
|
|
|
for (const item of analysisGroupItems) {
|
|
const analysisElement = item.UuringuElement;
|
|
|
|
const { data: insertedAnalysisElement, error } = await supabase
|
|
.schema('medreport')
|
|
.from('analysis_elements')
|
|
.upsert(
|
|
{
|
|
analysis_id_oid: analysisElement.UuringIdOID,
|
|
analysis_id_original: analysisElement.UuringId,
|
|
tehik_short_loinc: analysisElement.TLyhend,
|
|
tehik_loinc_name: analysisElement.KNimetus,
|
|
analysis_name_lab: analysisElement.UuringNimi,
|
|
order: analysisElement.Jarjekord,
|
|
parent_analysis_group_id: analysisGroupId,
|
|
material_groups: toArray(item.MaterjalideGrupp),
|
|
},
|
|
{ onConflict: 'analysis_id_original', ignoreDuplicates: false },
|
|
)
|
|
.select('id');
|
|
|
|
if (error || !insertedAnalysisElement[0]?.id) {
|
|
throw new Error(
|
|
`Failed to insert analysis element (id: ${analysisElement.UuringId}), error: ${error?.message}`,
|
|
);
|
|
}
|
|
|
|
const insertedAnalysisElementId = insertedAnalysisElement[0].id;
|
|
|
|
if (analysisElement.Kood) {
|
|
const analysisElementCodes = toArray(analysisElement.Kood);
|
|
codes.push(
|
|
...analysisElementCodes.map((kood) => ({
|
|
hk_code: kood.HkKood,
|
|
hk_code_multiplier: kood.HkKoodiKordaja,
|
|
coefficient: kood.Koefitsient,
|
|
price: kood.Hind,
|
|
analysis_element_id: insertedAnalysisElementId,
|
|
})),
|
|
);
|
|
}
|
|
|
|
const analyses = analysisElement.UuringuElement;
|
|
if (analyses?.length) {
|
|
for (const analysis of analyses) {
|
|
const { data: insertedAnalysis, error } = await supabase
|
|
.schema('medreport')
|
|
.from('analyses')
|
|
.upsert(
|
|
{
|
|
analysis_id_oid: analysis.UuringIdOID,
|
|
analysis_id_original: analysis.UuringId,
|
|
tehik_short_loinc: analysis.TLyhend,
|
|
tehik_loinc_name: analysis.KNimetus,
|
|
analysis_name_lab: analysis.UuringNimi,
|
|
order: analysis.Jarjekord,
|
|
parent_analysis_element_id: insertedAnalysisElementId,
|
|
},
|
|
{ onConflict: 'analysis_id_original', ignoreDuplicates: false },
|
|
)
|
|
.select('id');
|
|
|
|
if (error || !insertedAnalysis[0]?.id) {
|
|
throw new Error(
|
|
`Failed to insert analysis (id: ${analysis.UuringId}) error: ${error?.message}`,
|
|
);
|
|
}
|
|
|
|
const insertedAnalysisId = insertedAnalysis[0].id;
|
|
if (analysisElement.Kood) {
|
|
const analysisCodes = toArray(analysis.Kood);
|
|
|
|
codes.push(
|
|
...analysisCodes.map((kood) => ({
|
|
hk_code: kood.HkKood,
|
|
hk_code_multiplier: kood.HkKoodiKordaja,
|
|
coefficient: kood.Koefitsient,
|
|
price: kood.Hind,
|
|
analysis_id: insertedAnalysisId,
|
|
})),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const { error: codesError } = await supabase
|
|
.schema('medreport')
|
|
.from('codes')
|
|
.upsert(codes, { ignoreDuplicates: false });
|
|
|
|
if (codesError?.code) {
|
|
throw new Error(
|
|
`Failed to insert codes (analysis group id: ${analysisGroup.UuringuGruppId})`,
|
|
);
|
|
}
|
|
}
|
|
|
|
export async function syncPublicMessage(
|
|
message?: MedipostPublicMessageResponse | null,
|
|
) {
|
|
const supabase = createCustomClient(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
process.env.SUPABASE_SERVICE_ROLE_KEY!,
|
|
{
|
|
auth: {
|
|
persistSession: false,
|
|
autoRefreshToken: false,
|
|
detectSessionInUrl: false,
|
|
},
|
|
},
|
|
);
|
|
|
|
try {
|
|
const providers = toArray(message?.Saadetis?.Teenused.Teostaja);
|
|
const analysisGroups = providers.flatMap((provider) =>
|
|
toArray(provider.UuringuGrupp),
|
|
);
|
|
if (!message || !analysisGroups.length) {
|
|
return supabase.schema('audit').from('sync_entries').insert({
|
|
operation: 'ANALYSES_SYNC',
|
|
comment: 'No data received',
|
|
status: SyncStatus.Fail,
|
|
changed_by_role: 'service_role',
|
|
});
|
|
}
|
|
|
|
for (const analysisGroup of analysisGroups) {
|
|
await saveAnalysisGroup(analysisGroup, supabase);
|
|
}
|
|
|
|
await supabase.schema('audit').from('sync_entries').insert({
|
|
operation: 'ANALYSES_SYNC',
|
|
status: SyncStatus.Success,
|
|
changed_by_role: 'service_role',
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
await supabase
|
|
.schema('audit')
|
|
.from('sync_entries')
|
|
.insert({
|
|
operation: 'ANALYSES_SYNC',
|
|
status: SyncStatus.Fail,
|
|
comment: JSON.stringify(e),
|
|
changed_by_role: 'service_role',
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function composeOrderXML({
|
|
person,
|
|
orderedAnalysisElementsIds,
|
|
orderedAnalysesIds,
|
|
orderId,
|
|
orderCreatedAt,
|
|
comment,
|
|
}: {
|
|
person: {
|
|
idCode: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
phone: string;
|
|
};
|
|
orderedAnalysisElementsIds: number[];
|
|
orderedAnalysesIds: number[];
|
|
orderId: string;
|
|
orderCreatedAt: Date;
|
|
comment?: string;
|
|
}) {
|
|
const analysisElements = await getAnalysisElementsAdmin({ ids: orderedAnalysisElementsIds });
|
|
if (analysisElements.length !== orderedAnalysisElementsIds.length) {
|
|
throw new Error(`Got ${analysisElements.length} analysis elements, expected ${orderedAnalysisElementsIds.length}`);
|
|
}
|
|
|
|
const analyses = await getAnalyses({ ids: orderedAnalysesIds });
|
|
if (analyses.length !== orderedAnalysesIds.length) {
|
|
throw new Error(`Got ${analyses.length} analyses, expected ${orderedAnalysesIds.length}`);
|
|
}
|
|
|
|
const analysisGroups: Tables<{ schema: 'medreport' }, 'analysis_groups'>[] =
|
|
uniqBy(
|
|
(
|
|
analysisElements?.flatMap(({ analysis_groups }) => analysis_groups) ??
|
|
[]
|
|
).concat(
|
|
analyses?.flatMap(
|
|
({ analysis_elements }) => analysis_elements.analysis_groups,
|
|
) ?? [],
|
|
),
|
|
'id',
|
|
);
|
|
|
|
const specimenSection = [];
|
|
const analysisSection = [];
|
|
let order = 1;
|
|
for (const currentGroup of analysisGroups) {
|
|
let relatedAnalysisElement = analysisElements?.find(
|
|
(element) => element.analysis_groups.id === currentGroup.id,
|
|
);
|
|
const relatedAnalyses = analyses?.filter((analysis) => {
|
|
return analysis.analysis_elements.analysis_groups.id === currentGroup.id;
|
|
});
|
|
|
|
if (!relatedAnalysisElement) {
|
|
relatedAnalysisElement = relatedAnalyses?.find(
|
|
(relatedAnalysis) =>
|
|
relatedAnalysis.analysis_elements.analysis_groups.id ===
|
|
currentGroup.id,
|
|
)?.analysis_elements;
|
|
}
|
|
|
|
if (!relatedAnalysisElement || !relatedAnalysisElement.material_groups) {
|
|
throw new Error(
|
|
`Failed to find related analysis element for group ${currentGroup.name} (id: ${currentGroup.id})`,
|
|
);
|
|
}
|
|
|
|
for (const group of relatedAnalysisElement?.material_groups as MaterjalideGrupp[]) {
|
|
const materials = toArray(group.Materjal);
|
|
const specimenXml = materials.flatMap(
|
|
({ MaterjaliNimi, MaterjaliTyyp, MaterjaliTyypOID, Konteiner }) => {
|
|
return toArray(Konteiner).map((container) =>
|
|
getSpecimen(
|
|
MaterjaliTyypOID,
|
|
MaterjaliTyyp,
|
|
MaterjaliNimi,
|
|
order,
|
|
container.ProovinouKoodOID,
|
|
container.ProovinouKood,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
|
|
specimenSection.push(...specimenXml);
|
|
}
|
|
|
|
const groupXml = getAnalysisGroup(
|
|
currentGroup.original_id,
|
|
currentGroup.name,
|
|
order,
|
|
relatedAnalysisElement,
|
|
);
|
|
order++;
|
|
analysisSection.push(groupXml);
|
|
}
|
|
|
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
<Saadetis xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="TellimusLOINC.xsd">
|
|
${getPais(USER, RECIPIENT, orderCreatedAt, orderId)}
|
|
<Tellimus cito="EI">
|
|
<ValisTellimuseId>${orderId}</ValisTellimuseId>
|
|
<!--<TellijaAsutus>-->
|
|
${getClientInstitution()}
|
|
<!--<TeostajaAsutus>-->
|
|
${getProviderInstitution()}
|
|
<!--<TellijaIsik>-->
|
|
${getClientPerson(person)}
|
|
<TellijaMarkused>${comment ?? ''}</TellijaMarkused>
|
|
${getPatient(person)}
|
|
${getConfidentiality()}
|
|
${specimenSection.join('')}
|
|
${analysisSection?.join('')}
|
|
</Tellimus>
|
|
</Saadetis>`;
|
|
}
|
|
|
|
function getLatestMessage({
|
|
messages,
|
|
excludedMessageIds,
|
|
}: {
|
|
messages?: Message[];
|
|
excludedMessageIds?: string[];
|
|
}) {
|
|
if (!messages?.length) {
|
|
return null;
|
|
}
|
|
|
|
const filtered = messages.filter(({ messageId }) => !excludedMessageIds?.includes(messageId));
|
|
|
|
if (!filtered.length) {
|
|
return null;
|
|
}
|
|
|
|
return filtered.reduce((prev, current) =>
|
|
Number(prev.messageId) > Number(current.messageId) ? prev : current,
|
|
{ messageId: '' } as Message,
|
|
);
|
|
}
|
|
|
|
export async function syncPrivateMessage({
|
|
messageResponse,
|
|
}: {
|
|
messageResponse: MedipostOrderResponse['Saadetis']['Vastus'];
|
|
}) {
|
|
const supabase = getSupabaseServerAdminClient()
|
|
|
|
const status = messageResponse.TellimuseOlek;
|
|
const order = await getOrder({ medusaOrderId: messageResponse.ValisTellimuseId });
|
|
|
|
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 { data: analysisResponse, error } = await supabase
|
|
.schema('medreport')
|
|
.from('analysis_responses')
|
|
.upsert(
|
|
{
|
|
analysis_order_id: order.id,
|
|
order_number: messageResponse.TellimuseNumber,
|
|
order_status: AnalysisOrderStatus[status],
|
|
user_id: analysisOrder[0].user_id,
|
|
},
|
|
{ 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})`,
|
|
);
|
|
}
|
|
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'
|
|
>[] = [];
|
|
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);
|
|
|
|
responses.push(
|
|
...elementAnalysisResponses.map((response) => ({
|
|
analysis_element_original_id: element.UuringId,
|
|
analysis_response_id: analysisResponse[0]!.id,
|
|
norm_lower: response.NormAlum?.['#text'] ?? null,
|
|
norm_lower_included:
|
|
response.NormAlum?.['@_kaasaarvatud'].toLowerCase() === 'jah',
|
|
norm_status: response.NormiStaatus,
|
|
norm_upper: response.NormYlem?.['#text'] ?? null,
|
|
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,
|
|
})),
|
|
);
|
|
}
|
|
}
|
|
|
|
const { error: deleteError } = await supabase
|
|
.schema('medreport')
|
|
.from('analysis_response_elements')
|
|
.delete()
|
|
.eq('analysis_response_id', analysisResponse[0].id);
|
|
|
|
if (deleteError) {
|
|
throw new Error(
|
|
`Failed to clean up response elements for response id ${analysisResponse[0].id}`,
|
|
);
|
|
}
|
|
|
|
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 ${analysisResponse[0].id}`,
|
|
);
|
|
}
|
|
|
|
return AnalysisOrderStatus[status];
|
|
}
|
|
|
|
export async function sendOrderToMedipost({
|
|
medusaOrderId,
|
|
orderedAnalysisElements,
|
|
}: {
|
|
medusaOrderId: string;
|
|
orderedAnalysisElements: { analysisElementId: number }[];
|
|
}) {
|
|
const medreportOrder = await getOrder({ medusaOrderId });
|
|
const account = await getAccountAdmin({ primaryOwnerUserId: medreportOrder.user_id });
|
|
|
|
const orderXml = await composeOrderXML({
|
|
person: {
|
|
idCode: account.personal_code!,
|
|
firstName: account.name ?? '',
|
|
lastName: account.last_name ?? '',
|
|
phone: account.phone ?? '',
|
|
},
|
|
orderedAnalysisElementsIds: orderedAnalysisElements.map(({ analysisElementId }) => analysisElementId),
|
|
orderedAnalysesIds: [],
|
|
orderId: medusaOrderId,
|
|
orderCreatedAt: new Date(medreportOrder.created_at),
|
|
comment: '',
|
|
});
|
|
|
|
await sendPrivateMessage(orderXml);
|
|
}
|
|
|
|
export async function getOrderedAnalysisElementsIds({
|
|
medusaOrder,
|
|
}: {
|
|
medusaOrder: StoreOrder;
|
|
}): Promise<{
|
|
analysisElementId: number;
|
|
}[]> {
|
|
const countryCodes = await listRegions();
|
|
const countryCode = countryCodes[0]!.countries![0]!.iso_2!;
|
|
|
|
function getOrderedAnalysisElements(medusaOrder: StoreOrder) {
|
|
return (medusaOrder?.items ?? [])
|
|
.filter(({ product }) => product?.handle.startsWith(ANALYSIS_ELEMENT_HANDLE_PREFIX))
|
|
.map((line) => {
|
|
const analysisElementId = Number(line.product?.handle?.replace(ANALYSIS_ELEMENT_HANDLE_PREFIX, ''));
|
|
if (Number.isNaN(analysisElementId)) {
|
|
return null;
|
|
}
|
|
return { analysisElementId };
|
|
}) as { analysisElementId: number }[];
|
|
}
|
|
|
|
async function getOrderedAnalysisPackages(medusaOrder: StoreOrder) {
|
|
const orderedPackages = (medusaOrder?.items ?? []).filter(({ product }) => product?.handle.startsWith(ANALYSIS_PACKAGE_HANDLE_PREFIX));
|
|
const orderedPackageIds = orderedPackages.map(({ product }) => product?.id).filter(Boolean) as string[];
|
|
if (orderedPackageIds.length === 0) {
|
|
return [];
|
|
}
|
|
console.info(`Order has ${orderedPackageIds.length} packages`);
|
|
const { response: { products: orderedPackagesProducts } } = await listProducts({
|
|
countryCode,
|
|
queryParams: { limit: 100, id: orderedPackageIds },
|
|
});
|
|
console.info(`Order has ${orderedPackagesProducts.length} packages`);
|
|
if (orderedPackagesProducts.length !== orderedPackageIds.length) {
|
|
throw new Error(`Got ${orderedPackagesProducts.length} ordered packages products, expected ${orderedPackageIds.length}`);
|
|
}
|
|
|
|
const ids = getAnalysisElementMedusaProductIds(orderedPackagesProducts);
|
|
const { response: { products: analysisPackagesProducts } } = await listProducts({
|
|
countryCode,
|
|
queryParams: { limit: 100, id: ids },
|
|
});
|
|
if (analysisPackagesProducts.length !== ids.length) {
|
|
throw new Error(`Got ${analysisPackagesProducts.length} analysis packages products, expected ${ids.length}`);
|
|
}
|
|
|
|
const originalIds = analysisPackagesProducts
|
|
.map(({ metadata }) => metadata?.analysisIdOriginal)
|
|
.filter((id) => typeof id === 'string');
|
|
if (originalIds.length !== ids.length) {
|
|
throw new Error(`Got ${originalIds.length} analysis packages products with analysisIdOriginal, expected ${ids.length}`);
|
|
}
|
|
const analysisElements = await getAnalysisElements({ originalIds });
|
|
|
|
return analysisElements.map(({ id }) => ({ analysisElementId: id }));
|
|
}
|
|
|
|
const analysisPackageElements = await getOrderedAnalysisPackages(medusaOrder);
|
|
const orderedAnalysisElements = getOrderedAnalysisElements(medusaOrder);
|
|
|
|
return [...analysisPackageElements, ...orderedAnalysisElements];
|
|
}
|