import axios from 'axios'; import { XMLParser } from 'fast-xml-parser'; import fs from 'fs'; import { createAnalysisGroup, getAnalysisGroups } from '~/lib/services/analysis-group.service'; import { IMedipostPublicMessageDataParsed, IUuringElement } from '~/lib/services/medipost.types'; import { createAnalysis, createNoDataReceivedEntry, createNoNewDataReceivedEntry, createSyncFailEntry, createSyncSuccessEntry } from '~/lib/services/analyses.service'; import { getLastCheckedDate } from '~/lib/services/sync-entries.service'; import { createAnalysisElement, getAnalysisElements } from '~/lib/services/analysis-element.service'; import { createCodes } from '~/lib/services/codes.service'; import { getLatestPublicMessageListItem } from '~/lib/services/medipost.service'; import type { ICode } from '~/lib/types/code'; function toArray(input?: T | T[] | null): T[] { if (!input) return []; return Array.isArray(input) ? input : [input]; } const WRITE_XML_TO_FILE = false as boolean; export default async function syncAnalysisGroups() { const baseUrl = process.env.MEDIPOST_URL; const user = process.env.MEDIPOST_USER; const password = process.env.MEDIPOST_PASSWORD; const sender = process.env.MEDIPOST_MESSAGE_SENDER; if (!baseUrl || !user || !password || !sender) { throw new Error('Could not access all necessary environment variables'); } try { console.info('Getting latest public message id'); const lastCheckedDate = await getLastCheckedDate(); const latestMessage = await getLatestPublicMessageListItem(); if (!latestMessage) { console.info('No new data received'); await createNoNewDataReceivedEntry(); return; } console.info('Getting public message with id: ', latestMessage.messageId); const { data: publicMessageData } = await axios.get(baseUrl, { params: { Action: 'GetPublicMessage', User: user, Password: password, MessageId: latestMessage.messageId, }, headers: { Accept: 'application/xml', }, }); if (WRITE_XML_TO_FILE) { fs.writeFileSync('public-messages-list-response.xml', publicMessageData); } const parser = new XMLParser({ ignoreAttributes: false }); const parsed: IMedipostPublicMessageDataParsed = parser.parse(publicMessageData); if (parsed.ANSWER?.CODE && parsed.ANSWER?.CODE !== 0) { throw new Error( `Failed to get public message (id: ${latestMessage.messageId})`, ); } const existingAnalysisGroups = await getAnalysisGroups(); // SAVE PUBLIC MESSAGE DATA const providers = toArray(parsed?.Saadetis?.Teenused.Teostaja); const analysisGroups = providers.flatMap((provider) => toArray(provider.UuringuGrupp), ); if (!parsed || !analysisGroups.length) { console.info('No analysis groups data received'); await createNoDataReceivedEntry(); return; } const codes: ICode[] = []; const analysesToCreate: { analysisGroupId: number, analyses: IUuringElement[], analysisElementId: number }[] = []; for (const analysisGroup of analysisGroups) { let analysisGroupId: number | undefined; const existingAnalysisGroup = existingAnalysisGroups?.find(({ original_id }) => original_id === analysisGroup.UuringuGruppId); if (existingAnalysisGroup) { console.info(`Analysis group '${analysisGroup.UuringuGruppNimi}' already exists`); analysisGroupId = existingAnalysisGroup.id; } else { // SAVE ANALYSIS GROUP analysisGroupId = await createAnalysisGroup({ id: analysisGroup.UuringuGruppId, name: analysisGroup.UuringuGruppNimi, order: analysisGroup.UuringuGruppJarjekord, }); const analysisGroupCodes = toArray(analysisGroup.Kood); codes.push( ...analysisGroupCodes.map((kood) => ({ hk_code: kood.HkKood, hk_code_multiplier: kood.HkKoodiKordaja, coefficient: kood.Koefitsient, price: kood.Hind, analysis_group_id: analysisGroupId!, analysis_element_id: null, analysis_id: null, })), ); } const analysisGroupItems = toArray(analysisGroup.Uuring); for (const item of analysisGroupItems) { const analysisElement = item.UuringuElement; let insertedAnalysisElementId: number | undefined; const existingAnalysisElement = (await getAnalysisElements({ originalIds: [analysisElement.UuringId] }))?.[0]; if (existingAnalysisElement) { console.info(`Analysis element '${analysisElement.UuringNimi}' already exists`); insertedAnalysisElementId = existingAnalysisElement.id; } else { insertedAnalysisElementId = await createAnalysisElement({ analysisElement, analysisGroupId: analysisGroupId!, materialGroups: toArray(item.MaterjalideGrupp), }); 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_group_id: null, analysis_element_id: insertedAnalysisElementId!, analysis_id: null, })), ); } } const analyses = toArray(analysisElement.UuringuElement); if (analyses?.length && insertedAnalysisElementId) { analysesToCreate.push({ analysisGroupId: analysisGroupId!, analyses, analysisElementId: insertedAnalysisElementId }); } } } for (const { analysisGroupId, analyses, analysisElementId } of analysesToCreate) { for (const analysis of analyses) { const insertedAnalysisId = await createAnalysis(analysis, analysisElementId); if (analysis.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_group_id: null, analysis_element_id: null, analysis_id: insertedAnalysisId, })), ); } } } console.info('Inserting codes'); await createCodes(codes); console.info('Inserting sync entry'); await createSyncSuccessEntry(); } catch (e) { const errorMessage = e instanceof Error ? e.message : String(e); await createSyncFailEntry(JSON.stringify({ message: errorMessage, stack: e instanceof Error ? e.stack : undefined, name: e instanceof Error ? e.name : 'Unknown', }, null, 2)); console.error('Sync failed:', e); throw new Error( `Failed to sync public message data, error: ${errorMessage}`, ); } }