import { GetMessageListResponse, MedipostAction, MedipostPublicMessageResponse, Message, UuringuGrupp, } from "@/lib/types/medipost"; import { Tables } from "@/supabase/database.types"; import { createClient, SupabaseClient } from "@supabase/supabase-js"; import axios from "axios"; import { XMLParser } from "fast-xml-parser"; import { SyncStatus } from "@/lib/types/audit"; import { toArray } from "@/lib/utils"; const BASE_URL = process.env.MEDIPOST_URL!; const USER = process.env.MEDIPOST_USER!; const PASSWORD = process.env.MEDIPOST_PASSWORD!; export async function getMessages() { try { const publicMessage = await getLatestPublicMessageListItem(); if (!publicMessage) { return null; } //Teenused tuleb mappida kokku MedReport teenustega. alusel return getPublicMessage(publicMessage.messageId); } catch (error) { console.error(error); } } export async function getLatestPublicMessageListItem() { const { data } = await axios.get(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(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", }, }); const parser = new XMLParser({ ignoreAttributes: false }); const parsed: MedipostPublicMessageResponse = parser.parse(data); if (parsed.ANSWER?.CODE && parsed.ANSWER?.CODE !== 0) { throw new Error(`Failed to get public message (id: ${messageId})`); } return parsed; } export async function sendPrivateMessage(messageXml: string, receiver: string) { const body = new FormData(); body.append("Action", MedipostAction.SendPrivateMessage); body.append("User", USER); body.append("Password", PASSWORD); body.append("Receiver", receiver); body.append("MessageType", "Tellimus"); body.append( "Message", new Blob([messageXml], { type: "text/xml; charset=UTF-8", }) ); const { data } = await axios.post(BASE_URL, body); if (data.code && data.code !== 0) { throw new Error(`Failed to send private message`); } } export async function getLatestPrivateMessageListItem() { const { data } = await axios.get(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(data?.messages); } 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", }, }); if (data.code && data.code !== 0) { throw new Error(`Failed to get private message (id: ${messageId})`); } const parser = new XMLParser({ ignoreAttributes: false }); return parser.parse(data); } 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() { try { const privateMessage = await getLatestPrivateMessageListItem(); if (!privateMessage) { return null; } const privateMessageContent = await getPrivateMessage( privateMessage.messageId ); if (privateMessageContent) { // to be implemented: map and save, only delete if successful await deletePrivateMessage(privateMessage.messageId); } } catch (e) { console.error(e); } } async function saveAnalysisGroup( analysisGroup: UuringuGrupp, supabase: SupabaseClient ) { const { data: insertedAnalysisGroup, error } = await supabase .from("analysis_groups") .upsert( { original_id: analysisGroup.UuringuGruppId, name: analysisGroup.UuringuGruppNimi, order: analysisGroup.UuringuGruppJarjekord, }, { onConflict: "original_id", ignoreDuplicates: false } ) .select("id"); if (error || !insertedAnalysisGroup[0]?.id) { throw new Error( `Failed to insert analysis group (id: ${analysisGroup.UuringuGruppId}), error: ${error?.message}` ); } const analysisGroupId = insertedAnalysisGroup[0].id; const analysisGroupCodes = toArray(analysisGroup.Kood); const codes: Partial>[] = 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 .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 .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 .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 = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_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", }); } } function getLatestMessage(messages?: Message[]) { if (!messages?.length) { return null; } return messages.reduce((prev, current) => Number(prev.messageId) > Number(current.messageId) ? prev : current ); }