diff --git a/app/api/job/handler/sync-analysis-groups-store.ts b/app/api/job/handler/sync-analysis-groups-store.ts index 151d72e..ccf9c0a 100644 --- a/app/api/job/handler/sync-analysis-groups-store.ts +++ b/app/api/job/handler/sync-analysis-groups-store.ts @@ -30,8 +30,8 @@ async function createProductCategories({ }: { medusa: Medusa; }) { - const existingProductCategories = await medusa.admin.productCategory.list(); - const parentCategory = existingProductCategories.product_categories.find(({ handle }) => handle === SYNLAB_SERVICES_CATEGORY_HANDLE); + const { product_categories: existingProductCategories } = await medusa.admin.productCategory.list(); + const parentCategory = existingProductCategories.find(({ handle }) => handle === SYNLAB_SERVICES_CATEGORY_HANDLE); if (!parentCategory) { throw new Error('Parent category not found'); @@ -46,7 +46,7 @@ async function createProductCategories({ for (const analysisGroup of analysisGroups) { console.info(`Processing analysis group '${analysisGroup.name}'`); - const isExisting = existingProductCategories.product_categories.find(({ name }) => name === analysisGroup.name); + const isExisting = existingProductCategories.find(({ name }) => name === analysisGroup.name); const isNewlyCreated = createdCategories.find(({ name }) => name === analysisGroup.name); if (isExisting || isNewlyCreated) { console.info(`Analysis group '${analysisGroup.name}' already exists`); @@ -68,6 +68,28 @@ async function createProductCategories({ } } +async function getChildProductCategories({ + medusa, +}: { + medusa: Medusa; +}) { + const { product_categories: allCategories } = await medusa.admin.productCategory.list(); + const childCategories = allCategories.filter(({ parent_category_id }) => parent_category_id !== null); + return childCategories; +} + +async function deleteProductCategories({ + medusa, + categories, +}: { + medusa: Medusa; + categories: AdminProductCategory[]; +}) { + for (const category of categories) { + await medusa.admin.productCategory.delete(category.id); + } +} + /** * In case a reset is needed */ @@ -76,14 +98,12 @@ async function deleteProducts({ }: { medusa: Medusa; }) { - const { product_categories: allCategories } = await medusa.admin.productCategory.list(); const { products: existingProducts } = await medusa.admin.product.list({ - category_id: allCategories.map(({ id }) => id), + fields: 'id,collection_id', + limit: 1000, }); - for (const product of existingProducts) { - await medusa.admin.product.delete(product.id); - } + await Promise.all(existingProducts.filter((a) => !a.collection_id).map(({ id }) => medusa.admin.product.delete(id))); } async function getAnalysisPackagesType() { @@ -145,7 +165,7 @@ async function createProducts({ medusa.admin.product.list({ category_id: allCategories.map(({ id }) => id), }), - getAnalysisElements(), + getAnalysisElements({}), getAnalysisPackagesType(), getProductDefaultFields({ medusa }), ]) @@ -169,7 +189,7 @@ async function createProducts({ continue; } - const createResponse = await medusa.admin.product.create({ + await medusa.admin.product.create({ title: name, handle: `analysis-element-${analysisElement.id}`, categories: [{ id: category.id }], @@ -194,7 +214,6 @@ async function createProducts({ ], type_id: analysisPackagesType.id, }); - console.info(`Successfully created product, id=${createResponse.product.id}`); } } @@ -204,6 +223,8 @@ export default async function syncAnalysisGroupsStore() { try { await createProductCategories({ medusa }); + // const categories = await getChildProductCategories({ medusa }); + // await deleteProductCategories({ medusa, categories }); // await deleteProducts({ medusa }); // return; diff --git a/app/api/order/medipost-test-response/route.ts b/app/api/order/medipost-test-response/route.ts new file mode 100644 index 0000000..b6bf84d --- /dev/null +++ b/app/api/order/medipost-test-response/route.ts @@ -0,0 +1,52 @@ +import { NextResponse } from "next/server"; +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"; + +export async function POST(request: Request) { + const isDev = process.env.NODE_ENV === 'development'; + if (!isDev) { + return NextResponse.json({ error: 'This endpoint is only available in development mode' }, { status: 403 }); + } + + const { medusaOrderId } = await request.json(); + + const medusaOrder = await retrieveOrder(medusaOrderId) + const medreportOrder = await getOrder({ medusaOrderId }); + + const account = await getAccountAdmin({ primaryOwnerUserId: medreportOrder.user_id }); + + 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[]; + + const messageXml = await composeOrderTestResponseXML({ + person: { + idCode: account.personal_code!, + firstName: account.name ?? '', + lastName: account.last_name ?? '', + phone: account.phone ?? '', + }, + orderedAnalysisElementsIds, + orderedAnalysesIds: [], + orderId: medusaOrderId, + orderCreatedAt: new Date(medreportOrder.created_at), + }); + + try { + await sendPrivateMessageTestResponse({ messageXml }); + } catch (error) { + console.error("Error sending private message test response: ", error); + } + + return NextResponse.json({ success: true }); +} diff --git a/lib/services/medipostTest.service.ts b/lib/services/medipostTest.service.ts new file mode 100644 index 0000000..ad2ad39 --- /dev/null +++ b/lib/services/medipostTest.service.ts @@ -0,0 +1,177 @@ +'use server'; + +import { + getClientInstitution, + getClientPerson, + getPais, + getPatient, + getProviderInstitution, +} from '@/lib/templates/medipost-order'; +import { + MedipostAction, +} from '@/lib/types/medipost'; +import axios from 'axios'; +import { uniqBy } from 'lodash'; + +import { Tables } from '@kit/supabase/database'; +import { formatDate } from 'date-fns'; +import { getAnalyses } from './analyses.service'; +import { getAnalysisElements } from './analysis-element.service'; +import { validateMedipostResponse } from './medipost.service'; + +const BASE_URL = process.env.MEDIPOST_URL!; +const USER = process.env.MEDIPOST_USER!; +const PASSWORD = process.env.MEDIPOST_PASSWORD!; +const RECIPIENT = process.env.MEDIPOST_RECIPIENT!; + +export async function sendPrivateMessageTestResponse({ + messageXml, +}: { + messageXml: string; +}) { + const body = new FormData(); + body.append('Action', MedipostAction.SendPrivateMessage); + body.append('User', USER); + body.append('Password', PASSWORD); + body.append('Receiver', RECIPIENT); + body.append('MessageType', 'Vastus'); + body.append( + 'Message', + new Blob([messageXml], { + type: 'text/xml; charset=UTF-8', + }), + ); + + const { data } = await axios.post(BASE_URL, body); + await validateMedipostResponse(data); +} + +function getRandomInt(min: number, max: number) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +export async function composeOrderTestResponseXML({ + person, + orderedAnalysisElementsIds, + orderedAnalysesIds, + orderId, + orderCreatedAt, +}: { + person: { + idCode: string; + firstName: string; + lastName: string; + phone: string; + }; + orderedAnalysisElementsIds: number[]; + orderedAnalysesIds: number[]; + orderId: string; + orderCreatedAt: Date; +}) { + const analysisElements = await getAnalysisElements({ ids: orderedAnalysisElementsIds }); + const analyses = await getAnalyses({ ids: orderedAnalysesIds }); + + const analysisGroups: Tables<{ schema: 'medreport' }, 'analysis_groups'>[] = + uniqBy( + ( + analysisElements?.flatMap(({ analysis_groups }) => analysis_groups) ?? + [] + ).concat( + analyses?.flatMap( + ({ analysis_elements }) => analysis_elements.analysis_groups, + ) ?? [], + ), + 'id', + ); + + // Tellimuse olek: + // 1 – Järjekorras, 2 – Ootel, 3 - Töös, 4 – Lõpetatud, + // 5 – Tagasi lükatud, 6 – Tühistatud. + const orderStatus = 4; + const orderNumber = 'TSU000001200'; + return ` + + ${getPais(USER, RECIPIENT, orderCreatedAt, orderId, "AL")} + + ${orderId} + ${getClientInstitution({ index: 1 })} + ${getProviderInstitution({ index: 1 })} + ${getClientPerson(person)} + Siia tuleb tellija poolne märkus + + ${getPatient(person)} + + + 1.3.6.1.4.1.28284.1.625.2.17 + 16522314 + 1.3.6.1.4.1.28284.6.2.1.244.8 + 119297000 + Veri + 16522314 + 1 + 2022-08-19 08:53:00 + 2022-08-23 15:10:00 + + + ${orderNumber} + + ${orderStatus} + ${analysisGroups.map((group) => { + let relatedAnalysisElement = analysisElements?.find( + (element) => element.analysis_groups.id === group.id, + ); + const relatedAnalyses = analyses?.filter((analysis) => { + return analysis.analysis_elements.analysis_groups.id === group.id; + }); + + if (!relatedAnalysisElement) { + relatedAnalysisElement = relatedAnalyses?.find( + (relatedAnalysis) => + relatedAnalysis.analysis_elements.analysis_groups.id === + group.id, + )?.analysis_elements; + } + + if (!relatedAnalysisElement || !relatedAnalysisElement.material_groups) { + throw new Error( + `Failed to find related analysis element for group ${group.name} (id: ${group.id})`, + ); + } + + const lower = getRandomInt(0, 100); + const upper = getRandomInt(lower + 1, 500); + const result = getRandomInt(lower, upper); + return (` + + ${group.original_id} + ${group.name} + + + ${relatedAnalysisElement.analysis_id_oid} + ${relatedAnalysisElement.analysis_id_original} + ${relatedAnalysisElement.tehik_short_loinc} + ${relatedAnalysisElement.tehik_loinc_name} + ${relatedAnalysisElement.analysis_name_lab ?? relatedAnalysisElement.tehik_loinc_name} + ${relatedAnalysisElement.id} + ${relatedAnalysisElement.id} + 4 + % + + ${result} + ${formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss')} + ${upper} + ${lower} + 0 + 1 + + + 2 + + + `); + }).join('')} + +`; +} diff --git a/lib/templates/medipost-order.ts b/lib/templates/medipost-order.ts index d54b1f9..6568c22 100644 --- a/lib/templates/medipost-order.ts +++ b/lib/templates/medipost-order.ts @@ -1,5 +1,5 @@ import { format } from 'date-fns'; -import Isikukood from 'isikukood'; +import Isikukood, { Gender } from 'isikukood'; import { Tables } from '@/packages/supabase/src/database.types'; import { DATE_FORMAT, DATE_TIME_FORMAT } from '@/lib/constants'; @@ -9,26 +9,27 @@ export const getPais = ( sender: string, recipient: string, createdAt: Date, - messageId: number, + orderId: string, + packageName = "OL", ) => { if (isProd) { // return correct data } return ` - OL + ${packageName} ${sender} ${recipient} ${format(createdAt, DATE_TIME_FORMAT)} - ${messageId} + ${orderId} argo@medreport.ee `; }; -export const getClientInstitution = () => { +export const getClientInstitution = ({ index }: { index?: number } = {}) => { if (isProd) { // return correct data } - return ` + return ` 16381793 MedReport OÜ TSU @@ -36,11 +37,11 @@ export const getClientInstitution = () => { `; }; -export const getProviderInstitution = () => { +export const getProviderInstitution = ({ index }: { index?: number } = {}) => { if (isProd) { // return correct data } - return ` + return ` 11107913 Synlab HTI Tallinn SLA @@ -68,7 +69,7 @@ export const getClientPerson = ({ ${idCode} ${lastName} ${firstName} - ${phone} + ${phone ? `${phone.startsWith('+372') ? phone : `+372${phone}`}` : ''} `; };