From 8790b515d539d5d89a6eced9e7de9002ef082de1 Mon Sep 17 00:00:00 2001 From: k4rli Date: Mon, 4 Aug 2025 16:27:54 +0300 Subject: [PATCH] feat(MED-131): fix medusa vs medipost vs b2b product ids mixed --- app/api/order/medipost-test-response/route.ts | 17 +-- .../montonio-callback/[montonioId]/route.ts | 16 ++- lib/services/medipost.service.ts | 118 +++++++++++------- lib/services/order.service.ts | 8 +- 4 files changed, 88 insertions(+), 71 deletions(-) diff --git a/app/api/order/medipost-test-response/route.ts b/app/api/order/medipost-test-response/route.ts index b6bf84d..c91a0a9 100644 --- a/app/api/order/medipost-test-response/route.ts +++ b/app/api/order/medipost-test-response/route.ts @@ -3,6 +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"; export async function POST(request: Request) { const isDev = process.env.NODE_ENV === 'development'; @@ -16,19 +17,9 @@ export async function POST(request: Request) { const medreportOrder = await getOrder({ medusaOrderId }); const account = await getAccountAdmin({ primaryOwnerUserId: medreportOrder.user_id }); + const orderedAnalysisElementsIds = await getOrderedAnalysisElementsIds({ medusaOrder }); - const ANALYSIS_ELEMENT_HANDLE_PREFIX = 'analysis-element-'; - const orderedAnalysisElementsIds = (medusaOrder?.items ?? []) - .filter((item) => item.product?.handle?.startsWith(ANALYSIS_ELEMENT_HANDLE_PREFIX)) - .map((item) => { - const id = Number(item.product?.handle?.replace(ANALYSIS_ELEMENT_HANDLE_PREFIX, '')); - if (Number.isNaN(id)) { - return null; - } - return id; - }) - .filter(Boolean) as number[]; - + console.info(`Sending test response for order=${medusaOrderId} with ${orderedAnalysisElementsIds.length} ordered analysis elements`); const messageXml = await composeOrderTestResponseXML({ person: { idCode: account.personal_code!, @@ -36,7 +27,7 @@ export async function POST(request: Request) { lastName: account.last_name ?? '', phone: account.phone ?? '', }, - orderedAnalysisElementsIds, + orderedAnalysisElementsIds: orderedAnalysisElementsIds.map(({ analysisElementId }) => analysisElementId), orderedAnalysesIds: [], orderId: medusaOrderId, orderCreatedAt: new Date(medreportOrder.created_at), diff --git a/app/home/(user)/(dashboard)/cart/montonio-callback/[montonioId]/route.ts b/app/home/(user)/(dashboard)/cart/montonio-callback/[montonioId]/route.ts index 8ed9d33..6bddbf5 100644 --- a/app/home/(user)/(dashboard)/cart/montonio-callback/[montonioId]/route.ts +++ b/app/home/(user)/(dashboard)/cart/montonio-callback/[montonioId]/route.ts @@ -6,7 +6,7 @@ import { listProductTypes } from "@lib/data/products"; import { placeOrder, retrieveCart } from "@lib/data/cart"; import { createI18nServerInstance } from "~/lib/i18n/i18n.server"; import { createOrder } from '~/lib/services/order.service'; -import { sendOrderToMedipost } from '~/lib/services/medipost.service'; +import { getOrderedAnalysisElementsIds, sendOrderToMedipost } from '~/lib/services/medipost.service'; const emailSender = process.env.EMAIL_SENDER; const siteUrl = process.env.NEXT_PUBLIC_SITE_URL!; @@ -88,7 +88,8 @@ const handleOrderToken = async (orderToken: string) => { } const medusaOrder = await placeOrder(cartId, { revalidateCacheTags: true }); - await createOrder({ medusaOrder: medusaOrder }); + const orderedAnalysisElements = await getOrderedAnalysisElementsIds({ medusaOrder }); + await createOrder({ medusaOrder, orderedAnalysisElements }); const { productTypes } = await listProductTypes(); const analysisPackagesType = productTypes.find(({ metadata }) => metadata?.handle === ANALYSIS_PACKAGES_TYPE_HANDLE); @@ -98,6 +99,7 @@ const handleOrderToken = async (orderToken: string) => { email: medusaOrder.email, partnerLocationName: analysisPackageOrderItem?.metadata?.partner_location_name as string ?? '', analysisPackageName: analysisPackageOrderItem?.title ?? '', + orderedAnalysisElements, }; } catch (error) { console.error("Failed to place order", error); @@ -124,15 +126,19 @@ export async function GET(request: Request) { throw new Error("Order result is missing"); } - const { medusaOrderId, email, partnerLocationName, analysisPackageName } = orderResult; + const { medusaOrderId, email, partnerLocationName, analysisPackageName, orderedAnalysisElements } = orderResult; const personName = account.name; if (email && analysisPackageName) { - await sendEmail({ email, analysisPackageName, personName, partnerLocationName, language }); + try { + await sendEmail({ email, analysisPackageName, personName, partnerLocationName, language }); + } catch (error) { + console.error("Failed to send email", error); + } } else { // @TODO send email for separate analyses console.error("Missing email or analysisPackageName", orderResult); } - sendOrderToMedipost({ medusaOrderId }) + sendOrderToMedipost({ medusaOrderId, orderedAnalysisElements }) return Response.redirect(new URL('/home/order', baseUrl)) } catch (error) { console.error("Failed to place order", error); diff --git a/lib/services/medipost.service.ts b/lib/services/medipost.service.ts index 694b0bf..39a7d66 100644 --- a/lib/services/medipost.service.ts +++ b/lib/services/medipost.service.ts @@ -37,11 +37,10 @@ import { Tables } from '@kit/supabase/database'; import { createAnalysisGroup } from './analysis-group.service'; import { getSupabaseServerAdminClient } from '@/packages/supabase/src/clients/server-admin-client'; import { getOrder } from './order.service'; -import { getAnalysisElementsAdmin } from './analysis-element.service'; +import { getAnalysisElements, getAnalysisElementsAdmin } from './analysis-element.service'; import { getAnalyses } from './analyses.service'; -import { retrieveOrder } from '@lib/data/orders'; import { getAccountAdmin } from './account.service'; -import { StoreProduct } from '@medusajs/types'; +import { StoreOrder } from '@medusajs/types'; import { listProducts } from '@lib/data/products'; import { listRegions } from '@lib/data/regions'; import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product'; @@ -648,55 +647,14 @@ export async function syncPrivateMessage({ export async function sendOrderToMedipost({ medusaOrderId, + orderedAnalysisElements, }: { medusaOrderId: string; + orderedAnalysisElements: { analysisElementId: number }[]; }) { - const [medusaOrder, medreportOrder, countryCodes] = await Promise.all([ - retrieveOrder(medusaOrderId), - getOrder({ medusaOrderId }), - listRegions(), - ]); - const countryCode = countryCodes[0]!.countries![0]!.iso_2!; + const medreportOrder = await getOrder({ medusaOrderId }); const account = await getAccountAdmin({ primaryOwnerUserId: medreportOrder.user_id }); - - let analysisPackageElements: StoreProduct[] = []; - const orderedAnalysisPackages = (medusaOrder?.items ?? []).filter(({ product }) => product?.handle.startsWith(ANALYSIS_PACKAGE_HANDLE_PREFIX)); - const orderedAnalysisElements = (medusaOrder?.items ?? []).filter(({ product }) => product?.handle.startsWith(ANALYSIS_ELEMENT_HANDLE_PREFIX)); - if (orderedAnalysisPackages.length > 0) { - const { response: { products: analysisPackages } } = await listProducts({ - countryCode, - queryParams: { limit: 100, "type_id[0]": orderedAnalysisPackages[0]!.product_type_id! }, - }); - const analysisElementMedusaProductIds = getAnalysisElementMedusaProductIds(analysisPackages); - const { response: { products } } = await listProducts({ - countryCode, - queryParams: { - id: analysisElementMedusaProductIds, - limit: 100, - }, - }); - analysisPackageElements = products; - } - - const analysisPackageElementsIds = analysisPackageElements - .map((product) => { - const id = Number(product?.metadata?.analysisIdOriginal); - if (Number.isNaN(id)) { - return null; - } - return id; - }) - .filter(Boolean) as number[]; - const orderedAnalysisElementsIds = orderedAnalysisElements - .map((line) => { - const id = Number(line.product?.handle?.replace(ANALYSIS_ELEMENT_HANDLE_PREFIX, '')); - if (Number.isNaN(id)) { - return null; - } - return id; - }) - .filter(Boolean) as number[]; const orderXml = await composeOrderXML({ person: { idCode: account.personal_code!, @@ -704,7 +662,7 @@ export async function sendOrderToMedipost({ lastName: account.last_name ?? '', phone: account.phone ?? '', }, - orderedAnalysisElementsIds: [...analysisPackageElementsIds, ...orderedAnalysisElementsIds], + orderedAnalysisElementsIds: orderedAnalysisElements.map(({ analysisElementId }) => analysisElementId), orderedAnalysesIds: [], orderId: medusaOrderId, orderCreatedAt: new Date(medreportOrder.created_at), @@ -713,3 +671,67 @@ export async function sendOrderToMedipost({ await sendPrivateMessage(orderXml); } + +export async function getOrderedAnalysisElementsIds({ + medusaOrder, +}: { + medusaOrder: StoreOrder; +}): Promise<{ + analysisElementId: number; +}[]> { + const countryCodes = await listRegions(); + const countryCode = countryCodes[0]!.countries![0]!.iso_2!; + + function getOrderedAnalysisElements(medusaOrder: StoreOrder) { + return (medusaOrder?.items ?? []) + .filter(({ product }) => product?.handle.startsWith(ANALYSIS_ELEMENT_HANDLE_PREFIX)) + .map((line) => { + const analysisElementId = Number(line.product?.handle?.replace(ANALYSIS_ELEMENT_HANDLE_PREFIX, '')); + if (Number.isNaN(analysisElementId)) { + return null; + } + return { analysisElementId }; + }) as { analysisElementId: number }[]; + } + + async function getOrderedAnalysisPackages(medusaOrder: StoreOrder) { + const orderedPackages = (medusaOrder?.items ?? []).filter(({ product }) => product?.handle.startsWith(ANALYSIS_PACKAGE_HANDLE_PREFIX)); + const orderedPackageIds = orderedPackages.map(({ product }) => product?.id).filter(Boolean) as string[]; + if (orderedPackageIds.length === 0) { + return []; + } + console.info(`Order has ${orderedPackageIds.length} packages`); + const { response: { products: orderedPackagesProducts } } = await listProducts({ + countryCode, + queryParams: { limit: 100, id: orderedPackageIds }, + }); + console.info(`Order has ${orderedPackagesProducts.length} packages`); + if (orderedPackagesProducts.length !== orderedPackageIds.length) { + throw new Error(`Got ${orderedPackagesProducts.length} ordered packages products, expected ${orderedPackageIds.length}`); + } + + const ids = getAnalysisElementMedusaProductIds(orderedPackagesProducts); + const { response: { products: analysisPackagesProducts } } = await listProducts({ + countryCode, + queryParams: { limit: 100, id: ids }, + }); + if (analysisPackagesProducts.length !== ids.length) { + throw new Error(`Got ${analysisPackagesProducts.length} analysis packages products, expected ${ids.length}`); + } + + const originalIds = analysisPackagesProducts + .map(({ metadata }) => metadata?.analysisIdOriginal) + .filter((id) => typeof id === 'string'); + if (originalIds.length !== ids.length) { + throw new Error(`Got ${originalIds.length} analysis packages products with analysisIdOriginal, expected ${ids.length}`); + } + const analysisElements = await getAnalysisElements({ originalIds }); + + return analysisElements.map(({ id }) => ({ analysisElementId: id })); + } + + const analysisPackageElements = await getOrderedAnalysisPackages(medusaOrder); + const orderedAnalysisElements = getOrderedAnalysisElements(medusaOrder); + + return [...analysisPackageElements, ...orderedAnalysisElements]; +} diff --git a/lib/services/order.service.ts b/lib/services/order.service.ts index 309d702..7702d1b 100644 --- a/lib/services/order.service.ts +++ b/lib/services/order.service.ts @@ -5,15 +5,13 @@ import type { StoreOrder } from '@medusajs/types'; export async function createOrder({ medusaOrder, + orderedAnalysisElements, }: { medusaOrder: StoreOrder; + orderedAnalysisElements: { analysisElementId: number }[]; }) { const supabase = getSupabaseServerClient(); - const analysisElementIds = medusaOrder.items - ?.filter(({ product }) => product?.handle?.startsWith('analysis-element-')) - .map(({ product }) => Number(product?.handle.replace('analysis-element-', ''))) - .filter((id) => !Number.isNaN(id)) as number[]; const { data: { user } } = await supabase.auth.getUser(); if (!user) { throw new Error('User not found'); @@ -21,7 +19,7 @@ export async function createOrder({ const orderResult = await supabase.schema('medreport') .from('analysis_orders') .insert({ - analysis_element_ids: analysisElementIds, + analysis_element_ids: orderedAnalysisElements.map(({ analysisElementId }) => analysisElementId), analysis_ids: [], status: 'QUEUED', user_id: user.id,