'use server'; import { SupabaseClient, createClient as createCustomClient, } from '@supabase/supabase-js'; import { SyncStatus } from '@/lib/types/audit'; import { GetMessageListResponse, MedipostAction, MedipostPublicMessageResponse, UuringuGrupp, } from '@/lib/types/medipost'; import { toArray } from '@/lib/utils'; import axios from 'axios'; import { Tables } from '@kit/supabase/database'; import { createAnalysisGroup } from './analysis-group.service'; import { getAnalysisElements } from './analysis-element.service'; import { getAnalyses } from './analyses.service'; import { StoreOrder } from '@medusajs/types'; import { listProducts } from '@lib/data/products'; import { listRegions } from '@lib/data/regions'; import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product'; import { validateMedipostResponse } from './medipost/medipostValidate.service'; import { parseXML } from './util/xml.service'; import { getLatestMessage } from './medipost/medipostMessageBase.service'; 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_PACKAGE_HANDLE_PREFIX = 'analysis-package-'; 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: RECIPIENT, // 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; } 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>[] = 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 getOrderedAnalysisIds({ medusaOrder, }: { medusaOrder: StoreOrder; }): Promise<{ analysisElementId?: number; analysisId?: number; }[]> { const countryCodes = await listRegions(); const countryCode = countryCodes[0]!.countries![0]!.iso_2!; async function getOrderedAnalysisElements(medusaOrder: StoreOrder) { const originalIds = (medusaOrder?.items ?? []) .map((a) => a.product?.metadata?.analysisIdOriginal) .filter((a) => typeof a === 'string') as string[]; const analysisElements = await getAnalysisElements({ originalIds }); return analysisElements.map(({ id }) => ({ analysisElementId: id })); } async function getOrderedAnalyses(medusaOrder: StoreOrder) { const originalIds = (medusaOrder?.items ?? []) .map((a) => a.product?.metadata?.analysisIdOriginal) .filter((a) => typeof a === 'string') as string[]; const analyses = await getAnalyses({ originalIds }); return analyses.map(({ id }) => ({ analysisId: id })); } 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 = ${JSON.stringify(orderedPackageIds, null, 2)}`); if (orderedPackagesProducts.length !== orderedPackageIds.length) { throw new Error(`Got ${orderedPackagesProducts.length} ordered packages products, expected ${orderedPackageIds.length}`); } const ids = getAnalysisElementMedusaProductIds( orderedPackagesProducts.map(({ id, metadata }) => ({ metadata, variant: orderedPackages.find(({ product }) => product?.id === id)?.variant, })), ); if (ids.length === 0) { return []; } 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, orderedAnalysisElements, orderedAnalyses] = await Promise.all([ getOrderedAnalysisPackages(medusaOrder), getOrderedAnalysisElements(medusaOrder), getOrderedAnalyses(medusaOrder), ]); return [...analysisPackageElements, ...orderedAnalysisElements, ...orderedAnalyses]; }