@@ -41,8 +41,6 @@ export async function POST(request: NextRequest) {
|
||||
orderCreatedAt: new Date(medreportOrder.created_at),
|
||||
});
|
||||
|
||||
console.info("SEND XML", messageXml);
|
||||
|
||||
try {
|
||||
await sendPrivateMessageTestResponse({ messageXml });
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getOrder } from "~/lib/services/order.service";
|
||||
import { composeOrderTestResponseXML, sendPrivateMessageTestResponse } from "~/lib/services/medipostTest.service";
|
||||
import { retrieveOrder } from "@lib/data";
|
||||
import { getAccountAdmin } from "~/lib/services/account.service";
|
||||
import { getOrderedAnalysisElementsIds } from "~/lib/services/medipost.service";
|
||||
import { createMedipostActionLog, getOrderedAnalysisElementsIds } from "~/lib/services/medipost.service";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
// const isDev = process.env.NODE_ENV === 'development';
|
||||
@@ -35,6 +35,11 @@ export async function POST(request: Request) {
|
||||
});
|
||||
|
||||
try {
|
||||
await createMedipostActionLog({
|
||||
action: 'send_fake_analysis_results_to_medipost',
|
||||
xml: messageXml,
|
||||
medusaOrderId,
|
||||
});
|
||||
await sendPrivateMessageTestResponse({ messageXml });
|
||||
} catch (error) {
|
||||
console.error("Error sending private message test response: ", error);
|
||||
|
||||
@@ -31,8 +31,8 @@ export async function HomeMenuNavigation(props: {
|
||||
})
|
||||
: 0;
|
||||
|
||||
const cartItemsCount = props.cart?.items?.length ?? 0;
|
||||
const hasCartItems = cartItemsCount > 0;
|
||||
const cartQuantityTotal = props.cart?.items?.reduce((acc, item) => acc + item.quantity, 0) ?? 0;
|
||||
const hasCartItems = cartQuantityTotal > 0;
|
||||
|
||||
return (
|
||||
<div className={'flex w-full flex-1 items-center justify-between gap-3'}>
|
||||
@@ -64,7 +64,7 @@ export async function HomeMenuNavigation(props: {
|
||||
<ShoppingCart className="stroke-[1.5px]" />
|
||||
<Trans
|
||||
i18nKey="common:shoppingCartCount"
|
||||
values={{ count: cartItemsCount }}
|
||||
values={{ count: cartQuantityTotal }}
|
||||
/>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -51,8 +51,8 @@ export function HomeMobileNavigation(props: {
|
||||
}
|
||||
});
|
||||
|
||||
const cartItemsCount = props.cart?.items?.length ?? 0;
|
||||
const hasCartItems = cartItemsCount > 0;
|
||||
const cartQuantityTotal = props.cart?.items?.reduce((acc, item) => acc + item.quantity, 0) ?? 0;
|
||||
const hasCartItems = cartQuantityTotal > 0;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@@ -83,7 +83,7 @@ export function HomeMobileNavigation(props: {
|
||||
path="/home/cart"
|
||||
label="common:shoppingCartCount"
|
||||
Icon={<ShoppingCart className="stroke-[1.5px]" />}
|
||||
labelOptions={{ count: cartItemsCount }}
|
||||
labelOptions={{ count: cartQuantityTotal }}
|
||||
/>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
@@ -12,9 +12,9 @@ import {
|
||||
import { StoreProduct } from '@medusajs/types';
|
||||
import { useState } from 'react';
|
||||
import { handleAddToCart } from '~/lib/services/medusaCart.service';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
|
||||
export type OrderAnalysisCard = Pick<
|
||||
StoreProduct, 'title' | 'description' | 'subtitle'
|
||||
@@ -30,8 +30,6 @@ export default function OrderAnalysesCards({
|
||||
analyses: OrderAnalysisCard[];
|
||||
countryCode: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
|
||||
const [isAddingToCart, setIsAddingToCart] = useState(false);
|
||||
const handleSelect = async (variantId: string) => {
|
||||
if (isAddingToCart) {
|
||||
@@ -44,9 +42,10 @@ export default function OrderAnalysesCards({
|
||||
selectedVariant: { id: variantId },
|
||||
countryCode,
|
||||
});
|
||||
toast.success(<Trans i18nKey={'order-analysis:analysisAddedToCart'} />);
|
||||
setIsAddingToCart(false);
|
||||
router.push('/home/cart');
|
||||
} catch (e) {
|
||||
toast.error(<Trans i18nKey={'order-analysis:analysisAddToCartError'} />);
|
||||
setIsAddingToCart(false);
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
18
app/home/(user)/_components/orders/actions.ts
Normal file
18
app/home/(user)/_components/orders/actions.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
'use server';
|
||||
|
||||
import { createPageViewLog, PageViewAction } from "~/lib/services/audit/pageView.service";
|
||||
import { loadCurrentUserAccount } from "../../_lib/server/load-user-account";
|
||||
|
||||
export async function logAnalysisResultsNavigateAction(analysisOrderId: string) {
|
||||
const account = await loadCurrentUserAccount();
|
||||
if (!account) {
|
||||
throw new Error('Account not found');
|
||||
}
|
||||
await createPageViewLog({
|
||||
accountId: account.id,
|
||||
action: PageViewAction.VIEW_ANALYSIS_RESULTS_FROM_ORDER,
|
||||
extraData: {
|
||||
analysisOrderId,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import {
|
||||
Table,
|
||||
@@ -10,18 +12,26 @@ import {
|
||||
import { StoreOrderLineItem } from "@medusajs/types";
|
||||
import { AnalysisOrder } from '~/lib/services/order.service';
|
||||
import { formatDate } from 'date-fns';
|
||||
import Link from 'next/link';
|
||||
import { Eye } from 'lucide-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { logAnalysisResultsNavigateAction } from './actions';
|
||||
|
||||
export default function OrderItemsTable({ items, title, analysisOrder }: {
|
||||
items: StoreOrderLineItem[];
|
||||
title: string;
|
||||
analysisOrder: AnalysisOrder;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
|
||||
if (!items || items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const openAnalysisResults = async () => {
|
||||
await logAnalysisResultsNavigateAction(analysisOrder.medusa_order_id);
|
||||
router.push(`/home/analysis-results`);
|
||||
}
|
||||
|
||||
return (
|
||||
<Table className="rounded-lg border border-separate">
|
||||
<TableHeader className="text-ui-fg-subtle txt-medium-plus">
|
||||
@@ -60,13 +70,12 @@ export default function OrderItemsTable({ items, title, analysisOrder }: {
|
||||
|
||||
<TableCell className="text-right px-6">
|
||||
<span className="flex gap-x-1 justify-end w-[30px]">
|
||||
<Link href={`/home/analysis-results`} className="flex items-center justify-between text-small-regular">
|
||||
<button
|
||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
||||
>
|
||||
<Eye />
|
||||
</button>
|
||||
</Link>
|
||||
<button
|
||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer "
|
||||
onClick={openAnalysisResults}
|
||||
>
|
||||
<Eye />
|
||||
</button>
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
|
||||
export enum PageViewAction {
|
||||
VIEW_ANALYSIS_RESULTS = 'VIEW_ANALYSIS_RESULTS',
|
||||
VIEW_ANALYSIS_RESULTS_FROM_ORDER = 'VIEW_ANALYSIS_RESULTS_FROM_ORDER',
|
||||
REGISTRATION_SUCCESS = 'REGISTRATION_SUCCESS',
|
||||
VIEW_ORDER_ANALYSIS = 'VIEW_ORDER_ANALYSIS',
|
||||
VIEW_TEAM_ACCOUNT_DASHBOARD = 'VIEW_TEAM_ACCOUNT_DASHBOARD',
|
||||
@@ -10,9 +11,11 @@ export enum PageViewAction {
|
||||
export const createPageViewLog = async ({
|
||||
accountId,
|
||||
action,
|
||||
extraData,
|
||||
}: {
|
||||
accountId: string;
|
||||
action: PageViewAction;
|
||||
extraData?: Record<string, any>;
|
||||
}) => {
|
||||
try {
|
||||
const supabase = getSupabaseServerClient();
|
||||
@@ -34,6 +37,7 @@ export const createPageViewLog = async ({
|
||||
account_id: accountId,
|
||||
action,
|
||||
changed_by: user.id,
|
||||
extra_data: extraData,
|
||||
})
|
||||
.throwOnError();
|
||||
} catch (error) {
|
||||
|
||||
@@ -180,7 +180,10 @@ export async function getPrivateMessage(messageId: string) {
|
||||
|
||||
await validateMedipostResponse(data, { canHaveEmptyCode: true });
|
||||
|
||||
return parseXML(data) as MedipostOrderResponse;
|
||||
return {
|
||||
message: parseXML(data) as MedipostOrderResponse,
|
||||
xml: data as string,
|
||||
};
|
||||
}
|
||||
|
||||
export async function deletePrivateMessage(messageId: string) {
|
||||
@@ -211,7 +214,9 @@ export async function readPrivateMessageResponse({
|
||||
|
||||
try {
|
||||
const privateMessage = await getLatestPrivateMessageListItem({ excludedMessageIds });
|
||||
if (!privateMessage) {
|
||||
messageId = privateMessage?.messageId ?? null;
|
||||
|
||||
if (!privateMessage || !messageId) {
|
||||
return {
|
||||
messageId: null,
|
||||
hasAnalysisResponse: false,
|
||||
@@ -221,40 +226,28 @@ export async function readPrivateMessageResponse({
|
||||
};
|
||||
}
|
||||
|
||||
messageId = privateMessage.messageId;
|
||||
if (!messageId) {
|
||||
return {
|
||||
messageId: null,
|
||||
hasAnalysisResponse: false,
|
||||
hasPartialAnalysisResponse: false,
|
||||
hasFullAnalysisResponse: false,
|
||||
medusaOrderId: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const privateMessageContent = await getPrivateMessage(
|
||||
const { message: privateMessageContent, xml: privateMessageXml } = await getPrivateMessage(
|
||||
privateMessage.messageId,
|
||||
);
|
||||
|
||||
const messageResponse = privateMessageContent?.Saadetis?.Vastus;
|
||||
medusaOrderId = privateMessageContent?.Saadetis?.Tellimus?.ValisTellimuseId || messageResponse?.ValisTellimuseId;
|
||||
|
||||
if (!medusaOrderId || !medusaOrderId.toString().startsWith('order_')) {
|
||||
return {
|
||||
messageId,
|
||||
hasAnalysisResponse: false,
|
||||
hasPartialAnalysisResponse: false,
|
||||
hasFullAnalysisResponse: false,
|
||||
medusaOrderId: undefined,
|
||||
};
|
||||
}
|
||||
const hasInvalidOrderId = !medusaOrderId || !medusaOrderId.toString().startsWith('order_');
|
||||
|
||||
if (!messageResponse) {
|
||||
if (hasInvalidOrderId || !messageResponse) {
|
||||
await createMedipostActionLog({
|
||||
action: 'sync_analysis_results_from_medipost',
|
||||
xml: privateMessageXml,
|
||||
hasAnalysisResults: false,
|
||||
medusaOrderId: hasInvalidOrderId ? undefined : medusaOrderId,
|
||||
});
|
||||
return {
|
||||
messageId,
|
||||
hasAnalysisResponse: false,
|
||||
hasPartialAnalysisResponse: false,
|
||||
hasFullAnalysisResponse: false,
|
||||
medusaOrderId,
|
||||
medusaOrderId: hasInvalidOrderId ? undefined : medusaOrderId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -745,12 +738,36 @@ export async function sendOrderToMedipost({
|
||||
await sendPrivateMessage(orderXml);
|
||||
} catch (e) {
|
||||
const isMedipostError = e instanceof MedipostValidationError;
|
||||
await logMedipostDispatch({
|
||||
medusaOrderId,
|
||||
isSuccess: false,
|
||||
isMedipostError,
|
||||
errorMessage: isMedipostError ? e.response : undefined,
|
||||
});
|
||||
if (isMedipostError) {
|
||||
await logMedipostDispatch({
|
||||
medusaOrderId,
|
||||
isSuccess: false,
|
||||
isMedipostError,
|
||||
errorMessage: e.response,
|
||||
});
|
||||
await createMedipostActionLog({
|
||||
action: 'send_order_to_medipost',
|
||||
xml: orderXml,
|
||||
hasAnalysisResults: false,
|
||||
medusaOrderId,
|
||||
responseXml: e.response,
|
||||
hasError: true,
|
||||
});
|
||||
} else {
|
||||
await logMedipostDispatch({
|
||||
medusaOrderId,
|
||||
isSuccess: false,
|
||||
isMedipostError,
|
||||
});
|
||||
await createMedipostActionLog({
|
||||
action: 'send_order_to_medipost',
|
||||
xml: orderXml,
|
||||
hasAnalysisResults: false,
|
||||
medusaOrderId,
|
||||
hasError: true,
|
||||
});
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
await logMedipostDispatch({
|
||||
@@ -758,6 +775,12 @@ export async function sendOrderToMedipost({
|
||||
isSuccess: true,
|
||||
isMedipostError: false,
|
||||
});
|
||||
await createMedipostActionLog({
|
||||
action: 'send_order_to_medipost',
|
||||
xml: orderXml,
|
||||
hasAnalysisResults: false,
|
||||
medusaOrderId,
|
||||
});
|
||||
await updateOrderStatus({ medusaOrderId, orderStatus: 'PROCESSING' });
|
||||
}
|
||||
|
||||
@@ -825,3 +848,37 @@ export async function getOrderedAnalysisElementsIds({
|
||||
|
||||
return [...analysisPackageElements, ...orderedAnalysisElements];
|
||||
}
|
||||
|
||||
export async function createMedipostActionLog({
|
||||
action,
|
||||
xml,
|
||||
hasAnalysisResults = false,
|
||||
medusaOrderId,
|
||||
responseXml,
|
||||
hasError = false,
|
||||
}: {
|
||||
action:
|
||||
| 'send_order_to_medipost'
|
||||
| 'sync_analysis_results_from_medipost'
|
||||
| 'send_fake_analysis_results_to_medipost'
|
||||
| 'send_analysis_results_to_medipost';
|
||||
xml: string;
|
||||
hasAnalysisResults?: boolean;
|
||||
medusaOrderId?: string | null;
|
||||
responseXml?: string | null;
|
||||
hasError?: boolean;
|
||||
}) {
|
||||
await getSupabaseServerAdminClient()
|
||||
.schema('medreport')
|
||||
.from('medipost_actions')
|
||||
.insert({
|
||||
action,
|
||||
xml,
|
||||
has_analysis_results: hasAnalysisResults,
|
||||
medusa_order_id: medusaOrderId,
|
||||
response_xml: responseXml,
|
||||
has_error: hasError,
|
||||
})
|
||||
.select('id')
|
||||
.throwOnError();
|
||||
}
|
||||
|
||||
@@ -134,8 +134,10 @@ export async function getAnalysisOrders({
|
||||
|
||||
export async function getAnalysisOrdersAdmin({
|
||||
orderStatus,
|
||||
medusaOrderId,
|
||||
}: {
|
||||
orderStatus?: Tables<{ schema: 'medreport' }, 'analysis_orders'>['status'];
|
||||
medusaOrderId?: string | null;
|
||||
} = {}) {
|
||||
const query = getSupabaseServerAdminClient()
|
||||
.schema('medreport')
|
||||
@@ -144,6 +146,9 @@ export async function getAnalysisOrdersAdmin({
|
||||
if (orderStatus) {
|
||||
query.eq('status', orderStatus);
|
||||
}
|
||||
if (medusaOrderId) {
|
||||
query.eq('medusa_order_id', medusaOrderId);
|
||||
}
|
||||
const orders = await query.order('created_at', { ascending: false }).throwOnError();
|
||||
return orders.data;
|
||||
}
|
||||
|
||||
16
packages/email-templates/src/components/email-button.tsx
Normal file
16
packages/email-templates/src/components/email-button.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Button } from '@react-email/components';
|
||||
|
||||
export function EmailButton(
|
||||
props: React.PropsWithChildren<{
|
||||
href: string;
|
||||
}>,
|
||||
) {
|
||||
return (
|
||||
<Button
|
||||
className="hover:bg-primary/90 inline-flex w-full items-center justify-center gap-1 rounded bg-[#16a249] py-3 text-center text-[16px] font-semibold whitespace-nowrap text-white no-underline shadow-xs transition-colors focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
||||
href={props.href}
|
||||
>
|
||||
{props.children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Head,
|
||||
Html,
|
||||
Link,
|
||||
Preview,
|
||||
Tailwind,
|
||||
Text,
|
||||
@@ -13,6 +11,7 @@ import {
|
||||
import { BodyStyle } from '../components/body-style';
|
||||
import CommonFooter from '../components/common-footer';
|
||||
import { EmailContent } from '../components/content';
|
||||
import { EmailButton } from '../components/email-button';
|
||||
import { EmailHeader } from '../components/header';
|
||||
import { EmailHeading } from '../components/heading';
|
||||
import { EmailWrapper } from '../components/wrapper';
|
||||
@@ -72,11 +71,12 @@ export async function renderDoctorSummaryReceivedEmail({
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:summaryReceivedForOrder`, { orderNr })}
|
||||
</Text>
|
||||
<Link
|
||||
|
||||
<EmailButton
|
||||
href={`${process.env.NEXT_PUBLIC_SITE_URL}/home/order/${orderId}`}
|
||||
>
|
||||
<Button> {t(`${namespace}:linkText`, { orderNr })}</Button>
|
||||
</Link>
|
||||
{t(`${namespace}:linkText`, { orderNr })}
|
||||
</EmailButton>
|
||||
<Text>
|
||||
{t(`${namespace}:ifButtonDisabled`)}{' '}
|
||||
{`${process.env.NEXT_PUBLIC_SITE_URL}/home/order/${orderId}`}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { StoreProduct } from '@medusajs/types';
|
||||
import { Button } from '@medusajs/ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { handleAddToCart } from '../../../../lib/services/medusaCart.service';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
|
||||
import {
|
||||
Card,
|
||||
@@ -49,12 +50,19 @@ export default function SelectAnalysisPackage({
|
||||
|
||||
const handleSelect = async () => {
|
||||
setIsAddingToCart(true);
|
||||
await handleAddToCart({
|
||||
selectedVariant: { id: variantId },
|
||||
countryCode,
|
||||
});
|
||||
setIsAddingToCart(false);
|
||||
router.push('/home/cart');
|
||||
try {
|
||||
await handleAddToCart({
|
||||
selectedVariant: { id: variantId },
|
||||
countryCode,
|
||||
});
|
||||
setIsAddingToCart(false);
|
||||
toast.success(<Trans i18nKey={'order-analysis-package:analysisPackageAddedToCart'} />);
|
||||
router.push('/home/cart');
|
||||
} catch (e) {
|
||||
toast.error(<Trans i18nKey={'order-analysis-package:analysisPackageAddToCartError'} />);
|
||||
setIsAddingToCart(false);
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -199,6 +199,7 @@ export type Database = {
|
||||
changed_by: string
|
||||
created_at: string
|
||||
id: number
|
||||
extra_data?: Json | null
|
||||
}
|
||||
Insert: {
|
||||
account_id: string
|
||||
@@ -206,6 +207,7 @@ export type Database = {
|
||||
changed_by: string
|
||||
created_at?: string
|
||||
id?: number
|
||||
extra_data?: Json | null
|
||||
}
|
||||
Update: {
|
||||
account_id?: string
|
||||
@@ -213,6 +215,7 @@ export type Database = {
|
||||
changed_by?: string
|
||||
created_at?: string
|
||||
id?: number
|
||||
extra_data?: Json | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
@@ -1254,6 +1257,34 @@ export type Database = {
|
||||
},
|
||||
]
|
||||
}
|
||||
medipost_actions: {
|
||||
Row: {
|
||||
id: string
|
||||
action: string
|
||||
xml: string
|
||||
has_analysis_results: boolean
|
||||
created_at: string
|
||||
medusa_order_id: string
|
||||
response_xml: string
|
||||
has_error: boolean
|
||||
}
|
||||
Insert: {
|
||||
action: string
|
||||
xml: string
|
||||
has_analysis_results: boolean
|
||||
medusa_order_id: string
|
||||
response_xml: string
|
||||
has_error: boolean
|
||||
}
|
||||
Update: {
|
||||
action?: string
|
||||
xml?: string
|
||||
has_analysis_results?: boolean
|
||||
medusa_order_id?: string
|
||||
response_xml?: string
|
||||
has_error?: boolean
|
||||
}
|
||||
}
|
||||
medreport_product_groups: {
|
||||
Row: {
|
||||
created_at: string
|
||||
@@ -1919,6 +1950,15 @@ export type Database = {
|
||||
account_id: string
|
||||
}[]
|
||||
}
|
||||
get_latest_medipost_dispatch_state_for_order: {
|
||||
Args: {
|
||||
medusa_order_id: string
|
||||
}
|
||||
Returns: {
|
||||
has_success: boolean
|
||||
action_date: string
|
||||
}
|
||||
}
|
||||
get_medipost_dispatch_tries: {
|
||||
Args: { p_medusa_order_id: string }
|
||||
Returns: number
|
||||
@@ -2135,6 +2175,21 @@ export type Database = {
|
||||
}
|
||||
Returns: Json
|
||||
}
|
||||
sync_analysis_results: {
|
||||
}
|
||||
send_medipost_test_response_for_order: {
|
||||
Args: {
|
||||
medusa_order_id: string
|
||||
}
|
||||
}
|
||||
order_has_medipost_dispatch_error: {
|
||||
Args: {
|
||||
medusa_order_id: string
|
||||
}
|
||||
Returns: {
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
Enums: {
|
||||
analysis_feedback_status: "STARTED" | "DRAFT" | "COMPLETED"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"title": "Select analysis package",
|
||||
"noPackagesAvailable": "No packages available",
|
||||
"selectThisPackage": "Select this package",
|
||||
"selectPackage": "Select package",
|
||||
"comparePackages": "Compare packages"
|
||||
"title": "Select analysis package",
|
||||
"noPackagesAvailable": "No packages available",
|
||||
"selectThisPackage": "Select this package",
|
||||
"selectPackage": "Select package",
|
||||
"comparePackages": "Compare packages",
|
||||
"analysisPackageAddedToCart": "Analysis package added to cart",
|
||||
"analysisPackageAddToCartError": "Adding analysis package to cart failed"
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"title": "Select analysis",
|
||||
"description": "Select the analysis that suits your needs",
|
||||
"analysisNotAvailable": "Analysis is not available currently"
|
||||
"description": "All analysis results will appear within 1-3 days after the blood test.",
|
||||
"analysisNotAvailable": "Analysis is not available currently",
|
||||
"analysisAddedToCart": "Analysis added to cart",
|
||||
"analysisAddToCartError": "Adding analysis to cart failed"
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"title": "Vali analüüsi pakett",
|
||||
"noPackagesAvailable": "Teenuste loetelu ei leitud, proovi hiljem uuesti",
|
||||
"selectThisPackage": "Vali see pakett",
|
||||
"selectPackage": "Vali pakett",
|
||||
"comparePackages": "Võrdle pakette"
|
||||
"title": "Vali analüüsi pakett",
|
||||
"noPackagesAvailable": "Teenuste loetelu ei leitud, proovi hiljem uuesti",
|
||||
"selectThisPackage": "Vali see pakett",
|
||||
"selectPackage": "Vali pakett",
|
||||
"comparePackages": "Võrdle pakette",
|
||||
"analysisPackageAddedToCart": "Analüüsi pakett lisatud ostukorvi",
|
||||
"analysisPackageAddToCartError": "Analüüsi paketi lisamine ostukorvi ebaõnnestus"
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"title": "Vali analüüs",
|
||||
"description": "Vali enda vajadustele sobiv analüüs",
|
||||
"analysisNotAvailable": "Analüüsi tellimine ei ole hetkel saadaval"
|
||||
"description": "Kõikide analüüside tulemused ilmuvad 1–3 tööpäeva jooksul peale vere andmist.",
|
||||
"analysisNotAvailable": "Analüüsi tellimine ei ole hetkel saadaval",
|
||||
"analysisAddedToCart": "Analüüs lisatud ostukorvi",
|
||||
"analysisAddToCartError": "Analüüsi lisamine ostukorvi ebaõnnestus"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Enable required extensions for cron jobs and HTTP requests
|
||||
create extension if not exists pg_cron;
|
||||
create extension if not exists pg_net;
|
||||
@@ -1,5 +1,3 @@
|
||||
create extension if not exists pg_net;
|
||||
|
||||
create or replace function medreport.medipost_retry_dispatch(
|
||||
order_id text
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE OR REPLACE FUNCTION medreport.sync_analysis_results()
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
select net.http_post(
|
||||
url := 'https://test.medreport.ee/api/job/sync-analysis-results',
|
||||
headers := jsonb_build_object(
|
||||
'Content-Type', 'application/json',
|
||||
'x-jobs-api-key', 'fd26ec26-70ed-11f0-9e95-431ac3b15a84'
|
||||
)
|
||||
) as request_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
grant execute on function medreport.sync_analysis_results() to service_role;
|
||||
@@ -0,0 +1,19 @@
|
||||
CREATE OR REPLACE FUNCTION medreport.send_medipost_test_response_for_order(medusa_order_id text)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
select net.http_post(
|
||||
url := 'https://test.medreport.ee/api/order/medipost-test-response',
|
||||
headers := jsonb_build_object(
|
||||
'Content-Type', 'application/json',
|
||||
'x-jobs-api-key', 'fd26ec26-70ed-11f0-9e95-431ac3b15a84'
|
||||
),
|
||||
body := jsonb_build_object(
|
||||
'medusaOrderId', medusa_order_id
|
||||
)
|
||||
) as request_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
grant execute on function medreport.send_medipost_test_response_for_order(text) to service_role;
|
||||
@@ -1,12 +1,8 @@
|
||||
-- Enable required extensions for cron jobs and HTTP requests
|
||||
create extension if not exists pg_cron;
|
||||
create extension if not exists pg_net;
|
||||
|
||||
-- Schedule the test-medipost-responses job to run every 15 minutes
|
||||
select
|
||||
cron.schedule(
|
||||
'send-test-medipost-responses-every-15-minutes', -- Unique job name
|
||||
'*/15 * * * *', -- Cron schedule: every 15 minutes
|
||||
'send-test-medipost-responses-every-15-minutes',
|
||||
'*/15 * * * *',
|
||||
$$
|
||||
select
|
||||
net.http_post(
|
||||
@@ -1,7 +1,3 @@
|
||||
-- Enable required extensions for cron jobs and HTTP requests
|
||||
create extension if not exists pg_cron;
|
||||
create extension if not exists pg_net;
|
||||
|
||||
-- Schedule the sync-analysis-results job to run every 15 minutes
|
||||
select
|
||||
cron.schedule(
|
||||
4
supabase/migrations-env-specific/README.md
Normal file
4
supabase/migrations-env-specific/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Migrations that require env specific parameters.
|
||||
|
||||
- JOBS_API_TOKEN
|
||||
- app deploy public or internal URL
|
||||
16
supabase/migrations/20250828105844_medipost_actions.sql
Normal file
16
supabase/migrations/20250828105844_medipost_actions.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE medreport.medipost_actions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
action VARCHAR(255) NOT NULL,
|
||||
xml VARCHAR(131072),
|
||||
has_analysis_results BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
medusa_order_id VARCHAR(255),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
||||
);
|
||||
|
||||
ALTER TABLE medreport.medipost_actions ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "service_role_select" ON medreport.medipost_actions FOR SELECT TO service_role USING (true);
|
||||
CREATE POLICY "service_role_insert" ON medreport.medipost_actions FOR INSERT TO service_role WITH CHECK (true);
|
||||
CREATE POLICY "service_role_update" ON medreport.medipost_actions FOR UPDATE TO service_role USING (true);
|
||||
CREATE POLICY "service_role_delete" ON medreport.medipost_actions FOR DELETE TO service_role USING (true);
|
||||
grant select, insert, update, delete on table medreport.medipost_actions to service_role;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE medreport.medipost_actions ADD COLUMN response_xml VARCHAR(131072);
|
||||
ALTER TABLE medreport.medipost_actions ADD COLUMN has_error BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE audit.page_views ADD COLUMN extra_data JSONB;
|
||||
@@ -0,0 +1,11 @@
|
||||
CREATE OR REPLACE FUNCTION medreport.order_has_medipost_dispatch_error(medusa_order_id text)
|
||||
RETURNS boolean AS $$
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM medreport.medipost_actions
|
||||
WHERE medusa_order_id = $1
|
||||
AND action = 'send_order_to_medipost'
|
||||
AND has_error = true
|
||||
);
|
||||
$$ LANGUAGE sql STABLE;
|
||||
|
||||
grant execute on function medreport.order_has_medipost_dispatch_error(text) to service_role;
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE OR REPLACE FUNCTION medreport.get_latest_medipost_dispatch_state_for_order(medusa_order_id text)
|
||||
RETURNS TABLE(has_success boolean, action_date timestamp with time zone) AS $$
|
||||
SELECT
|
||||
CASE
|
||||
WHEN ma.has_error = false THEN true
|
||||
ELSE false
|
||||
END as has_success,
|
||||
ma.created_at as action_date
|
||||
FROM medreport.medipost_actions ma
|
||||
WHERE ma.medusa_order_id = $1
|
||||
AND ma.action = 'send_order_to_medipost'
|
||||
ORDER BY ma.created_at DESC
|
||||
LIMIT 1;
|
||||
$$ LANGUAGE sql STABLE;
|
||||
|
||||
grant execute on function medreport.get_latest_medipost_dispatch_state_for_order(text) to service_role;
|
||||
Reference in New Issue
Block a user