prettier fix

This commit is contained in:
Danel Kungla
2025-09-19 17:22:36 +03:00
parent efa94b3322
commit 0c2cfe6d18
509 changed files with 17988 additions and 9920 deletions

View File

@@ -1,7 +1,6 @@
'use server';
import type { Message } from '@/lib/types/medipost';
import { getSupabaseServerAdminClient } from '@/packages/supabase/src/clients/server-admin-client';
export async function getLatestMessage({
@@ -15,14 +14,17 @@ export async function getLatestMessage({
return null;
}
const filtered = messages.filter(({ messageId }) => !excludedMessageIds?.includes(messageId));
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,
return filtered.reduce(
(prev, current) =>
Number(prev.messageId) > Number(current.messageId) ? prev : current,
{ messageId: '' } as Message,
);
}
@@ -38,9 +40,9 @@ export async function upsertMedipostActionLog({
medipostPrivateMessageId,
}: {
action:
| 'send_order_to_medipost'
| 'sync_analysis_results_from_medipost'
| 'send_fake_analysis_results_to_medipost';
| 'send_order_to_medipost'
| 'sync_analysis_results_from_medipost'
| 'send_fake_analysis_results_to_medipost';
xml: string;
hasAnalysisResults?: boolean;
medusaOrderId?: string | null;
@@ -63,10 +65,10 @@ export async function upsertMedipostActionLog({
medipost_external_order_id: medipostExternalOrderId,
medipost_private_message_id: medipostPrivateMessageId,
},
{
onConflict: 'medipost_private_message_id',
ignoreDuplicates: false
}
{
onConflict: 'medipost_private_message_id',
ignoreDuplicates: false,
},
)
.select('id')
.throwOnError();
@@ -74,7 +76,7 @@ export async function upsertMedipostActionLog({
const medipostActionId = data?.[0]?.id;
if (!medipostActionId) {
throw new Error(
`Failed to insert or update medipost action (private message id: ${medipostPrivateMessageId})`
`Failed to insert or update medipost action (private message id: ${medipostPrivateMessageId})`,
);
}

View File

@@ -1,8 +1,16 @@
import { AnalysisResponseElement } from "~/lib/types/analysis-response-element";
import { canCreateAnalysisResponseElement, getAnalysisResponseElementsForGroup } from "./medipostPrivateMessage.service";
import { ResponseUuring } from "@/packages/shared/src/types/medipost-analysis";
import { ResponseUuring } from '@/packages/shared/src/types/medipost-analysis';
type TestExistingElement = Pick<AnalysisResponseElement, 'analysis_element_original_id' | 'status' | 'response_value'>;
import { AnalysisResponseElement } from '~/lib/types/analysis-response-element';
import {
canCreateAnalysisResponseElement,
getAnalysisResponseElementsForGroup,
} from './medipostPrivateMessage.service';
type TestExistingElement = Pick<
AnalysisResponseElement,
'analysis_element_original_id' | 'status' | 'response_value'
>;
describe('medipostPrivateMessage.service', () => {
describe('canCreateAnalysisResponseElement', () => {
@@ -16,11 +24,20 @@ describe('medipostPrivateMessage.service', () => {
} as const;
const responseValue = 1;
const log = jest.fn();
expect(await canCreateAnalysisResponseElement({ existingElements, groupUuring, responseValue, log })).toBe(true);
expect(
await canCreateAnalysisResponseElement({
existingElements,
groupUuring,
responseValue,
log,
}),
).toBe(true);
});
it('should return false if the analysis response element exists and the status is higher', async () => {
const existingElements = [{ analysis_element_original_id: '1', status: '2', response_value: 1 }] as TestExistingElement[];
const existingElements = [
{ analysis_element_original_id: '1', status: '2', response_value: 1 },
] as TestExistingElement[];
const groupUuring = {
UuringuElement: {
UuringOlek: 1,
@@ -29,7 +46,14 @@ describe('medipostPrivateMessage.service', () => {
} as const;
const responseValue = 1;
const log = jest.fn();
expect(await canCreateAnalysisResponseElement({ existingElements, groupUuring, responseValue, log })).toBe(false);
expect(
await canCreateAnalysisResponseElement({
existingElements,
groupUuring,
responseValue,
log,
}),
).toBe(false);
});
});
@@ -49,8 +73,14 @@ describe('medipostPrivateMessage.service', () => {
} as const;
const existingElements = [] as TestExistingElement[];
const log = jest.fn();
expect(await getAnalysisResponseElementsForGroup({ analysisGroup, existingElements, log }))
.toEqual([{
expect(
await getAnalysisResponseElementsForGroup({
analysisGroup,
existingElements,
log,
}),
).toEqual([
{
analysis_element_original_id: '1',
analysis_name: undefined,
comment: null,
@@ -68,7 +98,8 @@ describe('medipostPrivateMessage.service', () => {
UuringuVastus: [{ VastuseVaartus: '1' }],
},
status: '1',
}]);
},
]);
});
it('should return no new element if element already exists in higher status', async () => {
@@ -84,10 +115,17 @@ describe('medipostPrivateMessage.service', () => {
},
] as unknown as ResponseUuring[],
} as const;
const existingElements = [{ analysis_element_original_id: '1', status: '2', response_value: 1 }] as TestExistingElement[];
const existingElements = [
{ analysis_element_original_id: '1', status: '2', response_value: 1 },
] as TestExistingElement[];
const log = jest.fn();
expect(await getAnalysisResponseElementsForGroup({ analysisGroup, existingElements, log }))
.toEqual([]);
expect(
await getAnalysisResponseElementsForGroup({
analysisGroup,
existingElements,
log,
}),
).toEqual([]);
});
it('should return no new element if element already exists with response value', async () => {
@@ -103,10 +141,17 @@ describe('medipostPrivateMessage.service', () => {
},
] as unknown as ResponseUuring[],
} as const;
const existingElements = [{ analysis_element_original_id: '1', status: '1', response_value: 1 }] as TestExistingElement[];
const existingElements = [
{ analysis_element_original_id: '1', status: '1', response_value: 1 },
] as TestExistingElement[];
const log = jest.fn();
expect(await getAnalysisResponseElementsForGroup({ analysisGroup, existingElements, log }))
.toEqual([]);
expect(
await getAnalysisResponseElementsForGroup({
analysisGroup,
existingElements,
log,
}),
).toEqual([]);
});
});
});

View File

@@ -1,36 +1,42 @@
'use server';
import type { PostgrestError } from '@supabase/supabase-js';
import axios from 'axios';
import {
GetMessageListResponse,
MedipostAction,
} from '@/lib/types/medipost';
import { GetMessageListResponse, MedipostAction } from '@/lib/types/medipost';
import { createUserAnalysesApi } from '@/packages/features/user-analyses/src/server/api';
import { AnalysisOrderStatus } from '@/packages/shared/src/types/medipost-analysis';
import type {
ResponseUuringuGrupp,
MedipostOrderResponse,
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 { Tables } from '@kit/supabase/database';
import type { AnalysisOrder } from '~/lib/types/analysis-order';
import type { AnalysisResponseElement } from '~/lib/types/analysis-response-element';
import { createUserAnalysesApi } from '@/packages/features/user-analyses/src/server/api';
import { Tables } from '@kit/supabase/database';
import { getSupabaseServerAdminClient } from '@/packages/supabase/src/clients/server-admin-client';
import { getAnalysisElementsAdmin } from '../analysis-element.service';
import { getAccountAdmin } from '../account.service';
import { getAnalyses } from '../analyses.service';
import { upsertMedipostActionLog, getLatestMessage } from './medipostMessageBase.service';
import { validateMedipostResponse } from './medipostValidate.service';
import { getAnalysisElementsAdmin } from '../analysis-element.service';
import {
getExistingAnalysisResponseElements,
upsertAnalysisResponse,
upsertAnalysisResponseElement,
} from '../analysis-order.service';
import { logMedipostDispatch } from '../audit.service';
import { getAnalysisOrder, updateAnalysisOrderStatus } from '../order.service';
import { parseXML } from '../util/xml.service';
import { composeOrderXML, OrderedAnalysisElement } from './medipostXML.service';
import { getAccountAdmin } from '../account.service';
import { logMedipostDispatch } from '../audit.service';
import { MedipostValidationError } from './MedipostValidationError';
import { upsertAnalysisResponseElement, getExistingAnalysisResponseElements, upsertAnalysisResponse } from '../analysis-order.service';
import {
getLatestMessage,
upsertMedipostActionLog,
} from './medipostMessageBase.service';
import { validateMedipostResponse } from './medipostValidate.service';
import { OrderedAnalysisElement, composeOrderXML } from './medipostXML.service';
const BASE_URL = process.env.MEDIPOST_URL!;
const USER = process.env.MEDIPOST_USER!;
@@ -56,73 +62,102 @@ export async function getLatestPrivateMessageListItem({
throw new Error('Failed to get private message list');
}
return await getLatestMessage({ messages: data?.messages, excludedMessageIds });
return await getLatestMessage({
messages: data?.messages,
excludedMessageIds,
});
}
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 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);
}
};
export async function canCreateAnalysisResponseElement({
existingElements,
groupUuring: {
UuringuElement: {
UuringOlek: status,
UuringId: analysisElementOriginalId,
},
UuringuElement: { UuringOlek: status, UuringId: analysisElementOriginalId },
},
responseValue,
log,
}: {
existingElements: Pick<AnalysisResponseElement, 'analysis_element_original_id' | 'status' | 'response_value'>[];
groupUuring: { UuringuElement: Pick<UuringElement, 'UuringOlek' | 'UuringId'> };
existingElements: Pick<
AnalysisResponseElement,
'analysis_element_original_id' | 'status' | 'response_value'
>[];
groupUuring: {
UuringuElement: Pick<UuringElement, 'UuringOlek' | 'UuringId'>;
};
responseValue: number | null;
log: ReturnType<typeof logger>;
}) {
const existingAnalysisResponseElement = existingElements.find(({ analysis_element_original_id }) => analysis_element_original_id === analysisElementOriginalId);
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}`);
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} already exists for order with response value ${existingAnalysisResponseElement.response_value} but new response has no value`);
log(
`Analysis response element id=${analysisElementOriginalId} already exists for order with response value ${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'>[];
existingElements: Pick<
AnalysisResponseElement,
'analysis_element_original_id' | 'status' | 'response_value'
>[];
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 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'>[] = [];
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}`);
log(
`Group uuring '${analysisGroup.UuringuGruppNimi}' has status ${status}`,
);
for (const response of elementAnalysisResponses) {
const analysisElementOriginalId = groupUuringElement.UuringId;
@@ -135,12 +170,20 @@ export async function getAnalysisResponseElementsForGroup({
return valueAsNumber;
})();
if (!await canCreateAnalysisResponseElement({ existingElements, groupUuring, responseValue, log })) {
if (
!(await canCreateAnalysisResponseElement({
existingElements,
groupUuring,
responseValue,
log,
}))
) {
continue;
}
const mappedResponse = createUserAnalysesApi(getSupabaseServerAdminClient())
.mapUuringVastus({ uuringVastus: response });
const mappedResponse = createUserAnalysesApi(
getSupabaseServerAdminClient(),
).mapUuringVastus({ uuringVastus: response });
results.push({
analysis_element_original_id: analysisElementOriginalId,
@@ -153,7 +196,8 @@ export async function getAnalysisResponseElementsForGroup({
response_value: mappedResponse.responseValue,
unit: groupUuringElement.Mootyhik ?? null,
original_response_element: groupUuringElement,
analysis_name: groupUuringElement.UuringNimi || groupUuringElement.KNimetus,
analysis_name:
groupUuringElement.UuringNimi || groupUuringElement.KNimetus,
comment: groupUuringElement.UuringuKommentaar ?? null,
status: status.toString(),
response_value_is_within_norm: mappedResponse.responseValueIsWithinNorm,
@@ -174,9 +218,14 @@ async function getNewAnalysisResponseElements({
existingElements: AnalysisResponseElement[];
log: ReturnType<typeof logger>;
}) {
const newElements: Omit<AnalysisResponseElement, 'created_at' | 'updated_at' | 'id' | 'analysis_response_id'>[] = [];
const newElements: Omit<
AnalysisResponseElement,
'created_at' | 'updated_at' | 'id' | 'analysis_response_id'
>[] = [];
for (const analysisGroup of analysisGroups) {
log(`[${analysisGroups.indexOf(analysisGroup) + 1}/${analysisGroups.length}] Syncing analysis group '${analysisGroup.UuringuGruppNimi}'`);
log(
`[${analysisGroups.indexOf(analysisGroup) + 1}/${analysisGroups.length}] Syncing analysis group '${analysisGroup.UuringuGruppNimi}'`,
);
const elements = await getAnalysisResponseElementsForGroup({
analysisGroup,
existingElements,
@@ -194,7 +243,9 @@ async function hasAllAnalysisResponseElements({
analysisResponseId: number;
order: Pick<AnalysisOrder, 'analysis_element_ids'>;
}) {
const allOrderResponseElements = await getExistingAnalysisResponseElements({ analysisResponseId });
const allOrderResponseElements = await getExistingAnalysisResponseElements({
analysisResponseId,
});
const expectedOrderResponseElements = order.analysis_element_ids?.length ?? 0;
return allOrderResponseElements.length >= expectedOrderResponseElements;
}
@@ -208,7 +259,10 @@ export async function syncPrivateMessage({
},
order,
}: {
messageResponse: Pick<NonNullable<MedipostOrderResponse['Saadetis']['Vastus']>, 'ValisTellimuseId' | 'TellimuseNumber' | 'TellimuseOlek' | 'UuringuGrupp'>;
messageResponse: Pick<
NonNullable<MedipostOrderResponse['Saadetis']['Vastus']>,
'ValisTellimuseId' | 'TellimuseNumber' | 'TellimuseOlek' | 'UuringuGrupp'
>;
order: Tables<{ schema: 'medreport' }, 'analysis_orders'>;
}) {
const supabase = getSupabaseServerAdminClient();
@@ -232,11 +286,17 @@ export async function syncPrivateMessage({
userId: analysisOrder.user_id,
});
const existingElements = await getExistingAnalysisResponseElements({ analysisResponseId });
const existingElements = await getExistingAnalysisResponseElements({
analysisResponseId,
});
const analysisGroups = toArray(UuringuGrupp);
log(`Order has results for ${analysisGroups.length} analysis groups`);
const newElements = await getNewAnalysisResponseElements({ analysisGroups, existingElements, log });
const newElements = await getNewAnalysisResponseElements({
analysisGroups,
existingElements,
log,
});
for (const element of newElements) {
try {
@@ -247,11 +307,14 @@ export async function syncPrivateMessage({
},
});
} catch (e) {
log(`Failed to create order response element for response id ${analysisResponseId}, element id '${element.analysis_element_original_id}' (order id: ${order.id})`, e as PostgrestError);
log(
`Failed to create order response element for response id ${analysisResponseId}, element id '${element.analysis_element_original_id}' (order id: ${order.id})`,
e as PostgrestError,
);
}
}
return await hasAllAnalysisResponseElements({ analysisResponseId, order })
return (await hasAllAnalysisResponseElements({ analysisResponseId, order }))
? { isCompleted: orderStatus === 'COMPLETED' }
: { isPartial: true };
}
@@ -276,7 +339,9 @@ export async function readPrivateMessageResponse({
let analysisOrderId: number | undefined = undefined;
try {
const privateMessage = await getLatestPrivateMessageListItem({ excludedMessageIds });
const privateMessage = await getLatestPrivateMessageListItem({
excludedMessageIds,
});
messageId = privateMessage?.messageId ?? null;
if (!privateMessage || !messageId) {
@@ -286,17 +351,18 @@ export async function readPrivateMessageResponse({
hasPartialAnalysisResponse: false,
hasFullAnalysisResponse: false,
medusaOrderId: undefined,
analysisOrderId: undefined
analysisOrderId: undefined,
};
}
const { messageId: privateMessageId } = privateMessage;
const { message: privateMessageContent, xml: privateMessageXml } = await getPrivateMessage(
privateMessageId,
);
const { message: privateMessageContent, xml: privateMessageXml } =
await getPrivateMessage(privateMessageId);
const messageResponse = privateMessageContent?.Saadetis?.Vastus;
const medipostExternalOrderId = privateMessageContent?.Saadetis?.Tellimus?.ValisTellimuseId || messageResponse?.ValisTellimuseId;
const medipostExternalOrderId =
privateMessageContent?.Saadetis?.Tellimus?.ValisTellimuseId ||
messageResponse?.ValisTellimuseId;
const patientPersonalCode = messageResponse?.Patsient.Isikukood?.toString();
analysisOrderId = Number(medipostExternalOrderId);
@@ -318,27 +384,36 @@ export async function readPrivateMessageResponse({
hasPartialAnalysisResponse: false,
hasFullAnalysisResponse: false,
medusaOrderId: hasInvalidOrderId ? undefined : medusaOrderId,
analysisOrderId: hasInvalidOrderId ? undefined : analysisOrderId
analysisOrderId: hasInvalidOrderId ? undefined : analysisOrderId,
};
}
let analysisOrder: AnalysisOrder;
try {
analysisOrder = await getAnalysisOrder({ analysisOrderId })
analysisOrder = await getAnalysisOrder({ analysisOrderId });
medusaOrderId = analysisOrder.medusa_order_id;
} catch (e) {
if (IS_ENABLED_DELETE_PRIVATE_MESSAGE) {
await deletePrivateMessage(privateMessageId);
}
throw new Error(`No analysis order found for Medipost message ValisTellimuseId=${medipostExternalOrderId}`);
throw new Error(
`No analysis order found for Medipost message ValisTellimuseId=${medipostExternalOrderId}`,
);
}
const orderPerson = await getAccountAdmin({ primaryOwnerUserId: analysisOrder.user_id });
const orderPerson = await getAccountAdmin({
primaryOwnerUserId: analysisOrder.user_id,
});
if (orderPerson.personal_code !== patientPersonalCode) {
throw new Error(`Order person personal code does not match Medipost message Patsient.Isikukood=${patientPersonalCode}, orderPerson.personal_code=${orderPerson.personal_code}`);
throw new Error(
`Order person personal code does not match Medipost message Patsient.Isikukood=${patientPersonalCode}, orderPerson.personal_code=${orderPerson.personal_code}`,
);
}
const status = await syncPrivateMessage({ messageResponse, order: analysisOrder });
const status = await syncPrivateMessage({
messageResponse,
order: analysisOrder,
});
await upsertMedipostActionLog({
action: 'sync_analysis_results_from_medipost',
@@ -349,11 +424,17 @@ export async function readPrivateMessageResponse({
medipostExternalOrderId,
});
if (status.isPartial) {
await updateAnalysisOrderStatus({ medusaOrderId, orderStatus: 'PARTIAL_ANALYSIS_RESPONSE' });
await updateAnalysisOrderStatus({
medusaOrderId,
orderStatus: 'PARTIAL_ANALYSIS_RESPONSE',
});
hasAnalysisResponse = true;
hasPartialAnalysisResponse = true;
} else if (status.isCompleted) {
await updateAnalysisOrderStatus({ medusaOrderId, orderStatus: 'FULL_ANALYSIS_RESPONSE' });
await updateAnalysisOrderStatus({
medusaOrderId,
orderStatus: 'FULL_ANALYSIS_RESPONSE',
});
if (IS_ENABLED_DELETE_PRIVATE_MESSAGE) {
await deletePrivateMessage(privateMessageId);
}
@@ -361,10 +442,19 @@ export async function readPrivateMessageResponse({
hasFullAnalysisResponse = true;
}
} catch (e) {
console.warn(`Failed to process private message id=${messageId}, message=${(e as Error).message}`);
console.warn(
`Failed to process private message id=${messageId}, message=${(e as Error).message}`,
);
}
return { messageId, hasAnalysisResponse, hasPartialAnalysisResponse, hasFullAnalysisResponse, medusaOrderId, analysisOrderId };
return {
messageId,
hasAnalysisResponse,
hasPartialAnalysisResponse,
hasFullAnalysisResponse,
medusaOrderId,
analysisOrderId,
};
}
export async function deletePrivateMessage(messageId: string) {
@@ -430,7 +520,9 @@ export async function sendOrderToMedipost({
orderedAnalysisElements: OrderedAnalysisElement[];
}) {
const medreportOrder = await getAnalysisOrder({ medusaOrderId });
const account = await getAccountAdmin({ primaryOwnerUserId: medreportOrder.user_id });
const account = await getAccountAdmin({
primaryOwnerUserId: medreportOrder.user_id,
});
const orderedAnalysesIds = orderedAnalysisElements
.map(({ analysisId }) => analysisId)
@@ -441,11 +533,17 @@ export async function sendOrderToMedipost({
const analyses = await getAnalyses({ ids: orderedAnalysesIds });
if (analyses.length !== orderedAnalysesIds.length) {
throw new Error(`Got ${analyses.length} analyses, expected ${orderedAnalysesIds.length}`);
throw new Error(
`Got ${analyses.length} analyses, expected ${orderedAnalysesIds.length}`,
);
}
const analysisElements = await getAnalysisElementsAdmin({ ids: orderedAnalysisElementsIds });
const analysisElements = await getAnalysisElementsAdmin({
ids: orderedAnalysisElementsIds,
});
if (analysisElements.length !== orderedAnalysisElementsIds.length) {
throw new Error(`Got ${analysisElements.length} analysis elements, expected ${orderedAnalysisElementsIds.length}`);
throw new Error(
`Got ${analysisElements.length} analysis elements, expected ${orderedAnalysisElementsIds.length}`,
);
}
const orderXml = await composeOrderXML({

View File

@@ -1,9 +1,6 @@
'use server';
import {
GetMessageListResponse,
MedipostAction,
} from '@/lib/types/medipost';
import { GetMessageListResponse, MedipostAction } from '@/lib/types/medipost';
import axios from 'axios';
import { getLatestMessage } from './medipostMessageBase.service';

View File

@@ -8,14 +8,13 @@ import {
getPatient,
getProviderInstitution,
} from '@/lib/templates/medipost-order';
import {
MedipostAction,
} from '@/lib/types/medipost';
import { MedipostAction } from '@/lib/types/medipost';
import axios from 'axios';
import { formatDate } from 'date-fns';
import { uniqBy } from 'lodash';
import { Tables } from '@kit/supabase/database';
import { formatDate } from 'date-fns';
import { getAnalyses } from '../analyses.service';
import { getAnalysisElementsAdmin } from '../analysis-element.service';
import { validateMedipostResponse } from './medipostValidate.service';
@@ -71,7 +70,9 @@ export async function composeOrderTestResponseXML({
orderId: number;
orderCreatedAt: Date;
}) {
const analysisElements = await getAnalysisElementsAdmin({ ids: orderedAnalysisElementsIds });
const analysisElements = await getAnalysisElementsAdmin({
ids: orderedAnalysisElementsIds,
});
const analyses = await getAnalyses({ ids: orderedAnalysesIds });
const analysisGroups: Tables<{ schema: 'medreport' }, 'analysis_groups'>[] =
@@ -94,13 +95,15 @@ export async function composeOrderTestResponseXML({
const orderNumber = orderId;
const allAnalysisElementsForGroups = analysisElements?.filter((element) => {
return analysisGroups.some((group) => group.id === element.analysis_groups.id);
return analysisGroups.some(
(group) => group.id === element.analysis_groups.id,
);
});
const addedIds = new Set<number>();
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, orderId, "AL")}
${getPais(USER, RECIPIENT, orderId, 'AL')}
<Vastus>
<ValisTellimuseId>${orderId}</ValisTellimuseId>
${getClientInstitution({ index: 1 })}
@@ -126,38 +129,54 @@ export async function composeOrderTestResponseXML({
<TellimuseNumber>${orderNumber}</TellimuseNumber>
<TellimuseOlek>${orderStatus}</TellimuseOlek>
${allAnalysisElementsForGroups.map((analysisElement) => {
const group = analysisGroups.find((group) => group.id === analysisElement.analysis_groups.id);
if (!group) {
throw new Error(`Failed to find group for analysis element ${analysisElement.id}`);
}
${allAnalysisElementsForGroups
.map((analysisElement) => {
const group = analysisGroups.find(
(group) => group.id === analysisElement.analysis_groups.id,
);
if (!group) {
throw new Error(
`Failed to find group for analysis element ${analysisElement.id}`,
);
}
let relatedAnalysisElement = analysisElements?.find(
(element) => element.analysis_groups.id === group.id && !addedIds.has(element.id),
);
const relatedAnalyses = analyses?.filter((analysis) => {
return analysis.analysis_elements.analysis_groups.id === group.id && !addedIds.has(analysis.analysis_elements.id);
});
let relatedAnalysisElement = analysisElements?.find(
(element) =>
element.analysis_groups.id === group.id &&
!addedIds.has(element.id),
);
const relatedAnalyses = analyses?.filter((analysis) => {
return (
analysis.analysis_elements.analysis_groups.id === group.id &&
!addedIds.has(analysis.analysis_elements.id)
);
});
if (!relatedAnalysisElement) {
relatedAnalysisElement = relatedAnalyses?.find(
(relatedAnalysis) =>
relatedAnalysis.analysis_elements.analysis_groups.id ===
group.id,
)?.analysis_elements;
}
if (!relatedAnalysisElement) {
relatedAnalysisElement = relatedAnalyses?.find(
(relatedAnalysis) =>
relatedAnalysis.analysis_elements.analysis_groups.id ===
group.id,
)?.analysis_elements;
}
if (!relatedAnalysisElement || !relatedAnalysisElement.material_groups) {
throw new Error(
`Failed to find related analysis element for group ${group.name} (id: ${group.id})`,
);
}
if (
!relatedAnalysisElement ||
!relatedAnalysisElement.material_groups
) {
throw new Error(
`Failed to find related analysis element for group ${group.name} (id: ${group.id})`,
);
}
const lower = getRandomInt(0, 100);
const upper = getRandomInt(lower + 1, 500);
const result = getRandomInt(lower - Math.floor(lower * 0.1), upper + Math.floor(upper * 0.1));
addedIds.add(relatedAnalysisElement.id);
return (`
const lower = getRandomInt(0, 100);
const upper = getRandomInt(lower + 1, 500);
const result = getRandomInt(
lower - Math.floor(lower * 0.1),
upper + Math.floor(upper * 0.1),
);
addedIds.add(relatedAnalysisElement.id);
return `
<UuringuGrupp>
<UuringuGruppId>${group.original_id}</UuringuGruppId>
<UuringuGruppNimi>${group.name}</UuringuGruppNimi>
@@ -177,15 +196,16 @@ export async function composeOrderTestResponseXML({
<VastuseAeg>${formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss')}</VastuseAeg>
<NormYlem kaasaarvatud=\"EI\">${upper}</NormYlem>
<NormAlum kaasaarvatud=\"EI\">${lower}</NormAlum>
<NormiStaatus>${result < lower ? 1 : (result > upper ? 1 : 0)}</NormiStaatus>
<NormiStaatus>${result < lower ? 1 : result > upper ? 1 : 0}</NormiStaatus>
<ProoviJarjenumber>1</ProoviJarjenumber>
</UuringuVastus>
</UuringuElement>
<UuringuTaitjaAsutuseJnr>2</UuringuTaitjaAsutuseJnr>
</Uuring>
</UuringuGrupp>
`);
}).join('')}
`;
})
.join('')}
</Vastus>
</Saadetis>`;
}

View File

@@ -1,25 +1,26 @@
'use server';
import type {
IMedipostResponseXMLBase,
} from '@/packages/shared/src/types/medipost-analysis';
import type { IMedipostResponseXMLBase } from '@/packages/shared/src/types/medipost-analysis';
import { MedipostValidationError } from './MedipostValidationError';
import { parseXML } from '../util/xml.service';
import { MedipostValidationError } from './MedipostValidationError';
export async function validateMedipostResponse(response: string, { canHaveEmptyCode = false }: { canHaveEmptyCode?: boolean } = {}) {
export async function validateMedipostResponse(
response: string,
{ canHaveEmptyCode = false }: { canHaveEmptyCode?: boolean } = {},
) {
const parsed: IMedipostResponseXMLBase = await parseXML(response);
const code = parsed.ANSWER?.CODE;
if (canHaveEmptyCode) {
if (code && code !== 0) {
console.error("Bad response", response);
console.error('Bad response', response);
throw new MedipostValidationError(response);
}
return;
}
if (typeof code !== 'number' || (code !== 0 && !canHaveEmptyCode)) {
console.error("Bad response", response);
console.error('Bad response', response);
throw new MedipostValidationError(response);
}
}

View File

@@ -11,15 +11,14 @@ import {
getProviderInstitution,
getSpecimen,
} from '@/lib/templates/medipost-order';
import {
MaterjalideGrupp,
} from '@/lib/types/medipost';
import { toArray } from '@kit/shared/utils';
import { MaterjalideGrupp } from '@/lib/types/medipost';
import { uniqBy } from 'lodash';
import { toArray } from '@kit/shared/utils';
import { Tables } from '@kit/supabase/database';
import { AnalysisElement } from '../analysis-element.service';
import { AnalysesWithGroupsAndElements } from '../analyses.service';
import { AnalysisElement } from '../analysis-element.service';
const USER = process.env.MEDIPOST_USER!;
const RECIPIENT = process.env.MEDIPOST_RECIPIENT!;
@@ -27,7 +26,7 @@ const RECIPIENT = process.env.MEDIPOST_RECIPIENT!;
export type OrderedAnalysisElement = {
analysisElementId?: number;
analysisId?: number;
}
};
export async function composeOrderXML({
analyses,
@@ -63,24 +62,32 @@ export async function composeOrderXML({
);
// First, collect all unique materials across all analysis groups
const uniqueMaterials = new Map<string, {
MaterjaliTyypOID: string;
MaterjaliTyyp: string;
MaterjaliNimi: string;
ProovinouKoodOID?: string;
ProovinouKood?: string;
order: number;
}>();
const uniqueMaterials = new Map<
string,
{
MaterjaliTyypOID: string;
MaterjaliTyyp: string;
MaterjaliNimi: string;
ProovinouKoodOID?: string;
ProovinouKood?: string;
order: number;
}
>();
let specimenOrder = 1;
// Collect all materials from all analysis groups
for (const currentGroup of analysisGroups) {
let relatedAnalysisElements = analysisElements?.filter(({ analysis_groups }) => analysis_groups.id === currentGroup.id);
let relatedAnalysisElements = analysisElements?.filter(
({ analysis_groups }) => analysis_groups.id === currentGroup.id,
);
if (!relatedAnalysisElements || relatedAnalysisElements.length === 0) {
relatedAnalysisElements = analyses
.filter(({ analysis_elements }) => analysis_elements.analysis_groups.id === currentGroup.id)
.filter(
({ analysis_elements }) =>
analysis_elements.analysis_groups.id === currentGroup.id,
)
.flatMap(({ analysis_elements }) => analysis_elements);
}
@@ -91,7 +98,9 @@ export async function composeOrderXML({
}
for (const analysisElement of relatedAnalysisElements) {
for (const { Materjal } of analysisElement.material_groups as MaterjalideGrupp[]) {
for (const {
Materjal,
} of analysisElement.material_groups as MaterjalideGrupp[]) {
for (const material of toArray(Materjal)) {
const { MaterjaliTyyp } = material;
@@ -115,7 +124,7 @@ export async function composeOrderXML({
}
// Generate specimen section from unique materials
const specimenSection = Array.from(uniqueMaterials.values()).map(material =>
const specimenSection = Array.from(uniqueMaterials.values()).map((material) =>
getSpecimen(
material.MaterjaliTyypOID,
material.MaterjaliTyyp,
@@ -123,7 +132,7 @@ export async function composeOrderXML({
material.order,
material.ProovinouKoodOID,
material.ProovinouKood,
)
),
);
// Generate analysis section with correct specimen references
@@ -135,7 +144,10 @@ export async function composeOrderXML({
if (!relatedAnalysisElements) {
relatedAnalysisElements = analyses
.filter(({ analysis_elements }) => analysis_elements.analysis_groups.id === currentGroup.id)
.filter(
({ analysis_elements }) =>
analysis_elements.analysis_groups.id === currentGroup.id,
)
.flatMap(({ analysis_elements }) => analysis_elements);
}
@@ -146,8 +158,8 @@ export async function composeOrderXML({
}
const uuringElementInputs: {
analysisElement: Tables<{ schema: 'medreport' }, 'analysis_elements'>,
specimenOrderNr: number,
analysisElement: Tables<{ schema: 'medreport' }, 'analysis_elements'>;
specimenOrderNr: number;
}[] = [];
for (const analysisElement of relatedAnalysisElements) {
for (const group of analysisElement.material_groups as MaterjalideGrupp[]) {
@@ -155,7 +167,9 @@ export async function composeOrderXML({
for (const material of materials) {
const uniqueMaterial = uniqueMaterials.get(material.MaterjaliTyyp);
if (!uniqueMaterial) {
console.info(`Unique material not found for material: ${material.MaterjaliTyyp}, analysis element: ${analysisElement.id} ${analysisElement.analysis_id_original} ${analysisElement.analysis_name_lab}`);
console.info(
`Unique material not found for material: ${material.MaterjaliTyyp}, analysis element: ${analysisElement.id} ${analysisElement.analysis_id_original} ${analysisElement.analysis_name_lab}`,
);
continue;
}
uuringElementInputs.push({
@@ -177,7 +191,7 @@ export async function composeOrderXML({
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, orderId, "OL")}
${getPais(USER, RECIPIENT, orderId, 'OL')}
<Tellimus cito="EI">
<ValisTellimuseId>${orderId}</ValisTellimuseId>
${getClientInstitution()}