Updated product sync to include stock and urls

This commit is contained in:
Chris Rosenau 2025-11-22 20:07:27 -07:00
parent 0b93c87b93
commit e77f4447b4
1 changed files with 308 additions and 0 deletions

View File

@ -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)
*/