Merge branch 'main' into B2B-34
This commit is contained in:
@@ -9,6 +9,8 @@ MEDIPOST_USER=your-medipost-user
|
||||
MEDIPOST_PASSWORD=your-medipost-password
|
||||
MEDIPOST_RECIPIENT=your-medipost-recipient
|
||||
|
||||
CONNECTED_ONLINE_URL=your-connected-online-url
|
||||
|
||||
EMAIL_SENDER=
|
||||
EMAIL_USER= # refer to your email provider's documentation
|
||||
EMAIL_PASSWORD= # refer to your email provider's documentation
|
||||
|
||||
@@ -25,26 +25,32 @@ async function syncData() {
|
||||
config({ path: `.env.${process.env.NODE_ENV}` });
|
||||
}
|
||||
|
||||
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!;
|
||||
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;
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||
const supabaseServiceRoleKey =
|
||||
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY;
|
||||
|
||||
if (!baseUrl || !user || !password || !sender) {
|
||||
if (
|
||||
!baseUrl ||
|
||||
!supabaseUrl ||
|
||||
!supabaseServiceRoleKey ||
|
||||
!user ||
|
||||
!password ||
|
||||
!sender
|
||||
) {
|
||||
throw new Error('Could not access all necessary environment variables');
|
||||
}
|
||||
|
||||
const supabase = createCustomClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!,
|
||||
{
|
||||
const supabase = createCustomClient(supabaseUrl, supabaseServiceRoleKey, {
|
||||
auth: {
|
||||
persistSession: false,
|
||||
autoRefreshToken: false,
|
||||
detectSessionInUrl: false,
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
try {
|
||||
// GET LATEST PUBLIC MESSAGE ID
|
||||
|
||||
150
jobs/sync-connected-online.ts
Normal file
150
jobs/sync-connected-online.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { createClient as createCustomClient } from '@supabase/supabase-js';
|
||||
|
||||
import axios from 'axios';
|
||||
import { config } from 'dotenv';
|
||||
|
||||
async function syncData() {
|
||||
if (process.env.NODE_ENV === 'local') {
|
||||
config({ path: `.env.${process.env.NODE_ENV}` });
|
||||
}
|
||||
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
|
||||
const baseUrl = process.env.CONNECTED_ONLINE_URL;
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||
const supabaseServiceRoleKey =
|
||||
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY;
|
||||
|
||||
if (!baseUrl || !supabaseUrl || !supabaseServiceRoleKey) {
|
||||
throw new Error('Could not access all necessary environment variables');
|
||||
}
|
||||
|
||||
const supabase = createCustomClient(supabaseUrl, supabaseServiceRoleKey, {
|
||||
auth: {
|
||||
persistSession: false,
|
||||
autoRefreshToken: false,
|
||||
detectSessionInUrl: false,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await axios.post(`${baseUrl}/Search_Load`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
param: "{'Value':'|et|-1'}", // get all available services in Estonian
|
||||
});
|
||||
|
||||
const responseData: {
|
||||
Value: any;
|
||||
Data: any;
|
||||
ErrorCode: number;
|
||||
ErrorMessage: string;
|
||||
} = JSON.parse(response.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',
|
||||
});
|
||||
}
|
||||
|
||||
let clinics;
|
||||
let services;
|
||||
// Filter out "Dentas Demo OÜ" in prod or only sync "Dentas Demo OÜ" in any other environment
|
||||
if (isProd) {
|
||||
clinics = responseData.Data.T_Lic.filter((clinic) => clinic.ID !== 2);
|
||||
services = responseData.Data.T_Service.filter(
|
||||
(service) => service.ClinicID !== 2,
|
||||
);
|
||||
} else {
|
||||
clinics = responseData.Data.T_Lic.filter((clinic) => clinic.ID === 2);
|
||||
services = responseData.Data.T_Service.filter(
|
||||
(service) => service.ClinicID === 2,
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
});
|
||||
|
||||
const mappedServices = services.map((service) => {
|
||||
return {
|
||||
id: service.ID,
|
||||
clinic_id: service.ClinicID,
|
||||
code: service.Code,
|
||||
description: service.Description || null,
|
||||
display: service.Display,
|
||||
duration: service.Duration,
|
||||
has_free_codes: !!service.HasFreeCodes,
|
||||
name: service.Name,
|
||||
neto_duration: service.NetoDuration,
|
||||
online_hide_duration: service.OnlineHideDuration,
|
||||
online_hide_price: service.OnlineHidePrice,
|
||||
price: service.Price,
|
||||
price_periods: service.PricePeriods || null,
|
||||
requires_payment: !!service.RequiresPayment,
|
||||
sync_id: service.SyncID,
|
||||
};
|
||||
});
|
||||
|
||||
const { error: providersError } = await supabase
|
||||
.from('connected_online_providers')
|
||||
.upsert(mappedClinics);
|
||||
|
||||
const { error: servicesError } = await supabase
|
||||
.from('connected_online_services')
|
||||
.upsert(mappedServices, { onConflict: 'id', ignoreDuplicates: false });
|
||||
|
||||
if (providersError || servicesError) {
|
||||
return supabase
|
||||
.schema('audit')
|
||||
.from('sync_entries')
|
||||
.insert({
|
||||
operation: 'CONNECTED_ONLINE_SYNC',
|
||||
comment: providersError
|
||||
? 'Error saving providers: ' + JSON.stringify(providersError)
|
||||
: 'Error saving services: ' + JSON.stringify(servicesError),
|
||||
status: 'FAIL',
|
||||
changed_by_role: 'service_role',
|
||||
});
|
||||
}
|
||||
|
||||
await supabase.schema('audit').from('sync_entries').insert({
|
||||
operation: 'CONNECTED_ONLINE_SYNC',
|
||||
status: 'SUCCESS',
|
||||
changed_by_role: 'service_role',
|
||||
});
|
||||
} catch (e) {
|
||||
await supabase
|
||||
.schema('audit')
|
||||
.from('sync_entries')
|
||||
.insert({
|
||||
operation: 'CONNECTED_ONLINE_SYNC',
|
||||
status: 'FAIL',
|
||||
comment: JSON.stringify(e),
|
||||
changed_by_role: 'service_role',
|
||||
});
|
||||
throw new Error(
|
||||
`Failed to sync Connected Online data, error: ${JSON.stringify(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
syncData();
|
||||
48
lib/services/audit.service.ts
Normal file
48
lib/services/audit.service.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
'use server'
|
||||
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
import { RequestStatus } from '@/lib/types/audit';
|
||||
import { ConnectedOnlineMethodName } from '@/lib/types/connected-online';
|
||||
import { ExternalApi } from '@/lib/types/external';
|
||||
import { MedipostAction } from '@/lib/types/medipost';
|
||||
|
||||
export default async function logRequestResult(
|
||||
/* personalCode: string, */ requestApi: keyof typeof ExternalApi,
|
||||
requestApiMethod: `${ConnectedOnlineMethodName}` | `${MedipostAction}`,
|
||||
status: RequestStatus,
|
||||
comment?: string,
|
||||
startTime?: string,
|
||||
serviceId?: number,
|
||||
serviceProviderId?: number,
|
||||
) {
|
||||
const supabaseServiceUser = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!,
|
||||
{
|
||||
auth: {
|
||||
persistSession: false,
|
||||
autoRefreshToken: false,
|
||||
detectSessionInUrl: false,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const { error } = await supabaseServiceUser
|
||||
.schema('audit')
|
||||
.from('request_entries')
|
||||
.insert({
|
||||
/* personal_code: personalCode, */
|
||||
request_api: requestApi,
|
||||
request_api_method: requestApiMethod,
|
||||
requested_start_date: startTime,
|
||||
status,
|
||||
service_id: serviceId,
|
||||
service_provider_id: serviceProviderId,
|
||||
comment,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error('Failed to insert log entry, error: ' + error.message);
|
||||
}
|
||||
}
|
||||
268
lib/services/connected-online.service.ts
Normal file
268
lib/services/connected-online.service.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
'use server'
|
||||
|
||||
import logRequestResult from '@/lib/services/audit.service';
|
||||
import { RequestStatus } from '@/lib/types/audit';
|
||||
import {
|
||||
AvailableAppointmentsResponse,
|
||||
BookTimeResponse,
|
||||
ConfirmedLoadResponse,
|
||||
ConnectedOnlineMethodName,
|
||||
} from '@/lib/types/connected-online';
|
||||
import { ExternalApi } from '@/lib/types/external';
|
||||
import { Tables } from '@/supabase/database.types';
|
||||
import { createClient } from '@/utils/supabase/server';
|
||||
import axios from 'axios';
|
||||
|
||||
export async function getAvailableAppointmentsForService(
|
||||
serviceId: number,
|
||||
startTime?: Date,
|
||||
) {
|
||||
try {
|
||||
const showTimesFrom = startTime ? { StartTime: startTime } : {};
|
||||
|
||||
const response = await axios.post(
|
||||
`${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.GetAvailabilities}`,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
param: JSON.stringify({
|
||||
ServiceID: serviceId,
|
||||
Key: '7T624nlu',
|
||||
Lang: 'et',
|
||||
...showTimesFrom,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const responseData: AvailableAppointmentsResponse = JSON.parse(
|
||||
response.data.d,
|
||||
);
|
||||
|
||||
if (
|
||||
responseData?.ErrorCode !== 0 ||
|
||||
!responseData.Data.T_Service?.length ||
|
||||
!responseData.Data.T_Booking?.length
|
||||
) {
|
||||
let comment = `Response returned error code ${responseData.ErrorCode}, message: ${responseData.ErrorMessage}`;
|
||||
if (responseData?.ErrorCode === 0) {
|
||||
comment = responseData.Data.T_Service?.length
|
||||
? `No service present in appointment availability response, service id: ${serviceId}, start time: ${startTime}`
|
||||
: `No booking times present in appointment availability response, service id: ${serviceId}, start time: ${startTime}`;
|
||||
}
|
||||
|
||||
await logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.GetAvailabilities,
|
||||
RequestStatus.Fail,
|
||||
comment,
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
await logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.GetAvailabilities,
|
||||
RequestStatus.Success,
|
||||
JSON.stringify(responseData),
|
||||
);
|
||||
return responseData.Data;
|
||||
} catch (error) {
|
||||
await logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.GetAvailabilities,
|
||||
RequestStatus.Fail,
|
||||
JSON.stringify(error),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function bookAppointment(
|
||||
serviceSyncId: number,
|
||||
clinicId: number,
|
||||
appointmentUserId: number,
|
||||
syncUserID: number,
|
||||
startTime: string,
|
||||
locationId = 0,
|
||||
comments = '',
|
||||
isEarlierTimeRequested = false,
|
||||
earlierTimeRequestComment = '',
|
||||
) {
|
||||
const supabase = await createClient();
|
||||
|
||||
try {
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
if (!user?.id) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
|
||||
const [
|
||||
{ data: dbClinic, error: clinicError },
|
||||
{ data: dbService, error: serviceError },
|
||||
] = await Promise.all([
|
||||
supabase
|
||||
.from('connected_online_providers')
|
||||
.select('*')
|
||||
.eq('id', clinicId)
|
||||
.limit(1),
|
||||
supabase
|
||||
.from('connected_online_services')
|
||||
.select('*')
|
||||
.eq('sync_id', serviceSyncId)
|
||||
.eq('clinic_id', clinicId)
|
||||
.limit(1),
|
||||
]);
|
||||
|
||||
if (!dbClinic?.length || !dbService?.length) {
|
||||
return logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.BookTime,
|
||||
RequestStatus.Fail,
|
||||
dbClinic?.length
|
||||
? `Could not find clinic with id ${clinicId}, error: ${JSON.stringify(clinicError)}`
|
||||
: `Could not find service with sync id ${serviceSyncId} and clinic id ${clinicId}, error: ${JSON.stringify(serviceError)}`,
|
||||
startTime,
|
||||
serviceSyncId,
|
||||
clinicId,
|
||||
);
|
||||
}
|
||||
|
||||
const clinic: Tables<'connected_online_providers'> = dbClinic![0];
|
||||
const service: Tables<'connected_online_services'> = dbService![0];
|
||||
|
||||
// TODO the dummy data needs to be replaced with real values once they're present on the user/account
|
||||
const response = await axios.post(
|
||||
`${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.BookTime}`,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
param: JSON.stringify({
|
||||
EarlierTime: isEarlierTimeRequested, // once we have the e-shop functionality we can let the user select if she would like to be offered earlier time slots if they become available
|
||||
EarlierTimeComment: earlierTimeRequestComment,
|
||||
ClinicID: clinic.id,
|
||||
ServiceID: service.id,
|
||||
ClinicServiceID: service.sync_id,
|
||||
UserID: appointmentUserId,
|
||||
SyncUserID: syncUserID,
|
||||
StartTime: startTime,
|
||||
FirstName: 'Test',
|
||||
LastName: 'User',
|
||||
PersonalCode: '4',
|
||||
Email: user.email,
|
||||
Phone: 'phone',
|
||||
Comments: comments,
|
||||
Location: locationId,
|
||||
FreeCode: '',
|
||||
AddToBasket: false,
|
||||
Key: '7T624nlu',
|
||||
Lang: 'et', // update when integrated into app, if needed
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const responseData: BookTimeResponse = JSON.parse(response.data.d);
|
||||
|
||||
if (responseData?.ErrorCode !== 0 || !responseData.Value) {
|
||||
return logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.BookTime,
|
||||
RequestStatus.Fail,
|
||||
JSON.stringify(responseData),
|
||||
startTime,
|
||||
service.id,
|
||||
clinicId,
|
||||
);
|
||||
}
|
||||
|
||||
const responseParts = responseData.Value.split(',');
|
||||
|
||||
const { error } = await supabase
|
||||
.from('connected_online_reservation')
|
||||
.insert({
|
||||
booking_code: responseParts[1],
|
||||
clinic_id: clinic.id,
|
||||
comments,
|
||||
lang: 'et', // change later, if needed
|
||||
service_id: service.id,
|
||||
service_user_id: appointmentUserId,
|
||||
start_time: startTime,
|
||||
sync_user_id: syncUserID,
|
||||
requires_payment: !!responseParts[0],
|
||||
user_id: user.id,
|
||||
});
|
||||
|
||||
await logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.BookTime,
|
||||
RequestStatus.Success,
|
||||
JSON.stringify(responseData),
|
||||
startTime,
|
||||
service.id,
|
||||
clinicId,
|
||||
);
|
||||
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
|
||||
return responseData.Value;
|
||||
} catch (error) {
|
||||
return logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.BookTime,
|
||||
RequestStatus.Fail,
|
||||
JSON.stringify(error),
|
||||
startTime,
|
||||
serviceSyncId,
|
||||
clinicId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getConfirmedService(reservationCode: string) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.ConfirmedLoad}`,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
param: JSON.stringify({ Value: `${reservationCode}|7T624nlu|et` }),
|
||||
},
|
||||
);
|
||||
|
||||
const responseData: ConfirmedLoadResponse = JSON.parse(response.data.d);
|
||||
|
||||
if (responseData?.ErrorCode !== 0) {
|
||||
await logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.ConfirmedLoad,
|
||||
RequestStatus.Fail,
|
||||
JSON.stringify(responseData),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
await logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.ConfirmedLoad,
|
||||
RequestStatus.Success,
|
||||
JSON.stringify(responseData),
|
||||
);
|
||||
return responseData.Data;
|
||||
} catch (error) {
|
||||
await logRequestResult(
|
||||
ExternalApi.ConnectedOnline,
|
||||
ConnectedOnlineMethodName.ConfirmedLoad,
|
||||
RequestStatus.Fail,
|
||||
JSON.stringify(error),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use server';
|
||||
|
||||
import {
|
||||
SupabaseClient,
|
||||
createClient as createCustomClient,
|
||||
|
||||
@@ -2,3 +2,8 @@ export enum SyncStatus {
|
||||
Success = "SUCCESS",
|
||||
Fail = "FAIL",
|
||||
}
|
||||
|
||||
export enum RequestStatus {
|
||||
Success = "SUCCESS",
|
||||
Fail = "FAIL",
|
||||
}
|
||||
|
||||
226
lib/types/connected-online.ts
Normal file
226
lib/types/connected-online.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import * as z from 'zod';
|
||||
|
||||
export const BookTimeResponseSchema = z.object({
|
||||
Value: z.string(),
|
||||
Data: z.null(),
|
||||
ErrorCode: z.number(),
|
||||
ErrorMessage: z.null(),
|
||||
});
|
||||
export type BookTimeResponse = z.infer<typeof BookTimeResponseSchema>;
|
||||
|
||||
export enum ConnectedOnlineMethodName {
|
||||
SearchLoad = 'Search_Load',
|
||||
GetAvailabilities = 'GetAvailabilities',
|
||||
BookTime = 'BookTime',
|
||||
ConfirmedLoad = 'Confirmed_Load',
|
||||
}
|
||||
|
||||
export const AvailableAppointmentTBookingSchema = z.object({
|
||||
ClinicID: z.string(),
|
||||
LocationID: z.number(),
|
||||
UserID: z.number(),
|
||||
SyncUserID: z.number(),
|
||||
ServiceID: z.number(),
|
||||
HKServiceID: z.number(),
|
||||
StartTime: z.coerce.date(),
|
||||
EndTime: z.coerce.date(),
|
||||
PayorCode: z.string(),
|
||||
});
|
||||
export type AvailableAppointment = z.infer<
|
||||
typeof AvailableAppointmentTBookingSchema
|
||||
>;
|
||||
|
||||
export const TDocsASchema = z.object({
|
||||
List: z.string(),
|
||||
});
|
||||
export type TDocsA = z.infer<typeof TDocsASchema>;
|
||||
|
||||
export const TServiceSchema = z.object({
|
||||
Code: z.string(),
|
||||
Name: z.string(),
|
||||
Price: z.number(),
|
||||
VATType: z.number(),
|
||||
HKServiceID: z.union([z.number(), z.null()]),
|
||||
Duration: z.union([z.number(), z.null()]),
|
||||
Description: z.union([z.string(), z.null()]),
|
||||
OnlinePaymentRequired: z.number(),
|
||||
TehikServiceCode: z.union([z.string(), z.null()]),
|
||||
OnlineHideDuration: z.number(),
|
||||
OnlineHidePrice: z.number(),
|
||||
PricePeriods: z.string(),
|
||||
});
|
||||
export type TService = z.infer<typeof TServiceSchema>;
|
||||
|
||||
export const AvailableAppointmentsDataSchema = z.object({
|
||||
T_ScheduleType: z.array(z.any()),
|
||||
T_Service: z.array(TServiceSchema),
|
||||
T_Booking: z.array(AvailableAppointmentTBookingSchema),
|
||||
T_DocsAvailable: z.array(TDocsASchema),
|
||||
T_DocsAll: z.array(TDocsASchema),
|
||||
});
|
||||
export type AvailableAppointmentsData = z.infer<
|
||||
typeof AvailableAppointmentsDataSchema
|
||||
>;
|
||||
|
||||
export const AvailableAppointmentsResponseSchema = z.object({
|
||||
Value: z.null(),
|
||||
Data: AvailableAppointmentsDataSchema,
|
||||
ErrorCode: z.number(),
|
||||
ErrorMessage: z.union([z.string(), z.null()]),
|
||||
});
|
||||
export type AvailableAppointmentsResponse = z.infer<
|
||||
typeof AvailableAppointmentsResponseSchema
|
||||
>;
|
||||
|
||||
export const APaymentRequestSchema = z.object({
|
||||
ID: z.number(),
|
||||
Ref: z.string(),
|
||||
AmountTotal: z.number(),
|
||||
AmountVat: z.number(),
|
||||
PaidAmount: z.union([z.number(), z.null()]),
|
||||
Paid: z.union([z.number(), z.null()]),
|
||||
Failed: z.union([z.number(), z.null()]),
|
||||
PaidAmount1: z.union([z.number(), z.null()]),
|
||||
ScheduleID: z.number(),
|
||||
PaymentID: z.union([z.number(), z.null()]),
|
||||
Created: z.coerce.date(),
|
||||
Nonce: z.string(),
|
||||
DiscountPercent: z.number(),
|
||||
DiscountCode: z.string(),
|
||||
});
|
||||
export type APaymentRequest = z.infer<typeof APaymentRequestSchema>;
|
||||
|
||||
export const PClinicSchema = z.object({
|
||||
ID: z.number(),
|
||||
LicenseID: z.number(),
|
||||
Name: z.string(),
|
||||
Email: z.string(),
|
||||
Address: z.string(),
|
||||
Address2: z.string(),
|
||||
Phone: z.string(),
|
||||
VatNo: z.string(),
|
||||
RegistryCode: z.string(),
|
||||
OID: z.string(),
|
||||
PersonalCodeRequired: z.number(),
|
||||
OnlineCommentRequired: z.number(),
|
||||
OnlineLoginRequired: z.number(),
|
||||
OnlineSenderEmail: z.string(),
|
||||
OnlineReplyToEmail: z.string(),
|
||||
OnlineCCEmail: z.string(),
|
||||
OnlineRedirectkUrl: z.string(), // the typo is on their side
|
||||
OnlineAllowQueue: z.number(),
|
||||
});
|
||||
export type PClinic = z.infer<typeof PClinicSchema>;
|
||||
|
||||
export const PJobTitleTranslationSchema = z.object({
|
||||
ID: z.number(),
|
||||
SyncID: z.number(),
|
||||
TextEN: z.string(),
|
||||
TextET: z.string(),
|
||||
TextFI: z.string(),
|
||||
TextRU: z.string(),
|
||||
TextLT: z.string(),
|
||||
ClinicID: z.number(),
|
||||
Deleted: z.number(),
|
||||
});
|
||||
export type PJobTitleTranslation = z.infer<typeof PJobTitleTranslationSchema>;
|
||||
|
||||
export const PServiceSchema = z.object({
|
||||
ID: z.number(),
|
||||
NameET: z.string(),
|
||||
NameEN: z.string(),
|
||||
NameRU: z.string(),
|
||||
NameFI: z.string(),
|
||||
DescriptionET: z.string(),
|
||||
DescriptionEN: z.string(),
|
||||
DescriptionRU: z.string(),
|
||||
DescriptionFI: z.string(),
|
||||
ExtraEmailTextET: z.string(),
|
||||
ExtraEmailTextEN: z.string(),
|
||||
ExtraEmailTextRU: z.string(),
|
||||
ExtraEmailTextFI: z.string(),
|
||||
});
|
||||
export type PService = z.infer<typeof PServiceSchema>;
|
||||
|
||||
export const ParamSchema = z.object({
|
||||
PersonalCodeRequired: z.number(),
|
||||
OnlineCommentRequired: z.number(),
|
||||
OnlineLoginRequired: z.number(),
|
||||
ClinicName: z.string(),
|
||||
ClinicID: z.number(),
|
||||
OnlineRedirectkUrl: z.string(),
|
||||
OnlineAllowQueue: z.number(),
|
||||
Key: z.string(),
|
||||
});
|
||||
export type Param = z.infer<typeof ParamSchema>;
|
||||
|
||||
export const TBookingSchema = z.object({
|
||||
ID: z.number(),
|
||||
ClinicID: z.number(),
|
||||
FirstName: z.string(),
|
||||
LastName: z.string(),
|
||||
Email: z.string(),
|
||||
Phone: z.string(),
|
||||
PersonalCode: z.string(),
|
||||
Comments: z.string(),
|
||||
ServiceName: z.string(),
|
||||
DoctorName: z.string(),
|
||||
StartTime: z.coerce.date(),
|
||||
EndTime: z.coerce.date(),
|
||||
Status: z.number(),
|
||||
PaymentRequestID: z.number(),
|
||||
MailResponse: z.null(), // was not present in test data, might need to be specified in the future
|
||||
BookingCode: z.string(),
|
||||
LocationName: z.union([z.string(), z.null()]),
|
||||
isDR: z.number(),
|
||||
LocationID: z.number(),
|
||||
ScheduleID: z.number(),
|
||||
ServiceID: z.number(),
|
||||
ServiceSyncID: z.number(),
|
||||
ServiceCode: z.string(),
|
||||
ServiceIsRemote: z.number(),
|
||||
IsRegistered: z.number(),
|
||||
OfferEarlierTime: z.number(),
|
||||
EarlierTimeComment: z.string(),
|
||||
PartnerCode: z.union([z.number(), z.null()]),
|
||||
PartnerCallBackUrl: z.union([z.string(), z.null()]),
|
||||
LocationOfficialName: z.union([z.string(), z.null()]),
|
||||
LocationAddress1: z.union([z.string(), z.null()]),
|
||||
LocationAddress2: z.union([z.string(), z.null()]),
|
||||
LocationPhone: z.union([z.string(), z.null()]),
|
||||
});
|
||||
export type TBooking = z.infer<typeof TBookingSchema>;
|
||||
|
||||
export const TDoctorSchema = z.object({
|
||||
ID: z.number(),
|
||||
Name: z.string(),
|
||||
Prefix: z.string(),
|
||||
Photo: z.string(),
|
||||
SpokenLanguages: z.string(),
|
||||
JobTitleID: z.union([z.number(), z.null()]),
|
||||
ClinicID: z.number(),
|
||||
Deleted: z.number(),
|
||||
});
|
||||
export type TDoctor = z.infer<typeof TDoctorSchema>;
|
||||
|
||||
export const ConfirmedLoadDataSchema = z.object({
|
||||
T_Booking: z.array(TBookingSchema),
|
||||
P_Location: z.array(z.any()),
|
||||
P_Clinic: z.array(PClinicSchema),
|
||||
A_PaymentRequest: z.array(APaymentRequestSchema),
|
||||
P_Service: z.array(PServiceSchema),
|
||||
T_Service: z.array(z.any()),
|
||||
T_ServiceHK: z.array(z.any()),
|
||||
P_JobTitleTranslations: z.array(PJobTitleTranslationSchema),
|
||||
T_Doctor: z.array(TDoctorSchema),
|
||||
Params: z.array(ParamSchema),
|
||||
});
|
||||
export type ConfirmedLoadData = z.infer<typeof ConfirmedLoadDataSchema>;
|
||||
|
||||
export const ConfirmedLoadResponseSchema = z.object({
|
||||
Value: z.null(),
|
||||
Data: ConfirmedLoadDataSchema,
|
||||
ErrorCode: z.number(),
|
||||
ErrorMessage: z.union([z.string(), z.null()]),
|
||||
});
|
||||
export type ConfirmedLoadResponse = z.infer<typeof ConfirmedLoadResponseSchema>;
|
||||
4
lib/types/external.ts
Normal file
4
lib/types/external.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum ExternalApi {
|
||||
Medipost = 'Medipost',
|
||||
ConnectedOnline = 'ConnectedOnline',
|
||||
}
|
||||
25
package.json
25
package.json
@@ -19,19 +19,20 @@
|
||||
"supabase": "supabase",
|
||||
"supabase:start": "supabase status || supabase start",
|
||||
"supabase:stop": "supabase stop",
|
||||
"supabase:reset": "supabase db reset",
|
||||
"supabase:status": "supabase status",
|
||||
"supabase:test": "supabase db test",
|
||||
"supabase:db:reset": "supabase db reset",
|
||||
"supabase:db:lint": "supabase db lint",
|
||||
"supabase:db:diff": "supabase db diff",
|
||||
"supabase:deploy": "supabase link --project-ref $SUPABASE_PROJECT_REF && supabase db push",
|
||||
"supabase:typegen": "supabase gen types typescript --local > ./packages/supabase/src/database.types.ts",
|
||||
"supabase:db:dump:local": "supabase db dump --local --data-only",
|
||||
"sync-data:dev": "NODE_ENV=local ts-node jobs/sync-analysis-groups.ts"
|
||||
"sync-analysis-groups:dev": "NODE_ENV=local ts-node jobs/sync-analysis-groups.ts",
|
||||
"sync-connected-online:dev": "NODE_ENV=local ts-node jobs/sync-connected-online.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@edge-csrf/nextjs": "2.5.3-cloudflare-rc1",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@hookform/resolvers": "^5.1.1",
|
||||
"@kit/accounts": "workspace:*",
|
||||
"@kit/admin": "workspace:*",
|
||||
"@kit/analytics": "workspace:*",
|
||||
@@ -59,7 +60,7 @@
|
||||
"@supabase/supabase-js": "2.49.4",
|
||||
"@tanstack/react-query": "5.76.1",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"axios": "^1.9.0",
|
||||
"axios": "^1.10.0",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"fast-xml-parser": "^5.2.5",
|
||||
@@ -70,13 +71,13 @@
|
||||
"next-themes": "0.4.6",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-hook-form": "^7.56.3",
|
||||
"react-i18next": "^15.5.1",
|
||||
"react-hook-form": "^7.58.0",
|
||||
"react-i18next": "^15.5.3",
|
||||
"recharts": "2.15.3",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"sonner": "^2.0.5",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"zod": "^3.24.4"
|
||||
"zod": "^3.25.67"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
@@ -84,9 +85,9 @@
|
||||
"@kit/prettier-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@next/bundle-analyzer": "15.3.2",
|
||||
"@tailwindcss/postcss": "^4.1.7",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/lodash": "^4.17.17",
|
||||
"@types/node": "^22.15.18",
|
||||
"@types/node": "^22.15.32",
|
||||
"@types/react": "19.1.4",
|
||||
"@types/react-dom": "19.1.5",
|
||||
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
||||
@@ -95,7 +96,7 @@
|
||||
"pino-pretty": "^13.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"react-hook-form": "^7.57.0",
|
||||
"supabase": "^2.22.12",
|
||||
"supabase": "^2.26.9",
|
||||
"tailwindcss": "4.1.7",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5.8.3",
|
||||
|
||||
574
pnpm-lock.yaml
generated
574
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
||||
create table "public"."connected_online_providers" (
|
||||
"id" bigint not null,
|
||||
"name" text not null,
|
||||
"email" text,
|
||||
"phone_number" text,
|
||||
"can_select_worker" boolean not null,
|
||||
"personal_code_required" boolean not null,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp without time zone default now()
|
||||
);
|
||||
|
||||
|
||||
alter table "public"."connected_online_providers" enable row level security;
|
||||
|
||||
create table "public"."connected_online_services" (
|
||||
"id" bigint not null,
|
||||
"clinic_id" bigint not null,
|
||||
"sync_id" bigint not null,
|
||||
"name" text not null,
|
||||
"description" text,
|
||||
"price" double precision not null,
|
||||
"requires_payment" boolean not null,
|
||||
"duration" bigint not null,
|
||||
"neto_duration" bigint,
|
||||
"display" text,
|
||||
"price_periods" text,
|
||||
"online_hide_duration" bigint,
|
||||
"online_hide_price" bigint,
|
||||
"code" text not null,
|
||||
"has_free_codes" boolean not null,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp with time zone default now()
|
||||
);
|
||||
|
||||
|
||||
alter table "public"."connected_online_services" enable row level security;
|
||||
|
||||
CREATE UNIQUE INDEX connected_online_providers_id_key ON public.connected_online_providers USING btree (id);
|
||||
|
||||
CREATE UNIQUE INDEX connected_online_providers_pkey ON public.connected_online_providers USING btree (id);
|
||||
|
||||
CREATE UNIQUE INDEX connected_online_services_id_key ON public.connected_online_services USING btree (id);
|
||||
|
||||
CREATE UNIQUE INDEX connected_online_services_pkey ON public.connected_online_services USING btree (id);
|
||||
|
||||
alter table "public"."connected_online_providers" add constraint "connected_online_providers_pkey" PRIMARY KEY using index "connected_online_providers_pkey";
|
||||
|
||||
alter table "public"."connected_online_services" add constraint "connected_online_services_pkey" PRIMARY KEY using index "connected_online_services_pkey";
|
||||
|
||||
alter table "public"."connected_online_providers" add constraint "connected_online_providers_id_key" UNIQUE using index "connected_online_providers_id_key";
|
||||
|
||||
alter table "public"."connected_online_services" add constraint "connected_online_services_clinic_id_fkey" FOREIGN KEY (clinic_id) REFERENCES connected_online_providers(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "public"."connected_online_services" validate constraint "connected_online_services_clinic_id_fkey";
|
||||
|
||||
alter table "public"."connected_online_services" add constraint "connected_online_services_id_key" UNIQUE using index "connected_online_services_id_key";
|
||||
|
||||
grant delete on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
grant insert on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
grant references on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
grant select on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
grant trigger on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
grant truncate on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
grant update on table "public"."connected_online_providers" to "service_role";
|
||||
|
||||
grant select on table "public"."connected_online_providers" to "authenticated";
|
||||
|
||||
grant delete on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
grant insert on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
grant references on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
grant select on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
grant trigger on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
grant truncate on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
grant update on table "public"."connected_online_services" to "service_role";
|
||||
|
||||
grant select on table "public"."connected_online_services" to "authenticated";
|
||||
|
||||
create type "audit"."request_status" as enum ('SUCCESS', 'FAIL');
|
||||
|
||||
create table "audit"."request_entries" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"personal_code" bigint,
|
||||
"request_api" text not null,
|
||||
"request_api_method" text not null,
|
||||
"status" audit.request_status not null,
|
||||
"comment" text,
|
||||
"service_provider_id" bigint,
|
||||
"service_id" bigint,
|
||||
"requested_start_date" timestamp with time zone,
|
||||
"requested_end_date" timestamp with time zone,
|
||||
"created_at" timestamp with time zone not null default now()
|
||||
);
|
||||
|
||||
|
||||
alter table "audit"."request_entries" enable row level security;
|
||||
|
||||
CREATE UNIQUE INDEX request_entries_pkey ON audit.request_entries USING btree (id);
|
||||
|
||||
alter table "audit"."request_entries" add constraint "request_entries_pkey" PRIMARY KEY using index "request_entries_pkey";
|
||||
|
||||
grant delete on table "audit"."request_entries" to "service_role";
|
||||
|
||||
grant insert on table "audit"."request_entries" to "service_role";
|
||||
|
||||
grant references on table "audit"."request_entries" to "service_role";
|
||||
|
||||
grant select on table "audit"."request_entries" to "service_role";
|
||||
|
||||
grant trigger on table "audit"."request_entries" to "service_role";
|
||||
|
||||
grant truncate on table "audit"."request_entries" to "service_role";
|
||||
|
||||
grant update on table "audit"."request_entries" to "service_role";
|
||||
|
||||
create policy "service_role_all"
|
||||
on "audit"."request_entries"
|
||||
as permissive
|
||||
for all
|
||||
to service_role
|
||||
using (true);
|
||||
|
||||
create table "public"."connected_online_reservation" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"user_id" uuid not null,
|
||||
"booking_code" text not null,
|
||||
"service_id" bigint not null,
|
||||
"clinic_id" bigint not null,
|
||||
"service_user_id" bigint,
|
||||
"sync_user_id" bigint not null,
|
||||
"requires_payment" boolean not null,
|
||||
"comments" text,
|
||||
"start_time" timestamp with time zone not null,
|
||||
"lang" text not null,
|
||||
"discount_code" text,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp with time zone default now()
|
||||
);
|
||||
|
||||
|
||||
alter table "public"."connected_online_reservation" enable row level security;
|
||||
|
||||
CREATE UNIQUE INDEX connected_online_reservation_booking_code_key ON public.connected_online_reservation USING btree (booking_code);
|
||||
|
||||
CREATE UNIQUE INDEX connected_online_reservation_pkey ON public.connected_online_reservation USING btree (id);
|
||||
|
||||
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_pkey" PRIMARY KEY using index "connected_online_reservation_pkey";
|
||||
|
||||
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_booking_code_key" UNIQUE using index "connected_online_reservation_booking_code_key";
|
||||
|
||||
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "public"."connected_online_reservation" validate constraint "connected_online_reservation_user_id_fkey";
|
||||
|
||||
grant delete on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
grant insert on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
grant references on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
grant select on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
grant trigger on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
grant truncate on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
grant update on table "public"."connected_online_reservation" to "service_role";
|
||||
|
||||
create policy "service_role_all"
|
||||
on "public"."connected_online_reservation"
|
||||
as permissive
|
||||
for all
|
||||
to service_role
|
||||
using (true);
|
||||
|
||||
|
||||
CREATE TRIGGER connected_online_providers_change_record_timestamps AFTER INSERT OR UPDATE ON public.connected_online_providers FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamps();
|
||||
|
||||
CREATE TRIGGER connected_online_services_change_record_timestamps AFTER INSERT OR UPDATE ON public.connected_online_services FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamps();
|
||||
|
||||
create policy "service_role_all"
|
||||
on "public"."connected_online_providers"
|
||||
as permissive
|
||||
for all
|
||||
to service_role
|
||||
using (true);
|
||||
|
||||
|
||||
create policy "service_role_all"
|
||||
on "public"."connected_online_services"
|
||||
as permissive
|
||||
for all
|
||||
to service_role
|
||||
using (true);
|
||||
|
||||
create policy "authenticated_select"
|
||||
on "public"."connected_online_providers"
|
||||
as permissive
|
||||
for select
|
||||
to authenticated
|
||||
using (true);
|
||||
|
||||
create policy "authenticated_select"
|
||||
on "public"."connected_online_services"
|
||||
as permissive
|
||||
for select
|
||||
to authenticated
|
||||
using (true);
|
||||
|
||||
|
||||
create policy "own_all"
|
||||
on "public"."connected_online_reservation"
|
||||
as permissive
|
||||
for all
|
||||
to authenticated
|
||||
using ((( SELECT auth.uid() AS uid) = user_id));
|
||||
@@ -0,0 +1,225 @@
|
||||
|
||||
create table "public"."medreport_product_groups" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"name" text not null,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp with time zone
|
||||
);
|
||||
|
||||
create table "public"."medreport_products" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"name" text not null,
|
||||
"product_group_id" bigint,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"updated_at" timestamp with time zone default now()
|
||||
);
|
||||
|
||||
|
||||
alter table "public"."medreport_products" enable row level security;
|
||||
|
||||
create table "public"."medreport_products_analyses_relations" (
|
||||
"product_id" bigint not null,
|
||||
"analysis_element_id" bigint,
|
||||
"analysis_id" bigint
|
||||
);
|
||||
|
||||
alter table "public"."medreport_product_groups" enable row level security;
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" enable row level security;
|
||||
|
||||
CREATE UNIQUE INDEX medreport_product_groups_name_key ON public.medreport_product_groups USING btree (name);
|
||||
|
||||
CREATE UNIQUE INDEX medreport_product_groups_pkey ON public.medreport_product_groups USING btree (id);
|
||||
|
||||
alter table "public"."medreport_product_groups" add constraint "medreport_product_groups_pkey" PRIMARY KEY using index "medreport_product_groups_pkey";
|
||||
|
||||
alter table "public"."medreport_product_groups" add constraint "medreport_product_groups_name_key" UNIQUE using index "medreport_product_groups_name_key";
|
||||
|
||||
alter table "public"."medreport_products" add constraint "medreport_products_product_groups_id_fkey" FOREIGN KEY (product_group_id) REFERENCES medreport_product_groups(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "public"."medreport_products" validate constraint "medreport_products_product_groups_id_fkey";
|
||||
|
||||
grant select on table "public"."medreport_product_groups" to "anon";
|
||||
|
||||
grant select on table "public"."medreport_product_groups" to "authenticated";
|
||||
|
||||
grant delete on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
grant insert on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
grant references on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
grant select on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
grant trigger on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
grant truncate on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
grant update on table "public"."medreport_product_groups" to "service_role";
|
||||
|
||||
CREATE UNIQUE INDEX medreport_products_analyses_analysis_element_id_key ON public.medreport_products_analyses_relations USING btree (analysis_element_id);
|
||||
|
||||
CREATE UNIQUE INDEX medreport_products_analyses_analysis_id_key ON public.medreport_products_analyses_relations USING btree (analysis_id);
|
||||
|
||||
CREATE UNIQUE INDEX medreport_products_analyses_pkey ON public.medreport_products_analyses_relations USING btree (product_id);
|
||||
|
||||
CREATE UNIQUE INDEX medreport_products_name_key ON public.medreport_products USING btree (name);
|
||||
|
||||
CREATE UNIQUE INDEX medreport_products_pkey ON public.medreport_products USING btree (id);
|
||||
|
||||
alter table "public"."medreport_products" add constraint "medreport_products_pkey" PRIMARY KEY using index "medreport_products_pkey";
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_pkey" PRIMARY KEY using index "medreport_products_analyses_pkey";
|
||||
|
||||
alter table "public"."medreport_products" add constraint "medreport_products_name_key" UNIQUE using index "medreport_products_name_key";
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_element_id_fkey" FOREIGN KEY (analysis_element_id) REFERENCES analysis_elements(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_analysis_element_id_fkey";
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_element_id_key" UNIQUE using index "medreport_products_analyses_analysis_element_id_key";
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_id_fkey" FOREIGN KEY (analysis_id) REFERENCES analyses(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_analysis_id_fkey";
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_analysis_id_key" UNIQUE using index "medreport_products_analyses_analysis_id_key";
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "medreport_products_analyses_product_id_fkey" FOREIGN KEY (product_id) REFERENCES medreport_products(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" validate constraint "medreport_products_analyses_product_id_fkey";
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" add constraint "product_can_be_tied_to_only_one_external_item" CHECK (((analysis_id IS NULL) OR (analysis_element_id IS NULL))) not valid;
|
||||
|
||||
alter table "public"."medreport_products_analyses_relations" validate constraint "product_can_be_tied_to_only_one_external_item";
|
||||
|
||||
grant select on table "public"."medreport_products" to "anon";
|
||||
|
||||
grant select on table "public"."medreport_products" to "authenticated";
|
||||
|
||||
grant delete on table "public"."medreport_products" to "service_role";
|
||||
|
||||
grant insert on table "public"."medreport_products" to "service_role";
|
||||
|
||||
grant references on table "public"."medreport_products" to "service_role";
|
||||
|
||||
grant select on table "public"."medreport_products" to "service_role";
|
||||
|
||||
grant trigger on table "public"."medreport_products" to "service_role";
|
||||
|
||||
grant truncate on table "public"."medreport_products" to "service_role";
|
||||
|
||||
grant update on table "public"."medreport_products" to "service_role";
|
||||
|
||||
grant select on table "public"."medreport_products_analyses_relations" to "anon";
|
||||
|
||||
grant select on table "public"."medreport_products_analyses_relations" to "authenticated";
|
||||
|
||||
grant delete on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
grant insert on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
grant references on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
grant select on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
grant trigger on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
grant truncate on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
grant update on table "public"."medreport_products_analyses_relations" to "service_role";
|
||||
|
||||
create policy "Enable read access for all users"
|
||||
on "public"."medreport_products_analyses_relations"
|
||||
as permissive
|
||||
for select
|
||||
to public
|
||||
using (true);
|
||||
|
||||
|
||||
ALTER TABLE medreport_products_analyses_relations
|
||||
ADD CONSTRAINT product_can_be_tied_to_only_one_analysis_item
|
||||
CHECK (analysis_id IS NULL OR analysis_element_id IS NULL);
|
||||
|
||||
|
||||
create table "public"."medreport_products_external_services_relations" (
|
||||
"product_id" bigint not null,
|
||||
"connected_online_service_id" bigint not null
|
||||
);
|
||||
|
||||
alter table "public"."medreport_products_external_services_relations" enable row level security;
|
||||
|
||||
CREATE UNIQUE INDEX medreport_products_connected_online_services_id_key ON public.medreport_products_external_services_relations USING btree (connected_online_service_id);
|
||||
|
||||
CREATE UNIQUE INDEX medreport_products_connected_online_services_pkey ON public.medreport_products_external_services_relations USING btree (connected_online_service_id);
|
||||
|
||||
alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_pkey" PRIMARY KEY using index "medreport_products_connected_online_services_pkey";
|
||||
|
||||
alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_id_fkey" FOREIGN KEY (connected_online_service_id) REFERENCES connected_online_services(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "public"."medreport_products_external_services_relations" validate constraint "medreport_products_connected_online_services_id_fkey";
|
||||
|
||||
alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_id_key" UNIQUE using index "medreport_products_connected_online_services_id_key";
|
||||
|
||||
alter table "public"."medreport_products_external_services_relations" add constraint "medreport_products_connected_online_services_product_id_fkey" FOREIGN KEY (product_id) REFERENCES medreport_products(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
alter table "public"."medreport_products_external_services_relations" validate constraint "medreport_products_connected_online_services_product_id_fkey";
|
||||
|
||||
grant select on table "public"."medreport_products_external_services_relations" to "anon";
|
||||
|
||||
grant select on table "public"."medreport_products_external_services_relations" to "authenticated";
|
||||
|
||||
grant delete on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
grant insert on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
grant references on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
grant select on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
grant trigger on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
grant truncate on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
grant update on table "public"."medreport_products_external_services_relations" to "service_role";
|
||||
|
||||
CREATE OR REPLACE FUNCTION check_tied_to_connected_online()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM medreport_products_external_services_relations
|
||||
WHERE product_id = NEW.product_id
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Value "%" already exists in medreport_products_external_services_relations', NEW.product_id;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION check_tied_to_analysis_item()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM medreport_products_analyses_relations
|
||||
WHERE product_id = NEW.product_id
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Value "%" already exists in medreport_products_analyses_relations', NEW.product_id;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER check_not_already_tied_to_connected_online BEFORE INSERT OR UPDATE ON public.medreport_products_analyses_relations FOR EACH ROW EXECUTE FUNCTION check_tied_to_connected_online();
|
||||
|
||||
CREATE TRIGGER check_not_already_tied_to_analysis BEFORE INSERT OR UPDATE ON public.medreport_products_external_services_relations FOR EACH ROW EXECUTE FUNCTION check_tied_to_analysis_item();
|
||||
|
||||
create policy "read_all"
|
||||
on "public"."medreport_product_groups"
|
||||
as permissive
|
||||
for select
|
||||
to public
|
||||
using (true);
|
||||
Reference in New Issue
Block a user