Explorar o código

fix: improve ShopRenter order formatting and add Qdrant support to list_orders #88

Claude hai 5 meses
pai
achega
3f0b07831c
Modificáronse 1 ficheiros con 196 adicións e 58 borrados
  1. 196 58
      supabase/functions/mcp-shoprenter/index.ts

+ 196 - 58
supabase/functions/mcp-shoprenter/index.ts

@@ -202,31 +202,84 @@ function formatCustomerForLlm(customer: any): LlmCustomer {
 
 
 /**
 /**
  * Convert ShopRenter order to LLM-friendly format
  * Convert ShopRenter order to LLM-friendly format
+ *
+ * ShopRenter API returns orders from /orderExtend endpoint with this structure:
+ * - firstname, lastname, email, phone (top-level fields)
+ * - orderProducts[] array with product details
+ * - orderTotals[] array with pricing breakdown
+ * - shippingAddress1, shippingCity, etc.
+ * - paymentAddress1, paymentCity, etc.
+ *
+ * We preserve the raw data and apply cleanResponseData to remove URLs/empty values
  */
  */
-function formatOrderForLlm(order: any): LlmOrder {
-  const customerName = order.customer_name ||
-    (order.customer ? `${order.customer.firstname || ''} ${order.customer.lastname || ''}`.trim() : 'Unknown');
-  const customerEmail = order.customer_email || order.customer?.email || '';
-  const customerPhone = order.customer_phone || order.customer?.phone || undefined;
+function formatOrderForLlm(order: any): any {
+  // ShopRenter orderExtend API returns the order data in a specific structure
+  // We'll preserve most of the original structure but ensure key fields are present
 
 
-  return {
+  const formattedOrder: any = {
     id: order.id,
     id: order.id,
-    orderNumber: order.innerId || order.order_number || order.number || order.id,
-    customer: {
-      name: customerName,
-      email: customerEmail,
-      phone: customerPhone
-    },
-    status: order.status,
+    orderNumber: order.innerId || order.orderNumber || order.id,
+
+    // Customer details (top-level fields in ShopRenter API)
+    firstname: order.firstname || '',
+    lastname: order.lastname || '',
+    email: order.email || '',
+    phone: order.phone || '',
+
+    // Order status and totals
+    status: order.status || order.orderStatus,
     total: order.total || '0',
     total: order.total || '0',
-    currency: order.currency || 'HUF',
-    items: (order.items || order.line_items || []).map((item: any) => ({
-      name: item.name || item.product_name || 'Unknown',
-      quantity: item.quantity || 1,
-      price: item.price || item.total || '0'
-    })),
-    createdAt: order.created_at || order.date_created || new Date().toISOString()
+    currency: order.currency || {},
+
+    // Shipping details
+    shippingFirstname: order.shippingFirstname,
+    shippingLastname: order.shippingLastname,
+    shippingAddress1: order.shippingAddress1,
+    shippingAddress2: order.shippingAddress2,
+    shippingCity: order.shippingCity,
+    shippingPostcode: order.shippingPostcode,
+    shippingCountryName: order.shippingCountryName,
+    shippingMethodName: order.shippingMethodName,
+
+    // Payment details
+    paymentFirstname: order.paymentFirstname,
+    paymentLastname: order.paymentLastname,
+    paymentAddress1: order.paymentAddress1,
+    paymentAddress2: order.paymentAddress2,
+    paymentCity: order.paymentCity,
+    paymentPostcode: order.paymentPostcode,
+    paymentCountryName: order.paymentCountryName,
+    paymentMethodName: order.paymentMethodName,
+
+    // Order items (orderProducts array)
+    orderProducts: order.orderProducts || [],
+
+    // Order totals breakdown
+    orderTotals: order.orderTotals || [],
+
+    // Additional fields
+    comment: order.comment,
+    dateCreated: order.dateCreated || order.created_at,
+    dateUpdated: order.dateUpdated || order.updated_at,
+
+    // Language and other metadata
+    language: order.language,
+
+    // Coupon info
+    coupon: order.coupon,
+
+    // Shipping mode
+    shippingMode: order.shippingMode
   };
   };
+
+  // Remove undefined values (cleanResponseData will handle empty strings, empty arrays, etc.)
+  Object.keys(formattedOrder).forEach(key => {
+    if (formattedOrder[key] === undefined) {
+      delete formattedOrder[key];
+    }
+  });
+
+  return formattedOrder;
 }
 }
 
 
 /**
 /**
@@ -590,6 +643,8 @@ async function handleListOrders(args: Record<string, any>): Promise<ToolCallResu
     limit = 5
     limit = 5
   } = args;
   } = args;
 
 
+  console.log('[MCP ShopRenter] handleListOrders called with:', { shop_id, email, status, limit });
+
   // Validate at least one filter is provided
   // Validate at least one filter is provided
   const hasFilter = createdAtMin || createdAtMax || updatedAtMin || updatedAtMax || email;
   const hasFilter = createdAtMin || createdAtMax || updatedAtMin || updatedAtMax || email;
   if (!hasFilter) {
   if (!hasFilter) {
@@ -610,18 +665,21 @@ async function handleListOrders(args: Record<string, any>): Promise<ToolCallResu
   // Validate shop exists and is ShopRenter
   // Validate shop exists and is ShopRenter
   const { data: store, error: storeError } = await supabase
   const { data: store, error: storeError } = await supabase
     .from('stores')
     .from('stores')
-    .select('id, platform_name, data_access_permissions')
+    .select('id, platform_name, store_name, data_access_permissions')
     .eq('id', shop_id)
     .eq('id', shop_id)
     .eq('platform_name', 'shoprenter')
     .eq('platform_name', 'shoprenter')
     .single();
     .single();
 
 
   if (storeError || !store) {
   if (storeError || !store) {
+    console.error('[MCP ShopRenter] Store not found:', { shop_id, error: storeError });
     return {
     return {
       content: [{ type: 'text', text: JSON.stringify({ error: 'ShopRenter store not found' }) }],
       content: [{ type: 'text', text: JSON.stringify({ error: 'ShopRenter store not found' }) }],
       isError: true
       isError: true
     };
     };
   }
   }
 
 
+  console.log('[MCP ShopRenter] Store found:', { id: store.id, store_name: store.store_name });
+
   // Check permissions
   // Check permissions
   const permissions = store.data_access_permissions as any;
   const permissions = store.data_access_permissions as any;
   if (permissions && !permissions.allow_order_access) {
   if (permissions && !permissions.allow_order_access) {
@@ -632,47 +690,127 @@ async function handleListOrders(args: Record<string, any>): Promise<ToolCallResu
   }
   }
 
 
   try {
   try {
-    // Build filters using correct parameter names
-    const filters: ShopRenterOrderFilters = {};
-    if (status) filters.status = status;
-    if (email) filters.email = email;
-    if (createdAtMin) filters.createdAtMin = createdAtMin;
-    if (createdAtMax) filters.createdAtMax = createdAtMax;
-    if (updatedAtMin) filters.updatedAtMin = updatedAtMin;
-    if (updatedAtMax) filters.updatedAtMax = updatedAtMax;
-
-    // Fetch orders from ShopRenter API
-    const response = await fetchOrders(shop_id, 0, actualLimit, filters);
-    const orders = Array.isArray(response) ? response : (response.items || response.data || response.orders || []);
+    // Check if Qdrant is enabled for this store
+    const qdrantConfig = await getStoreQdrantConfig(shop_id);
+
+    if (qdrantConfig && qdrantConfig.enabled && qdrantConfig.syncOrders) {
+      console.log('[MCP ShopRenter] Using Qdrant for orders');
 
 
-    // Apply limit
-    const limitedOrders = orders.slice(0, actualLimit);
+      // Query from Qdrant with filters
+      const qdrantOrders = await queryQdrantOrders(
+        shop_id,
+        qdrantConfig.shopname,
+        { email, status },
+        actualLimit
+      );
 
 
-    // Format for LLM
-    const formattedOrders = limitedOrders.map(formatOrderForLlm);
+      // Apply date filters client-side if provided
+      let filteredOrders = qdrantOrders;
 
 
-    // Clean response data
-    const cleanedOrders = cleanResponseData(formattedOrders);
+      if (createdAtMin) {
+        const minDate = new Date(createdAtMin);
+        filteredOrders = filteredOrders.filter((o: any) => {
+          const created = new Date(o.dateCreated || o.createdAt || 0);
+          return created >= minDate;
+        });
+      }
 
 
-    return {
-      content: [{
-        type: 'text',
-        text: JSON.stringify({
-          count: formattedOrders.length,
-          limit: actualLimit,
-          source: 'api',
-          filters_applied: {
-            createdAtMin,
-            createdAtMax,
-            updatedAtMin,
-            updatedAtMax,
-            email,
-            status
-          },
-          orders: cleanedOrders
-        })
-      }]
-    };
+      if (createdAtMax) {
+        const maxDate = new Date(createdAtMax);
+        filteredOrders = filteredOrders.filter((o: any) => {
+          const created = new Date(o.dateCreated || o.createdAt || 0);
+          return created <= maxDate;
+        });
+      }
+
+      if (updatedAtMin) {
+        const minDate = new Date(updatedAtMin);
+        filteredOrders = filteredOrders.filter((o: any) => {
+          const updated = new Date(o.dateUpdated || o.updatedAt || 0);
+          return updated >= minDate;
+        });
+      }
+
+      if (updatedAtMax) {
+        const maxDate = new Date(updatedAtMax);
+        filteredOrders = filteredOrders.filter((o: any) => {
+          const updated = new Date(o.dateUpdated || o.updatedAt || 0);
+          return updated <= maxDate;
+        });
+      }
+
+      // Limit after filtering
+      filteredOrders = filteredOrders.slice(0, actualLimit);
+
+      // Clean response data
+      const cleanedOrders = cleanResponseData(filteredOrders);
+
+      return {
+        content: [{
+          type: 'text',
+          text: JSON.stringify({
+            count: filteredOrders.length,
+            limit: actualLimit,
+            source: 'qdrant',
+            filters_applied: {
+              createdAtMin,
+              createdAtMax,
+              updatedAtMin,
+              updatedAtMax,
+              email,
+              status
+            },
+            orders: cleanedOrders
+          })
+        }]
+      };
+    } else {
+      console.log('[MCP ShopRenter] Qdrant not enabled, using ShopRenter API');
+
+      // Build filters using correct parameter names
+      const filters: ShopRenterOrderFilters = {};
+      if (status) filters.status = status;
+      if (email) filters.email = email;
+      if (createdAtMin) filters.createdAtMin = createdAtMin;
+      if (createdAtMax) filters.createdAtMax = createdAtMax;
+      if (updatedAtMin) filters.updatedAtMin = updatedAtMin;
+      if (updatedAtMax) filters.updatedAtMax = updatedAtMax;
+
+      // Fetch orders from ShopRenter API
+      const response = await fetchOrders(shop_id, 0, actualLimit, filters);
+      const orders = Array.isArray(response) ? response : (response.items || response.data || response.orders || []);
+
+      console.log('[MCP ShopRenter] API response:', { ordersCount: orders.length });
+
+      // Apply limit
+      const limitedOrders = orders.slice(0, actualLimit);
+
+      // Format for LLM (now preserves all ShopRenter fields)
+      const formattedOrders = limitedOrders.map(formatOrderForLlm);
+
+      // Clean response data (removes URLs, empty values)
+      const cleanedOrders = cleanResponseData(formattedOrders);
+
+      return {
+        content: [{
+          type: 'text',
+          text: JSON.stringify({
+            count: formattedOrders.length,
+            limit: actualLimit,
+            source: 'api',
+            filters_applied: {
+              createdAtMin,
+              createdAtMax,
+              updatedAtMin,
+              updatedAtMax,
+              email,
+              status
+            },
+            orders: cleanedOrders
+          })
+        }]
+      };
+    }
   } catch (error) {
   } catch (error) {
     console.error('[MCP ShopRenter] Error fetching orders:', error);
     console.error('[MCP ShopRenter] Error fetching orders:', error);
     return {
     return {