Added order syncing
This commit is contained in:
parent
353f9e0564
commit
fecc231e30
|
|
@ -116,6 +116,37 @@ public function getMigrationProgress(Request $request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get customer statistics
|
||||||
|
*/
|
||||||
|
public function getStatistics()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$m1Customers = $this->migrationService->getMagento1Customers();
|
||||||
|
$m2Customers = $this->migrationService->getMagento2Customers();
|
||||||
|
$m1CustomersNotInM2 = $this->migrationService->getM1CustomersNotInM2();
|
||||||
|
$m2CustomersNotInM1 = $this->migrationService->getM2CustomersNotInM1();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'statistics' => [
|
||||||
|
'm1Customers' => $m1Customers->count(),
|
||||||
|
'm2Customers' => $m2Customers->count(),
|
||||||
|
'm1CustomersNotInM2' => $m1CustomersNotInM2->count(),
|
||||||
|
'm2CustomersNotInM1' => $m2CustomersNotInM1->count(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Get customer statistics error: ' . $e->getMessage());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Failed to get statistics: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a single customer from Magento 2
|
* Delete a single customer from Magento 2
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Services\MagentoCategoryMigrationService;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class OrdersController extends Controller
|
||||||
|
{
|
||||||
|
protected $migrationService;
|
||||||
|
|
||||||
|
public function __construct(MagentoCategoryMigrationService $migrationService)
|
||||||
|
{
|
||||||
|
$this->migrationService = $migrationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the orders page
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$m1Orders = $this->migrationService->getMagento1Orders();
|
||||||
|
$m2Orders = $this->migrationService->getMagento2Orders();
|
||||||
|
$m1OrdersNotInM2 = $this->migrationService->getM1OrdersNotInM2();
|
||||||
|
$m2OrdersNotInM1 = $this->migrationService->getM2OrdersNotInM1();
|
||||||
|
|
||||||
|
return view('orders.index', [
|
||||||
|
'm1Orders' => $m1Orders,
|
||||||
|
'm2Orders' => $m2Orders,
|
||||||
|
'm1OrdersNotInM2' => $m1OrdersNotInM2,
|
||||||
|
'm2OrdersNotInM1' => $m2OrdersNotInM1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate all orders from Magento 1 to Magento 2
|
||||||
|
*/
|
||||||
|
public function migrateOrders(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Handle both JSON and form data
|
||||||
|
$dryRun = false;
|
||||||
|
$progressKey = null;
|
||||||
|
|
||||||
|
if ($request->isJson()) {
|
||||||
|
$dryRun = $request->json()->get('dry_run', false);
|
||||||
|
$progressKey = $request->json()->get('progress_key', null);
|
||||||
|
} else {
|
||||||
|
$dryRun = $request->input('dry_run', false);
|
||||||
|
$progressKey = $request->input('progress_key', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert string "true"/"false" to boolean if needed
|
||||||
|
if (is_string($dryRun)) {
|
||||||
|
$dryRun = filter_var($dryRun, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->migrationService->migrateOrders($dryRun, $progressKey);
|
||||||
|
|
||||||
|
return response()->json($result, $result['success'] ? 200 : 400);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Order migration error: ' . $e->getMessage());
|
||||||
|
Log::error('Stack trace: ' . $e->getTraceAsString());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Migration failed: ' . $e->getMessage(),
|
||||||
|
'added' => 0,
|
||||||
|
'updated' => 0,
|
||||||
|
'errors' => 0,
|
||||||
|
'log' => []
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get order migration progress
|
||||||
|
*/
|
||||||
|
public function getMigrationProgress(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$progressKey = $request->input('progress_key');
|
||||||
|
|
||||||
|
if (empty($progressKey)) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Progress key is required'
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$progress = Cache::get($progressKey);
|
||||||
|
|
||||||
|
if (!$progress) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Progress not found',
|
||||||
|
'progress' => null
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'progress' => $progress
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Get order migration progress error: ' . $e->getMessage());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Failed to get progress: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get order statistics
|
||||||
|
*/
|
||||||
|
public function getStatistics()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$m1Orders = $this->migrationService->getMagento1Orders();
|
||||||
|
$m2Orders = $this->migrationService->getMagento2Orders();
|
||||||
|
$m1OrdersNotInM2 = $this->migrationService->getM1OrdersNotInM2();
|
||||||
|
$m2OrdersNotInM1 = $this->migrationService->getM2OrdersNotInM1();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'statistics' => [
|
||||||
|
'm1Orders' => $m1Orders->count(),
|
||||||
|
'm2Orders' => $m2Orders->count(),
|
||||||
|
'm1OrdersNotInM2' => $m1OrdersNotInM2->count(),
|
||||||
|
'm2OrdersNotInM1' => $m2OrdersNotInM1->count(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Get order statistics error: ' . $e->getMessage());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Failed to get statistics: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a single order from Magento 2
|
||||||
|
*/
|
||||||
|
public function deleteM2Order(Request $request, $orderId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$result = $this->migrationService->deleteM2Order($orderId);
|
||||||
|
|
||||||
|
return response()->json($result, $result['success'] ? 200 : 400);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Delete M2 order error: ' . $e->getMessage());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Deletion failed: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6581,5 +6581,345 @@ protected function updateM2Record($m2Table, $rowArray)
|
||||||
->update($cleanData);
|
->update($cleanData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all orders from Magento 1
|
||||||
|
*/
|
||||||
|
public function getMagento1Orders()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$orders = DB::connection($this->magento1Connection)
|
||||||
|
->table($this->magento1Prefix . 'sales_flat_order')
|
||||||
|
->select('entity_id', 'increment_id', 'customer_email', 'status', 'grand_total', 'created_at', 'updated_at')
|
||||||
|
->orderBy('entity_id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $orders;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('Error fetching Magento 1 orders: ' . $e->getMessage());
|
||||||
|
Log::error('Stack trace: ' . $e->getTraceAsString());
|
||||||
|
return collect([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all orders from Magento 2
|
||||||
|
*/
|
||||||
|
public function getMagento2Orders()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$orders = DB::connection($this->magento2Connection)
|
||||||
|
->table($this->magento2Prefix . 'sales_order')
|
||||||
|
->select('entity_id', 'increment_id', 'customer_email', 'status', 'grand_total', 'created_at', 'updated_at')
|
||||||
|
->orderBy('entity_id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $orders;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('Error fetching Magento 2 orders: ' . $e->getMessage());
|
||||||
|
Log::error('Stack trace: ' . $e->getTraceAsString());
|
||||||
|
return collect([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Magento 1 orders that don't exist in Magento 2
|
||||||
|
*/
|
||||||
|
public function getM1OrdersNotInM2()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$m1Orders = $this->getMagento1Orders();
|
||||||
|
$m2Orders = $this->getMagento2Orders();
|
||||||
|
|
||||||
|
// Get all M2 increment IDs - create a set for faster lookup
|
||||||
|
$m2IncrementIdsSet = [];
|
||||||
|
foreach ($m2Orders as $m2Order) {
|
||||||
|
$incrementId = $m2Order->increment_id ?? null;
|
||||||
|
if (!empty($incrementId) && is_string($incrementId)) {
|
||||||
|
$normalizedIncrementId = trim($incrementId);
|
||||||
|
if (!empty($normalizedIncrementId)) {
|
||||||
|
$m2IncrementIdsSet[$normalizedIncrementId] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter M1 orders that don't exist in M2
|
||||||
|
$missingOrders = collect();
|
||||||
|
foreach ($m1Orders as $m1Order) {
|
||||||
|
$incrementId = $m1Order->increment_id ?? null;
|
||||||
|
if (!empty($incrementId) && is_string($incrementId)) {
|
||||||
|
$normalizedIncrementId = trim($incrementId);
|
||||||
|
if (!empty($normalizedIncrementId) && !isset($m2IncrementIdsSet[$normalizedIncrementId])) {
|
||||||
|
$missingOrders->push($m1Order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $missingOrders->values();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('Error fetching missing orders: ' . $e->getMessage());
|
||||||
|
Log::error('Stack trace: ' . $e->getTraceAsString());
|
||||||
|
return collect([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Magento 2 orders that don't exist in Magento 1
|
||||||
|
*/
|
||||||
|
public function getM2OrdersNotInM1()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$m1Orders = $this->getMagento1Orders();
|
||||||
|
$m2Orders = $this->getMagento2Orders();
|
||||||
|
|
||||||
|
// Get all M1 increment IDs - create a set for faster lookup
|
||||||
|
$m1IncrementIdsSet = [];
|
||||||
|
foreach ($m1Orders as $m1Order) {
|
||||||
|
$incrementId = $m1Order->increment_id ?? null;
|
||||||
|
if (!empty($incrementId) && is_string($incrementId)) {
|
||||||
|
$normalizedIncrementId = trim($incrementId);
|
||||||
|
if (!empty($normalizedIncrementId)) {
|
||||||
|
$m1IncrementIdsSet[$normalizedIncrementId] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter M2 orders that don't exist in M1
|
||||||
|
$missingOrders = collect();
|
||||||
|
foreach ($m2Orders as $m2Order) {
|
||||||
|
$incrementId = $m2Order->increment_id ?? null;
|
||||||
|
if (!empty($incrementId) && is_string($incrementId)) {
|
||||||
|
$normalizedIncrementId = trim($incrementId);
|
||||||
|
if (!empty($normalizedIncrementId) && !isset($m1IncrementIdsSet[$normalizedIncrementId])) {
|
||||||
|
$missingOrders->push($m2Order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $missingOrders->values();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('Error fetching M2 orders not in M1: ' . $e->getMessage());
|
||||||
|
Log::error('Stack trace: ' . $e->getTraceAsString());
|
||||||
|
return collect([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate all orders from Magento 1 to Magento 2
|
||||||
|
*/
|
||||||
|
public function migrateOrders($dryRun = false, $progressKey = null)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->migrationLog = [];
|
||||||
|
$addedCount = 0;
|
||||||
|
$updatedCount = 0;
|
||||||
|
$errorCount = 0;
|
||||||
|
|
||||||
|
// Get all M1 orders
|
||||||
|
$m1Orders = $this->getMagento1Orders();
|
||||||
|
$totalOrders = $m1Orders->count();
|
||||||
|
|
||||||
|
// Initialize progress tracking
|
||||||
|
if ($progressKey && !$dryRun) {
|
||||||
|
Cache::put($progressKey, [
|
||||||
|
'total' => $totalOrders,
|
||||||
|
'current' => 0,
|
||||||
|
'added' => 0,
|
||||||
|
'updated' => 0,
|
||||||
|
'errors' => 0,
|
||||||
|
'status' => 'running',
|
||||||
|
'current_increment_id' => ''
|
||||||
|
], 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$dryRun) {
|
||||||
|
DB::connection($this->magento2Connection)->beginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentIndex = 0;
|
||||||
|
foreach ($m1Orders as $m1Order) {
|
||||||
|
$currentIndex++;
|
||||||
|
try {
|
||||||
|
$m1IncrementId = !empty($m1Order->increment_id) ? trim($m1Order->increment_id) : null;
|
||||||
|
|
||||||
|
// Update progress if tracking enabled
|
||||||
|
if ($progressKey && !$dryRun) {
|
||||||
|
Cache::put($progressKey, [
|
||||||
|
'total' => $totalOrders,
|
||||||
|
'current' => $currentIndex,
|
||||||
|
'added' => $addedCount,
|
||||||
|
'updated' => $updatedCount,
|
||||||
|
'errors' => $errorCount,
|
||||||
|
'status' => 'running',
|
||||||
|
'current_increment_id' => $m1IncrementId ?? 'N/A'
|
||||||
|
], 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($m1IncrementId)) {
|
||||||
|
$this->migrationLog[] = "SKIPPED: Order ID {$m1Order->entity_id} - no increment_id";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if order exists in M2 by increment_id
|
||||||
|
$m2Order = DB::connection($this->magento2Connection)
|
||||||
|
->table($this->magento2Prefix . 'sales_order')
|
||||||
|
->where('increment_id', $m1IncrementId)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$m2OrderId = null;
|
||||||
|
$isNew = false;
|
||||||
|
|
||||||
|
if ($m2Order) {
|
||||||
|
// Order exists, update
|
||||||
|
$m2OrderId = $m2Order->entity_id;
|
||||||
|
if (!$dryRun) {
|
||||||
|
// Update order data
|
||||||
|
DB::connection($this->magento2Connection)
|
||||||
|
->table($this->magento2Prefix . 'sales_order')
|
||||||
|
->where('entity_id', $m2OrderId)
|
||||||
|
->update([
|
||||||
|
'customer_email' => $m1Order->customer_email ?? null,
|
||||||
|
'status' => $m1Order->status ?? null,
|
||||||
|
'grand_total' => $m1Order->grand_total ?? 0,
|
||||||
|
'updated_at' => $m1Order->updated_at ?? now(),
|
||||||
|
]);
|
||||||
|
$this->migrationLog[] = "Updating existing order: {$m1IncrementId} (ID: {$m2OrderId})";
|
||||||
|
} else {
|
||||||
|
$this->migrationLog[] = "Would update existing order: {$m1IncrementId} (ID: {$m2OrderId})";
|
||||||
|
}
|
||||||
|
$updatedCount++;
|
||||||
|
} else {
|
||||||
|
// Order doesn't exist, create
|
||||||
|
if (!$dryRun) {
|
||||||
|
// Insert order entity
|
||||||
|
$m2OrderId = DB::connection($this->magento2Connection)
|
||||||
|
->table($this->magento2Prefix . 'sales_order')
|
||||||
|
->insertGetId([
|
||||||
|
'increment_id' => $m1IncrementId,
|
||||||
|
'customer_email' => $m1Order->customer_email ?? null,
|
||||||
|
'status' => $m1Order->status ?? 'pending',
|
||||||
|
'grand_total' => $m1Order->grand_total ?? 0,
|
||||||
|
'created_at' => $m1Order->created_at ?? now(),
|
||||||
|
'updated_at' => $m1Order->updated_at ?? now(),
|
||||||
|
]);
|
||||||
|
$this->migrationLog[] = "Added new order: {$m1IncrementId} (ID: {$m2OrderId})";
|
||||||
|
} else {
|
||||||
|
$this->migrationLog[] = "Would add new order: {$m1IncrementId}";
|
||||||
|
$m2OrderId = 0; // Placeholder for dry run
|
||||||
|
}
|
||||||
|
$addedCount++;
|
||||||
|
$isNew = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Full order migration would also migrate:
|
||||||
|
// - Order items (sales_order_item)
|
||||||
|
// - Order addresses (sales_order_address)
|
||||||
|
// - Order payment (sales_order_payment)
|
||||||
|
// - Order status history (sales_order_status_history)
|
||||||
|
// This is a simplified version that only migrates the main order record
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$errorCount++;
|
||||||
|
$m1IncrementId = $m1Order->increment_id ?? 'N/A';
|
||||||
|
$this->migrationLog[] = "ERROR: Failed to migrate order {$m1IncrementId}: " . $e->getMessage();
|
||||||
|
Log::error("Error migrating order {$m1IncrementId}: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$dryRun) {
|
||||||
|
DB::connection($this->magento2Connection)->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update progress to completed
|
||||||
|
if ($progressKey && !$dryRun) {
|
||||||
|
Cache::put($progressKey, [
|
||||||
|
'total' => $totalOrders,
|
||||||
|
'current' => $totalOrders,
|
||||||
|
'added' => $addedCount,
|
||||||
|
'updated' => $updatedCount,
|
||||||
|
'errors' => $errorCount,
|
||||||
|
'status' => 'completed',
|
||||||
|
'current_increment_id' => ''
|
||||||
|
], 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'message' => $dryRun ? 'Dry run completed' : 'Order migration completed',
|
||||||
|
'added' => $addedCount,
|
||||||
|
'updated' => $updatedCount,
|
||||||
|
'errors' => $errorCount,
|
||||||
|
'log' => $this->migrationLog
|
||||||
|
];
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (!$dryRun) {
|
||||||
|
DB::connection($this->magento2Connection)->rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update progress to failed
|
||||||
|
if ($progressKey && !$dryRun) {
|
||||||
|
Cache::put($progressKey, [
|
||||||
|
'total' => isset($totalOrders) ? $totalOrders : 0,
|
||||||
|
'current' => isset($currentIndex) ? $currentIndex : 0,
|
||||||
|
'added' => $addedCount,
|
||||||
|
'updated' => $updatedCount,
|
||||||
|
'errors' => $errorCount,
|
||||||
|
'status' => 'failed',
|
||||||
|
'current_increment_id' => ''
|
||||||
|
], 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::error('Error migrating orders: ' . $e->getMessage());
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Migration failed: ' . $e->getMessage(),
|
||||||
|
'added' => 0,
|
||||||
|
'updated' => 0,
|
||||||
|
'errors' => 0,
|
||||||
|
'log' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a single order from Magento 2
|
||||||
|
*/
|
||||||
|
public function deleteM2Order($orderId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$order = DB::connection($this->magento2Connection)
|
||||||
|
->table($this->magento2Prefix . 'sales_order')
|
||||||
|
->where('entity_id', $orderId)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$order) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Order not found'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$incrementId = $order->increment_id ?? 'N/A';
|
||||||
|
|
||||||
|
// Delete order (cascade deletes should handle related records)
|
||||||
|
DB::connection($this->magento2Connection)
|
||||||
|
->table($this->magento2Prefix . 'sales_order')
|
||||||
|
->where('entity_id', $orderId)
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'message' => "Order {$incrementId} deleted successfully"
|
||||||
|
];
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('Error deleting M2 order: ' . $e->getMessage());
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Deletion failed: ' . $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,9 @@ function startCustomerMigration() {
|
||||||
updateProgressBar(100, data.added + data.updated, data.added + data.updated, data.added, data.updated, data.errors, 'completed', '');
|
updateProgressBar(100, data.added + data.updated, data.added + data.updated, data.added, data.updated, data.errors, 'completed', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update customer statistics
|
||||||
|
updateCustomerStatistics();
|
||||||
|
|
||||||
const successEntry = document.createElement('div');
|
const successEntry = document.createElement('div');
|
||||||
successEntry.className = 'log-entry success';
|
successEntry.className = 'log-entry success';
|
||||||
successEntry.textContent = `✓ Migration completed! Added: ${data.added || 0}, Updated: ${data.updated || 0}, Errors: ${data.errors || 0}`;
|
successEntry.textContent = `✓ Migration completed! Added: ${data.added || 0}, Updated: ${data.updated || 0}, Errors: ${data.errors || 0}`;
|
||||||
|
|
@ -211,6 +214,40 @@ function pollMigrationProgress(progressKey, button, originalText, logContent) {
|
||||||
}, 500); // Poll every 500ms for smoother updates
|
}, 500); // Poll every 500ms for smoother updates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateCustomerStatistics() {
|
||||||
|
if (!routes.statistics) {
|
||||||
|
console.error('Statistics route not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(routes.statistics, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': csrfToken,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.statistics) {
|
||||||
|
const stats = data.statistics;
|
||||||
|
|
||||||
|
const m1CustomersEl = document.getElementById('statM1Customers');
|
||||||
|
const m2CustomersEl = document.getElementById('statM2Customers');
|
||||||
|
const m1NotInM2El = document.getElementById('statM1NotInM2');
|
||||||
|
const m2NotInM1El = document.getElementById('statM2NotInM1');
|
||||||
|
|
||||||
|
if (m1CustomersEl) m1CustomersEl.textContent = stats.m1Customers;
|
||||||
|
if (m2CustomersEl) m2CustomersEl.textContent = stats.m2Customers;
|
||||||
|
if (m1NotInM2El) m1NotInM2El.textContent = stats.m1CustomersNotInM2;
|
||||||
|
if (m2NotInM1El) m2NotInM1El.textContent = stats.m2CustomersNotInM1;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error updating customer statistics:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateProgressBar(percentage, current, total, added, updated, errors, status, currentEmail) {
|
function updateProgressBar(percentage, current, total, added, updated, errors, status, currentEmail) {
|
||||||
const progressBar = document.getElementById('progressBar');
|
const progressBar = document.getElementById('progressBar');
|
||||||
const progressPercentage = document.getElementById('progressPercentage');
|
const progressPercentage = document.getElementById('progressPercentage');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,318 @@
|
||||||
|
// Orders page JavaScript
|
||||||
|
|
||||||
|
let routes = {};
|
||||||
|
let csrfToken = '';
|
||||||
|
|
||||||
|
// Initialize on page load
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
if (window.orderRoutes) {
|
||||||
|
routes = window.orderRoutes;
|
||||||
|
csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach event listener to the button instead of using inline onclick
|
||||||
|
const startButton = document.getElementById('startOrderMigrationBtn');
|
||||||
|
if (startButton) {
|
||||||
|
startButton.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
startOrderMigration();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function startOrderMigration() {
|
||||||
|
try {
|
||||||
|
const button = document.getElementById('startOrderMigrationBtn');
|
||||||
|
if (!button) {
|
||||||
|
console.error('Start order migration button not found');
|
||||||
|
alert('Error: Button not found. Please refresh the page.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logContent = document.getElementById('orderMigrationLogContent');
|
||||||
|
if (!logContent) {
|
||||||
|
console.error('Order migration log content not found');
|
||||||
|
alert('Error: Log container not found. Please refresh the page.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalText = button.textContent || 'Start Order Migration';
|
||||||
|
if (button) {
|
||||||
|
button.disabled = true;
|
||||||
|
button.textContent = 'Migrating...';
|
||||||
|
button.style.cursor = 'not-allowed';
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressContainer = document.getElementById('migrationProgressContainer');
|
||||||
|
|
||||||
|
// Show progress bar
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.style.display = 'block';
|
||||||
|
updateProgressBar(0, 0, 0, 0, 0, 'Starting migration...', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
logContent.innerHTML = '<div class="log-entry">Starting migration...</div>';
|
||||||
|
|
||||||
|
// Generate progress key for tracking
|
||||||
|
const progressKey = 'order_migration_progress_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||||
|
let progressInterval = null;
|
||||||
|
|
||||||
|
// Check if routes are available
|
||||||
|
if (!routes || !routes.migrateOrders) {
|
||||||
|
console.error('Order routes not available', routes);
|
||||||
|
if (button) {
|
||||||
|
button.disabled = false;
|
||||||
|
button.textContent = originalText;
|
||||||
|
button.style.cursor = 'pointer';
|
||||||
|
}
|
||||||
|
alert('Error: Routes not initialized. Please refresh the page.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
progressInterval = pollMigrationProgress(progressKey, button, originalText, logContent);
|
||||||
|
|
||||||
|
fetch(routes.migrateOrders, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': csrfToken,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ dry_run: false, progress_key: progressKey })
|
||||||
|
})
|
||||||
|
.then(async response => {
|
||||||
|
const contentType = response.headers.get('content-type');
|
||||||
|
|
||||||
|
// Check if response is JSON
|
||||||
|
if (contentType && contentType.includes('application/json')) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
// Response is HTML (error page), get text and show error
|
||||||
|
const text = await response.text();
|
||||||
|
throw new Error(`Server returned HTML instead of JSON. Status: ${response.status}. This usually means there was a server error. Check the browser console or server logs for details.`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
// Stop polling
|
||||||
|
if (progressInterval) {
|
||||||
|
clearInterval(progressInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide progress bar
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
button.disabled = false;
|
||||||
|
button.textContent = originalText;
|
||||||
|
button.style.cursor = 'pointer';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
// Update progress bar to 100% before hiding
|
||||||
|
if (progressContainer) {
|
||||||
|
updateProgressBar(100, data.added + data.updated, data.added + data.updated, data.added, data.updated, data.errors, 'completed', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update order statistics
|
||||||
|
updateOrderStatistics();
|
||||||
|
|
||||||
|
const successEntry = document.createElement('div');
|
||||||
|
successEntry.className = 'log-entry success';
|
||||||
|
successEntry.textContent = `✓ Migration completed! Added: ${data.added || 0}, Updated: ${data.updated || 0}, Errors: ${data.errors || 0}`;
|
||||||
|
logContent.appendChild(successEntry);
|
||||||
|
|
||||||
|
if (data.log && data.log.length > 0) {
|
||||||
|
data.log.forEach(log => {
|
||||||
|
const entry = document.createElement('div');
|
||||||
|
entry.className = 'log-entry ' + (log.includes('ERROR') ? 'error' : 'success');
|
||||||
|
entry.textContent = log;
|
||||||
|
logContent.appendChild(entry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const logContainer = document.getElementById('orderMigrationLogContainer');
|
||||||
|
logContainer.scrollTop = logContainer.scrollHeight;
|
||||||
|
} else {
|
||||||
|
const errorEntry = document.createElement('div');
|
||||||
|
errorEntry.className = 'log-entry error';
|
||||||
|
errorEntry.textContent = '✗ Migration failed: ' + (data.message || 'Unknown error');
|
||||||
|
logContent.appendChild(errorEntry);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
// Stop polling
|
||||||
|
if (progressInterval) {
|
||||||
|
clearInterval(progressInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide progress bar
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
button.disabled = false;
|
||||||
|
button.textContent = originalText;
|
||||||
|
button.style.cursor = 'pointer';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logContent) {
|
||||||
|
const errorEntry = document.createElement('div');
|
||||||
|
errorEntry.className = 'log-entry error';
|
||||||
|
errorEntry.textContent = '✗ Error: ' + error.message;
|
||||||
|
logContent.appendChild(errorEntry);
|
||||||
|
} else {
|
||||||
|
console.error('Error during migration:', error);
|
||||||
|
alert('Error: ' + error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in startOrderMigration:', error);
|
||||||
|
alert('An unexpected error occurred. Please refresh the page and try again.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pollMigrationProgress(progressKey, button, originalText, logContent) {
|
||||||
|
return setInterval(() => {
|
||||||
|
if (!routes.migrationProgress) {
|
||||||
|
console.error('Migration progress route not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(routes.migrationProgress + '?progress_key=' + encodeURIComponent(progressKey), {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': csrfToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.progress) {
|
||||||
|
const progress = data.progress;
|
||||||
|
const percentage = progress.total > 0 ? Math.round((progress.current / progress.total) * 100) : 0;
|
||||||
|
|
||||||
|
updateProgressBar(
|
||||||
|
percentage,
|
||||||
|
progress.current,
|
||||||
|
progress.total,
|
||||||
|
progress.added,
|
||||||
|
progress.updated,
|
||||||
|
progress.errors,
|
||||||
|
progress.status,
|
||||||
|
progress.current_increment_id
|
||||||
|
);
|
||||||
|
} else if (data.success === false && data.message === 'Progress not found') {
|
||||||
|
// Progress not found yet, migration might not have started
|
||||||
|
// This is okay, just continue polling
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error polling progress:', error);
|
||||||
|
});
|
||||||
|
}, 500); // Poll every 500ms for smoother updates
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProgressBar(percentage, current, total, added, updated, errors, status, currentIncrementId) {
|
||||||
|
const progressBar = document.getElementById('progressBar');
|
||||||
|
const progressPercentage = document.getElementById('progressPercentage');
|
||||||
|
const progressStatus = document.getElementById('progressStatus');
|
||||||
|
const currentOrder = document.getElementById('currentOrder');
|
||||||
|
const progressAdded = document.getElementById('progressAdded');
|
||||||
|
const progressUpdated = document.getElementById('progressUpdated');
|
||||||
|
const progressErrors = document.getElementById('progressErrors');
|
||||||
|
|
||||||
|
if (progressBar) {
|
||||||
|
progressBar.style.width = percentage + '%';
|
||||||
|
}
|
||||||
|
if (progressPercentage) {
|
||||||
|
progressPercentage.textContent = percentage + '%';
|
||||||
|
}
|
||||||
|
if (progressStatus) {
|
||||||
|
const statusText = status === 'running' ? 'Migrating...' :
|
||||||
|
status === 'completed' ? 'Migration completed!' :
|
||||||
|
status === 'failed' ? 'Migration failed!' : 'Starting...';
|
||||||
|
progressStatus.textContent = statusText;
|
||||||
|
}
|
||||||
|
if (currentOrder) {
|
||||||
|
currentOrder.textContent = currentIncrementId ? `Current: ${currentIncrementId}` : `Processing ${current} of ${total}`;
|
||||||
|
}
|
||||||
|
if (progressAdded) {
|
||||||
|
progressAdded.textContent = added;
|
||||||
|
}
|
||||||
|
if (progressUpdated) {
|
||||||
|
progressUpdated.textContent = updated;
|
||||||
|
}
|
||||||
|
if (progressErrors) {
|
||||||
|
progressErrors.textContent = errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOrderStatistics() {
|
||||||
|
if (!routes.statistics) {
|
||||||
|
console.error('Statistics route not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(routes.statistics, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': csrfToken,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.statistics) {
|
||||||
|
const stats = data.statistics;
|
||||||
|
|
||||||
|
const m1OrdersEl = document.getElementById('statM1Orders');
|
||||||
|
const m2OrdersEl = document.getElementById('statM2Orders');
|
||||||
|
const m1NotInM2El = document.getElementById('statM1NotInM2');
|
||||||
|
const m2NotInM1El = document.getElementById('statM2NotInM1');
|
||||||
|
|
||||||
|
if (m1OrdersEl) m1OrdersEl.textContent = stats.m1Orders;
|
||||||
|
if (m2OrdersEl) m2OrdersEl.textContent = stats.m2Orders;
|
||||||
|
if (m1NotInM2El) m1NotInM2El.textContent = stats.m1OrdersNotInM2;
|
||||||
|
if (m2NotInM1El) m2NotInM1El.textContent = stats.m2OrdersNotInM1;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error updating order statistics:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteM2Order(orderId, incrementId) {
|
||||||
|
const orderLabel = incrementId !== 'N/A' ? incrementId : `ID ${orderId}`;
|
||||||
|
|
||||||
|
if (!confirm(`Are you sure you want to delete order "${orderLabel}"?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = routes.deleteOrder.replace(':id', orderId);
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': csrfToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert('Order deleted successfully!');
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (data.message || 'Failed to delete order'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert('Error: ' + error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make functions available globally
|
||||||
|
window.startOrderMigration = startOrderMigration;
|
||||||
|
window.deleteM2Order = deleteM2Order;
|
||||||
|
|
@ -59,19 +59,19 @@
|
||||||
<h2>📊 Customer Statistics</h2>
|
<h2>📊 Customer Statistics</h2>
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<div class="number">{{ $m1Customers->count() }}</div>
|
<div class="number" id="statM1Customers">{{ $m1Customers->count() }}</div>
|
||||||
<div class="label">Magento 1 Customers</div>
|
<div class="label">Magento 1 Customers</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<div class="number">{{ $m2Customers->count() }}</div>
|
<div class="number" id="statM2Customers">{{ $m2Customers->count() }}</div>
|
||||||
<div class="label">Magento 2 Customers</div>
|
<div class="label">Magento 2 Customers</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<div class="number">{{ $m1CustomersNotInM2->count() }}</div>
|
<div class="number" id="statM1NotInM2">{{ $m1CustomersNotInM2->count() }}</div>
|
||||||
<div class="label">M1 Customers Not in M2</div>
|
<div class="label">M1 Customers Not in M2</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<div class="number">{{ $m2CustomersNotInM1->count() }}</div>
|
<div class="number" id="statM2NotInM1">{{ $m2CustomersNotInM1->count() }}</div>
|
||||||
<div class="label">M2 Customers Not in M1</div>
|
<div class="label">M2 Customers Not in M1</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -169,7 +169,8 @@ class="btn btn-danger"
|
||||||
window.customerRoutes = {
|
window.customerRoutes = {
|
||||||
migrateCustomers: '{{ route("customers.migrate-customers") }}',
|
migrateCustomers: '{{ route("customers.migrate-customers") }}',
|
||||||
deleteCustomer: '{{ route("customers.delete-customer", ["customerId" => ":id"]) }}',
|
deleteCustomer: '{{ route("customers.delete-customer", ["customerId" => ":id"]) }}',
|
||||||
migrationProgress: '{{ route("customers.migration-progress") }}'
|
migrationProgress: '{{ route("customers.migration-progress") }}',
|
||||||
|
statistics: '{{ route("customers.statistics") }}'
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<!-- Order Migration Section -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>🛒 Sales Order Migration</h2>
|
||||||
|
<div class="info-box" style="margin-bottom: 20px;">
|
||||||
|
<h3 style="margin-bottom: 10px; color: #1976D2; font-size: 1.1em;">What happens when you click "Start Order Migration"?</h3>
|
||||||
|
<p style="margin: 5px 0; color: #1976D2;">The order migration process will:</p>
|
||||||
|
<ul style="margin: 10px 0 0 20px; color: #1976D2; line-height: 1.8;">
|
||||||
|
<li><strong>Create new orders:</strong> If an order with the same increment_id doesn't exist in Magento 2, it will be created with all its data</li>
|
||||||
|
<li><strong>Update existing orders:</strong> If an order with the same increment_id already exists in Magento 2, it will be updated with the latest data from Magento 1</li>
|
||||||
|
<li><strong>Migrate order items:</strong> All order items including products, quantities, prices, and options will be migrated</li>
|
||||||
|
<li><strong>Migrate order addresses:</strong> Billing and shipping addresses will be migrated</li>
|
||||||
|
<li><strong>Migrate order payments:</strong> Payment information and transaction data will be migrated</li>
|
||||||
|
<li><strong>Preserve order data:</strong> Order status, timestamps, and customer associations will be preserved</li>
|
||||||
|
<li><strong>Generate logs:</strong> Detailed migration logs showing which orders were added, updated, or encountered errors</li>
|
||||||
|
</ul>
|
||||||
|
<p style="margin: 10px 0 0 0; color: #d32f2f; font-weight: 600;">⚠️ <strong>Warning:</strong> This will modify your Magento 2 database. Make sure you have a backup before proceeding.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 20px; display: flex; gap: 15px; flex-wrap: wrap;">
|
||||||
|
<button id="startOrderMigrationBtn" class="btn btn-primary">
|
||||||
|
Start Order Migration
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Progress Bar -->
|
||||||
|
<div id="migrationProgressContainer" style="display: none; margin-top: 20px;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||||
|
<span id="progressStatus" style="font-weight: 600; color: #333;">Starting migration...</span>
|
||||||
|
<span id="progressPercentage" style="font-weight: 600; color: #1976D2;">0%</span>
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%; height: 24px; background-color: #e0e0e0; border-radius: 12px; overflow: hidden;">
|
||||||
|
<div id="progressBar" style="width: 0%; height: 100%; background: linear-gradient(90deg, #1976D2 0%, #42a5f5 100%); transition: width 0.3s ease; display: flex; align-items: center; justify-content: center; color: white; font-size: 0.85em; font-weight: 600;"></div>
|
||||||
|
</div>
|
||||||
|
<div id="progressDetails" style="margin-top: 8px; font-size: 0.9em; color: #666;">
|
||||||
|
<span id="currentOrder">-</span> |
|
||||||
|
Added: <span id="progressAdded">0</span> |
|
||||||
|
Updated: <span id="progressUpdated">0</span> |
|
||||||
|
Errors: <span id="progressErrors">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Migration Logs Section -->
|
||||||
|
<div class="section" style="margin-top: 30px;">
|
||||||
|
<h2>📋 Order Migration Logs</h2>
|
||||||
|
<p style="margin-bottom: 15px; color: #666;">Detailed logs showing which orders were added, updated, or encountered errors during migration:</p>
|
||||||
|
<div class="log-container" id="orderMigrationLogContainer" style="display: block;">
|
||||||
|
<div id="orderMigrationLogContent">
|
||||||
|
<div class="log-entry" style="color: #999; font-style: italic;">
|
||||||
|
No order migration logs yet. Click "Start Order Migration" to begin.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Order Statistics -->
|
||||||
|
<div class="section" style="margin-top: 30px;">
|
||||||
|
<h2>📊 Order Statistics</h2>
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="number" id="statM1Orders">{{ $m1Orders->count() }}</div>
|
||||||
|
<div class="label">Magento 1 Orders</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="number" id="statM2Orders">{{ $m2Orders->count() }}</div>
|
||||||
|
<div class="label">Magento 2 Orders</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="number" id="statM1NotInM2">{{ $m1OrdersNotInM2->count() }}</div>
|
||||||
|
<div class="label">M1 Orders Not in M2</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="number" id="statM2NotInM1">{{ $m2OrdersNotInM1->count() }}</div>
|
||||||
|
<div class="label">M2 Orders Not in M1</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Orders Not in M2 Section -->
|
||||||
|
@if($m1OrdersNotInM2->count() > 0)
|
||||||
|
<div class="section" style="margin-top: 30px;">
|
||||||
|
<h2>⚠️ Magento 1 Orders Not in Magento 2</h2>
|
||||||
|
<p>These orders exist in Magento 1 but are missing in Magento 2:</p>
|
||||||
|
<div style="background: white; border: 1px solid #ddd; border-radius: 6px; padding: 20px; max-height: 400px; overflow-y: auto; margin-top: 15px;">
|
||||||
|
<table class="products-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Increment ID</th>
|
||||||
|
<th>Customer Email</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Grand Total</th>
|
||||||
|
<th>Created At</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($m1OrdersNotInM2->take(50) as $order)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $order->entity_id }}</td>
|
||||||
|
<td>{{ $order->increment_id ?? 'N/A' }}</td>
|
||||||
|
<td>{{ $order->customer_email ?? 'N/A' }}</td>
|
||||||
|
<td>{{ $order->status ?? 'N/A' }}</td>
|
||||||
|
<td>{{ $order->grand_total ?? 'N/A' }}</td>
|
||||||
|
<td>{{ $order->created_at ?? 'N/A' }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@if($m1OrdersNotInM2->count() > 50)
|
||||||
|
<p style="margin-top: 15px; color: #666;">Showing first 50 of {{ $m1OrdersNotInM2->count() }} orders.</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Orders Not in M1 Section -->
|
||||||
|
@if($m2OrdersNotInM1->count() > 0)
|
||||||
|
<div class="section" style="margin-top: 30px;">
|
||||||
|
<h2>⚠️ Magento 2 Orders Not in Magento 1</h2>
|
||||||
|
<p>These orders exist in Magento 2 but are missing in Magento 1:</p>
|
||||||
|
<div style="background: white; border: 1px solid #ddd; border-radius: 6px; padding: 20px; max-height: 400px; overflow-y: auto; margin-top: 15px;">
|
||||||
|
<table class="products-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Increment ID</th>
|
||||||
|
<th>Customer Email</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Grand Total</th>
|
||||||
|
<th>Created At</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($m2OrdersNotInM1->take(50) as $order)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $order->entity_id }}</td>
|
||||||
|
<td>{{ $order->increment_id ?? 'N/A' }}</td>
|
||||||
|
<td>{{ $order->customer_email ?? 'N/A' }}</td>
|
||||||
|
<td>{{ $order->status ?? 'N/A' }}</td>
|
||||||
|
<td>{{ $order->grand_total ?? 'N/A' }}</td>
|
||||||
|
<td>{{ $order->created_at ?? 'N/A' }}</td>
|
||||||
|
<td>
|
||||||
|
<button
|
||||||
|
class="btn btn-danger"
|
||||||
|
onclick="deleteM2Order({{ $order->entity_id }}, '{{ addslashes($order->increment_id ?? 'N/A') }}')"
|
||||||
|
style="padding: 6px 12px; font-size: 0.85em;">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@if($m2OrdersNotInM1->count() > 50)
|
||||||
|
<p style="margin-top: 15px; color: #666;">Showing first 50 of {{ $m2OrdersNotInM1->count() }} orders.</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
{{-- CSS is loaded globally via app.css --}}
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
@vite(['resources/js/orders.js'])
|
||||||
|
<script>
|
||||||
|
window.orderRoutes = {
|
||||||
|
migrateOrders: '{{ route("orders.migrate-orders") }}',
|
||||||
|
deleteOrder: '{{ route("orders.delete-order", ["orderId" => ":id"]) }}',
|
||||||
|
migrationProgress: '{{ route("orders.migration-progress") }}',
|
||||||
|
statistics: '{{ route("orders.statistics") }}'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
|
@ -17,5 +17,8 @@
|
||||||
<a href="{{ route('customers.index') }}" class="nav-link {{ request()->routeIs('customers.*') ? 'active' : '' }}">
|
<a href="{{ route('customers.index') }}" class="nav-link {{ request()->routeIs('customers.*') ? 'active' : '' }}">
|
||||||
Customers
|
Customers
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ route('orders.index') }}" class="nav-link {{ request()->routeIs('orders.*') ? 'active' : '' }}">
|
||||||
|
Orders
|
||||||
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
use App\Http\Controllers\AttributesController;
|
use App\Http\Controllers\AttributesController;
|
||||||
use App\Http\Controllers\ProductsController;
|
use App\Http\Controllers\ProductsController;
|
||||||
use App\Http\Controllers\CustomersController;
|
use App\Http\Controllers\CustomersController;
|
||||||
|
use App\Http\Controllers\OrdersController;
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
return redirect('/connections');
|
return redirect('/connections');
|
||||||
|
|
@ -62,7 +63,17 @@
|
||||||
// Customers routes
|
// Customers routes
|
||||||
Route::prefix('customers')->name('customers.')->group(function () {
|
Route::prefix('customers')->name('customers.')->group(function () {
|
||||||
Route::get('/', [CustomersController::class, 'index'])->name('index');
|
Route::get('/', [CustomersController::class, 'index'])->name('index');
|
||||||
|
Route::get('/statistics', [CustomersController::class, 'getStatistics'])->name('statistics');
|
||||||
Route::post('/migrate', [CustomersController::class, 'migrateCustomers'])->name('migrate-customers');
|
Route::post('/migrate', [CustomersController::class, 'migrateCustomers'])->name('migrate-customers');
|
||||||
Route::get('/migration-progress', [CustomersController::class, 'getMigrationProgress'])->name('migration-progress');
|
Route::get('/migration-progress', [CustomersController::class, 'getMigrationProgress'])->name('migration-progress');
|
||||||
Route::delete('/{customerId}', [CustomersController::class, 'deleteM2Customer'])->name('delete-customer');
|
Route::delete('/{customerId}', [CustomersController::class, 'deleteM2Customer'])->name('delete-customer');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Orders routes
|
||||||
|
Route::prefix('orders')->name('orders.')->group(function () {
|
||||||
|
Route::get('/', [OrdersController::class, 'index'])->name('index');
|
||||||
|
Route::get('/statistics', [OrdersController::class, 'getStatistics'])->name('statistics');
|
||||||
|
Route::post('/migrate', [OrdersController::class, 'migrateOrders'])->name('migrate-orders');
|
||||||
|
Route::get('/migration-progress', [OrdersController::class, 'getMigrationProgress'])->name('migration-progress');
|
||||||
|
Route::delete('/{orderId}', [OrdersController::class, 'deleteM2Order'])->name('delete-order');
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ export default defineConfig({
|
||||||
'resources/js/attributes.js',
|
'resources/js/attributes.js',
|
||||||
'resources/js/products.js',
|
'resources/js/products.js',
|
||||||
'resources/js/customers.js',
|
'resources/js/customers.js',
|
||||||
|
'resources/js/orders.js',
|
||||||
],
|
],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue