Merge branch 'main' into B2B-88
This commit is contained in:
@@ -4,6 +4,7 @@ NEXT_PUBLIC_SUPABASE_URL=your-project-url
|
|||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||||
NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
|
NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
|
||||||
|
|
||||||
MEDIPOST_URL=your-medpost-url
|
MEDIPOST_URL=your-medipost-url
|
||||||
MEDIPOST_USER=your-medpost-user
|
MEDIPOST_USER=your-medipost-user
|
||||||
MEDIPOST_PASSWORD=your-medpost-password
|
MEDIPOST_PASSWORD=your-medipost-password
|
||||||
|
MEDIPOST_RECIPIENT=your-medipost-recipient
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { MedReportTitle } from "@/components/MedReportTitle";
|
import { MedReportTitle } from "@/components/med-report-title";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { MedReportTitle } from "@/components/MedReportTitle";
|
import { MedReportTitle } from "@/components/med-report-title";
|
||||||
import { Button } from "@/packages/ui/src/shadcn/button";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { signOutAction } from "@/lib/actions/sign-out";
|
import { signOutAction } from "@/lib/actions/sign-out";
|
||||||
import { hasEnvVars } from "@/utils/supabase/check-env-vars";
|
import { hasEnvVars } from "@/utils/supabase/check-env-vars";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Badge } from "./ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { createClient } from "@/utils/supabase/server";
|
import { createClient } from "@/utils/supabase/server";
|
||||||
|
|
||||||
export default async function AuthButton() {
|
export default async function AuthButton() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MedReportSmallLogo } from "@/public/assets/MedReportSmallLogo";
|
import { MedReportSmallLogo } from "@/public/assets/med-report-small-logo";
|
||||||
|
|
||||||
export const MedReportTitle = () => (
|
export const MedReportTitle = () => (
|
||||||
<div className="flex gap-2 justify-center">
|
<div className="flex gap-2 justify-center">
|
||||||
2
lib/constants.ts
Normal file
2
lib/constants.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export const DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
export const DATE_FORMAT = "yyyy-mm-dd";
|
||||||
@@ -1,16 +1,34 @@
|
|||||||
import {
|
import {
|
||||||
|
SupabaseClient,
|
||||||
|
createClient as createCustomClient,
|
||||||
|
} from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getAnalysisGroup,
|
||||||
|
getClientInstitution,
|
||||||
|
getClientPerson,
|
||||||
|
getConfidentiality,
|
||||||
|
getOrderEnteredByPerson,
|
||||||
|
getPais,
|
||||||
|
getPatient,
|
||||||
|
getProviderInstitution,
|
||||||
|
getSpecimen,
|
||||||
|
} from '@/lib/templates/medipost-order';
|
||||||
|
import { SyncStatus } from '@/lib/types/audit';
|
||||||
|
import {
|
||||||
|
AnalysisOrderStatus,
|
||||||
GetMessageListResponse,
|
GetMessageListResponse,
|
||||||
|
MaterjalideGrupp,
|
||||||
MedipostAction,
|
MedipostAction,
|
||||||
MedipostPublicMessageResponse,
|
MedipostPublicMessageResponse,
|
||||||
Message,
|
Message,
|
||||||
UuringuGrupp,
|
UuringuGrupp,
|
||||||
} from "@/lib/types/medipost";
|
} from '@/lib/types/medipost';
|
||||||
import { Tables } from "@/supabase/database.types";
|
import { toArray } from '@/lib/utils';
|
||||||
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
import { Tables } from '@/supabase/database.types';
|
||||||
import axios from "axios";
|
import axios from 'axios';
|
||||||
import { XMLParser } from "fast-xml-parser";
|
import { XMLParser } from 'fast-xml-parser';
|
||||||
import { SyncStatus } from "@/lib/types/audit";
|
import { uniqBy } from 'lodash';
|
||||||
import { toArray } from "@/lib/utils";
|
|
||||||
|
|
||||||
const BASE_URL = process.env.MEDIPOST_URL!;
|
const BASE_URL = process.env.MEDIPOST_URL!;
|
||||||
const USER = process.env.MEDIPOST_USER!;
|
const USER = process.env.MEDIPOST_USER!;
|
||||||
@@ -37,14 +55,14 @@ export async function getLatestPublicMessageListItem() {
|
|||||||
Action: MedipostAction.GetPublicMessageList,
|
Action: MedipostAction.GetPublicMessageList,
|
||||||
User: USER,
|
User: USER,
|
||||||
Password: PASSWORD,
|
Password: PASSWORD,
|
||||||
Sender: "syndev",
|
Sender: 'syndev',
|
||||||
// LastChecked (date+time) can be used here to get only messages since the last check - add when cron is created
|
// 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
|
// MessageType check only for messages of certain type
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data.code && data.code !== 0) {
|
if (data.code && data.code !== 0) {
|
||||||
throw new Error("Failed to get public message list");
|
throw new Error('Failed to get public message list');
|
||||||
}
|
}
|
||||||
|
|
||||||
return getLatestMessage(data?.messages);
|
return getLatestMessage(data?.messages);
|
||||||
@@ -59,7 +77,7 @@ export async function getPublicMessage(messageId: string) {
|
|||||||
MessageId: messageId,
|
MessageId: messageId,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/xml",
|
Accept: 'application/xml',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const parser = new XMLParser({ ignoreAttributes: false });
|
const parser = new XMLParser({ ignoreAttributes: false });
|
||||||
@@ -74,16 +92,16 @@ export async function getPublicMessage(messageId: string) {
|
|||||||
|
|
||||||
export async function sendPrivateMessage(messageXml: string, receiver: string) {
|
export async function sendPrivateMessage(messageXml: string, receiver: string) {
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
body.append("Action", MedipostAction.SendPrivateMessage);
|
body.append('Action', MedipostAction.SendPrivateMessage);
|
||||||
body.append("User", USER);
|
body.append('User', USER);
|
||||||
body.append("Password", PASSWORD);
|
body.append('Password', PASSWORD);
|
||||||
body.append("Receiver", receiver);
|
body.append('Receiver', receiver);
|
||||||
body.append("MessageType", "Tellimus");
|
body.append('MessageType', 'Tellimus');
|
||||||
body.append(
|
body.append(
|
||||||
"Message",
|
'Message',
|
||||||
new Blob([messageXml], {
|
new Blob([messageXml], {
|
||||||
type: "text/xml; charset=UTF-8",
|
type: 'text/xml; charset=UTF-8',
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data } = await axios.post(BASE_URL, body);
|
const { data } = await axios.post(BASE_URL, body);
|
||||||
@@ -103,7 +121,7 @@ export async function getLatestPrivateMessageListItem() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (data.code && data.code !== 0) {
|
if (data.code && data.code !== 0) {
|
||||||
throw new Error("Failed to get private message list");
|
throw new Error('Failed to get private message list');
|
||||||
}
|
}
|
||||||
|
|
||||||
return getLatestMessage(data?.messages);
|
return getLatestMessage(data?.messages);
|
||||||
@@ -118,7 +136,7 @@ export async function getPrivateMessage(messageId: string) {
|
|||||||
MessageId: messageId,
|
MessageId: messageId,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/xml",
|
Accept: 'application/xml',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -154,7 +172,7 @@ export async function readPrivateMessageResponse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const privateMessageContent = await getPrivateMessage(
|
const privateMessageContent = await getPrivateMessage(
|
||||||
privateMessage.messageId
|
privateMessage.messageId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (privateMessageContent) {
|
if (privateMessageContent) {
|
||||||
@@ -168,29 +186,29 @@ export async function readPrivateMessageResponse() {
|
|||||||
|
|
||||||
async function saveAnalysisGroup(
|
async function saveAnalysisGroup(
|
||||||
analysisGroup: UuringuGrupp,
|
analysisGroup: UuringuGrupp,
|
||||||
supabase: SupabaseClient
|
supabase: SupabaseClient,
|
||||||
) {
|
) {
|
||||||
const { data: insertedAnalysisGroup, error } = await supabase
|
const { data: insertedAnalysisGroup, error } = await supabase
|
||||||
.from("analysis_groups")
|
.from('analysis_groups')
|
||||||
.upsert(
|
.upsert(
|
||||||
{
|
{
|
||||||
original_id: analysisGroup.UuringuGruppId,
|
original_id: analysisGroup.UuringuGruppId,
|
||||||
name: analysisGroup.UuringuGruppNimi,
|
name: analysisGroup.UuringuGruppNimi,
|
||||||
order: analysisGroup.UuringuGruppJarjekord,
|
order: analysisGroup.UuringuGruppJarjekord,
|
||||||
},
|
},
|
||||||
{ onConflict: "original_id", ignoreDuplicates: false }
|
{ onConflict: 'original_id', ignoreDuplicates: false },
|
||||||
)
|
)
|
||||||
.select("id");
|
.select('id');
|
||||||
|
|
||||||
if (error || !insertedAnalysisGroup[0]?.id) {
|
if (error || !insertedAnalysisGroup[0]?.id) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to insert analysis group (id: ${analysisGroup.UuringuGruppId}), error: ${error?.message}`
|
`Failed to insert analysis group (id: ${analysisGroup.UuringuGruppId}), error: ${error?.message}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const analysisGroupId = insertedAnalysisGroup[0].id;
|
const analysisGroupId = insertedAnalysisGroup[0].id;
|
||||||
|
|
||||||
const analysisGroupCodes = toArray(analysisGroup.Kood);
|
const analysisGroupCodes = toArray(analysisGroup.Kood);
|
||||||
const codes: Partial<Tables<"codes">>[] = analysisGroupCodes.map((kood) => ({
|
const codes: Partial<Tables<'codes'>>[] = analysisGroupCodes.map((kood) => ({
|
||||||
hk_code: kood.HkKood,
|
hk_code: kood.HkKood,
|
||||||
hk_code_multiplier: kood.HkKoodiKordaja,
|
hk_code_multiplier: kood.HkKoodiKordaja,
|
||||||
coefficient: kood.Koefitsient,
|
coefficient: kood.Koefitsient,
|
||||||
@@ -204,7 +222,7 @@ async function saveAnalysisGroup(
|
|||||||
const analysisElement = item.UuringuElement;
|
const analysisElement = item.UuringuElement;
|
||||||
|
|
||||||
const { data: insertedAnalysisElement, error } = await supabase
|
const { data: insertedAnalysisElement, error } = await supabase
|
||||||
.from("analysis_elements")
|
.from('analysis_elements')
|
||||||
.upsert(
|
.upsert(
|
||||||
{
|
{
|
||||||
analysis_id_oid: analysisElement.UuringIdOID,
|
analysis_id_oid: analysisElement.UuringIdOID,
|
||||||
@@ -216,13 +234,13 @@ async function saveAnalysisGroup(
|
|||||||
parent_analysis_group_id: analysisGroupId,
|
parent_analysis_group_id: analysisGroupId,
|
||||||
material_groups: toArray(item.MaterjalideGrupp),
|
material_groups: toArray(item.MaterjalideGrupp),
|
||||||
},
|
},
|
||||||
{ onConflict: "analysis_id_original", ignoreDuplicates: false }
|
{ onConflict: 'analysis_id_original', ignoreDuplicates: false },
|
||||||
)
|
)
|
||||||
.select('id');
|
.select('id');
|
||||||
|
|
||||||
if (error || !insertedAnalysisElement[0]?.id) {
|
if (error || !insertedAnalysisElement[0]?.id) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to insert analysis element (id: ${analysisElement.UuringId}), error: ${error?.message}`
|
`Failed to insert analysis element (id: ${analysisElement.UuringId}), error: ${error?.message}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +255,7 @@ async function saveAnalysisGroup(
|
|||||||
coefficient: kood.Koefitsient,
|
coefficient: kood.Koefitsient,
|
||||||
price: kood.Hind,
|
price: kood.Hind,
|
||||||
analysis_element_id: insertedAnalysisElementId,
|
analysis_element_id: insertedAnalysisElementId,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +263,7 @@ async function saveAnalysisGroup(
|
|||||||
if (analyses?.length) {
|
if (analyses?.length) {
|
||||||
for (const analysis of analyses) {
|
for (const analysis of analyses) {
|
||||||
const { data: insertedAnalysis, error } = await supabase
|
const { data: insertedAnalysis, error } = await supabase
|
||||||
.from("analyses")
|
.from('analyses')
|
||||||
.upsert(
|
.upsert(
|
||||||
{
|
{
|
||||||
analysis_id_oid: analysis.UuringIdOID,
|
analysis_id_oid: analysis.UuringIdOID,
|
||||||
@@ -256,13 +274,13 @@ async function saveAnalysisGroup(
|
|||||||
order: analysis.Jarjekord,
|
order: analysis.Jarjekord,
|
||||||
parent_analysis_element_id: insertedAnalysisElementId,
|
parent_analysis_element_id: insertedAnalysisElementId,
|
||||||
},
|
},
|
||||||
{ onConflict: "analysis_id_original", ignoreDuplicates: false }
|
{ onConflict: 'analysis_id_original', ignoreDuplicates: false },
|
||||||
)
|
)
|
||||||
.select('id');
|
.select('id');
|
||||||
|
|
||||||
if (error || !insertedAnalysis[0]?.id) {
|
if (error || !insertedAnalysis[0]?.id) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to insert analysis (id: ${analysis.UuringId}) error: ${error?.message}`
|
`Failed to insert analysis (id: ${analysis.UuringId}) error: ${error?.message}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +295,7 @@ async function saveAnalysisGroup(
|
|||||||
coefficient: kood.Koefitsient,
|
coefficient: kood.Koefitsient,
|
||||||
price: kood.Hind,
|
price: kood.Hind,
|
||||||
analysis_id: insertedAnalysisId,
|
analysis_id: insertedAnalysisId,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,20 +303,21 @@ async function saveAnalysisGroup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { error: codesError } = await supabase
|
const { error: codesError } = await supabase
|
||||||
.from("codes")
|
.from('codes')
|
||||||
.upsert(codes, { ignoreDuplicates: false });
|
.upsert(codes, { ignoreDuplicates: false });
|
||||||
|
|
||||||
if (codesError?.code) {
|
if (codesError?.code) {
|
||||||
|
console.error(codesError); // TODO remove this
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to insert codes (analysis group id: ${analysisGroup.UuringuGruppId})`
|
`Failed to insert codes (analysis group id: ${analysisGroup.UuringuGruppId})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function syncPublicMessage(
|
export async function syncPublicMessage(
|
||||||
message?: MedipostPublicMessageResponse | null
|
message?: MedipostPublicMessageResponse | null,
|
||||||
) {
|
) {
|
||||||
const supabase = createClient(
|
const supabase = createCustomClient(
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!,
|
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!,
|
||||||
{
|
{
|
||||||
@@ -307,20 +326,20 @@ export async function syncPublicMessage(
|
|||||||
autoRefreshToken: false,
|
autoRefreshToken: false,
|
||||||
detectSessionInUrl: false,
|
detectSessionInUrl: false,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const providers = toArray(message?.Saadetis?.Teenused.Teostaja);
|
const providers = toArray(message?.Saadetis?.Teenused.Teostaja);
|
||||||
const analysisGroups = providers.flatMap((provider) =>
|
const analysisGroups = providers.flatMap((provider) =>
|
||||||
toArray(provider.UuringuGrupp)
|
toArray(provider.UuringuGrupp),
|
||||||
);
|
);
|
||||||
if (!message || !analysisGroups.length) {
|
if (!message || !analysisGroups.length) {
|
||||||
return supabase.schema("audit").from("sync_entries").insert({
|
return supabase.schema('audit').from('sync_entries').insert({
|
||||||
operation: "ANALYSES_SYNC",
|
operation: 'ANALYSES_SYNC',
|
||||||
comment: "No data received",
|
comment: 'No data received',
|
||||||
status: SyncStatus.Fail,
|
status: SyncStatus.Fail,
|
||||||
changed_by_role: "service_role",
|
changed_by_role: 'service_role',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,31 +347,170 @@ export async function syncPublicMessage(
|
|||||||
await saveAnalysisGroup(analysisGroup, supabase);
|
await saveAnalysisGroup(analysisGroup, supabase);
|
||||||
}
|
}
|
||||||
|
|
||||||
await supabase.schema("audit").from("sync_entries").insert({
|
await supabase.schema('audit').from('sync_entries').insert({
|
||||||
operation: "ANALYSES_SYNC",
|
operation: 'ANALYSES_SYNC',
|
||||||
status: SyncStatus.Success,
|
status: SyncStatus.Success,
|
||||||
changed_by_role: "service_role",
|
changed_by_role: 'service_role',
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
await supabase
|
await supabase
|
||||||
.schema("audit")
|
.schema('audit')
|
||||||
.from("sync_entries")
|
.from('sync_entries')
|
||||||
.insert({
|
.insert({
|
||||||
operation: "ANALYSES_SYNC",
|
operation: 'ANALYSES_SYNC',
|
||||||
status: SyncStatus.Fail,
|
status: SyncStatus.Fail,
|
||||||
comment: JSON.stringify(e),
|
comment: JSON.stringify(e),
|
||||||
changed_by_role: "service_role",
|
changed_by_role: 'service_role',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO use actual parameters
|
||||||
|
export async function composeOrderXML(
|
||||||
|
/* chosenAnalysisElements?: number[],
|
||||||
|
chosenAnalyses?: number[], */
|
||||||
|
comment?: string,
|
||||||
|
) {
|
||||||
|
const supabase = createCustomClient(
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!,
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
persistSession: false,
|
||||||
|
autoRefreshToken: false,
|
||||||
|
detectSessionInUrl: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO remove dummy when actual implemetation is present
|
||||||
|
const orderedElements = [1, 75];
|
||||||
|
const orderedAnalyses = [10, 11, 100];
|
||||||
|
|
||||||
|
const createdAnalysisOrder = {
|
||||||
|
id: 4,
|
||||||
|
user_id: 'currentUser.user?.id',
|
||||||
|
analysis_element_ids: orderedElements,
|
||||||
|
analysis_ids: orderedAnalyses,
|
||||||
|
status: AnalysisOrderStatus[1],
|
||||||
|
created_at: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data: analysisElements } = (await supabase
|
||||||
|
.from('analysis_elements')
|
||||||
|
.select(`*, analysis_groups(*)`)
|
||||||
|
.in('id', orderedElements)) as {
|
||||||
|
data: ({
|
||||||
|
analysis_groups: Tables<'analysis_groups'>;
|
||||||
|
} & Tables<'analysis_elements'>)[];
|
||||||
|
};
|
||||||
|
const { data: analyses } = (await supabase
|
||||||
|
.from('analyses')
|
||||||
|
.select(`*, analysis_elements(*, analysis_groups(*))`)
|
||||||
|
.in('id', orderedAnalyses)) as {
|
||||||
|
data: ({
|
||||||
|
analysis_elements: Tables<'analysis_elements'> & {
|
||||||
|
analysis_groups: Tables<'analysis_groups'>;
|
||||||
|
};
|
||||||
|
} & Tables<'analyses'>)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const analysisGroups: Tables<'analysis_groups'>[] = uniqBy(
|
||||||
|
(
|
||||||
|
analysisElements?.flatMap(({ analysis_groups }) => analysis_groups) ?? []
|
||||||
|
).concat(
|
||||||
|
analyses?.flatMap(
|
||||||
|
({ analysis_elements }) => analysis_elements.analysis_groups,
|
||||||
|
) ?? [],
|
||||||
|
),
|
||||||
|
'id',
|
||||||
|
);
|
||||||
|
|
||||||
|
const specimenSection = [];
|
||||||
|
const analysisSection = [];
|
||||||
|
let order = 1;
|
||||||
|
for (const currentGroup of analysisGroups) {
|
||||||
|
let relatedAnalysisElement = analysisElements?.find(
|
||||||
|
(element) => element.analysis_groups.id === currentGroup.id,
|
||||||
|
);
|
||||||
|
const relatedAnalyses = analyses?.filter((analysis) => {
|
||||||
|
return analysis.analysis_elements.analysis_groups.id === currentGroup.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!relatedAnalysisElement) {
|
||||||
|
relatedAnalysisElement = relatedAnalyses?.find(
|
||||||
|
(relatedAnalysis) =>
|
||||||
|
relatedAnalysis.analysis_elements.analysis_groups.id ===
|
||||||
|
currentGroup.id,
|
||||||
|
)?.analysis_elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!relatedAnalysisElement || !relatedAnalysisElement.material_groups) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to find related analysis element for group ${currentGroup.name} (id: ${currentGroup.id})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const group of relatedAnalysisElement?.material_groups as MaterjalideGrupp[]) {
|
||||||
|
const materials = toArray(group.Materjal);
|
||||||
|
const specimenXml = materials.flatMap(
|
||||||
|
({ MaterjaliNimi, MaterjaliTyyp, MaterjaliTyypOID, Konteiner }) => {
|
||||||
|
return toArray(Konteiner).map((container) =>
|
||||||
|
getSpecimen(
|
||||||
|
MaterjaliTyypOID,
|
||||||
|
MaterjaliTyyp,
|
||||||
|
MaterjaliNimi,
|
||||||
|
order,
|
||||||
|
container.ProovinouKoodOID,
|
||||||
|
container.ProovinouKood,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
specimenSection.push(...specimenXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupXml = getAnalysisGroup(
|
||||||
|
currentGroup.original_id,
|
||||||
|
currentGroup.name,
|
||||||
|
order,
|
||||||
|
relatedAnalysisElement,
|
||||||
|
);
|
||||||
|
order++;
|
||||||
|
analysisSection.push(groupXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO get actual data when order creation is implemented
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Saadetis xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="TellimusLOINC.xsd">
|
||||||
|
${getPais(process.env.MEDIPOST_USER!, process.env.MEDIPOST_RECIPIENT!, createdAnalysisOrder.created_at, createdAnalysisOrder.id)}
|
||||||
|
<Tellimus cito="EI">
|
||||||
|
<ValisTellimuseId>${createdAnalysisOrder.id}</ValisTellimuseId>
|
||||||
|
<!--<TellijaAsutus>-->
|
||||||
|
${getClientInstitution()}
|
||||||
|
<!--<TeostajaAsutus>-->
|
||||||
|
${getProviderInstitution()}
|
||||||
|
<!--<TellijaIsik>-->
|
||||||
|
${getClientPerson()}
|
||||||
|
<!--<SisestajaIsik>-->
|
||||||
|
${getOrderEnteredByPerson()}
|
||||||
|
<TellijaMarkused>${comment ?? ''}</TellijaMarkused>
|
||||||
|
${getPatient(49610230861, 'Surname', 'First Name', '1996-10-23', 'N')}
|
||||||
|
${getConfidentiality()}
|
||||||
|
${specimenSection.join('')}
|
||||||
|
${analysisSection?.join('')}
|
||||||
|
</Tellimus>
|
||||||
|
</Saadetis>`;
|
||||||
|
}
|
||||||
|
|
||||||
function getLatestMessage(messages?: Message[]) {
|
function getLatestMessage(messages?: Message[]) {
|
||||||
if (!messages?.length) {
|
if (!messages?.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return messages.reduce((prev, current) =>
|
return messages.reduce((prev, current) =>
|
||||||
Number(prev.messageId) > Number(current.messageId) ? prev : current
|
Number(prev.messageId) > Number(current.messageId) ? prev : current,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
172
lib/templates/medipost-order.ts
Normal file
172
lib/templates/medipost-order.ts
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import { DATE_TIME_FORMAT } from '@/lib/constants';
|
||||||
|
import { Tables } from '@/supabase/database.types';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
|
||||||
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
export const getPais = (
|
||||||
|
sender: string,
|
||||||
|
recipient: string,
|
||||||
|
createdAt: Date,
|
||||||
|
messageId: number,
|
||||||
|
) => {
|
||||||
|
if (isProd) {
|
||||||
|
// return correct data
|
||||||
|
}
|
||||||
|
return `<Pais>
|
||||||
|
<Pakett versioon="20">OL</Pakett>
|
||||||
|
<Saatja>${sender}</Saatja>
|
||||||
|
<Saaja>${recipient}</Saaja>
|
||||||
|
<Aeg>${format(createdAt, DATE_TIME_FORMAT)}</Aeg>
|
||||||
|
<SaadetisId>${messageId}</SaadetisId>
|
||||||
|
<Email>argo@medreport.ee</Email>
|
||||||
|
</Pais>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getClientInstitution = () => {
|
||||||
|
if (isProd) {
|
||||||
|
// return correct data
|
||||||
|
}
|
||||||
|
return `<Asutus tyyp="TELLIJA">
|
||||||
|
<AsutuseId>16381793</AsutuseId>
|
||||||
|
<AsutuseNimi>MedReport OÜ</AsutuseNimi>
|
||||||
|
<AsutuseKood>TSU</AsutuseKood>
|
||||||
|
<Telefon>+37258871517</Telefon>
|
||||||
|
</Asutus>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProviderInstitution = () => {
|
||||||
|
if (isProd) {
|
||||||
|
// return correct data
|
||||||
|
}
|
||||||
|
return `<Asutus tyyp="TEOSTAJA">
|
||||||
|
<AsutuseId>11107913</AsutuseId>
|
||||||
|
<AsutuseNimi>Synlab HTI Tallinn</AsutuseNimi>
|
||||||
|
<AsutuseKood>SLA</AsutuseKood>
|
||||||
|
<AllyksuseNimi>Synlab HTI Tallinn</AllyksuseNimi>
|
||||||
|
<Telefon>+3723417123</Telefon>
|
||||||
|
</Asutus>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getClientPerson = () => {
|
||||||
|
if (isProd) {
|
||||||
|
// return correct data
|
||||||
|
}
|
||||||
|
return `<Personal tyyp="TELLIJA" jarjenumber="1">
|
||||||
|
<PersonalOID>1.3.6.1.4.1.28284.6.2.4.9</PersonalOID>
|
||||||
|
<PersonalKood>D07907</PersonalKood>
|
||||||
|
<PersonalPerekonnaNimi>Eduard</PersonalPerekonnaNimi>
|
||||||
|
<PersonalEesNimi>Tsvetkov</PersonalEesNimi>
|
||||||
|
<Telefon>+37258131202</Telefon>
|
||||||
|
</Personal>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getOrderEnteredPerson = () => {
|
||||||
|
if (isProd) {
|
||||||
|
// return correct data
|
||||||
|
}
|
||||||
|
return `<Personal tyyp="SISESTAJA" jarjenumber="1">
|
||||||
|
<PersonalOID>1.3.6.1.4.1.28284.6.2.4.9</PersonalOID>
|
||||||
|
<PersonalKood>D07907</PersonalKood>
|
||||||
|
<PersonalPerekonnaNimi>Eduard</PersonalPerekonnaNimi>
|
||||||
|
<PersonalEesNimi>Tsvetkov</PersonalEesNimi>
|
||||||
|
<Telefon>+37258131202</Telefon>
|
||||||
|
</Personal>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPatient = (
|
||||||
|
idCode: number,
|
||||||
|
surname: string,
|
||||||
|
firstName: string,
|
||||||
|
birthDate: string,
|
||||||
|
genderLetter: string,
|
||||||
|
) => {
|
||||||
|
return `<Patsient>
|
||||||
|
<IsikukoodiOID>1.3.6.1.4.1.28284.6.2.2.1</IsikukoodiOID>
|
||||||
|
<Isikukood>${idCode}</Isikukood>
|
||||||
|
<PerekonnaNimi>${surname}</PerekonnaNimi>
|
||||||
|
<EesNimi>${firstName}</EesNimi>
|
||||||
|
<SynniAeg>${birthDate}</SynniAeg>
|
||||||
|
<SuguOID>1.3.6.1.4.1.28284.6.2.3.16.2</SuguOID>
|
||||||
|
<Sugu>${genderLetter}</Sugu>
|
||||||
|
</Patsient>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getConfidentiality = () => {
|
||||||
|
if (isProd) {
|
||||||
|
// return correct data
|
||||||
|
}
|
||||||
|
return `<Konfidentsiaalsus>
|
||||||
|
<PatsiendileOID>2.16.840.1.113883.5.25</PatsiendileOID>
|
||||||
|
<Patsiendile>N</Patsiendile>
|
||||||
|
<ArstileOID>1.3.6.1.4.1.28284.6.2.2.39.1</ArstileOID>
|
||||||
|
<Arstile>N</Arstile>
|
||||||
|
<EsindajaleOID>1.3.6.1.4.1.28284.6.2.2.37.1</EsindajaleOID>
|
||||||
|
<Esindajale>N</Esindajale>
|
||||||
|
</Konfidentsiaalsus>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getOrderEnteredByPerson = () => {
|
||||||
|
if (isProd) {
|
||||||
|
// return correct data
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<Personal tyyp="SISESTAJA" jarjenumber="1">
|
||||||
|
<PersonalOID>1.3.6.1.4.1.28284.6.2.4.9</PersonalOID>
|
||||||
|
<PersonalKood>D07907</PersonalKood>
|
||||||
|
<PersonalPerekonnaNimi>Eduard</PersonalPerekonnaNimi>
|
||||||
|
<PersonalEesNimi>Tsvetkov</PersonalEesNimi>
|
||||||
|
<Telefon>+37258131202</Telefon>
|
||||||
|
</Personal>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSpecimen = (
|
||||||
|
materialTypeOid: string,
|
||||||
|
materialGroupId: string,
|
||||||
|
materialName: string,
|
||||||
|
order: number,
|
||||||
|
sampleContainerOid?: string,
|
||||||
|
sampleContainerId?: string,
|
||||||
|
) =>
|
||||||
|
`
|
||||||
|
<Proov>
|
||||||
|
${sampleContainerOid ? `<ProovinouIdOID>${sampleContainerOid}</ProovinouIdOID>` : null}
|
||||||
|
${sampleContainerId ? `<ProovinouId>${sampleContainerId}</ProovinouId>` : null}
|
||||||
|
<MaterjaliTyypOID>${materialTypeOid}</MaterjaliTyypOID>
|
||||||
|
<MaterjaliTyyp>${materialGroupId}</MaterjaliTyyp>
|
||||||
|
<MaterjaliNimi>${materialName}</MaterjaliNimi>
|
||||||
|
<Jarjenumber>${order}</Jarjenumber>
|
||||||
|
</Proov>`;
|
||||||
|
|
||||||
|
export const getAnalysisElement = (
|
||||||
|
analysisIdOid: string,
|
||||||
|
analysisIdOriginal: string,
|
||||||
|
tehikShortLoinc: string,
|
||||||
|
tehikLoincName: string,
|
||||||
|
analysisElementId: number,
|
||||||
|
analysisNameLab?: string | null,
|
||||||
|
) => {
|
||||||
|
return `<UuringuElement>
|
||||||
|
<UuringIdOID>${analysisIdOid}</UuringIdOID>
|
||||||
|
<UuringId>${analysisIdOriginal}</UuringId>
|
||||||
|
<TLyhend>${tehikShortLoinc}</TLyhend>
|
||||||
|
<KNimetus>${tehikLoincName}</KNimetus>
|
||||||
|
<UuringNimi>${analysisNameLab ?? tehikLoincName}</UuringNimi>
|
||||||
|
<TellijaUuringId>${analysisElementId}</TellijaUuringId>
|
||||||
|
</UuringuElement>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAnalysisGroup = (
|
||||||
|
analysisGroupOriginalId: string,
|
||||||
|
analysisGroupName: string,
|
||||||
|
specimenOrderNr: number,
|
||||||
|
analysisElement: Tables<'analysis_elements'>,
|
||||||
|
) =>
|
||||||
|
`<UuringuGrupp>
|
||||||
|
<UuringuGruppId>${analysisGroupOriginalId}</UuringuGruppId>
|
||||||
|
<UuringuGruppNimi>${analysisGroupName}</UuringuGruppNimi>
|
||||||
|
<Uuring>
|
||||||
|
${getAnalysisElement(analysisElement.analysis_id_oid, analysisElement.analysis_id_original, analysisElement.tehik_short_loinc, analysisElement.tehik_loinc_name, analysisElement.id, analysisElement.analysis_name_lab)}
|
||||||
|
<ProoviJarjenumber>${specimenOrderNr}</ProoviJarjenumber>
|
||||||
|
</Uuring>
|
||||||
|
</UuringuGrupp>`;
|
||||||
@@ -74,7 +74,7 @@ export type UuringuElement = {
|
|||||||
export type Uuring = {
|
export type Uuring = {
|
||||||
tellitav: "JAH" | "EI";
|
tellitav: "JAH" | "EI";
|
||||||
UuringuElement: UuringuElement; //1..1
|
UuringuElement: UuringuElement; //1..1
|
||||||
MaterjalideGrupp?: MaterjalideGrupp[]; //0..n
|
MaterjalideGrupp?: MaterjalideGrupp | MaterjalideGrupp[]; //0..n If this is not present, then the analysis can't be ordered.
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UuringuGrupp = {
|
export type UuringuGrupp = {
|
||||||
@@ -86,10 +86,10 @@ export type UuringuGrupp = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Konteiner = {
|
export type Konteiner = {
|
||||||
ProovinouKoodOID: string;
|
ProovinouKoodOID?: string; //0..1
|
||||||
ProovinouKood: string;
|
ProovinouKood?: string; //0..1
|
||||||
KonteineriNimi: string;
|
KonteineriNimi?: string; //0..1
|
||||||
KonteineriKirjeldus: string;
|
KonteineriKirjeldus: string; //1..1
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Materjal = {
|
export type Materjal = {
|
||||||
@@ -98,12 +98,13 @@ export type Materjal = {
|
|||||||
MaterjaliNimi: string;
|
MaterjaliNimi: string;
|
||||||
KonteineriOmadus: string;
|
KonteineriOmadus: string;
|
||||||
MaterjaliPaige: { Kohustuslik: "JAH" | "EI" }; //0..1
|
MaterjaliPaige: { Kohustuslik: "JAH" | "EI" }; //0..1
|
||||||
Konteiner?: Konteiner[]; //0..n
|
MaterjalJarjekord?: number; //0..1
|
||||||
|
Konteiner?: Konteiner | Konteiner[]; //0..n
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MaterjalideGrupp = {
|
export type MaterjalideGrupp = {
|
||||||
vaikimisi: "JAH" | "EI";
|
vaikimisi: "JAH" | "EI";
|
||||||
Materjal: Materjal; //1..n
|
Materjal: Materjal | Materjal[]; //1..n
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Teostaja = {
|
export type Teostaja = {
|
||||||
@@ -139,3 +140,12 @@ export type MedipostPublicMessageResponse = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AnalysisOrderStatus = {
|
||||||
|
1: "QUEUED",
|
||||||
|
2: "ON_HOLD",
|
||||||
|
3: "PROCESSING",
|
||||||
|
4: "COMPLETED",
|
||||||
|
5: "REJECTED",
|
||||||
|
6: "CANCELLED",
|
||||||
|
} as const;
|
||||||
|
|||||||
@@ -63,7 +63,8 @@
|
|||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"fast-xml-parser": "^5.2.3",
|
"fast-xml-parser": "^5.2.5",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.510.0",
|
"lucide-react": "^0.510.0",
|
||||||
"next": "15.3.2",
|
"next": "15.3.2",
|
||||||
"next-sitemap": "^4.2.3",
|
"next-sitemap": "^4.2.3",
|
||||||
@@ -87,6 +88,7 @@
|
|||||||
"@types/node": "^22.15.18",
|
"@types/node": "^22.15.18",
|
||||||
"@types/react": "19.1.4",
|
"@types/react": "19.1.4",
|
||||||
"@types/react-dom": "19.1.5",
|
"@types/react-dom": "19.1.5",
|
||||||
|
"@types/lodash": "^4.17.17",
|
||||||
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
||||||
"cssnano": "^7.0.7",
|
"cssnano": "^7.0.7",
|
||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
|
|||||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -105,8 +105,11 @@ importers:
|
|||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
fast-xml-parser:
|
fast-xml-parser:
|
||||||
specifier: ^5.2.3
|
specifier: ^5.2.5
|
||||||
version: 5.2.5
|
version: 5.2.5
|
||||||
|
lodash:
|
||||||
|
specifier: ^4.17.21
|
||||||
|
version: 4.17.21
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: ^0.510.0
|
specifier: ^0.510.0
|
||||||
version: 0.510.0(react@19.1.0)
|
version: 0.510.0(react@19.1.0)
|
||||||
@@ -159,6 +162,9 @@ importers:
|
|||||||
'@tailwindcss/postcss':
|
'@tailwindcss/postcss':
|
||||||
specifier: ^4.1.7
|
specifier: ^4.1.7
|
||||||
version: 4.1.8
|
version: 4.1.8
|
||||||
|
'@types/lodash':
|
||||||
|
specifier: ^4.17.17
|
||||||
|
version: 4.17.17
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.15.18
|
specifier: ^22.15.18
|
||||||
version: 22.15.30
|
version: 22.15.30
|
||||||
|
|||||||
@@ -50,24 +50,27 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
sync_entries: {
|
sync_entries: {
|
||||||
Row: {
|
Row: {
|
||||||
changed_by_role: string | null
|
changed_by_role: string
|
||||||
|
comment: string | null
|
||||||
created_at: string
|
created_at: string
|
||||||
id: number
|
id: number
|
||||||
operation: string | null
|
operation: string
|
||||||
status: string
|
status: string
|
||||||
}
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
changed_by_role?: string | null
|
changed_by_role: string
|
||||||
|
comment?: string | null
|
||||||
created_at?: string
|
created_at?: string
|
||||||
id?: number
|
id?: number
|
||||||
operation?: string | null
|
operation: string
|
||||||
status: string
|
status: string
|
||||||
}
|
}
|
||||||
Update: {
|
Update: {
|
||||||
changed_by_role?: string | null
|
changed_by_role?: string
|
||||||
|
comment?: string | null
|
||||||
created_at?: string
|
created_at?: string
|
||||||
id?: number
|
id?: number
|
||||||
operation?: string | null
|
operation?: string
|
||||||
status?: string
|
status?: string
|
||||||
}
|
}
|
||||||
Relationships: []
|
Relationships: []
|
||||||
@@ -224,7 +227,7 @@ export type Database = {
|
|||||||
analysis_name_lab: string | null
|
analysis_name_lab: string | null
|
||||||
created_at: string
|
created_at: string
|
||||||
id: number
|
id: number
|
||||||
order: number | null
|
order: number
|
||||||
parent_analysis_element_id: number
|
parent_analysis_element_id: number
|
||||||
tehik_loinc_name: string | null
|
tehik_loinc_name: string | null
|
||||||
tehik_short_loinc: string | null
|
tehik_short_loinc: string | null
|
||||||
@@ -236,7 +239,7 @@ export type Database = {
|
|||||||
analysis_name_lab?: string | null
|
analysis_name_lab?: string | null
|
||||||
created_at?: string
|
created_at?: string
|
||||||
id?: number
|
id?: number
|
||||||
order?: number | null
|
order: number
|
||||||
parent_analysis_element_id: number
|
parent_analysis_element_id: number
|
||||||
tehik_loinc_name?: string | null
|
tehik_loinc_name?: string | null
|
||||||
tehik_short_loinc?: string | null
|
tehik_short_loinc?: string | null
|
||||||
@@ -248,7 +251,7 @@ export type Database = {
|
|||||||
analysis_name_lab?: string | null
|
analysis_name_lab?: string | null
|
||||||
created_at?: string
|
created_at?: string
|
||||||
id?: number
|
id?: number
|
||||||
order?: number | null
|
order?: number
|
||||||
parent_analysis_element_id?: number
|
parent_analysis_element_id?: number
|
||||||
tehik_loinc_name?: string | null
|
tehik_loinc_name?: string | null
|
||||||
tehik_short_loinc?: string | null
|
tehik_short_loinc?: string | null
|
||||||
@@ -266,42 +269,42 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
analysis_elements: {
|
analysis_elements: {
|
||||||
Row: {
|
Row: {
|
||||||
analysis_id_oid: string | null
|
analysis_id_oid: string
|
||||||
analysis_id_original: string
|
analysis_id_original: string
|
||||||
analysis_name_lab: string | null
|
analysis_name_lab: string | null
|
||||||
created_at: string
|
created_at: string
|
||||||
id: number
|
id: number
|
||||||
material_groups: Json[] | null
|
material_groups: Json[] | null
|
||||||
order: number | null
|
order: number
|
||||||
parent_analysis_group_id: number
|
parent_analysis_group_id: number
|
||||||
tehik_loinc_name: string | null
|
tehik_loinc_name: string
|
||||||
tehik_short_loinc: string | null
|
tehik_short_loinc: string
|
||||||
updated_at: string | null
|
updated_at: string | null
|
||||||
}
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
analysis_id_oid?: string | null
|
analysis_id_oid: string
|
||||||
analysis_id_original: string
|
analysis_id_original: string
|
||||||
analysis_name_lab?: string | null
|
analysis_name_lab?: string | null
|
||||||
created_at?: string
|
created_at?: string
|
||||||
id?: number
|
id?: number
|
||||||
material_groups?: Json[] | null
|
material_groups?: Json[] | null
|
||||||
order?: number | null
|
order: number
|
||||||
parent_analysis_group_id: number
|
parent_analysis_group_id: number
|
||||||
tehik_loinc_name?: string | null
|
tehik_loinc_name: string
|
||||||
tehik_short_loinc?: string | null
|
tehik_short_loinc: string
|
||||||
updated_at?: string | null
|
updated_at?: string | null
|
||||||
}
|
}
|
||||||
Update: {
|
Update: {
|
||||||
analysis_id_oid?: string | null
|
analysis_id_oid?: string
|
||||||
analysis_id_original?: string
|
analysis_id_original?: string
|
||||||
analysis_name_lab?: string | null
|
analysis_name_lab?: string | null
|
||||||
created_at?: string
|
created_at?: string
|
||||||
id?: number
|
id?: number
|
||||||
material_groups?: Json[] | null
|
material_groups?: Json[] | null
|
||||||
order?: number | null
|
order?: number
|
||||||
parent_analysis_group_id?: number
|
parent_analysis_group_id?: number
|
||||||
tehik_loinc_name?: string | null
|
tehik_loinc_name?: string
|
||||||
tehik_short_loinc?: string | null
|
tehik_short_loinc?: string
|
||||||
updated_at?: string | null
|
updated_at?: string | null
|
||||||
}
|
}
|
||||||
Relationships: [
|
Relationships: [
|
||||||
@@ -318,29 +321,53 @@ export type Database = {
|
|||||||
Row: {
|
Row: {
|
||||||
created_at: string
|
created_at: string
|
||||||
id: number
|
id: number
|
||||||
name: string | null
|
name: string
|
||||||
order: number | null
|
order: number
|
||||||
original_id: string
|
original_id: string
|
||||||
updated_at: string | null
|
updated_at: string | null
|
||||||
}
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
created_at?: string
|
created_at?: string
|
||||||
id?: number
|
id?: number
|
||||||
name?: string | null
|
name: string
|
||||||
order?: number | null
|
order: number
|
||||||
original_id: string
|
original_id: string
|
||||||
updated_at?: string | null
|
updated_at?: string | null
|
||||||
}
|
}
|
||||||
Update: {
|
Update: {
|
||||||
created_at?: string
|
created_at?: string
|
||||||
id?: number
|
id?: number
|
||||||
name?: string | null
|
name?: string
|
||||||
order?: number | null
|
order?: number
|
||||||
original_id?: string
|
original_id?: string
|
||||||
updated_at?: string | null
|
updated_at?: string | null
|
||||||
}
|
}
|
||||||
Relationships: []
|
Relationships: []
|
||||||
}
|
}
|
||||||
|
analysis_orders: {
|
||||||
|
Row: {
|
||||||
|
analysis_group_ids: number[] | null
|
||||||
|
created_at: string
|
||||||
|
id: string
|
||||||
|
status: Database["public"]["Enums"]["analysis_order_status"]
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
analysis_group_ids?: number[] | null
|
||||||
|
created_at?: string
|
||||||
|
id: string
|
||||||
|
status: Database["public"]["Enums"]["analysis_order_status"]
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
analysis_group_ids?: number[] | null
|
||||||
|
created_at?: string
|
||||||
|
id?: string
|
||||||
|
status?: Database["public"]["Enums"]["analysis_order_status"]
|
||||||
|
user_id?: string
|
||||||
|
}
|
||||||
|
Relationships: []
|
||||||
|
}
|
||||||
billing_customers: {
|
billing_customers: {
|
||||||
Row: {
|
Row: {
|
||||||
account_id: string
|
account_id: string
|
||||||
@@ -1111,6 +1138,13 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Enums: {
|
Enums: {
|
||||||
|
analysis_order_status:
|
||||||
|
| "QUEUED"
|
||||||
|
| "ON_HOLD"
|
||||||
|
| "PROCESSING"
|
||||||
|
| "COMPLETED"
|
||||||
|
| "REJECTED"
|
||||||
|
| "CANCELLED"
|
||||||
app_permissions:
|
app_permissions:
|
||||||
| "roles.manage"
|
| "roles.manage"
|
||||||
| "billing.manage"
|
| "billing.manage"
|
||||||
@@ -1257,6 +1291,14 @@ export const Constants = {
|
|||||||
},
|
},
|
||||||
public: {
|
public: {
|
||||||
Enums: {
|
Enums: {
|
||||||
|
analysis_order_status: [
|
||||||
|
"QUEUED",
|
||||||
|
"ON_HOLD",
|
||||||
|
"PROCESSING",
|
||||||
|
"COMPLETED",
|
||||||
|
"REJECTED",
|
||||||
|
"CANCELLED",
|
||||||
|
],
|
||||||
app_permissions: [
|
app_permissions: [
|
||||||
"roles.manage",
|
"roles.manage",
|
||||||
"billing.manage",
|
"billing.manage",
|
||||||
|
|||||||
91
supabase/migrations/20250605150146_add_analysis_orders.sql
Normal file
91
supabase/migrations/20250605150146_add_analysis_orders.sql
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
create type "public"."analysis_order_status" as enum ('QUEUED', 'ON_HOLD', 'PROCESSING', 'COMPLETED', 'REJECTED', 'CANCELLED');
|
||||||
|
|
||||||
|
create table "public"."analysis_orders" (
|
||||||
|
"id" uuid not null,
|
||||||
|
"analysis_element_ids" bigint[],
|
||||||
|
"analysis_ids" bigint[],
|
||||||
|
"user_id" uuid not null,
|
||||||
|
"status" public.analysis_order_status not null,
|
||||||
|
"created_at" timestamp with time zone not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
alter table "public"."analysis_orders" enable row level security;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX analysis_orders_pkey ON public.analysis_orders USING btree (id);
|
||||||
|
|
||||||
|
alter table "public"."analysis_orders" add constraint "analysis_orders_pkey" PRIMARY KEY using index "analysis_orders_pkey";
|
||||||
|
|
||||||
|
alter table "public"."analysis_orders" add constraint "analysis_orders_id_fkey" FOREIGN KEY (id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||||
|
|
||||||
|
alter table "public"."analysis_orders" validate constraint "analysis_orders_id_fkey";
|
||||||
|
|
||||||
|
grant delete on table "public"."analysis_orders" to "authenticated";
|
||||||
|
|
||||||
|
grant insert on table "public"."analysis_orders" to "authenticated";
|
||||||
|
|
||||||
|
grant references on table "public"."analysis_orders" to "authenticated";
|
||||||
|
|
||||||
|
grant select on table "public"."analysis_orders" to "authenticated";
|
||||||
|
|
||||||
|
grant trigger on table "public"."analysis_orders" to "authenticated";
|
||||||
|
|
||||||
|
grant truncate on table "public"."analysis_orders" to "authenticated";
|
||||||
|
|
||||||
|
grant update on table "public"."analysis_orders" to "authenticated";
|
||||||
|
|
||||||
|
grant delete on table "public"."analysis_orders" to "service_role";
|
||||||
|
|
||||||
|
grant insert on table "public"."analysis_orders" to "service_role";
|
||||||
|
|
||||||
|
grant references on table "public"."analysis_orders" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "public"."analysis_orders" to "service_role";
|
||||||
|
|
||||||
|
grant trigger on table "public"."analysis_orders" to "service_role";
|
||||||
|
|
||||||
|
grant truncate on table "public"."analysis_orders" to "service_role";
|
||||||
|
|
||||||
|
grant update on table "public"."analysis_orders" to "service_role";
|
||||||
|
|
||||||
|
create policy "analysis_all"
|
||||||
|
on "public"."analysis_orders"
|
||||||
|
as permissive
|
||||||
|
for all
|
||||||
|
to authenticated, service_role
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
create policy "analysis_select"
|
||||||
|
on "public"."analyses"
|
||||||
|
as permissive
|
||||||
|
for select
|
||||||
|
to public
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
create policy "analysis_elements_select"
|
||||||
|
on "public"."analysis_elements"
|
||||||
|
as permissive
|
||||||
|
for select
|
||||||
|
to public
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
create policy "analysis_groups_select"
|
||||||
|
on "public"."analysis_groups"
|
||||||
|
as permissive
|
||||||
|
for select
|
||||||
|
to public
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
-- Drop previously created unnecessary indices
|
||||||
|
drop index if exists "public"."analysis_elements_original_id_key";
|
||||||
|
drop index if exists "public"."analysis_original_id_key";
|
||||||
|
|
||||||
|
-- Remove nullable from previously added fields
|
||||||
|
alter table "public"."analyses" alter column "order" set not null;
|
||||||
|
alter table "public"."analysis_elements" alter column "analysis_id_oid" set not null;
|
||||||
|
alter table "public"."analysis_elements" alter column "order" set not null;
|
||||||
|
alter table "public"."analysis_groups" alter column "name" set not null;
|
||||||
|
alter table "public"."analysis_groups" alter column "order" set not null;
|
||||||
|
alter table "public"."analysis_elements" alter column "tehik_loinc_name" set not null;
|
||||||
|
alter table "public"."analysis_elements" alter column "tehik_short_loinc" set not null;
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user