From e77f4447b4c2735793c813c3b8c916d265d5d1e0 Mon Sep 17 00:00:00 2001 From: Chris Rosenau Date: Sat, 22 Nov 2025 20:07:27 -0700 Subject: [PATCH] Updated product sync to include stock and urls --- .../MagentoCategoryMigrationService.php | 308 ++++++++++++++++++ 1 file changed, 308 insertions(+) diff --git a/app/Services/MagentoCategoryMigrationService.php b/app/Services/MagentoCategoryMigrationService.php index 4238f97..ecb17f5 100644 --- a/app/Services/MagentoCategoryMigrationService.php +++ b/app/Services/MagentoCategoryMigrationService.php @@ -2209,6 +2209,12 @@ public function migrateProducts($dryRun = false) if ($isNew) { $this->migrateProductUrlRewrites($m1Product->entity_id, $m2ProductId); } + + // Migrate stock inventory for all products + $this->migrateProductStock($m1Product->entity_id, $m2ProductId); + + // Migrate product website assignments for all products + $this->migrateProductWebsites($m1Product->entity_id, $m2ProductId); } } catch (Exception $e) { @@ -3499,6 +3505,153 @@ protected function getStoreMapping() return $mapping; } + /** + * Get website mapping by matching website codes or IDs from stores + */ + protected function getWebsiteMapping() + { + $mapping = []; + + try { + $m1Stores = $this->getMagento1Stores(); + $m2Stores = $this->getMagento2Stores(); + + // Build website mapping from store website_ids + // Group stores by website_id and match by store codes + $m1WebsiteMap = []; + foreach ($m1Stores as $m1Store) { + $websiteId = $m1Store->website_id ?? 0; + if ($websiteId > 0 && !isset($m1WebsiteMap[$websiteId])) { + $m1WebsiteMap[$websiteId] = $m1Store->code ?? ''; + } + } + + $m2WebsiteMap = []; + foreach ($m2Stores as $m2Store) { + $websiteId = $m2Store->website_id ?? 0; + if ($websiteId > 0 && !isset($m2WebsiteMap[$websiteId])) { + $m2WebsiteMap[$websiteId] = $m2Store->code ?? ''; + } + } + + // Match websites by store codes (websites with stores that have matching codes) + foreach ($m1WebsiteMap as $m1WebsiteId => $m1StoreCode) { + $m1StoreCodeLower = strtolower(trim($m1StoreCode)); + if (!empty($m1StoreCodeLower)) { + // Find M2 website with matching store code + foreach ($m2WebsiteMap as $m2WebsiteId => $m2StoreCode) { + $m2StoreCodeLower = strtolower(trim($m2StoreCode)); + if ($m1StoreCodeLower === $m2StoreCodeLower) { + $mapping[$m1WebsiteId] = $m2WebsiteId; + break; + } + } + } + + // If no match found, try to match by website_id (fallback) + if (!isset($mapping[$m1WebsiteId])) { + // Check if M2 has a website with the same ID + if (isset($m2WebsiteMap[$m1WebsiteId])) { + $mapping[$m1WebsiteId] = $m1WebsiteId; + } else { + // Default to website ID 1 (main website) if no match + $mapping[$m1WebsiteId] = 1; + } + } + } + + // If no mapping found, ensure at least default website (ID 1) is mapped + if (empty($mapping)) { + $mapping[1] = 1; // Default website mapping + } + } catch (Exception $e) { + Log::warning('Error building website mapping: ' . $e->getMessage()); + // Default to website ID 1 if error + $mapping[1] = 1; + } + + return $mapping; + } + + /** + * Migrate product website assignments from M1 catalog_product_website to M2 catalog_product_website + */ + protected function migrateProductWebsites($m1ProductId, $m2ProductId) + { + try { + // Get website mapping + $websiteMapping = $this->getWebsiteMapping(); + + // Get product websites from M1 + $m1ProductWebsites = DB::connection($this->magento1Connection) + ->table($this->magento1Prefix . 'catalog_product_website') + ->where('product_id', $m1ProductId) + ->get(); + + if ($m1ProductWebsites->isEmpty()) { + // No websites assigned in M1, assign to default website (ID 1) + $this->assignProductToWebsite($m2ProductId, 1); + return; + } + + // Migrate each website assignment + foreach ($m1ProductWebsites as $m1ProductWebsite) { + $m1WebsiteId = $m1ProductWebsite->website_id ?? 1; + $m2WebsiteId = isset($websiteMapping[$m1WebsiteId]) ? $websiteMapping[$m1WebsiteId] : 1; + + // Check if assignment already exists + $exists = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'catalog_product_website') + ->where('product_id', $m2ProductId) + ->where('website_id', $m2WebsiteId) + ->exists(); + + if (!$exists) { + DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'catalog_product_website') + ->insert([ + 'product_id' => $m2ProductId, + 'website_id' => $m2WebsiteId, + ]); + } + } + } catch (Exception $e) { + Log::error("Error migrating product websites for M1 ID {$m1ProductId}, M2 ID {$m2ProductId}: " . $e->getMessage()); + // Don't throw - just log the error and assign to default website + try { + $this->assignProductToWebsite($m2ProductId, 1); + } catch (Exception $e2) { + Log::error("Error assigning product to default website: " . $e2->getMessage()); + } + } + } + + /** + * Assign a product to a website + */ + protected function assignProductToWebsite($productId, $websiteId) + { + try { + // Check if assignment already exists + $exists = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'catalog_product_website') + ->where('product_id', $productId) + ->where('website_id', $websiteId) + ->exists(); + + if (!$exists) { + DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'catalog_product_website') + ->insert([ + 'product_id' => $productId, + 'website_id' => $websiteId, + ]); + } + } catch (Exception $e) { + Log::error("Error assigning product {$productId} to website {$websiteId}: " . $e->getMessage()); + } + } + /** * Migrate URL rewrites from M1 core_url_rewrite to M2 url_rewrite for a product */ @@ -3573,6 +3726,161 @@ protected function migrateProductUrlRewrites($m1ProductId, $m2ProductId) } } + /** + * Migrate stock inventory from M1 cataloginventory_stock_item to M2 cataloginventory_stock_item + */ + protected function migrateProductStock($m1ProductId, $m2ProductId) + { + try { + // Get stock item from M1 + $m1StockItem = DB::connection($this->magento1Connection) + ->table($this->magento1Prefix . 'cataloginventory_stock_item') + ->where('product_id', $m1ProductId) + ->first(); + + if (!$m1StockItem) { + // No stock record in M1, create a default one in M2 + $this->createDefaultStockItem($m2ProductId); + return; + } + + // Check if stock item already exists in M2 + $existingStockItem = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'cataloginventory_stock_item') + ->where('product_id', $m2ProductId) + ->first(); + + // Prepare stock data for M2 + $stockData = [ + 'product_id' => $m2ProductId, + 'stock_id' => $m1StockItem->stock_id ?? 1, // Default to 1 if not set + 'qty' => $m1StockItem->qty ?? 0, + 'is_in_stock' => $m1StockItem->is_in_stock ?? 0, + 'manage_stock' => $m1StockItem->manage_stock ?? 1, + 'use_config_manage_stock' => $m1StockItem->use_config_manage_stock ?? 1, + 'min_qty' => $m1StockItem->min_qty ?? 0, + 'min_sale_qty' => $m1StockItem->min_sale_qty ?? 1, + 'max_sale_qty' => $m1StockItem->max_sale_qty ?? 0, + 'is_qty_decimal' => $m1StockItem->is_qty_decimal ?? 0, + 'backorders' => $m1StockItem->backorders ?? 0, + 'notify_stock_qty' => $m1StockItem->notify_stock_qty ?? 0, + 'use_config_notify_stock_qty' => $m1StockItem->use_config_notify_stock_qty ?? 1, + 'use_config_min_qty' => $m1StockItem->use_config_min_qty ?? 1, + 'use_config_min_sale_qty' => $m1StockItem->use_config_min_sale_qty ?? 1, + 'use_config_max_sale_qty' => $m1StockItem->use_config_max_sale_qty ?? 1, + 'use_config_backorders' => $m1StockItem->use_config_backorders ?? 1, + 'use_config_enable_qty_inc' => $m1StockItem->use_config_enable_qty_inc ?? 1, + 'enable_qty_increments' => $m1StockItem->enable_qty_increments ?? 0, + 'use_config_qty_increments' => $m1StockItem->use_config_qty_increments ?? 1, + 'qty_increments' => $m1StockItem->qty_increments ?? 0, + ]; + + // Add optional fields if they exist in M1 + if (isset($m1StockItem->low_stock_date)) { + $stockData['low_stock_date'] = $m1StockItem->low_stock_date; + } + if (isset($m1StockItem->is_decimal_divided)) { + $stockData['is_decimal_divided'] = $m1StockItem->is_decimal_divided; + } + + if ($existingStockItem) { + // Update existing stock item + DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'cataloginventory_stock_item') + ->where('product_id', $m2ProductId) + ->update($stockData); + } else { + // Insert new stock item + DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'cataloginventory_stock_item') + ->insert($stockData); + } + + // Also update cataloginventory_stock_status if it exists + try { + $stockStatusData = [ + 'product_id' => $m2ProductId, + 'website_id' => 0, // Default website + 'stock_id' => $m1StockItem->stock_id ?? 1, + 'qty' => $m1StockItem->qty ?? 0, + 'stock_status' => $m1StockItem->is_in_stock ?? 0, + ]; + + $existingStatus = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'cataloginventory_stock_status') + ->where('product_id', $m2ProductId) + ->where('website_id', 0) + ->where('stock_id', $stockStatusData['stock_id']) + ->first(); + + if ($existingStatus) { + DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'cataloginventory_stock_status') + ->where('product_id', $m2ProductId) + ->where('website_id', 0) + ->where('stock_id', $stockStatusData['stock_id']) + ->update($stockStatusData); + } else { + DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'cataloginventory_stock_status') + ->insert($stockStatusData); + } + } catch (Exception $e) { + // Stock status table might not exist or have different structure, log and continue + Log::debug("Could not update stock status table: " . $e->getMessage()); + } + + } catch (Exception $e) { + Log::error("Error migrating stock for product M1 ID {$m1ProductId}, M2 ID {$m2ProductId}: " . $e->getMessage()); + // Don't throw - just log the error and continue + } + } + + /** + * Create a default stock item for a product + */ + protected function createDefaultStockItem($productId) + { + try { + // Check if stock item already exists + $exists = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'cataloginventory_stock_item') + ->where('product_id', $productId) + ->exists(); + + if (!$exists) { + // Create default stock item + DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'cataloginventory_stock_item') + ->insert([ + 'product_id' => $productId, + 'stock_id' => 1, + 'qty' => 0, + 'is_in_stock' => 0, + 'manage_stock' => 1, + 'use_config_manage_stock' => 1, + 'min_qty' => 0, + 'min_sale_qty' => 1, + 'max_sale_qty' => 0, + 'is_qty_decimal' => 0, + 'backorders' => 0, + 'notify_stock_qty' => 0, + 'use_config_notify_stock_qty' => 1, + 'use_config_min_qty' => 1, + 'use_config_min_sale_qty' => 1, + 'use_config_max_sale_qty' => 1, + 'use_config_backorders' => 1, + 'use_config_enable_qty_inc' => 1, + 'enable_qty_increments' => 0, + 'use_config_qty_increments' => 1, + 'qty_increments' => 0, + ]); + } + } catch (Exception $e) { + Log::error("Error creating default stock item for product ID {$productId}: " . $e->getMessage()); + } + } + /** * Ensure product is enabled and visible (required for products to appear in categories after reindex) */