Browse Source

feat: add ShopRenter orders cache table and sync #31

- Created shoprenter_orders_cache table with customer phone field
- Added order sync logic to shoprenter-sync Edge Function
- Updated ShopRenterOrder interface with comprehensive fields
- Added indexes for efficient queries (order_number, status, customer_email, customer_phone)
- Implemented RLS policies for data security
- Created get_shoprenter_sync_status helper function

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude 5 months ago
parent
commit
5e9878f5d6

+ 24 - 4
supabase/functions/_shared/shoprenter-client.ts

@@ -30,13 +30,33 @@ export interface ShopRenterCustomer {
 
 export interface ShopRenterOrder {
   id: string
-  order_number: string
-  customer_id: string
+  order_number?: string
+  number?: string
+  customer_id?: string
+  customer?: {
+    firstname?: string
+    lastname?: string
+    email?: string
+    phone?: string
+  }
+  customer_name?: string
+  customer_email?: string
+  customer_phone?: string
   status: string
   total: string
-  currency: string
-  created_at: string
+  currency?: string
+  created_at?: string
+  date_created?: string
   items?: any[]
+  line_items?: any[]
+  billing_address?: {
+    phone?: string
+    [key: string]: any
+  }
+  shipping_address?: {
+    phone?: string
+    [key: string]: any
+  }
 }
 
 // Get valid access token (with automatic refresh)

+ 62 - 0
supabase/functions/shoprenter-sync/index.ts

@@ -133,6 +133,68 @@ serve(async (req) => {
       syncStats.products.errors++
     }
 
+    // Sync Orders
+    try {
+      console.log('[ShopRenter] Syncing orders...')
+      let page = 1
+      let hasMore = true
+      const limit = 50
+
+      while (hasMore) {
+        const ordersData = await fetchOrders(storeId, page, limit)
+
+        if (ordersData.items && ordersData.items.length > 0) {
+          const ordersToCache = ordersData.items.map((order: any) => ({
+            store_id: storeId,
+            shoprenter_order_id: order.id,
+            order_number: order.order_number || order.number || order.id,
+            status: order.status,
+            total: parseFloat(order.total) || 0,
+            currency: order.currency || 'HUF',
+            customer_name: order.customer_name || `${order.customer?.firstname || ''} ${order.customer?.lastname || ''}`.trim() || null,
+            customer_email: order.customer_email || order.customer?.email || null,
+            customer_phone: order.customer_phone || order.customer?.phone || order.billing_address?.phone || order.shipping_address?.phone || null,
+            line_items: order.items || order.line_items || [],
+            billing_address: order.billing_address || null,
+            shipping_address: order.shipping_address || null,
+            order_created_at: order.created_at || order.date_created || new Date().toISOString(),
+            raw_data: order,
+            last_synced_at: new Date().toISOString()
+          }))
+
+          const { error: upsertError } = await supabaseAdmin
+            .from('shoprenter_orders_cache')
+            .upsert(ordersToCache, {
+              onConflict: 'store_id,shoprenter_order_id'
+            })
+
+          if (upsertError) {
+            console.error('[ShopRenter] Error caching orders:', upsertError)
+            syncStats.orders.errors += ordersToCache.length
+          } else {
+            syncStats.orders.synced += ordersToCache.length
+          }
+
+          // Check if there are more pages
+          if (ordersData.pagination && ordersData.pagination.total) {
+            const totalPages = Math.ceil(ordersData.pagination.total / limit)
+            hasMore = page < totalPages
+          } else {
+            hasMore = ordersData.items.length === limit
+          }
+
+          page++
+        } else {
+          hasMore = false
+        }
+      }
+
+      console.log(`[ShopRenter] Orders synced: ${syncStats.orders.synced}`)
+    } catch (error) {
+      console.error('[ShopRenter] Order sync error:', error)
+      syncStats.orders.errors++
+    }
+
     // Sync Customers
     try {
       console.log('[ShopRenter] Syncing customers...')

+ 129 - 0
supabase/migrations/20251031_shoprenter_orders_cache.sql

@@ -0,0 +1,129 @@
+-- Migration: ShopRenter Orders Cache Table
+-- Description: Creates table for caching ShopRenter orders data
+-- Date: 2025-10-31
+-- Related Issue: #31
+
+-- ============================================================================
+-- STEP 1: Create ShopRenter Orders Cache Table
+-- ============================================================================
+
+CREATE TABLE IF NOT EXISTS shoprenter_orders_cache (
+  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+  store_id UUID NOT NULL REFERENCES stores(id) ON DELETE CASCADE,
+  shoprenter_order_id TEXT NOT NULL,
+  order_number TEXT,
+  status TEXT,
+  total DECIMAL(10, 2),
+  currency TEXT,
+  customer_name TEXT,
+  customer_email TEXT,
+  customer_phone TEXT,  -- Critical for calling customers
+  line_items JSONB,
+  billing_address JSONB,
+  shipping_address JSONB,
+  order_created_at TIMESTAMPTZ,
+  raw_data JSONB,
+  last_synced_at TIMESTAMPTZ DEFAULT NOW(),
+  created_at TIMESTAMPTZ DEFAULT NOW(),
+  UNIQUE(store_id, shoprenter_order_id)
+);
+
+-- ============================================================================
+-- STEP 2: Create Indexes for Efficient Queries
+-- ============================================================================
+
+-- Index for querying orders by store
+CREATE INDEX IF NOT EXISTS idx_shoprenter_orders_store_id
+  ON shoprenter_orders_cache(store_id, order_created_at DESC);
+
+-- Index for querying orders by order number
+CREATE INDEX IF NOT EXISTS idx_shoprenter_orders_order_number
+  ON shoprenter_orders_cache(store_id, order_number);
+
+-- Index for querying orders by status
+CREATE INDEX IF NOT EXISTS idx_shoprenter_orders_status
+  ON shoprenter_orders_cache(store_id, status)
+  WHERE status IS NOT NULL;
+
+-- Index for querying orders by customer email
+CREATE INDEX IF NOT EXISTS idx_shoprenter_orders_customer_email
+  ON shoprenter_orders_cache(store_id, customer_email)
+  WHERE customer_email IS NOT NULL;
+
+-- Index for querying orders by customer phone
+CREATE INDEX IF NOT EXISTS idx_shoprenter_orders_customer_phone
+  ON shoprenter_orders_cache(store_id, customer_phone)
+  WHERE customer_phone IS NOT NULL;
+
+-- Index for sorting by last sync
+CREATE INDEX IF NOT EXISTS idx_shoprenter_orders_last_synced
+  ON shoprenter_orders_cache(store_id, last_synced_at DESC);
+
+-- ============================================================================
+-- STEP 3: Enable Row Level Security
+-- ============================================================================
+
+ALTER TABLE shoprenter_orders_cache ENABLE ROW LEVEL SECURITY;
+
+-- ============================================================================
+-- STEP 4: Create RLS Policies
+-- ============================================================================
+
+-- Policy: Users can view orders from their own stores
+CREATE POLICY "Users can view their ShopRenter orders"
+  ON shoprenter_orders_cache FOR SELECT
+  TO authenticated
+  USING (
+    EXISTS (
+      SELECT 1 FROM stores s
+      WHERE s.id = shoprenter_orders_cache.store_id
+      AND s.user_id = auth.uid()
+    )
+  );
+
+-- Policy: Service role can manage all orders
+CREATE POLICY "Service role can manage ShopRenter orders"
+  ON shoprenter_orders_cache FOR ALL
+  TO service_role
+  USING (true);
+
+-- ============================================================================
+-- STEP 5: Create Helper Function to Get ShopRenter Sync Status
+-- ============================================================================
+
+CREATE OR REPLACE FUNCTION get_shoprenter_sync_status(p_store_id UUID)
+RETURNS TABLE (
+  last_sync_at TIMESTAMPTZ,
+  sync_status TEXT,
+  products_count BIGINT,
+  orders_count BIGINT,
+  customers_count BIGINT
+) AS $$
+BEGIN
+  RETURN QUERY
+  SELECT
+    GREATEST(
+      (SELECT MAX(last_synced_at) FROM shoprenter_products_cache WHERE store_id = p_store_id),
+      (SELECT MAX(last_synced_at) FROM shoprenter_orders_cache WHERE store_id = p_store_id),
+      (SELECT MAX(last_synced_at) FROM shoprenter_customers_cache WHERE store_id = p_store_id)
+    ) as last_sync_at,
+    'idle'::TEXT as sync_status,
+    (SELECT COUNT(*) FROM shoprenter_products_cache WHERE store_id = p_store_id) as products_count,
+    (SELECT COUNT(*) FROM shoprenter_orders_cache WHERE store_id = p_store_id) as orders_count,
+    (SELECT COUNT(*) FROM shoprenter_customers_cache WHERE store_id = p_store_id) as customers_count;
+END;
+$$ LANGUAGE plpgsql SECURITY DEFINER;
+
+-- ============================================================================
+-- Migration Complete
+-- ============================================================================
+
+DO $$
+BEGIN
+  RAISE NOTICE 'ShopRenter orders cache migration completed successfully';
+  RAISE NOTICE 'Created table: shoprenter_orders_cache';
+  RAISE NOTICE 'Next steps:';
+  RAISE NOTICE '1. Update shoprenter-sync Edge Function to include order syncing';
+  RAISE NOTICE '2. Update shoprenter-scheduled-sync Edge Function to include orders';
+  RAISE NOTICE '3. Test sync with connected ShopRenter stores';
+END $$;