import axios from 'axios'; import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client'; import { logSyncResult } from '~/lib/services/audit.service'; import { SyncStatus } from '~/lib/types/audit'; import type { ISearchLoadResponse, P_JobTitleTranslation, } from '~/lib/types/connected-online'; function createTranslationMap(translations: P_JobTitleTranslation[]) { const result: Map< number, Map > = new Map(); for (const translation of translations) { const { ClinicID, TextET, TextEN, TextRU, SyncID } = translation; if (!result.has(ClinicID)) { result.set(ClinicID, new Map()); } result.get(ClinicID)!.set(SyncID, { textET: TextET, textEN: TextEN, textRU: TextRU, }); } return result; } function getSpokenLanguages(spokenLanguages?: string) { if (!spokenLanguages || !spokenLanguages.length) return []; return spokenLanguages.split(','); } export default async function syncConnectedOnline() { const isProd = !['test', 'localhost'].some((pathString) => process.env.NEXT_PUBLIC_SITE_URL?.includes(pathString), ); const baseUrl = process.env.CONNECTED_ONLINE_URL; if (!baseUrl) { throw new Error('Could not access all necessary environment variables'); } const supabase = getSupabaseServerAdminClient(); try { const searchLoadResponse = await axios.post<{ d: string }>( `${baseUrl}/Search_Load`, { headers: { 'Content-Type': 'application/json; charset=utf-8', }, param: "{'Value':'|et|-1'}", // get all available services in Estonian }, ); const responseData: ISearchLoadResponse = JSON.parse( searchLoadResponse.data.d, ); if (responseData?.ErrorCode !== 0) { throw new Error('Failed to get Connected Online data'); } if ( !responseData.Data.T_Lic?.length || !responseData.Data.T_Service?.length ) { return supabase.schema('audit').from('sync_entries').insert({ operation: 'CONNECTED_ONLINE_SYNC', comment: 'No clinic or service data received', status: 'FAIL', changed_by_role: 'service_role', }); } // Filter out "Dentas Demo OÜ" in prod or only sync "Dentas Demo OÜ" in any other environment const isDemoClinic = (clinicId: number) => isProd ? clinicId !== 2 : clinicId === 2; const clinics = responseData.Data.T_Lic.filter(({ ID }) => isDemoClinic(ID), ); const services = responseData.Data.T_Service.filter(({ ClinicID }) => isDemoClinic(ClinicID), ); const serviceProviders = responseData.Data.T_Doctor.filter(({ ClinicID }) => isDemoClinic(ClinicID), ); const jobTitleTranslations = createTranslationMap( responseData.Data.P_JobTitleTranslations.filter(({ ClinicID }) => isDemoClinic(ClinicID), ), ); const mappedClinics = clinics.map((clinic) => { return { id: clinic.ID, can_select_worker: !!clinic.OnlineCanSelectWorker, email: clinic.Email || null, name: clinic.Name, personal_code_required: !!clinic.PersonalCodeRequired, phone_number: clinic.Phone || null, key: clinic.Key, address: clinic.Address, }; }); const mappedServices = services.map((service) => { return { id: service.ID, clinic_id: service.ClinicID, sync_id: Number(service.SyncID), name: service.Name, description: service.Description || null, price: service.Price, requires_payment: !!service.RequiresPayment, duration: service.Duration, neto_duration: service.NetoDuration, display: service.Display, price_periods: service.PricePeriods || null, online_hide_duration: service.OnlineHideDuration, online_hide_price: service.OnlineHidePrice, code: service.Code, has_free_codes: !!service.HasFreeCodes, }; }); const mappedServiceProviders = serviceProviders.map((serviceProvider) => { const jobTitleTranslation = serviceProvider.JobTitleID ? jobTitleTranslations .get(serviceProvider.ClinicID) ?.get(serviceProvider.JobTitleID) : null; return { id: serviceProvider.ID, prefix: serviceProvider.Prefix, name: serviceProvider.Name, spoken_languages: getSpokenLanguages(serviceProvider.SpokenLanguages), job_title_et: jobTitleTranslation?.textET, job_title_en: jobTitleTranslation?.textEN, job_title_ru: jobTitleTranslation?.textRU, job_title_id: serviceProvider.JobTitleID, is_deleted: !!serviceProvider.Deleted, clinic_id: serviceProvider.ClinicID, }; }); const { error: providersError } = await supabase .schema('medreport') .from('connected_online_providers') .upsert(mappedClinics); if (providersError) { return logSyncResult({ operation: 'CONNECTED_ONLINE_SYNC', comment: 'Error saving connected online providers: ' + JSON.stringify(providersError), status: SyncStatus.Fail, changed_by_role: 'service_role', }); } const { error: servicesError } = await supabase .schema('medreport') .from('connected_online_services') .upsert(mappedServices, { onConflict: 'id', ignoreDuplicates: false, }); if (servicesError) { return logSyncResult({ operation: 'CONNECTED_ONLINE_SYNC', comment: 'Error saving connected online services: ' + JSON.stringify(servicesError), status: SyncStatus.Fail, changed_by_role: 'service_role', }); } const { error: serviceProvidersError } = await supabase .schema('medreport') .from('connected_online_service_providers') .upsert(mappedServiceProviders, { onConflict: 'id', ignoreDuplicates: false, }); if (serviceProvidersError) { return logSyncResult({ operation: 'CONNECTED_ONLINE_SYNC', comment: 'Error saving service providers: ' + JSON.stringify(serviceProvidersError), status: SyncStatus.Fail, changed_by_role: 'service_role', }); } for (const mappedClinic of mappedClinics) { const defaultLoadResponse = await axios.post<{ d: string }>( `${baseUrl}/Default_Load`, { headers: { 'Content-Type': 'application/json; charset=utf-8', }, param: `{'Value':'${mappedClinic.key}|et'}`, }, ); const defaultLoadResponseData = JSON.parse(defaultLoadResponse.data.d); if (defaultLoadResponseData?.ErrorCode !== 0) { throw new Error('Failed to get Connected Online location data'); } const clinicLocations: { SyncID: number; Address: string; Name: string; }[] = defaultLoadResponseData.Data.T_SelectableLocation; if (clinicLocations?.length) { const mappedLocations = clinicLocations.map( ({ SyncID, Address, Name }) => ({ address: Address, clinic_id: mappedClinic.id, sync_id: SyncID, name: Name, }), ); await supabase .schema('medreport') .from('connected_online_locations') .insert(mappedLocations) .throwOnError(); } } await logSyncResult({ operation: 'CONNECTED_ONLINE_SYNC', status: SyncStatus.Success, changed_by_role: 'service_role', }); } catch (e) { await logSyncResult({ operation: 'CONNECTED_ONLINE_SYNC', status: SyncStatus.Fail, comment: JSON.stringify(e), changed_by_role: 'service_role', }); throw new Error( `Failed to sync Connected Online data, error: ${JSON.stringify(e)}`, ); } }